932 lines
45 KiB
Python
932 lines
45 KiB
Python
# -*- coding: utf-8 -*
|
|
from odoo import Command
|
|
from odoo.tests import tagged
|
|
from odoo.tools import file_open
|
|
from .common import TestPeEdiCommon, mocked_l10n_pe_edi_post_invoice_web_service
|
|
from unittest.mock import patch
|
|
|
|
from freezegun import freeze_time
|
|
from lxml import etree
|
|
|
|
|
|
@tagged('post_install_l10n', 'post_install', '-at_install')
|
|
class TestEdiXmls(TestPeEdiCommon):
|
|
|
|
def test_price_amount_rounding(self):
|
|
with freeze_time(self.frozen_today), \
|
|
patch('odoo.addons.l10n_pe_edi.models.account_edi_format.AccountEdiFormat._l10n_pe_edi_post_invoice_web_service',
|
|
new=mocked_l10n_pe_edi_post_invoice_web_service):
|
|
move = self._create_invoice(invoice_line_ids=[(0, 0, {
|
|
'product_id': self.product.id,
|
|
'product_uom_id': self.env.ref('uom.product_uom_kgm').id,
|
|
'price_unit': 83.6, # We will compute 250.8 / 3, which results in 83.60000000000001. It must be rounded.
|
|
'quantity': 3,
|
|
'tax_ids': [(6, 0, self.tax_18.ids)],
|
|
})])
|
|
move.action_post()
|
|
|
|
generated_files = self._process_documents_web_services(move, {'pe_ubl_2_1'})
|
|
self.assertTrue(generated_files)
|
|
zip_edi_str = generated_files[0]
|
|
edi_xml = self.edi_format._l10n_pe_edi_unzip_edi_document(zip_edi_str)
|
|
|
|
etree = self.get_xml_tree_from_string(edi_xml)
|
|
price_amount = etree.find('.//{*}InvoiceLine/{*}Price/{*}PriceAmount')
|
|
self.assertEqual(price_amount.text, '83.6')
|
|
|
|
def test_invoice_simple_case(self):
|
|
with freeze_time(self.frozen_today), \
|
|
patch('odoo.addons.l10n_pe_edi.models.account_edi_format.AccountEdiFormat._l10n_pe_edi_post_invoice_web_service',
|
|
new=mocked_l10n_pe_edi_post_invoice_web_service):
|
|
move = self._create_invoice()
|
|
move.action_post()
|
|
|
|
generated_files = self._process_documents_web_services(move, {'pe_ubl_2_1'})
|
|
self.assertTrue(generated_files)
|
|
zip_edi_str = generated_files[0]
|
|
edi_xml = self.edi_format._l10n_pe_edi_unzip_edi_document(zip_edi_str)
|
|
|
|
current_etree = self.get_xml_tree_from_string(edi_xml)
|
|
expected_etree = self.get_xml_tree_from_string(self.expected_invoice_xml_values)
|
|
self.assertXmlTreeEqual(current_etree, expected_etree)
|
|
|
|
def test_invoice_address_nodes(self):
|
|
""" The address must set the district as cbc:District and the street2 as
|
|
cbc:CitySubdivisionName, and must not set cbc:PostalZone (UBL 2.1 / SUNAT). """
|
|
self.partner_a.write({
|
|
'street': 'Av. test 123',
|
|
'street2': 'Urb. test',
|
|
'zip': '15001',
|
|
'l10n_pe_district': self.env.ref('l10n_pe.district_pe_150101').id,
|
|
})
|
|
with freeze_time(self.frozen_today), \
|
|
patch(
|
|
'odoo.addons.l10n_pe_edi.models.account_edi_format.AccountEdiFormat._l10n_pe_edi_post_invoice_web_service',
|
|
new=mocked_l10n_pe_edi_post_invoice_web_service):
|
|
move = self._create_invoice()
|
|
move.action_post()
|
|
generated_files = self._process_documents_web_services(move, {'pe_ubl_2_1'})
|
|
self.assertTrue(generated_files)
|
|
edi_xml = self.edi_format._l10n_pe_edi_unzip_edi_document(generated_files[0])
|
|
|
|
invoice_etree = self.get_xml_tree_from_string(edi_xml)
|
|
address_node = invoice_etree.find('.//{*}AccountingCustomerParty//{*}PostalAddress')
|
|
expected_address = self.get_xml_tree_from_string(b'''
|
|
<cac:PostalAddress
|
|
xmlns:cbc="urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2"
|
|
xmlns:cac="urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2">
|
|
<cbc:ID>150101</cbc:ID>
|
|
<cbc:StreetName>Av. test 123</cbc:StreetName>
|
|
<cbc:CitySubdivisionName>Urb. test</cbc:CitySubdivisionName>
|
|
<cbc:District>Lima</cbc:District>
|
|
<cac:Country>
|
|
<cbc:IdentificationCode>PE</cbc:IdentificationCode>
|
|
<cbc:Name>Peru</cbc:Name>
|
|
</cac:Country>
|
|
</cac:PostalAddress>
|
|
''')
|
|
self.assertXmlTreeEqual(address_node, expected_address)
|
|
|
|
def test_refund_simple_case(self):
|
|
with freeze_time(self.frozen_today), \
|
|
patch('odoo.addons.l10n_pe_edi.models.account_edi_format.AccountEdiFormat._l10n_pe_edi_post_invoice_web_service',
|
|
new=mocked_l10n_pe_edi_post_invoice_web_service):
|
|
move = self._create_refund()
|
|
(move.reversed_entry_id + move).action_post()
|
|
|
|
generated_files = self._process_documents_web_services(move, {'pe_ubl_2_1'})
|
|
self.assertTrue(generated_files)
|
|
zip_edi_str = generated_files[0]
|
|
edi_xml = self.edi_format._l10n_pe_edi_unzip_edi_document(zip_edi_str)
|
|
|
|
current_etree = self.get_xml_tree_from_string(edi_xml)
|
|
expected_etree = self.get_xml_tree_from_string(self.expected_refund_xml_values)
|
|
self.assertXmlTreeEqual(current_etree, expected_etree)
|
|
|
|
def test_debit_note_simple_case(self):
|
|
with freeze_time(self.frozen_today), \
|
|
patch('odoo.addons.l10n_pe_edi.models.account_edi_format.AccountEdiFormat._l10n_pe_edi_post_invoice_web_service',
|
|
new=mocked_l10n_pe_edi_post_invoice_web_service):
|
|
move = self._create_debit_note()
|
|
(move.debit_origin_id + move).action_post()
|
|
|
|
generated_files = self._process_documents_web_services(move, {'pe_ubl_2_1'})
|
|
self.assertTrue(generated_files)
|
|
zip_edi_str = generated_files[0]
|
|
edi_xml = self.edi_format._l10n_pe_edi_unzip_edi_document(zip_edi_str)
|
|
|
|
current_etree = self.get_xml_tree_from_string(edi_xml)
|
|
expected_etree = self.get_xml_tree_from_string(self.expected_debit_note_xml_values)
|
|
self.assertXmlTreeEqual(current_etree, expected_etree)
|
|
|
|
def test_invoice_payment_term_detraction_case(self):
|
|
""" Invoice in USD with detractions and multiple payment term lines"""
|
|
self.product.l10n_pe_withhold_percentage = 10
|
|
self.product.l10n_pe_withhold_code = '001'
|
|
with freeze_time(self.frozen_today), \
|
|
patch('odoo.addons.l10n_pe_edi.models.account_edi_format.AccountEdiFormat._l10n_pe_edi_post_invoice_web_service',
|
|
new=mocked_l10n_pe_edi_post_invoice_web_service):
|
|
update_vals_dict = {"l10n_pe_edi_operation_type": "1001",
|
|
"invoice_payment_term_id": self.env.ref("account.account_payment_term_advance_60days").id}
|
|
invoice = self._create_invoice(**update_vals_dict).with_context(edi_test_mode=True)
|
|
invoice.action_post()
|
|
|
|
generated_files = self._process_documents_web_services(invoice, {'pe_ubl_2_1'})
|
|
self.assertTrue(generated_files)
|
|
zip_edi_str = generated_files[0]
|
|
edi_xml = self.edi_format._l10n_pe_edi_unzip_edi_document(zip_edi_str)
|
|
current_etree = self.get_xml_tree_from_string(edi_xml)
|
|
|
|
with file_open('l10n_pe_edi/tests/test_files/invoice_detraction_payment_terms.xml', 'rb') as expected_file:
|
|
expected_etree = self.get_xml_tree_from_string(expected_file.read())
|
|
self.assertXmlTreeEqual(current_etree, expected_etree)
|
|
|
|
def test_invoice_detraction_with_decimal(self):
|
|
""" Invoice in PEN with detraction containing decimal digits"""
|
|
self.product.l10n_pe_withhold_percentage = 10
|
|
self.product.l10n_pe_withhold_code = '019'
|
|
|
|
with freeze_time(self.frozen_today), \
|
|
patch('odoo.addons.l10n_pe_edi.models.account_edi_format.AccountEdiFormat._l10n_pe_edi_post_invoice_web_service',
|
|
new=mocked_l10n_pe_edi_post_invoice_web_service):
|
|
vals = {
|
|
'name': 'F FFI-%s1' % self.time_name,
|
|
'move_type': 'out_invoice',
|
|
'partner_id': self.partner_a.id,
|
|
'invoice_date': '2017-01-01',
|
|
'date': '2017-01-01',
|
|
'invoice_payment_term_id': self.env.ref("account.account_payment_term_end_following_month").id,
|
|
'l10n_latam_document_type_id': self.env.ref('l10n_pe.document_type01').id,
|
|
'l10n_pe_edi_operation_type': '1001',
|
|
'invoice_line_ids': [Command.create({
|
|
'product_id': self.product.id,
|
|
'price_unit': 990.0,
|
|
'quantity': 1,
|
|
'tax_ids': [Command.set(self.tax_18.ids)],
|
|
})],
|
|
}
|
|
invoice = self.env['account.move'].create(vals).with_context(edi_test_mode=True)
|
|
invoice.action_post()
|
|
|
|
generated_files = self._process_documents_web_services(invoice, {'pe_ubl_2_1'})
|
|
self.assertTrue(generated_files)
|
|
zip_edi_str = generated_files[0]
|
|
edi_xml = self.edi_format._l10n_pe_edi_unzip_edi_document(zip_edi_str)
|
|
current_etree = self.get_xml_tree_from_string(edi_xml)
|
|
|
|
with file_open('l10n_pe_edi/tests/test_files/invoice_detraction_with_decimal.xml', 'rb') as expected_invoice_file:
|
|
expected_etree = self.get_xml_tree_from_string(expected_invoice_file.read())
|
|
self.assertXmlTreeEqual(current_etree, expected_etree)
|
|
|
|
def test_invoice_detraction_usd_company_currency(self):
|
|
""" Invoice in USD with detraction when company currency is also USD (not PEN).
|
|
The detraction cbc:Amount must still be in PEN, converted via the exchange rate. """
|
|
self.product.l10n_pe_withhold_percentage = 10
|
|
self.product.l10n_pe_withhold_code = '019'
|
|
|
|
with freeze_time(self.frozen_today), \
|
|
patch('odoo.addons.l10n_pe_edi.models.account_edi_format.AccountEdiFormat._l10n_pe_edi_post_invoice_web_service',
|
|
new=mocked_l10n_pe_edi_post_invoice_web_service):
|
|
self.company_data['company'].currency_id = self.other_currency
|
|
vals = {
|
|
'name': 'F FFI-%s1' % self.time_name,
|
|
'move_type': 'out_invoice',
|
|
'partner_id': self.partner_a.id,
|
|
'invoice_date': '2017-01-01',
|
|
'date': '2017-01-01',
|
|
'currency_id': self.other_currency.id,
|
|
'invoice_payment_term_id': self.env.ref("account.account_payment_term_end_following_month").id,
|
|
'l10n_latam_document_type_id': self.env.ref('l10n_pe.document_type01').id,
|
|
'l10n_pe_edi_operation_type': '1001',
|
|
'invoice_line_ids': [Command.create({
|
|
'product_id': self.product.id,
|
|
'price_unit': 990.0,
|
|
'quantity': 1,
|
|
'tax_ids': [Command.set(self.tax_18.ids)],
|
|
})],
|
|
}
|
|
invoice = self.env['account.move'].create(vals).with_context(edi_test_mode=True)
|
|
invoice.action_post()
|
|
|
|
generated_files = self._process_documents_web_services(invoice, {'pe_ubl_2_1'})
|
|
self.assertTrue(generated_files)
|
|
zip_edi_str = generated_files[0]
|
|
edi_xml = self.edi_format._l10n_pe_edi_unzip_edi_document(zip_edi_str)
|
|
current_etree = self.get_xml_tree_from_string(edi_xml)
|
|
|
|
with file_open('l10n_pe_edi/tests/test_files/invoice_detraction_with_decimal_foreign_currency.xml', 'rb') as expected_invoice_file:
|
|
expected_etree = self.get_xml_tree_from_string(expected_invoice_file.read())
|
|
self.assertXmlTreeEqual(current_etree, expected_etree)
|
|
|
|
def test_invoice_detraction_with_decimal_foreign_currency(self):
|
|
""" Invoice in USD with detraction containing decimal digits"""
|
|
self.product.l10n_pe_withhold_percentage = 10
|
|
self.product.l10n_pe_withhold_code = '019'
|
|
|
|
with freeze_time(self.frozen_today), \
|
|
patch('odoo.addons.l10n_pe_edi.models.account_edi_format.AccountEdiFormat._l10n_pe_edi_post_invoice_web_service',
|
|
new=mocked_l10n_pe_edi_post_invoice_web_service):
|
|
vals = {
|
|
'name': 'F FFI-%s1' % self.time_name,
|
|
'move_type': 'out_invoice',
|
|
'partner_id': self.partner_a.id,
|
|
'invoice_date': '2017-01-01',
|
|
'date': '2017-01-01',
|
|
'currency_id': self.other_currency.id,
|
|
'invoice_payment_term_id': self.env.ref("account.account_payment_term_end_following_month").id,
|
|
'l10n_latam_document_type_id': self.env.ref('l10n_pe.document_type01').id,
|
|
'l10n_pe_edi_operation_type': '1001',
|
|
'invoice_line_ids': [Command.create({
|
|
'product_id': self.product.id,
|
|
'price_unit': 990.0,
|
|
'quantity': 1,
|
|
'tax_ids': [Command.set(self.tax_18.ids)],
|
|
})],
|
|
}
|
|
invoice = self.env['account.move'].create(vals).with_context(edi_test_mode=True)
|
|
invoice.action_post()
|
|
|
|
generated_files = self._process_documents_web_services(invoice, {'pe_ubl_2_1'})
|
|
self.assertTrue(generated_files)
|
|
zip_edi_str = generated_files[0]
|
|
edi_xml = self.edi_format._l10n_pe_edi_unzip_edi_document(zip_edi_str)
|
|
current_etree = self.get_xml_tree_from_string(edi_xml)
|
|
|
|
with file_open('l10n_pe_edi/tests/test_files/invoice_detraction_with_decimal_foreign_currency.xml', 'rb') as expected_invoice_file:
|
|
expected_etree = self.get_xml_tree_from_string(expected_invoice_file.read())
|
|
self.assertXmlTreeEqual(current_etree, expected_etree)
|
|
|
|
def test_low_unit_price_with_higher_decimal_precision(self):
|
|
""" Invoice with a decimal precition of 4 digits for the product price
|
|
and a non-zero unit price that is rounded to 0.00 in the decimal
|
|
precision of the currency.
|
|
"""
|
|
self.env.ref('product.decimal_price').digits = 4
|
|
self.currency.rounding = 0.01
|
|
with freeze_time(self.frozen_today), \
|
|
patch('odoo.addons.l10n_pe_edi.models.account_edi_format.AccountEdiFormat._l10n_pe_edi_post_invoice_web_service',
|
|
new=mocked_l10n_pe_edi_post_invoice_web_service):
|
|
invoice_line_vals = {
|
|
'invoice_line_ids': [
|
|
Command.create({
|
|
'product_id': self.product.id,
|
|
'product_uom_id': self.env.ref('uom.product_uom_kgm').id,
|
|
'price_unit': 0.0045,
|
|
'quantity': 100,
|
|
'tax_ids': [Command.set(self.tax_18.ids)],
|
|
})
|
|
],
|
|
}
|
|
move = self._create_invoice(**invoice_line_vals)
|
|
move.action_post()
|
|
|
|
generated_files = self._process_documents_web_services(move, {'pe_ubl_2_1'})
|
|
self.assertTrue(generated_files)
|
|
zip_edi_str = generated_files[0]
|
|
edi_xml = self.edi_format._l10n_pe_edi_unzip_edi_document(zip_edi_str)
|
|
|
|
current_etree = self.get_xml_tree_from_string(edi_xml)
|
|
|
|
with file_open('l10n_pe_edi/tests/test_files/invoice_low_unit_price.xml', 'rb') as expected_file:
|
|
expected_etree = self.get_xml_tree_from_string(expected_file.read())
|
|
|
|
self.assertXmlTreeEqual(current_etree, expected_etree)
|
|
|
|
def test_invoice_free(self):
|
|
""" Test the UBL generated for an invoice that uses the '18% Free' tax
|
|
(feature introduced in enterprise#56767)
|
|
"""
|
|
tax_18_free = self.env['account.chart.template'].ref('tax_free_group')
|
|
with freeze_time(self.frozen_today), \
|
|
patch('odoo.addons.l10n_pe_edi.models.account_edi_format.AccountEdiFormat._l10n_pe_edi_post_invoice_web_service',
|
|
new=mocked_l10n_pe_edi_post_invoice_web_service):
|
|
move = self._create_invoice(
|
|
l10n_pe_edi_legend='1002',
|
|
invoice_line_ids=[(0, 0, {
|
|
'product_id': self.product.id,
|
|
'product_uom_id': self.env.ref('uom.product_uom_kgm').id,
|
|
'price_unit': 2000.0,
|
|
'quantity': 5,
|
|
'discount': 20.0,
|
|
'tax_ids': [(6, 0, tax_18_free.ids)],
|
|
})],
|
|
)
|
|
move.action_post()
|
|
|
|
generated_files = self._process_documents_web_services(move, {'pe_ubl_2_1'})
|
|
self.assertTrue(generated_files)
|
|
zip_edi_str = generated_files[0]
|
|
edi_xml = self.edi_format._l10n_pe_edi_unzip_edi_document(zip_edi_str)
|
|
|
|
current_etree = self.get_xml_tree_from_string(edi_xml)
|
|
|
|
with file_open('l10n_pe_edi/tests/test_files/invoice_free.xml', 'rb') as expected_file:
|
|
expected_etree = self.get_xml_tree_from_string(expected_file.read())
|
|
|
|
self.assertXmlTreeEqual(current_etree, expected_etree)
|
|
|
|
def test_invoice_isc(self):
|
|
""" Test the UBL generated for an invoice that uses both the ISC and the IGV taxes.
|
|
(feature introduced in enterprise#35280)
|
|
"""
|
|
tax_isc = self.env['account.tax'].create({
|
|
'name': 'tax_ics_20',
|
|
'sequence': -1, # So it precedes the IGV 18% tax
|
|
'amount_type': 'percent',
|
|
'amount': 20,
|
|
'include_base_amount': True,
|
|
'l10n_pe_edi_isc_type': '01',
|
|
'l10n_pe_edi_tax_code': '2000',
|
|
'l10n_pe_edi_unece_category': 'S',
|
|
'type_tax_use': 'sale',
|
|
'tax_group_id': self.env['account.chart.template'].ref('tax_group_isc').id,
|
|
})
|
|
with freeze_time(self.frozen_today), \
|
|
patch('odoo.addons.l10n_pe_edi.models.account_edi_format.AccountEdiFormat._l10n_pe_edi_post_invoice_web_service',
|
|
new=mocked_l10n_pe_edi_post_invoice_web_service):
|
|
move = self._create_invoice(
|
|
invoice_line_ids=[
|
|
(0, 0, {
|
|
'product_id': self.product.id,
|
|
'product_uom_id': self.env.ref('uom.product_uom_kgm').id,
|
|
'price_unit': 2000.0,
|
|
'quantity': 5,
|
|
'discount': 20.0,
|
|
'tax_ids': [(6, 0, [tax_isc.id, self.tax_18.id])],
|
|
}),
|
|
(0, 0, {
|
|
'product_id': self.product.id,
|
|
'product_uom_id': self.env.ref('uom.product_uom_kgm').id,
|
|
'price_unit': 2000.0,
|
|
'quantity': 5,
|
|
'discount': 20.0,
|
|
'tax_ids': [(6, 0, [self.tax_18.id])],
|
|
})
|
|
],
|
|
)
|
|
move.action_post()
|
|
|
|
generated_files = self._process_documents_web_services(move, {'pe_ubl_2_1'})
|
|
self.assertTrue(generated_files)
|
|
zip_edi_str = generated_files[0]
|
|
edi_xml = self.edi_format._l10n_pe_edi_unzip_edi_document(zip_edi_str)
|
|
|
|
current_etree = self.get_xml_tree_from_string(edi_xml)
|
|
|
|
with file_open('l10n_pe_edi/tests/test_files/invoice_isc.xml', 'rb') as expected_file:
|
|
expected_etree = self.get_xml_tree_from_string(expected_file.read())
|
|
self.assertXmlTreeEqual(current_etree, expected_etree)
|
|
|
|
def test_invoice_global_discount(self):
|
|
""" Invoice in USD with a global and line nevel discount."""
|
|
with freeze_time(self.frozen_today), \
|
|
patch('odoo.addons.l10n_pe_edi.models.account_edi_format.AccountEdiFormat._l10n_pe_edi_post_invoice_web_service',
|
|
new=mocked_l10n_pe_edi_post_invoice_web_service):
|
|
update_vals_dict = {
|
|
'invoice_line_ids': [
|
|
Command.create({
|
|
'product_id': self.product.id,
|
|
'product_uom_id': self.env.ref('uom.product_uom_kgm').id,
|
|
'price_unit': 2000.0,
|
|
'quantity': 5,
|
|
'discount': 20.0,
|
|
'tax_ids': [(6, 0, self.tax_18.ids)],
|
|
}),
|
|
Command.create({
|
|
"name": "Discount",
|
|
"price_unit": -200.0,
|
|
"tax_ids": [Command.set(self.tax_18.ids)]
|
|
}),
|
|
],
|
|
}
|
|
invoice = self._create_invoice(**update_vals_dict)
|
|
invoice.action_post()
|
|
|
|
generated_files = self._process_documents_web_services(invoice, {'pe_ubl_2_1'})
|
|
self.assertTrue(generated_files)
|
|
zip_edi_str = generated_files[0]
|
|
edi_xml = self.edi_format._l10n_pe_edi_unzip_edi_document(zip_edi_str)
|
|
current_etree = self.get_xml_tree_from_string(edi_xml)
|
|
|
|
with file_open('l10n_pe_edi/tests/test_files/invoice_global_discount.xml', 'rb') as expected_file:
|
|
expected_etree = self.get_xml_tree_from_string(expected_file.read())
|
|
self.assertXmlTreeEqual(current_etree, expected_etree)
|
|
|
|
def test_invoice_down_payment(self):
|
|
""" Invoice with a downpayment on a sale order. Note the downpayment invoice is not different
|
|
than any other invoice which is why we only look at the final invoice XML to make sure all the
|
|
right data is there. """
|
|
|
|
if 'sale' not in self.env["ir.module.module"]._installed():
|
|
self.skipTest("Sale module is not installed")
|
|
|
|
self.env.user.group_ids |= self.env.ref('sales_team.group_sale_manager')
|
|
|
|
with freeze_time(self.frozen_today):
|
|
sale_order = self.env['sale.order'].create({
|
|
'partner_id': self.partner_a.id,
|
|
'order_line': [
|
|
Command.create({
|
|
'product_id': self.product.id,
|
|
'product_uom_id': self.env.ref('uom.product_uom_kgm').id,
|
|
'price_unit': 2000.0,
|
|
'product_uom_qty': 5,
|
|
'tax_ids': [(6, 0, self.tax_18.ids)],
|
|
})
|
|
]
|
|
})
|
|
sale_order.action_confirm()
|
|
|
|
context = {
|
|
'active_model': 'sale.order',
|
|
'active_ids': [sale_order.id],
|
|
'active_id': sale_order.id,
|
|
'default_journal_id': self.company_data['default_journal_sale'].id,
|
|
}
|
|
downpayment_1 = self.env['sale.advance.payment.inv'].with_context(context).create({
|
|
'advance_payment_method': 'fixed',
|
|
'fixed_amount': 115,
|
|
})._create_invoices(sale_order)
|
|
|
|
downpayment_2 = self.env['sale.advance.payment.inv'].with_context(context).create({
|
|
'advance_payment_method': 'fixed',
|
|
'fixed_amount': 115,
|
|
})._create_invoices(sale_order)
|
|
|
|
final = self.env['sale.advance.payment.inv'].with_context(context).create({})._create_invoices(sale_order)
|
|
|
|
with patch('odoo.addons.l10n_pe_edi.models.account_edi_format.AccountEdiFormat._l10n_pe_edi_post_invoice_web_service',
|
|
new=mocked_l10n_pe_edi_post_invoice_web_service):
|
|
downpayment_1.action_post()
|
|
downpayment_2.action_post()
|
|
final.action_post()
|
|
|
|
generated_files = self._process_documents_web_services(final, {'pe_ubl_2_1'})
|
|
self.assertTrue(generated_files)
|
|
|
|
zip_edi_str = generated_files[0]
|
|
edi_xml = self.edi_format._l10n_pe_edi_unzip_edi_document(zip_edi_str)
|
|
current_etree = self.get_xml_tree_from_string(edi_xml)
|
|
with file_open('l10n_pe_edi/tests/test_files/invoice_final_downpayment.xml', 'rb') as expected_file:
|
|
expected_etree = self.get_xml_tree_from_string(expected_file.read())
|
|
self.assertXmlTreeEqual(current_etree, expected_etree)
|
|
|
|
def test_invoice_down_payment_foreign_currency(self):
|
|
""" Invoice with a downpayment on a sale order. Note the downpayment invoice is not different
|
|
than any other invoice which is why we only look at the final invoice XML to make sure all the
|
|
right data is there. """
|
|
|
|
if self.env["ir.module.module"]._get('sale').state != 'installed':
|
|
self.skipTest("Sale module is not installed")
|
|
self.env.user.group_ids += self.env.ref('sales_team.group_sale_salesman')
|
|
|
|
pricelist = self.env['product.pricelist'].create({
|
|
'name': 'Test Pricelist',
|
|
'currency_id': self.other_currency.id,
|
|
})
|
|
|
|
with freeze_time(self.frozen_today):
|
|
sale_order = self.env['sale.order'].create({
|
|
'partner_id': self.partner_a.id,
|
|
'pricelist_id': pricelist.id,
|
|
'order_line': [
|
|
Command.create({
|
|
'product_id': self.product.id,
|
|
'product_uom_id': self.env.ref('uom.product_uom_kgm').id,
|
|
'price_unit': 2000.0,
|
|
'product_uom_qty': 5,
|
|
'tax_ids': [(6, 0, self.tax_18.ids)],
|
|
}),
|
|
],
|
|
})
|
|
sale_order.action_confirm()
|
|
|
|
context = {
|
|
'active_model': 'sale.order',
|
|
'active_ids': [sale_order.id],
|
|
'active_id': sale_order.id,
|
|
'default_journal_id': self.company_data['default_journal_sale'].id,
|
|
}
|
|
downpayment = self.env['sale.advance.payment.inv'].with_context(context).create({
|
|
'advance_payment_method': 'fixed',
|
|
'fixed_amount': 115,
|
|
})._create_invoices(sale_order)
|
|
|
|
final = self.env['sale.advance.payment.inv'].with_context(context).create({})._create_invoices(sale_order)
|
|
|
|
with patch('odoo.addons.l10n_pe_edi.models.account_edi_format.AccountEdiFormat._l10n_pe_edi_post_invoice_web_service',
|
|
new=mocked_l10n_pe_edi_post_invoice_web_service):
|
|
downpayment.action_post()
|
|
final.action_post()
|
|
|
|
generated_files = self._process_documents_web_services(final, {'pe_ubl_2_1'})
|
|
self.assertTrue(generated_files)
|
|
|
|
zip_edi_str = generated_files[0]
|
|
edi_xml = self.edi_format._l10n_pe_edi_unzip_edi_document(zip_edi_str)
|
|
current_etree = self.get_xml_tree_from_string(edi_xml)
|
|
with file_open('l10n_pe_edi/tests/test_files/invoice_final_downpayment_foreign_currency.xml', 'rb') as expected_file:
|
|
expected_etree = self.get_xml_tree_from_string(expected_file.read())
|
|
self.assertXmlTreeEqual(current_etree, expected_etree)
|
|
|
|
def test_invoice_down_payment_with_withholding_tax(self):
|
|
"""
|
|
Test the UBL XML of a final invoice generated from a sale order with two fixed-amount
|
|
down payments and a withholding tax applied on the order line.
|
|
|
|
Withholding tax totals must be excluded from the XML.
|
|
"""
|
|
|
|
if 'sale' not in self.env["ir.module.module"]._installed():
|
|
self.skipTest("Sale module is not installed")
|
|
self.env.user.group_ids |= self.env.ref('sales_team.group_sale_manager')
|
|
|
|
self.tax_18.include_base_amount = True
|
|
tax_withholding = self.env['account.tax'].create({
|
|
'name': 'tax_withholding',
|
|
'amount_type': 'percent',
|
|
'amount': -3.0,
|
|
'type_tax_use': 'sale',
|
|
'tax_group_id': self.env['account.chart.template'].ref('tax_group_igv_withholding').id,
|
|
})
|
|
|
|
with freeze_time(self.frozen_today):
|
|
sale_order = self.env['sale.order'].create({ # noqa: OLS03001
|
|
'partner_id': self.partner_a.id,
|
|
'order_line': [
|
|
Command.create({
|
|
'product_id': self.product.id,
|
|
'product_uom_id': self.env.ref('uom.product_uom_kgm').id,
|
|
'price_unit': 1000.0,
|
|
'product_uom_qty': 5,
|
|
'tax_ids': [Command.set([self.tax_18.id, tax_withholding.id])],
|
|
})
|
|
]
|
|
})
|
|
sale_order.action_confirm()
|
|
|
|
context = {
|
|
'active_model': 'sale.order',
|
|
'active_ids': [sale_order.id],
|
|
'active_id': sale_order.id,
|
|
'default_journal_id': self.company_data['default_journal_sale'].id,
|
|
}
|
|
downpayment_1 = self.env['sale.advance.payment.inv'].with_context(context).create({ # noqa: OLS03001
|
|
'advance_payment_method': 'fixed',
|
|
'fixed_amount': 115,
|
|
})._create_invoices(sale_order)
|
|
downpayment_1.action_post()
|
|
|
|
downpayment_to_reverse = self.env['sale.advance.payment.inv'].with_context(context).create({ # noqa: OLS03001
|
|
'advance_payment_method': 'fixed',
|
|
'fixed_amount': 115,
|
|
})._create_invoices(sale_order)
|
|
downpayment_to_reverse.action_post()
|
|
|
|
reversal_wizard = self.env['account.move.reversal'].with_context(
|
|
active_model="account.move",
|
|
active_ids=downpayment_to_reverse.ids
|
|
).create({
|
|
'reason': 'Test reason',
|
|
'journal_id': downpayment_to_reverse.journal_id.id,
|
|
'l10n_pe_edi_refund_reason': '01',
|
|
})
|
|
action = reversal_wizard.modify_moves()
|
|
downpayment_2 = self.env['account.move'].browse(action['res_id'])
|
|
downpayment_2.action_post()
|
|
|
|
final = self.env['sale.advance.payment.inv'].with_context(context).create({})._create_invoices(sale_order) # noqa: OLS03001
|
|
final.action_post()
|
|
|
|
with patch('odoo.addons.l10n_pe_edi.models.account_edi_format.AccountEdiFormat._l10n_pe_edi_post_invoice_web_service',
|
|
new=mocked_l10n_pe_edi_post_invoice_web_service):
|
|
generated_files = self._process_documents_web_services(final, {'pe_ubl_2_1'})
|
|
self.assertTrue(generated_files)
|
|
|
|
zip_edi_str = generated_files[0]
|
|
edi_xml = self.edi_format._l10n_pe_edi_unzip_edi_document(zip_edi_str)
|
|
current_etree = self.get_xml_tree_from_string(edi_xml)
|
|
with file_open('l10n_pe_edi/tests/test_files/invoice_final_downpayment_withhold.xml', 'rb') as expected_file:
|
|
expected_etree = self.get_xml_tree_from_string(expected_file.read())
|
|
self.assertXmlTreeEqual(current_etree, expected_etree)
|
|
|
|
def test_invoice_withholding(self):
|
|
""" Invoice with withholding tax associated. There should be only one allowance node
|
|
even though there are two lines with the withholding tax. """
|
|
self.tax_18.include_base_amount = True
|
|
tax_withholding = self.env['account.tax'].create({
|
|
'name': 'tax_withholding',
|
|
'amount_type': 'percent',
|
|
'amount': -3.0,
|
|
'type_tax_use': 'sale',
|
|
'tax_group_id': self.env['account.chart.template'].ref('tax_group_igv_withholding').id,
|
|
})
|
|
|
|
with freeze_time(self.frozen_today), \
|
|
patch('odoo.addons.l10n_pe_edi.models.account_edi_format.AccountEdiFormat._l10n_pe_edi_post_invoice_web_service',
|
|
new=mocked_l10n_pe_edi_post_invoice_web_service):
|
|
update_vals_dict = {
|
|
'invoice_line_ids': [
|
|
Command.create({
|
|
'product_id': self.product.id,
|
|
'product_uom_id': self.env.ref('uom.product_uom_kgm').id,
|
|
'price_unit': 0.481936,
|
|
'quantity': 300,
|
|
'tax_ids': [(6, 0, [tax_withholding.id, self.tax_18.id])],
|
|
}),
|
|
Command.create({
|
|
'product_id': self.product.id,
|
|
'product_uom_id': self.env.ref('uom.product_uom_kgm').id,
|
|
'price_unit': 0.747376,
|
|
'quantity': 300,
|
|
'tax_ids': [(6, 0, [tax_withholding.id, self.tax_18.id])],
|
|
}),
|
|
],
|
|
}
|
|
invoice = self._create_invoice(**update_vals_dict)
|
|
invoice.action_post()
|
|
|
|
generated_files = self._process_documents_web_services(invoice, {'pe_ubl_2_1'})
|
|
self.assertTrue(generated_files)
|
|
zip_edi_str = generated_files[0]
|
|
edi_xml = self.edi_format._l10n_pe_edi_unzip_edi_document(zip_edi_str)
|
|
current_etree = self.get_xml_tree_from_string(edi_xml)
|
|
|
|
with file_open('l10n_pe_edi/tests/test_files/invoice_withholding.xml', 'rb') as expected_file:
|
|
expected_etree = self.get_xml_tree_from_string(expected_file.read())
|
|
self.assertXmlTreeEqual(current_etree, expected_etree)
|
|
|
|
def test_invoice_icbper_fixed_tax(self):
|
|
""" Test that an invoice with an ICBPER fixed-amount tax generates EDI XML without errors."""
|
|
tax_icbper = self.env['account.tax'].create({
|
|
'name': 'ICBPER',
|
|
'amount_type': 'fixed',
|
|
'amount': 0.5,
|
|
'l10n_pe_edi_tax_code': '7152',
|
|
'l10n_pe_edi_unece_category': 'S',
|
|
'type_tax_use': 'sale',
|
|
'tax_group_id': self.env['account.chart.template'].ref('tax_group_icbper').id,
|
|
})
|
|
with freeze_time(self.frozen_today), \
|
|
patch('odoo.addons.l10n_pe_edi.models.account_edi_format.AccountEdiFormat._l10n_pe_edi_post_invoice_web_service',
|
|
new=mocked_l10n_pe_edi_post_invoice_web_service):
|
|
move = self._create_invoice(
|
|
invoice_line_ids=[Command.create({
|
|
'product_id': self.product.id,
|
|
'product_uom_id': self.env.ref('uom.product_uom_kgm').id,
|
|
'price_unit': 2000.0,
|
|
'quantity': 5,
|
|
'tax_ids': [Command.set([tax_icbper.id])],
|
|
})],
|
|
)
|
|
move.action_post()
|
|
|
|
generated_files = self._process_documents_web_services(move, {'pe_ubl_2_1'})
|
|
self.assertTrue(generated_files)
|
|
|
|
def test_invoice_foreign_customer(self):
|
|
"""Invoice for a foreign customer"""
|
|
co_identification_type = self.env['l10n_latam.identification.type'].sudo().create({
|
|
"name": "Cédula de ciudadanía (CO)",
|
|
"country_id": self.env.ref('base.co').id,
|
|
})
|
|
self.partner_a.write({
|
|
"l10n_latam_identification_type_id": co_identification_type.id,
|
|
"country_id": self.env.ref('base.co').id,
|
|
})
|
|
with freeze_time(self.frozen_today), \
|
|
patch('odoo.addons.l10n_pe_edi.models.account_edi_format.AccountEdiFormat._l10n_pe_edi_post_invoice_web_service',
|
|
new=mocked_l10n_pe_edi_post_invoice_web_service):
|
|
vals = {
|
|
'name': 'F FFI-%s1' % self.time_name,
|
|
'move_type': 'out_invoice',
|
|
'partner_id': self.partner_a.id,
|
|
'invoice_date': '2017-01-01',
|
|
'date': '2017-01-01',
|
|
'currency_id': self.other_currency.id,
|
|
'invoice_payment_term_id': self.env.ref("account.account_payment_term_end_following_month").id,
|
|
'l10n_latam_document_type_id': self.env.ref('l10n_pe.document_type02').id,
|
|
'l10n_pe_edi_operation_type': '0200',
|
|
'invoice_line_ids': [Command.create({
|
|
'product_id': self.product.id,
|
|
'price_unit': 990.0,
|
|
'quantity': 1,
|
|
'tax_ids': [Command.set(self.tax_18.ids)],
|
|
})],
|
|
}
|
|
invoice = self.env['account.move'].create(vals).with_context(edi_test_mode=True)
|
|
invoice.action_post()
|
|
|
|
generated_files = self._process_documents_web_services(invoice, {'pe_ubl_2_1'})
|
|
self.assertTrue(generated_files)
|
|
zip_edi_str = generated_files[0]
|
|
edi_xml = self.edi_format._l10n_pe_edi_unzip_edi_document(zip_edi_str)
|
|
current_etree = self.get_xml_tree_from_string(edi_xml)
|
|
|
|
with file_open('l10n_pe_edi/tests/test_files/foreign_customer.xml', 'rb') as expected_invoice_file:
|
|
expected_etree = self.get_xml_tree_from_string(expected_invoice_file.read())
|
|
self.assertXmlTreeEqual(current_etree, expected_etree)
|
|
|
|
def test_invoice_payment_term_ordering(self):
|
|
""" Instalments (Cuota001/002/003) must be emitted in ascending due-date order.
|
|
|
|
When a 3-instalment payment term is used (e.g. 50 % / 30 % / 20 %), the
|
|
resulting cac:PaymentTerms nodes must be sorted by PaymentDueDate, not
|
|
by database row insertion order. Regression for bug where the amounts
|
|
were mixed up between cuotas.
|
|
"""
|
|
payment_term = self.env['account.payment.term'].create({
|
|
'name': '20/30/50 instalment',
|
|
# Lines intentionally created in descending nb_days order so that without the
|
|
# date_maturity sort fix, the DB insertion order would assign Cuota001 to the
|
|
# latest/largest instalment instead of the earliest/smallest one.
|
|
'line_ids': [
|
|
Command.create({'value': 'percent', 'value_amount': 50, 'nb_days': 15}),
|
|
Command.create({'value': 'percent', 'value_amount': 30, 'nb_days': 10}),
|
|
Command.create({'value': 'percent', 'value_amount': 20, 'nb_days': 5}),
|
|
],
|
|
})
|
|
|
|
with freeze_time(self.frozen_today), \
|
|
patch('odoo.addons.l10n_pe_edi.models.account_edi_format.AccountEdiFormat._l10n_pe_edi_post_invoice_web_service',
|
|
new=mocked_l10n_pe_edi_post_invoice_web_service):
|
|
vals = {
|
|
'name': 'F FFI-%s1' % self.time_name,
|
|
'move_type': 'out_invoice',
|
|
'partner_id': self.partner_a.id,
|
|
'invoice_date': '2017-01-01',
|
|
'date': '2017-01-01',
|
|
'invoice_payment_term_id': payment_term.id,
|
|
'l10n_latam_document_type_id': self.env.ref('l10n_pe.document_type01').id,
|
|
'invoice_line_ids': [Command.create({
|
|
'product_id': self.product.id,
|
|
'product_uom_id': self.env.ref('uom.product_uom_kgm').id,
|
|
'price_unit': 530.0,
|
|
'quantity': 1,
|
|
'tax_ids': [Command.set(self.tax_18.ids)],
|
|
})],
|
|
}
|
|
invoice = self.env['account.move'].create(vals).with_context(edi_test_mode=True)
|
|
invoice.action_post()
|
|
|
|
generated_files = self._process_documents_web_services(invoice, {'pe_ubl_2_1'})
|
|
self.assertTrue(generated_files)
|
|
edi_xml = self.edi_format._l10n_pe_edi_unzip_edi_document(generated_files[0])
|
|
invoice_etree = self.get_xml_tree_from_string(edi_xml)
|
|
|
|
# Extract only the Cuota instalment nodes (skip the summary FormaPago/Credito node)
|
|
cuota_nodes = [
|
|
node for node in invoice_etree.findall('.//{*}PaymentTerms')
|
|
if (node.findtext('{*}PaymentMeansID') or '').startswith('Cuota')
|
|
]
|
|
|
|
# Wrap extracted nodes in a root element so we can use assertXmlTreeEqual
|
|
generated_root = etree.Element('root')
|
|
for node in cuota_nodes:
|
|
generated_root.append(node)
|
|
|
|
expected_root = self.get_xml_tree_from_string(b'''<root
|
|
xmlns:cbc="urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2"
|
|
xmlns:cac="urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2">
|
|
<cac:PaymentTerms>
|
|
<cbc:ID>FormaPago</cbc:ID>
|
|
<cbc:PaymentMeansID>Cuota001</cbc:PaymentMeansID>
|
|
<cbc:Amount currencyID="PEN">125.08</cbc:Amount>
|
|
<cbc:PaymentDueDate>2017-01-06</cbc:PaymentDueDate>
|
|
</cac:PaymentTerms>
|
|
<cac:PaymentTerms>
|
|
<cbc:ID>FormaPago</cbc:ID>
|
|
<cbc:PaymentMeansID>Cuota002</cbc:PaymentMeansID>
|
|
<cbc:Amount currencyID="PEN">187.62</cbc:Amount>
|
|
<cbc:PaymentDueDate>2017-01-11</cbc:PaymentDueDate>
|
|
</cac:PaymentTerms>
|
|
<cac:PaymentTerms>
|
|
<cbc:ID>FormaPago</cbc:ID>
|
|
<cbc:PaymentMeansID>Cuota003</cbc:PaymentMeansID>
|
|
<cbc:Amount currencyID="PEN">312.70</cbc:Amount>
|
|
<cbc:PaymentDueDate>2017-01-16</cbc:PaymentDueDate>
|
|
</cac:PaymentTerms>
|
|
</root>''')
|
|
self.assertXmlTreeEqual(generated_root, expected_root)
|
|
|
|
def test_reversal_cancel_reason_mapping(self):
|
|
"""Test that the cancel and credit reason in the reversal wizard are correctly mapped to the fields in the Peruvian EDI tab."""
|
|
|
|
move = self._create_invoice()
|
|
move.action_post()
|
|
|
|
reversal_wizard = self.env['account.move.reversal'].with_context(
|
|
active_model="account.move",
|
|
active_ids=move.ids
|
|
).create({
|
|
'reason': 'Test reason',
|
|
'journal_id': move.journal_id.id,
|
|
'l10n_pe_edi_refund_reason': '01',
|
|
})
|
|
action = reversal_wizard.reverse_moves()
|
|
reverse_move = self.env['account.move'].browse(action['res_id'])
|
|
self.assertEqual(reverse_move.l10n_pe_edi_cancel_reason, 'Test reason')
|
|
self.assertEqual(reverse_move.l10n_pe_edi_refund_reason, '01')
|
|
|
|
def test_invoice_downpayment_rounding(self):
|
|
""" Test a 40% down payment invoice on a sale order with several 0% taxes and
|
|
products having price unit with more than 2 decimals.
|
|
"""
|
|
self.ensure_installed('sale')
|
|
|
|
self.env.user.group_ids |= self.env.ref('sales_team.group_sale_manager')
|
|
self.env.company.tax_calculation_rounding_method = 'round_globally'
|
|
|
|
tax_group_exo = self.env['account.tax.group'].create({
|
|
'name': 'EXO',
|
|
'l10n_pe_edi_code': 'EXO',
|
|
})
|
|
tax_group_ina = self.env['account.tax.group'].create({
|
|
'name': 'INA',
|
|
'l10n_pe_edi_code': 'INA',
|
|
})
|
|
tax_0_ina = self.env['account.tax'].create({
|
|
'name': 'tax_0_ina',
|
|
'amount_type': 'percent',
|
|
'amount': 0,
|
|
'l10n_pe_edi_tax_code': '9998',
|
|
'l10n_pe_edi_unece_category': 'Z',
|
|
'type_tax_use': 'sale',
|
|
'tax_group_id': tax_group_ina.id,
|
|
})
|
|
tax_0_exo = self.env['account.tax'].create({
|
|
'name': 'tax_0_exo',
|
|
'amount_type': 'percent',
|
|
'amount': 0,
|
|
'l10n_pe_edi_tax_code': '9997',
|
|
'l10n_pe_edi_unece_category': 'Z',
|
|
'type_tax_use': 'sale',
|
|
'tax_group_id': tax_group_exo.id,
|
|
})
|
|
product_a = self.env['product.product'].create({
|
|
'name': 'product_pe_a',
|
|
'uom_id': self.env.ref('uom.product_uom_unit').id,
|
|
'unspsc_code_id': self.env.ref('product_unspsc.unspsc_code_01010101').id,
|
|
})
|
|
product_b = self.env['product.product'].create({
|
|
'name': 'product_pe_b',
|
|
'uom_id': self.env.ref('uom.product_uom_unit').id,
|
|
'unspsc_code_id': self.env.ref('product_unspsc.unspsc_code_01010101').id,
|
|
})
|
|
product_c = self.env['product.product'].create({
|
|
'name': 'product_pe_c',
|
|
'uom_id': self.env.ref('uom.product_uom_unit').id,
|
|
'unspsc_code_id': self.env.ref('product_unspsc.unspsc_code_01010101').id,
|
|
})
|
|
|
|
with freeze_time(self.frozen_today):
|
|
sale_order = self.env['sale.order'].create({ # noqa: OLS03001
|
|
'partner_id': self.partner_a.id,
|
|
'order_line': [
|
|
Command.create({
|
|
'product_id': product_a.id,
|
|
'price_unit': 123.50,
|
|
'product_uom_qty': 3,
|
|
'tax_ids': [Command.set(self.tax_18.ids)],
|
|
}),
|
|
Command.create({
|
|
'product_id': product_b.id,
|
|
'price_unit': 27.544216,
|
|
'product_uom_qty': 2,
|
|
'tax_ids': [Command.set(tax_0_ina.ids)],
|
|
}),
|
|
Command.create({
|
|
'product_id': product_c.id,
|
|
'price_unit': 43.490867,
|
|
'product_uom_qty': 1,
|
|
'tax_ids': [Command.set(tax_0_exo.ids)],
|
|
}),
|
|
]
|
|
})
|
|
sale_order.action_confirm()
|
|
|
|
context = {
|
|
'active_model': 'sale.order',
|
|
'active_ids': [sale_order.id],
|
|
'active_id': sale_order.id,
|
|
'default_journal_id': self.company_data['default_journal_sale'].id,
|
|
}
|
|
downpayment = self.env['sale.advance.payment.inv'].with_context(context).create({ # noqa: OLS03001
|
|
'advance_payment_method': 'percentage',
|
|
'amount': 40,
|
|
})._create_invoices(sale_order)
|
|
|
|
with patch('odoo.addons.l10n_pe_edi.models.account_edi_format.AccountEdiFormat._l10n_pe_edi_post_invoice_web_service',
|
|
new=mocked_l10n_pe_edi_post_invoice_web_service):
|
|
downpayment.action_post()
|
|
|
|
generated_files = self._process_documents_web_services(downpayment, {'pe_ubl_2_1'})
|
|
self.assertTrue(generated_files)
|
|
|
|
zip_edi_str = generated_files[0]
|
|
edi_xml = self.edi_format._l10n_pe_edi_unzip_edi_document(zip_edi_str)
|
|
current_etree = self.get_xml_tree_from_string(edi_xml)
|
|
with file_open('l10n_pe_edi/tests/test_files/invoice_downpayment_rounding.xml', 'rb') as expected_file:
|
|
expected_etree = self.get_xml_tree_from_string(expected_file.read())
|
|
self.assertXmlTreeEqual(current_etree, expected_etree)
|