diff --git a/l10n_br_delivery_nfe/README.rst b/l10n_br_delivery_nfe/README.rst index ea2562fb7911..d2807045d1e8 100644 --- a/l10n_br_delivery_nfe/README.rst +++ b/l10n_br_delivery_nfe/README.rst @@ -76,6 +76,7 @@ Authors ~~~~~~~ * Akretion +* KMEE Contributors ~~~~~~~~~~~~ @@ -84,6 +85,10 @@ Contributors * Magno Costa +* `KMEE `_: + + * Diego Paradeda + Maintainers ~~~~~~~~~~~ diff --git a/l10n_br_delivery_nfe/__init__.py b/l10n_br_delivery_nfe/__init__.py index 0650744f6bc6..aee8895e7a31 100644 --- a/l10n_br_delivery_nfe/__init__.py +++ b/l10n_br_delivery_nfe/__init__.py @@ -1 +1,2 @@ from . import models +from . import wizards diff --git a/l10n_br_delivery_nfe/__manifest__.py b/l10n_br_delivery_nfe/__manifest__.py index 35286730b2e3..533bff02d2e2 100644 --- a/l10n_br_delivery_nfe/__manifest__.py +++ b/l10n_br_delivery_nfe/__manifest__.py @@ -6,14 +6,28 @@ "name": "Brazilian Localization Delivery NFe", "category": "Localisation", "license": "AGPL-3", - "author": "Akretion, Odoo Community Association (OCA)", + "author": "Akretion, KMEE, Odoo Community Association (OCA)", "maintainers": ["mbcosta"], "website": "https://github.com/OCA/l10n-brazil", "version": "14.0.1.1.0", - "depends": ["l10n_br_nfe", "l10n_br_account", "l10n_br_delivery"], + "depends": [ + "l10n_br_nfe", + "l10n_br_account", + "l10n_br_delivery", + "product_net_weight", + "product_brand", + ], "data": [ + # Security + "security/ir.model.access.csv", + # Wizards + "wizards/stock_invoice_onshipping_view.xml", + "wizards/stock_generate_volumes_view.xml", # Views - "views/nfe_document_view.xml" + "views/nfe_document_view.xml", + "views/product_product_view.xml", + "views/product_template_view.xml", + "views/stock_picking_view.xml", ], "installable": True, "auto_install": True, diff --git a/l10n_br_delivery_nfe/models/__init__.py b/l10n_br_delivery_nfe/models/__init__.py index 9c01ea2470e4..23eb7fda31ca 100644 --- a/l10n_br_delivery_nfe/models/__init__.py +++ b/l10n_br_delivery_nfe/models/__init__.py @@ -1 +1,6 @@ from . import document +from . import product_product +from . import product_template +from . import stock_picking_vol +from . import stock_picking_lacres +from . import stock_picking diff --git a/l10n_br_delivery_nfe/models/product_product.py b/l10n_br_delivery_nfe/models/product_product.py new file mode 100644 index 000000000000..c814feab2162 --- /dev/null +++ b/l10n_br_delivery_nfe/models/product_product.py @@ -0,0 +1,13 @@ +# Copyright (C) 2024 Diego Paradeda - KMEE +# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html + +from odoo import fields, models + + +class ProductProduct(models.Model): + _inherit = "product.product" + + product_volume_type = fields.Char( + string="Volume Type", + help="Type of transported volumes: Mapped onto 'nfe40_esp'", + ) diff --git a/l10n_br_delivery_nfe/models/product_template.py b/l10n_br_delivery_nfe/models/product_template.py new file mode 100644 index 000000000000..28ffc6b83484 --- /dev/null +++ b/l10n_br_delivery_nfe/models/product_template.py @@ -0,0 +1,35 @@ +# Copyright (C) 2024 Diego Paradeda - KMEE +# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html + +from odoo import api, fields, models + + +class ProductTemplate(models.Model): + _inherit = "product.template" + + product_volume_type = fields.Char( + string="Volume Type", + help="Type of transported volumes: Mapped onto 'nfe40_esp'", + compute="_compute_product_volume_type", + inverse="_inverse_product_volume_type", + store=True, + ) + + @api.depends("product_variant_ids", "product_variant_ids.product_volume_type") + def _compute_product_volume_type(self): + unique_variants = self.filtered( + lambda template: len(template.product_variant_ids) == 1 + ) + for template in unique_variants: + template.product_volume_type = ( + template.product_variant_ids.product_volume_type + ) + for template in self - unique_variants: + template.product_volume_type = "" + + def _inverse_product_volume_type(self): + for template in self: + if len(template.product_variant_ids) == 1: + template.product_variant_ids.product_volume_type = ( + template.product_volume_type + ) diff --git a/l10n_br_delivery_nfe/models/stock_picking.py b/l10n_br_delivery_nfe/models/stock_picking.py new file mode 100644 index 000000000000..7828c7cebd27 --- /dev/null +++ b/l10n_br_delivery_nfe/models/stock_picking.py @@ -0,0 +1,137 @@ +# Copyright (C) 2024 Diego Paradeda - KMEE +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). +from odoo import fields, models + + +class StockPicking(models.Model): + _inherit = "stock.picking" + + has_vol = fields.Boolean( + string="Has Vol IDs", + help="Technical Field: True if the picking has already generated Volume IDs.", + copy=False, + ) + + vol_ids = fields.One2many( + string="Volume Data", + comodel_name="stock.picking.vol", + inverse_name="picking_id", + copy=False, + ) + + number_of_volumes = fields.Integer( + string="Number of Volumes", + default=0, + copy=False, + ) + + def _get_volume_data_package_level(self): + """Generate a single volume for packages""" + vols_data = [] + for picking_id in self: + if picking_id.package_ids: + for package_level_id in picking_id.package_level_ids: + manual_weight = package_level_id.package_id.shipping_weight + vol_data = { + "nfe40_qVol": 1, + "nfe40_esp": "", + "nfe40_marca": "", + "nfe40_pesoL": 0, + "nfe40_pesoB": (manual_weight if manual_weight else 0), + "picking_id": picking_id.id, + } + + for line in package_level_id.move_line_ids: + vol_data["nfe40_esp"] = ( + vol_data["nfe40_esp"] or line.product_id.product_volume_type + ) + product_nfe40_marca = ( + line.product_id.product_brand_id.name + if line.product_id.product_brand_id + else "" + ) + vol_data["nfe40_marca"] = ( + vol_data["nfe40_marca"] or product_nfe40_marca + ) + pesoL = line.qty_done * line.product_id.net_weight + pesoB = line.qty_done * line.product_id.weight + vol_data["nfe40_pesoL"] += pesoL + vol_data["nfe40_pesoB"] += 0 if manual_weight else pesoB + vols_data.append(vol_data) + + return vols_data + + def _get_volume_data_wo_package(self): + """Generate a single volume for lines without package""" + vols_data = [] + for picking_id in self: + # Filter out move lines with in a package + if not picking_id.move_line_ids_without_package.filtered( + lambda ml: not ml.package_level_id + ): + continue + + new_vol = { + "nfe40_qVol": 0, + "nfe40_esp": "", + "nfe40_marca": "", + "nfe40_pesoL": 0, + "nfe40_pesoB": 0, + "picking_id": picking_id.id, + } + + for line in picking_id.move_line_ids_without_package.filtered( + lambda ml: not ml.package_level_id and not ml.result_package_id + ): + new_vol["nfe40_qVol"] += line.qty_done + new_vol["nfe40_esp"] = ( + new_vol["nfe40_esp"] or line.product_id.product_volume_type + ) + product_nfe40_marca = ( + line.product_id.product_brand_id.name + if line.product_id.product_brand_id + else "" + ) + new_vol["nfe40_marca"] = new_vol["nfe40_marca"] or product_nfe40_marca + pesoL = line.qty_done * line.product_id.net_weight + pesoB = line.qty_done * line.product_id.weight + new_vol["nfe40_pesoL"] += pesoL + new_vol["nfe40_pesoB"] += pesoB + + new_vol["nfe40_qVol"] = f"{new_vol['nfe40_qVol']:.0f}" + vols_data.append(new_vol) + + return vols_data + + def _get_pre_generated_volumes(self): + """Retreive and convert already generated volumes that are stored on picking""" + vols_data = [] + for picking_id in self: + for vol_id in picking_id.vol_ids: + vol_data = vol_id.copy_data()[0] + vols_data.append(vol_data) + return vols_data + + def prepare_vols_data_from_picking(self): + pre_generated_pickings = self.filtered(lambda p: p.has_vol) + to_generate_pickings = self.filtered(lambda p: not p.has_vol) + + vols_data = [] + vols_data += pre_generated_pickings._get_pre_generated_volumes() + vols_data += to_generate_pickings._get_volume_data_package_level() + vols_data += to_generate_pickings._get_volume_data_wo_package() + return vols_data + + def _compute_number_of_volumes(self): + for picking in self: + if len(picking.invoice_ids) == 1: + picking.number_of_volumes = sum( + [ + float(v) + for v in picking.invoice_ids.mapped( + "fiscal_document_id.nfe40_vol.nfe40_qVol" + ) + ] + ) + else: + picking.number_of_volumes = 0 diff --git a/l10n_br_delivery_nfe/models/stock_picking_lacres.py b/l10n_br_delivery_nfe/models/stock_picking_lacres.py new file mode 100644 index 000000000000..1366d0b2691a --- /dev/null +++ b/l10n_br_delivery_nfe/models/stock_picking_lacres.py @@ -0,0 +1,24 @@ +# Copyright (C) 2024 Diego Paradeda - KMEE +# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html + +from odoo import fields, models + + +class StockPickingLacres(models.Model): + _name = "stock.picking.lacres" + _description = "lacres" + # _inherit = "nfe.40.lacres" TODO: consider using inherit in the future + + """ + NFe40 fields start + ################## + this section copies fields from nfe.40.vol + sadly, _name/_inherit breaks spec_model + inheriting would be better than recreating the same fields. + """ + nfe40_lacres_vol_id = fields.Many2one(comodel_name="stock.picking.vol") + nfe40_nLacre = fields.Char(string="Número dos Lacres") + """ + NFe40 fields end + ################ + """ diff --git a/l10n_br_delivery_nfe/models/stock_picking_vol.py b/l10n_br_delivery_nfe/models/stock_picking_vol.py new file mode 100644 index 000000000000..c9731614480f --- /dev/null +++ b/l10n_br_delivery_nfe/models/stock_picking_vol.py @@ -0,0 +1,53 @@ +# Copyright (C) 2024 Diego Paradeda - KMEE +# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html + +from odoo import fields, models + + +class StockPickingVol(models.Model): + _name = "stock.picking.vol" + _description = "Volume Data" + # _inherit = "nfe.40.vol" TODO: consider using inherit in the future + + """ + NFe40 fields start + ################## + this section copies fields from nfe.40.vol + sadly, _name/_inherit breaks spec_model + inheriting would be better than recreating the same fields. + TODO: learn how to inherit nfe mixin (https://github.com/OCA/l10n-brazil/pull/3091) + """ + nfe40_vol_transp_id = fields.Many2one(comodel_name="nfe.40.transp") + nfe40_qVol = fields.Char(string="Quantidade de volumes transportados") + nfe40_esp = fields.Char(string="Espécie dos volumes transportados") + nfe40_marca = fields.Char(string="Marca dos volumes transportados") + nfe40_nVol = fields.Char(string="Numeração dos volumes transportados") + + nfe40_pesoL = fields.Float( + string="Peso líquido (em kg)", + digits=( + 12, + 3, + ), + ) + + nfe40_pesoB = fields.Float( + string="Peso bruto (em kg)", + digits=( + 12, + 3, + ), + ) + + nfe40_lacres = fields.One2many( + "stock.picking.lacres", "nfe40_lacres_vol_id", string="lacres" + ) + """ + NFe40 fields end + ################ + """ + + picking_id = fields.Many2one( + comodel_name="stock.picking", + string="Stock Picking", + ) diff --git a/l10n_br_delivery_nfe/readme/CONTRIBUTORS.rst b/l10n_br_delivery_nfe/readme/CONTRIBUTORS.rst index 280e08d23910..234988620b59 100644 --- a/l10n_br_delivery_nfe/readme/CONTRIBUTORS.rst +++ b/l10n_br_delivery_nfe/readme/CONTRIBUTORS.rst @@ -1,3 +1,7 @@ * `Akretion `_: * Magno Costa + +* `KMEE `_: + + * Diego Paradeda diff --git a/l10n_br_delivery_nfe/security/ir.model.access.csv b/l10n_br_delivery_nfe/security/ir.model.access.csv new file mode 100644 index 000000000000..ce32f4255c4a --- /dev/null +++ b/l10n_br_delivery_nfe/security/ir.model.access.csv @@ -0,0 +1,6 @@ +"id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink" +l10n_br_delivery_nfe_onshipping_vol,l10n_br_delivery_nfe.onshipping_vol,l10n_br_delivery_nfe.model_stock_invoice_onshipping_vol,stock.group_stock_user,1,1,1,1 +l10n_br_delivery_nfe_onshipping_lacres,l10n_br_delivery_nfe.onshipping_lacres,l10n_br_delivery_nfe.model_stock_invoice_onshipping_lacres,stock.group_stock_user,1,1,1,1 +l10n_br_delivery_nfe_stock_generate_volumes,l10n_br_delivery_nfe.stock_generate_volumes,l10n_br_delivery_nfe.model_stock_generate_volumes,stock.group_stock_user,1,1,1,1 +l10n_br_delivery_nfe_stock_picking_vol,l10n_br_delivery_nfe.stock_picking_vol,l10n_br_delivery_nfe.model_stock_picking_vol,stock.group_stock_user,1,1,1,1 +l10n_br_delivery_nfe_stock_picking_lacres,l10n_br_delivery_nfe.stock_picking_lacres,l10n_br_delivery_nfe.model_stock_picking_lacres,stock.group_stock_user,1,1,1,1 diff --git a/l10n_br_delivery_nfe/static/description/index.html b/l10n_br_delivery_nfe/static/description/index.html index 00d23e7db97f..272d4306b292 100644 --- a/l10n_br_delivery_nfe/static/description/index.html +++ b/l10n_br_delivery_nfe/static/description/index.html @@ -1,4 +1,3 @@ - @@ -9,10 +8,11 @@ /* :Author: David Goodger (goodger@python.org) -:Id: $Id: html4css1.css 8954 2022-01-20 10:10:25Z milde $ +:Id: $Id: html4css1.css 9511 2024-01-13 09:50:07Z milde $ :Copyright: This stylesheet has been placed in the public domain. Default cascading style sheet for the HTML output of Docutils. +Despite the name, some widely supported CSS2 features are used. See https://docutils.sourceforge.io/docs/howto/html-stylesheets.html for how to customize this style sheet. @@ -275,7 +275,7 @@ margin-left: 2em ; margin-right: 2em } -pre.code .ln { color: grey; } /* line numbers */ +pre.code .ln { color: gray; } /* line numbers */ pre.code, code { background-color: #eeeeee } pre.code .comment, code .comment { color: #5C6576 } pre.code .keyword, code .keyword { color: #3B0D06; font-weight: bold } @@ -301,7 +301,7 @@ span.pre { white-space: pre } -span.problematic { +span.problematic, pre.problematic { color: red } span.section-subtitle { @@ -428,6 +428,7 @@

Credits

Authors

  • Akretion
  • +
  • KMEE
@@ -437,12 +438,18 @@

Contributors

  • Magno Costa <magno.costa@akretion.com.br>
  • +
  • KMEE: +
  • Maintainers

    This module is maintained by the OCA.

    -Odoo Community Association + +Odoo Community Association +

    OCA, or the Odoo Community Association, is a nonprofit organization whose mission is to support the collaborative development of Odoo features and promote its widespread use.

    diff --git a/l10n_br_delivery_nfe/tests/__init__.py b/l10n_br_delivery_nfe/tests/__init__.py new file mode 100644 index 000000000000..8c4ef429dc18 --- /dev/null +++ b/l10n_br_delivery_nfe/tests/__init__.py @@ -0,0 +1,2 @@ +from . import test_delivery_nfe +from . import test_delivery_nfe_grouped diff --git a/l10n_br_delivery_nfe/tests/test_delivery_nfe.py b/l10n_br_delivery_nfe/tests/test_delivery_nfe.py new file mode 100644 index 000000000000..13d6da976fa6 --- /dev/null +++ b/l10n_br_delivery_nfe/tests/test_delivery_nfe.py @@ -0,0 +1,291 @@ +# Copyright (C) 2024 Diego Paradeda - KMEE +# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html + +from odoo.addons.l10n_br_stock_account.tests.common import TestBrPickingInvoicingCommon + + +class TestDeliveryNFe(TestBrPickingInvoicingCommon): + @classmethod + def setUpClass(cls): + super().setUpClass() + + # Testing Lucro Presumido - wo pack + def test_invoicing_picking_volume_lucro_presumido(self): + """Test Invoicing Picking NFe volume - Lucro Presumido""" + self.invoice_model = self.env["account.move"] + self.invoice_wizard = self.env["stock.invoice.onshipping"] + self.prod1 = self.env.ref("product.product_product_12") + self.prod2 = self.env.ref("product.product_product_16") + self._change_user_company(self.env.ref("l10n_br_base.empresa_lucro_presumido")) + picking = self.env.ref("l10n_br_stock_account.lucro_presumido-picking_1") + self.brand_id1 = self.env["product.brand"].create({"name": "marca teste"}) + self.brand_id2 = self.env["product.brand"].create({"name": "marca2 teste"}) + + # Set product volume data + self.prod1.product_volume_type = "" + self.prod1.product_brand_id = self.brand_id1 + self.prod2.product_volume_type = "esp2 teste" + self.prod2.product_brand_id = self.brand_id2 + self.prod1.weight = 1 + self.prod2.weight = 3 + self.prod1.net_weight = 1 + self.prod2.net_weight = 5 + + # Number of Volumes - zero before invoice + self.assertEqual( + picking.number_of_volumes, + 0, + "No of Vols must be zero before invoicing.", + ) + + # Invoice + picking.set_to_be_invoiced() + self.picking_move_state(picking) + + self.assertEqual(picking.state, "done") + wizard_obj = self.invoice_wizard.with_context( + active_ids=picking.ids, + active_model=picking._name, + active_id=picking.id, + ) + fields_list = wizard_obj.fields_get().keys() + wizard_values = wizard_obj.default_get(fields_list) + wizard = wizard_obj.create(wizard_values) + wizard.onchange_group() + + self.assertEqual( + wizard.vol_ids.mapped("nfe40_esp"), + ["esp2 teste"], + "Unexpected value for the field nfe40_esp in Stock Invoice Onshipping.", + ) + self.assertEqual( + wizard.vol_ids.mapped("nfe40_marca"), + ["marca2 teste"], + "Unexpected value for the field nfe40_marca in Stock Invoice Onshipping.", + ) + self.assertEqual( + wizard.vol_ids.mapped("nfe40_pesoB"), + [8], + "Unexpected value for the field nfe40_pesoB in Stock Invoice Onshipping.", + ) + self.assertEqual( + wizard.vol_ids.mapped("nfe40_pesoL"), + [12], + "Unexpected value for the field nfe40_pesoL in Stock Invoice Onshipping.", + ) + self.assertEqual( + wizard.vol_ids.mapped("nfe40_qVol"), + ["4"], + "Unexpected value for the field nfe40_qVol in Stock Invoice Onshipping.", + ) + + wizard.fiscal_operation_journal = False + wizard.action_generate() + + self.assertEqual(picking.invoice_state, "invoiced") + + # Fiscal details + volume_ids = picking.invoice_ids.fiscal_document_id.nfe40_vol + self.assertEqual( + volume_ids.mapped("nfe40_esp"), + ["esp2 teste"], + "Unexpected value for the field nfe40_esp in Fiscal Details.", + ) + self.assertEqual( + volume_ids.mapped("nfe40_marca"), + ["marca2 teste"], + "Unexpected value for the field nfe40_marca in Fiscal Details.", + ) + self.assertEqual( + volume_ids.mapped("nfe40_pesoB"), + [8], + "Unexpected value for the field nfe40_pesoB in Fiscal Details.", + ) + self.assertEqual( + volume_ids.mapped("nfe40_pesoL"), + [12], + "Unexpected value for the field nfe40_pesoL in Fiscal Details.", + ) + self.assertEqual( + volume_ids.mapped("nfe40_qVol"), + ["4"], + "Unexpected value for the field nfe40_qVol in Fiscal Details.", + ) + + # Number of Volumes + picking._compute_number_of_volumes() + self.assertEqual( + picking.number_of_volumes, + 4, + "Wrong number of volumes.", + ) + + # Testing Lucro Presumido - with pack + def test_invoicing_picking_volume_with_package_lucro_presumido(self): + """Test Invoicing Picking NFe volume - Lucro Presumido - with package""" + self.invoice_model = self.env["account.move"] + self.invoice_wizard = self.env["stock.invoice.onshipping"] + self.prod1 = self.env.ref("product.product_product_12") + self.prod2 = self.env.ref("product.product_product_16") + self._change_user_company(self.env.ref("l10n_br_base.empresa_lucro_presumido")) + picking = self.env.ref("l10n_br_stock_account.lucro_presumido-picking_2") + self.brand_id1 = self.env["product.brand"].create({"name": "marca teste"}) + + # Set product volume data + self.prod1.product_volume_type = "esp teste" + self.prod1.product_brand_id = self.brand_id1 + self.prod1.weight = 2 + self.prod2.weight = 4 + self.prod1.net_weight = 3 + self.prod2.net_weight = 6 + + # Number of Volumes - zero before invoice + self.assertEqual( + picking.number_of_volumes, + 0, + "No of Vols must be zero before invoicing.", + ) + + # Invoice + picking.set_to_be_invoiced() + picking.action_confirm() + # Check product availability + picking.action_assign() + # Put in pack + picking.action_put_in_pack() + # Validate + picking.button_validate() + + self.assertEqual(picking.state, "done") + wizard_obj = self.invoice_wizard.with_context( + active_ids=picking.ids, + active_model=picking._name, + active_id=picking.id, + ) + fields_list = wizard_obj.fields_get().keys() + wizard_values = wizard_obj.default_get(fields_list) + wizard = wizard_obj.create(wizard_values) + wizard.onchange_group() + + self.assertEqual( + wizard.vol_ids.mapped("nfe40_esp"), + ["esp teste"], + "Unexpected value for the field nfe40_esp in Stock Invoice Onshipping.", + ) + self.assertEqual( + wizard.vol_ids.mapped("nfe40_marca"), + ["marca teste"], + "Unexpected value for the field nfe40_marca in Stock Invoice Onshipping.", + ) + self.assertEqual( + wizard.vol_ids.mapped("nfe40_pesoB"), + [12], + "Unexpected value for the field nfe40_pesoB in Stock Invoice Onshipping.", + ) + self.assertEqual( + wizard.vol_ids.mapped("nfe40_pesoL"), + [18], + "Unexpected value for the field nfe40_pesoL in Stock Invoice Onshipping.", + ) + self.assertEqual( + wizard.vol_ids.mapped("nfe40_qVol"), + ["1"], + "Unexpected value for the field nfe40_qVol in Stock Invoice Onshipping.", + ) + + wizard.fiscal_operation_journal = False + wizard.action_generate() + + self.assertEqual(picking.invoice_state, "invoiced") + + # Fiscal details + volume_ids = picking.invoice_ids.fiscal_document_id.nfe40_vol + self.assertEqual( + volume_ids.mapped("nfe40_esp"), + ["esp teste"], + "Unexpected value for the field nfe40_esp in Fiscal Details.", + ) + self.assertEqual( + volume_ids.mapped("nfe40_marca"), + ["marca teste"], + "Unexpected value for the field nfe40_marca in Fiscal Details.", + ) + self.assertEqual( + volume_ids.mapped("nfe40_pesoB"), + [12], + "Unexpected value for the field nfe40_pesoB in Fiscal Details.", + ) + self.assertEqual( + volume_ids.mapped("nfe40_pesoL"), + [18], + "Unexpected value for the field nfe40_pesoL in Fiscal Details.", + ) + self.assertEqual( + volume_ids.mapped("nfe40_qVol"), + ["1"], + "Unexpected value for the field nfe40_qVol in Fiscal Details.", + ) + + # Number of Volumes + picking._compute_number_of_volumes() + self.assertEqual( + picking.number_of_volumes, + 1, + "Wrong number of volumes.", + ) + + def test_pre_generate_nfe_volume(self): + """Test pre generate nfe volume from picking""" + self.invoice_model = self.env["account.move"] + self.vol_generation_wizard = self.env["stock.generate.volumes"] + self.prod1 = self.env.ref("product.product_product_12") + self.prod2 = self.env.ref("product.product_product_16") + self._change_user_company(self.env.ref("l10n_br_base.empresa_lucro_presumido")) + picking = self.env.ref("l10n_br_stock_account.lucro_presumido-picking_2") + self.brand_id1 = self.env["product.brand"].create({"name": "marca teste"}) + + # Set product volume data + self.prod1.product_volume_type = "esp teste" + self.prod1.product_brand_id = self.brand_id1 + self.prod1.weight = 2 + self.prod2.weight = 4 + self.prod1.net_weight = 3 + self.prod2.net_weight = 6 + + # Check product availability + picking.action_confirm() + picking.action_assign() + # Put in pack + picking.action_put_in_pack() + # Validate + picking.button_validate() + + self.assertEqual(picking.state, "done") + wizard_obj = self.vol_generation_wizard.with_context( + active_ids=picking.ids, + active_model=picking._name, + active_id=picking.id, + ) + fields_list = wizard_obj.fields_get().keys() + wizard_values = wizard_obj.default_get(fields_list) + wizard = wizard_obj.create(wizard_values) + + self.assertEqual( + wizard.vol_ids.mapped("nfe40_qVol"), + ["1"], + "Unexpected value for the field nfe40_qVol in Stock Invoice Onshipping.", + ) + + wizard.action_generate() + + self.assertEqual( + picking.vol_ids.mapped("nfe40_qVol"), + ["1"], + "Unexpected value for the field nfe40_marca in Fiscal Details.", + ) + + self.assertEqual( + picking.number_of_volumes, + 1, + "Unexpected value for picking number_of_volumes.", + ) diff --git a/l10n_br_delivery_nfe/tests/test_delivery_nfe_grouped.py b/l10n_br_delivery_nfe/tests/test_delivery_nfe_grouped.py new file mode 100644 index 000000000000..9737520c6d68 --- /dev/null +++ b/l10n_br_delivery_nfe/tests/test_delivery_nfe_grouped.py @@ -0,0 +1,79 @@ +# Copyright (C) 2024 Diego Paradeda - KMEE +# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html + +from odoo.addons.l10n_br_stock_account.tests.common import TestBrPickingInvoicingCommon + + +class TestDeliveryNFe(TestBrPickingInvoicingCommon): + @classmethod + def setUpClass(cls): + super().setUpClass() + + # Test Grouped Picking + def test_invoicing_picking_volume_grouped_by_partner(self): + """Test Invoicing Picking NFe volume - Grouped by partner""" + self.invoice_model = self.env["account.move"] + self.invoice_wizard = self.env["stock.invoice.onshipping"] + self.prod1 = self.env.ref("product.product_product_12") + self.prod2 = self.env.ref("product.product_product_16") + self._change_user_company(self.env.ref("l10n_br_base.empresa_lucro_presumido")) + picking1 = self.env.ref("l10n_br_stock_account.lucro_presumido-picking_1") + picking2 = self.env.ref("l10n_br_stock_account.lucro_presumido-picking_2") + self.brand_id1 = self.env["product.brand"].create({"name": "marca teste"}) + self.brand_id2 = self.env["product.brand"].create({"name": "marca2 teste"}) + + # Ensure pickings have the same partner_id. This is a basic requirement + self.assertEqual( + picking1.partner_id, + picking2.partner_id, + "Partner must be the same for both pickings. This is a test " + "requirement.", + ) + + # Set product volume data + self.prod1.product_volume_type = "esp1 teste" + self.prod1.product_brand_id = self.brand_id1 + self.prod2.product_volume_type = "esp2 teste" + self.prod2.product_brand_id = self.brand_id2 + self.prod1.weight = 1 + self.prod2.weight = 3 + self.prod1.net_weight = 1 + self.prod2.net_weight = 5 + + # Validate: Picking 1 + picking1.set_to_be_invoiced() + self.picking_move_state(picking1) + self.assertEqual(picking1.state, "done") + + # Validate: Picking 2 + picking2.set_to_be_invoiced() + self.picking_move_state(picking2) + self.assertEqual(picking2.state, "done") + + # Invoice + wizard_obj = self.invoice_wizard.with_context( + active_ids=[picking1.id, picking2.id], + active_model=picking1._name, + ) + fields_list = wizard_obj.fields_get().keys() + wizard_values = wizard_obj.default_get(fields_list) + wizard = wizard_obj.create(wizard_values) + wizard.group = "partner" + wizard.onchange_group() + wizard.fiscal_operation_journal = False + wizard.action_generate() + self.assertEqual(picking1.invoice_state, "invoiced") + self.assertEqual(picking2.invoice_state, "invoiced") + + # Fiscal details + p1_doc_id = picking1.invoice_ids.fiscal_document_id + p2_doc_id = picking2.invoice_ids.fiscal_document_id + p1_volume_ids = p1_doc_id.nfe40_vol + p2_volume_ids = p2_doc_id.nfe40_vol + + self.assertEqual( + p1_volume_ids.mapped("nfe40_qVol"), + ["4", "4"], + "Unexpected value for the field nfe40_qVol in Fiscal Details.", + ) + self.assertEqual(p1_volume_ids, p2_volume_ids) diff --git a/l10n_br_delivery_nfe/views/product_product_view.xml b/l10n_br_delivery_nfe/views/product_product_view.xml new file mode 100644 index 000000000000..3ddddc3dd614 --- /dev/null +++ b/l10n_br_delivery_nfe/views/product_product_view.xml @@ -0,0 +1,13 @@ + + + + product.product + + + + + + + + + diff --git a/l10n_br_delivery_nfe/views/product_template_view.xml b/l10n_br_delivery_nfe/views/product_template_view.xml new file mode 100644 index 000000000000..8afdfdba26a5 --- /dev/null +++ b/l10n_br_delivery_nfe/views/product_template_view.xml @@ -0,0 +1,16 @@ + + + + product.template + + + + + + + + + diff --git a/l10n_br_delivery_nfe/views/stock_picking_view.xml b/l10n_br_delivery_nfe/views/stock_picking_view.xml new file mode 100644 index 000000000000..36a94634c79b --- /dev/null +++ b/l10n_br_delivery_nfe/views/stock_picking_view.xml @@ -0,0 +1,57 @@ + + + stock.picking + + + + + + + + + + + + +