From 77df1953b24b03b09ac551668c3377e4a1acabe0 Mon Sep 17 00:00:00 2001 From: Carolina Fernandez Date: Tue, 28 Nov 2023 09:52:29 -0300 Subject: [PATCH] [MIG] datev_export_xml: Migration to 13.0 --- datev_export_xml/__manifest__.py | 7 +- datev_export_xml/models/account_move.py | 26 +++-- datev_export_xml/models/datev_export.py | 9 +- .../models/datev_pdf_generator.py | 8 +- .../models/datev_xml_generator.py | 11 +-- .../models/datev_zip_generator.py | 3 - datev_export_xml/readme/CONTRIBUTORS.rst | 1 + .../static/description/index.html | 1 + datev_export_xml/tests/test_datev_export.py | 99 +++++++++++-------- datev_export_xml/tests/test_generator.py | 4 - datev_export_xml/views/datev_export_views.xml | 3 +- datev_export_xml/views/templates.xml | 6 +- 12 files changed, 91 insertions(+), 87 deletions(-) diff --git a/datev_export_xml/__manifest__.py b/datev_export_xml/__manifest__.py index fefee7b7..3d2c08fc 100644 --- a/datev_export_xml/__manifest__.py +++ b/datev_export_xml/__manifest__.py @@ -5,18 +5,19 @@ # @author Guenter Selbert # @author Thorsten Vocks # @author Grzegorz Grzelak +# Copyright 2023 Tecnativa - Carolina Fernandez # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). { "name": "Datev Export XML", - "version": "14.0.1.0.0", + "version": "13.0.1.0.0", "category": "Accounting", "license": "AGPL-3", "author": "Guenter Selbert, Thorsten Vocks, Maciej Wichowski, Daniela Scarpa, " "Maria Sparenberg, initOS GmbH, Odoo Community Association (OCA)", "summary": "Export invoices and refunds as xml and pdf files zipped in DATEV format.", "website": "https://github.com/OCA/l10n-germany", - "depends": ["datev_export",], + "depends": ["datev_export"], "data": [ "data/ir_cron_data.xml", "security/groups.xml", @@ -27,6 +28,6 @@ "views/res_config_settings_views.xml", "views/templates.xml", ], - "demo": ["demo/export_data.xml",], + "demo": ["demo/export_data.xml"], "installable": True, } diff --git a/datev_export_xml/models/account_move.py b/datev_export_xml/models/account_move.py index 30709c27..2f250710 100644 --- a/datev_export_xml/models/account_move.py +++ b/datev_export_xml/models/account_move.py @@ -5,6 +5,7 @@ # @author Guenter Selbert # @author Thorsten Vocks # @author Grzegorz Grzelak +# Copyright 2023 Tecnativa - Carolina Fernandez # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). import logging @@ -41,7 +42,7 @@ class AccountMove(models.Model): def datev_format_total(self, value): self.ensure_one() - return f"-{value:.2f}" if self.move_type.endswith("_refund") else f"{value:.2f}" + return f"-{value:.2f}" if self.type.endswith("_refund") else f"{value:.2f}" def datev_sanitize(self, value, length=36): return re.sub(r"[^a-zA-Z0-9$%&\*\+\-/]", "-", value)[:length] @@ -57,20 +58,22 @@ def datev_delivery_date(self): return self.invoice_date pickings = self.env["stock.move"] - if self.move_type == "out_invoice": + if self.type == "out_invoice": pickings = self.mapped("invoice_line_ids.sale_line_ids.move_ids.picking_id") - elif self.move_type == "in_invoice": - pickings = self.mapped("invoice_line_ids.purchase_order_id.picking_ids") + elif self.type == "in_invoice": + pickings = self.mapped( + "invoice_line_ids.purchase_line_id.order_id.picking_ids" + ) if pickings: - return pickings.sorted("date DESC")[0].date.date() + return sorted(pickings, key=lambda x: x.date, reverse=True)[0].date.date() _logger.info("Invoice date used as delivery date.") return self.invoice_date def datev_invoice_type(self): self.ensure_one() - if self.move_type in ["out_invoice", "in_invoice"]: + if self.type in ["out_invoice", "in_invoice"]: return "Rechnung" return "Gutschrift/Rechnungskorrektur" @@ -81,22 +84,17 @@ def datev_invoice_id(self): def datev_order_id(self): self.ensure_one() origin = self.invoice_origin or "" - if self.move_type not in ( - "in_invoice", - "in_refund", - "out_invoice", - "out_refund", - ): + if self.type not in ("in_invoice", "in_refund", "out_invoice", "out_refund",): return self.datev_sanitize(origin) # Use the correct setting - if self.move_type.startswith("in_"): + if self.type.startswith("in_"): ref_field = self.sudo().company_id.datev_vendor_order_ref else: ref_field = self.sudo().company_id.datev_customer_order_ref # Show the original move because ref is a combined value for refund - if ref_field == "partner" and self.move_type.endswith("_refund"): + if ref_field == "partner" and self.type.endswith("_refund"): return self.datev_sanitize(self.reversed_entry_id.name or origin) # Show the partner reference from the orders stored in ref diff --git a/datev_export_xml/models/datev_export.py b/datev_export_xml/models/datev_export.py index 53922651..8bff27e4 100644 --- a/datev_export_xml/models/datev_export.py +++ b/datev_export_xml/models/datev_export.py @@ -5,6 +5,7 @@ # @author Guenter Selbert # @author Thorsten Vocks # @author Grzegorz Grzelak +# Copyright 2023 Tecnativa - Carolina Fernandez # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). import datetime @@ -187,7 +188,7 @@ def get_invoices(self): ("amount_untaxed", "!=", 0), ("amount_total", "!=", 0), ("state", "in", ("posted", "open")), - ("move_type", "in", list_invoice_type), + ("type", "in", list_invoice_type), ("company_id", "=", self.company_id.id), ] if self.company_id.datev_export_state: @@ -281,7 +282,7 @@ def export_zip_invoice(self, invoice_ids=None): invoice_ids = self.env.context.get("active_ids") invoices = self.env["account.move"].browse(invoice_ids) - types = invoices.mapped("move_type") + types = invoices.mapped("type") if all(x.startswith("in_") for x in types): export_type = "in" @@ -364,9 +365,7 @@ def action_pending(self): raise ValidationError( _("It's not allowed to set an already running export to pending!") ) - r.write( - {"state": "pending", "exception_info": None,} - ) + r.write({"state": "pending", "exception_info": None}) def action_draft(self): for r in self: diff --git a/datev_export_xml/models/datev_pdf_generator.py b/datev_export_xml/models/datev_pdf_generator.py index e9ddbf26..62832b5e 100644 --- a/datev_export_xml/models/datev_pdf_generator.py +++ b/datev_export_xml/models/datev_pdf_generator.py @@ -5,14 +5,12 @@ # @author Guenter Selbert # @author Thorsten Vocks # @author Grzegorz Grzelak +# Copyright 2023 Tecnativa - Carolina Fernandez # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). import base64 -import logging from odoo import api, models -_logger = logging.getLogger(__name__) - class DatevPdfGenerator(models.AbstractModel): _name = "datev.pdf.generator" @@ -47,7 +45,7 @@ def generate_pdf(self, invoice): # Otherwise generate a new once report = self.env["ir.actions.report"].search( - [("model", "=", "account.move"), ("report_name", "=", self.report_name()),], + [("model", "=", "account.move"), ("report_name", "=", self.report_name())], ) if report: - return report._render(invoice.ids)[0] + return report.render(invoice.ids)[0] diff --git a/datev_export_xml/models/datev_xml_generator.py b/datev_export_xml/models/datev_xml_generator.py index 68a91c9f..3badf11e 100644 --- a/datev_export_xml/models/datev_xml_generator.py +++ b/datev_export_xml/models/datev_xml_generator.py @@ -5,9 +5,9 @@ # @author Guenter Selbert # @author Thorsten Vocks # @author Grzegorz Grzelak +# Copyright 2023 Tecnativa - Carolina Fernandez # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). -import logging import re from lxml import etree @@ -15,8 +15,6 @@ from odoo import _, api, models, tools from odoo.exceptions import UserError -_logger = logging.getLogger(__name__) - class DatevXmlGenerator(models.AbstractModel): _name = "datev.xml.generator" @@ -27,12 +25,11 @@ def check_xml_file(self, doc_name, root, xsd=None): if not xsd: xsd = "Document_v050.xsd" - schema = tools.file_open(xsd, subdir="addons/datev_export_xml/xsd_files") + schema = tools.file_open(f"datev_export_xml/xsd_files/{xsd}") try: schema = etree.XMLSchema(etree.parse(schema)) schema.assertValid(root) except (etree.DocumentInvalid, etree.XMLSyntaxError) as e: - _logger.warning(etree.tostring(root)) raise UserError( _( "Wrong Data in XML file!\nTry to solve the problem with " @@ -47,7 +44,7 @@ def check_xml_file(self, doc_name, root, xsd=None): def generate_xml_document(self, invoices, check_xsd=True): template = self.env.ref("datev_export_xml.export_invoice_document") root = etree.fromstring( - template._render({"docs": invoices, "company": self.env.company}), + template.render({"docs": invoices, "company": self.env.company}), parser=etree.XMLParser(remove_blank_text=True), ) @@ -63,7 +60,7 @@ def generate_xml_invoice(self, invoice, check_xsd=True): doc_name = re.sub(r"[^a-zA-Z0-9_\-.()]", "", f"{invoice.name}.xml") template = self.env.ref("datev_export_xml.export_invoice") root = etree.fromstring( - template._render({"doc": invoice}), + template.render({"doc": invoice}), parser=etree.XMLParser(remove_blank_text=True), ) diff --git a/datev_export_xml/models/datev_zip_generator.py b/datev_export_xml/models/datev_zip_generator.py index 6edb9f63..6f09919c 100644 --- a/datev_export_xml/models/datev_zip_generator.py +++ b/datev_export_xml/models/datev_zip_generator.py @@ -9,14 +9,11 @@ import base64 import io -import logging import zipfile from odoo import _, api, models from odoo.exceptions import UserError -_logger = logging.getLogger(__name__) - class DatevZipGenerator(models.AbstractModel): _name = "datev.zip.generator" diff --git a/datev_export_xml/readme/CONTRIBUTORS.rst b/datev_export_xml/readme/CONTRIBUTORS.rst index 9f25cab3..ab2aca9d 100644 --- a/datev_export_xml/readme/CONTRIBUTORS.rst +++ b/datev_export_xml/readme/CONTRIBUTORS.rst @@ -1,3 +1,4 @@ * Thorsten Vocks (OpenBIG.org) * Guenter Selbert (sewisoft.de) * initOS GmbH (initOS.com) +* Tecnativa - Carolina Fernandez diff --git a/datev_export_xml/static/description/index.html b/datev_export_xml/static/description/index.html index 9f98ed5e..9553c1e7 100644 --- a/datev_export_xml/static/description/index.html +++ b/datev_export_xml/static/description/index.html @@ -516,6 +516,7 @@

Contributors

  • Thorsten Vocks (OpenBIG.org)
  • Guenter Selbert (sewisoft.de)
  • initOS GmbH (initOS.com)
  • +
  • Tecnativa - Carolina Fernandez
  • diff --git a/datev_export_xml/tests/test_datev_export.py b/datev_export_xml/tests/test_datev_export.py index d86e98f8..93ebb9fe 100644 --- a/datev_export_xml/tests/test_datev_export.py +++ b/datev_export_xml/tests/test_datev_export.py @@ -1,9 +1,9 @@ # Copyright (C) 2022-2023 initOS GmbH # Copyright (C) 2020, Elego Software Solutions GmbH, Berlin +# Copyright 2023 Tecnativa - Carolina Fernandez # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). import base64 import io -import logging import zipfile from datetime import date, timedelta @@ -13,8 +13,6 @@ from odoo.exceptions import UserError, ValidationError from odoo.tests.common import TransactionCase -_logger = logging.getLogger(__name__) - class TestDatevExport(TransactionCase): def setUp(self): @@ -81,6 +79,7 @@ def setUp(self): self.refund_date = self.today - timedelta(days=55) self.start_date = self.today - timedelta(days=34) self.end_date = self.today - timedelta(days=32) + self.InvoiceObj.search([("state", "=", "posted")]).button_draft() self.InvoiceObj.with_context(force_delete=True).search([]).unlink() self.env.company.datev_default_period = "week" @@ -108,21 +107,17 @@ def _check_filecontent(self, export): doc_data = z.read(doc_file) inv_data = z.read(inv_file) # document.xml - doc_root = etree.fromstring(doc_data.decode("utf-8")) + doc_root = etree.fromstring(doc_data) # invoice.xml file - inv_root = etree.fromstring(inv_data.decode("utf-8")) + inv_root = etree.fromstring(inv_data) for i in inv_root: invoice_xml.update(i.attrib) return { "file_list": file_list, "zip_file": z, - "document": lambda xpath: doc_root.find( - xpath, namespaces=doc_root.nsmap - ), - "invoice": lambda xpath: inv_root.find( - xpath, namespaces=inv_root.nsmap - ), + "document": doc_root, + "invoice": inv_root, } def create_out_invoice(self, customer, start_date, end_date): @@ -143,7 +138,7 @@ def create_out_invoice(self, customer, start_date, end_date): "invoice_date_due": end_date, "company_id": self.env.company.id, "currency_id": self.env.company.currency_id.id, - "move_type": "out_invoice", + "type": "out_invoice", "invoice_line_ids": [ ( 0, @@ -178,7 +173,7 @@ def create_out_invoice_with_tax(self, customer, start_date, end_date, tax): "invoice_date_due": end_date, "company_id": self.env.company.id, "currency_id": self.env.company.currency_id.id, - "move_type": "out_invoice", + "type": "out_invoice", "invoice_line_ids": [ ( 0, @@ -218,7 +213,7 @@ def create_in_invoice(self, vendor, start_date, end_date): "invoice_date_due": end_date, "company_id": self.env.company.id, "currency_id": self.env.company.currency_id.id, - "move_type": "in_invoice", + "type": "in_invoice", "invoice_line_ids": [ ( 0, @@ -293,55 +288,73 @@ def create_customer_datev_export_manually(self, invoice): return datev_export def update_attachment(self, attachment, invoice): - attachment.write( - {"res_model": "account.invoice", "res_id": invoice.id,} - ) + attachment.write({"res_model": "account.move", "res_id": invoice.id}) return attachment def _run_test_document(self, doc, invoice): inv_number = invoice.name.replace("/", "-") - self.assertEqual(doc(".//header/clientName").text, invoice.company_id.name) - self.assertEqual(doc(".//document/description").text, invoice.name) - self.assertEqual( - doc(".//extension[@datafile]").attrib["datafile"], inv_number + ".xml" + + namespace_url = doc.nsmap.get(None, "") + client_name = doc.find(".//ns:clientName", namespaces={"ns": namespace_url}) + description = doc.find( + ".//ns:content/ns:document/ns:description", namespaces={"ns": namespace_url} + ) + extension_datafile = doc.find( + ".//ns:extension[@datafile]", namespaces={"ns": namespace_url} + ) + extension_name = doc.find( + ".//ns:extension[@name]", namespaces={"ns": namespace_url} + ) + property_invoice_type = doc.find( + ".//ns:property[@key='InvoiceType']", namespaces={"ns": namespace_url} ) - self.assertEqual(doc(".//extension[@name]").attrib["name"], inv_number + ".pdf") + self.assertEqual(client_name.text, invoice.company_id.name) + self.assertEqual(description.text, invoice.name) + self.assertEqual(extension_datafile.attrib["datafile"], inv_number + ".xml") + self.assertEqual(extension_name.attrib["name"], inv_number + ".pdf") self.assertEqual( - doc(".//property[@key='InvoiceType']").attrib["value"], - "Outgoing" if invoice.move_type.startswith("out_") else "Incoming", + property_invoice_type.attrib["value"], + "Outgoing" if invoice.type.startswith("out_") else "Incoming", ) def _run_test_invoice(self, doc, invoice): inv_line = invoice.invoice_line_ids.filtered("product_id")[0] - info = doc(".//invoice_info").attrib - line = doc(".//invoice_item_list").attrib - total = doc(".//total_amount").attrib + namespace_url = doc.nsmap.get(None, "") + info = doc.find(".//ns:invoice_info", namespaces={"ns": namespace_url}).attrib + line = doc.find( + ".//ns:invoice_item_list", namespaces={"ns": namespace_url} + ).attrib + total = doc.find(".//ns:total_amount", namespaces={"ns": namespace_url}).attrib + invoice_party_address = doc.find( + ".//ns:invoice_party/ns:address", namespaces={"ns": namespace_url} + ) + supplier_party_address = doc.find( + ".//ns:supplier_party/ns:address", namespaces={"ns": namespace_url} + ) self.assertEqual( info["invoice_type"], "Rechnung" - if invoice.move_type.endswith("_invoice") + if invoice.type.endswith("_invoice") else "Gutschrift/Rechnungskorrektur", ) self.assertEqual(info["invoice_date"], invoice.invoice_date.isoformat()) - if invoice.move_type.startswith("out_"): + if invoice.type.startswith("out_"): self.assertEqual( - doc(".//invoice_party/address").attrib["name"], - invoice.partner_id.display_name, + invoice_party_address.attrib["name"], invoice.partner_id.display_name ) else: self.assertEqual( - doc(".//supplier_party/address").attrib["name"], - invoice.partner_id.display_name, + supplier_party_address.attrib["name"], invoice.partner_id.display_name ) self.assertEqual(float(line["quantity"]), inv_line.quantity) self.assertEqual(line["product_id"], inv_line.product_id.default_code) - sign = -1 if invoice.move_type.endswith("_refund") else 1 + sign = -1 if invoice.type.endswith("_refund") else 1 self.assertEqual( float(total["net_total_amount"]), sign * invoice.amount_untaxed, ) @@ -351,7 +364,9 @@ def _run_test_invoice(self, doc, invoice): ) # partner has bank account if invoice.partner_id.bank_ids: - bank = doc(".//invoice_party/account").attrib + bank = doc.find( + ".//ns:invoice_party/ns:account", namespaces={"ns": namespace_url} + ).attrib self.assertEqual( bank["bank_name"], invoice.partner_id.bank_ids[0].bank_name ) @@ -362,7 +377,7 @@ def _run_test_invoice(self, doc, invoice): def _run_test_out_refund_datev_export(self, refund): line = refund.invoice_line_ids.filtered("product_id")[0] - self.assertEqual(refund.move_type, "out_refund") + self.assertEqual(refund.type, "out_refund") self.assertEqual(line.account_id, self.account_income) self.assertEqual(line.price_unit, 120.00) self.assertEqual(line.quantity, 5.00) @@ -409,7 +424,7 @@ def _run_test_out_refund_datev_export(self, refund): def _run_test_in_refund_datev_export(self, refund, attachment): line = refund.invoice_line_ids.filtered("product_id")[0] - self.assertEqual(refund.move_type, "in_refund") + self.assertEqual(refund.type, "in_refund") self.assertEqual(line.account_id, self.account_expense) self.assertEqual(line.price_unit, 900.00) self.assertEqual(line.quantity, 1.00) @@ -458,7 +473,7 @@ def _run_test_in_refund_datev_export(self, refund, attachment): def _run_test_out_invoice_datev_export(self, invoice): line = invoice.invoice_line_ids.filtered("product_id")[0] - self.assertEqual(invoice.move_type, "out_invoice") + self.assertEqual(invoice.type, "out_invoice") self.assertEqual(line.account_id, self.account_income) self.assertEqual(line.price_unit, 120.00) self.assertEqual(line.quantity, 5.00) @@ -505,7 +520,7 @@ def _run_test_out_invoice_datev_export(self, invoice): def _run_test_in_invoice_datev_export(self, invoice, attachment): line = invoice.invoice_line_ids.filtered("product_id")[0] - self.assertEqual(invoice.move_type, "in_invoice") + self.assertEqual(invoice.type, "in_invoice") self.assertEqual(line.account_id, self.account_expense) self.assertEqual(line.price_unit, 900.00) self.assertEqual(line.quantity, 1.00) @@ -553,7 +568,7 @@ def _run_test_in_invoice_datev_export(self, invoice, attachment): def _run_test_out_inv_datev_export_manually(self, invoice): line = invoice.invoice_line_ids.filtered("product_id")[0] - self.assertEqual(invoice.move_type, "out_invoice") + self.assertEqual(invoice.type, "out_invoice") self.assertEqual(line.account_id, self.account_income) self.assertEqual(line.price_unit, 120.00) self.assertEqual(line.quantity, 5.00) @@ -721,7 +736,7 @@ def test_14_datev_export_without_invoice(self): # (Invoices/Refunds) you want to export!" with self.assertRaises(ValidationError): datev_export = self.DatevExportObj.create( - {"export_type": "out", "export_invoice": False, "export_refund": False,} + {"export_type": "out", "export_invoice": False, "export_refund": False} ) # 2. when default values are set # date_start, date_end (based on datev_default_period of current company) @@ -729,7 +744,7 @@ def test_14_datev_export_without_invoice(self): # export_refund = True, # check_xsd = True, # manually_document_selection = Flase - datev_export = self.DatevExportObj.create({"export_type": "out",}) + datev_export = self.DatevExportObj.create({"export_type": "out"}) self.assertEqual(datev_export.datev_file, False) self.assertEqual( datev_export.client_number, self.env.company.datev_client_number, diff --git a/datev_export_xml/tests/test_generator.py b/datev_export_xml/tests/test_generator.py index d19a29e0..6e838787 100644 --- a/datev_export_xml/tests/test_generator.py +++ b/datev_export_xml/tests/test_generator.py @@ -1,15 +1,11 @@ # Copyright (C) 2022-2023 initOS GmbH # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -import logging - from lxml import etree from odoo.exceptions import UserError from odoo.tests.common import TransactionCase -_logger = logging.getLogger(__name__) - class TestGenerator(TransactionCase): def test_xml_generator(self): diff --git a/datev_export_xml/views/datev_export_views.xml b/datev_export_xml/views/datev_export_views.xml index b6e96f73..ba386cbf 100644 --- a/datev_export_xml/views/datev_export_views.xml +++ b/datev_export_xml/views/datev_export_views.xml @@ -7,6 +7,7 @@ # @author Guenter Selbert # @author Thorsten Vocks # @author Grzegorz Grzelak +# Copyright 2023 Tecnativa - Carolina Fernandez # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). --> @@ -252,7 +253,7 @@ - + diff --git a/datev_export_xml/views/templates.xml b/datev_export_xml/views/templates.xml index e5046c06..d1ad4be0 100644 --- a/datev_export_xml/views/templates.xml +++ b/datev_export_xml/views/templates.xml @@ -87,7 +87,7 @@ /> - + - + @@ -165,7 +165,7 @@