diff --git a/l10n_br_account/models/account_move.py b/l10n_br_account/models/account_move.py index d77a9fcb3371..ec21dc9da459 100644 --- a/l10n_br_account/models/account_move.py +++ b/l10n_br_account/models/account_move.py @@ -383,6 +383,7 @@ def _compute_taxes_mapped(self, base_line): icmssn_range=base_line.icmssn_range_id, icms_origin=base_line.icms_origin, ind_final=base_line.ind_final, + inss_reduction_manual=base_line.inss_reduction_manual, ) return balance_taxes_res diff --git a/l10n_br_account/models/account_move_line.py b/l10n_br_account/models/account_move_line.py index 64bafca377a7..d82e0d148ea3 100644 --- a/l10n_br_account/models/account_move_line.py +++ b/l10n_br_account/models/account_move_line.py @@ -335,6 +335,7 @@ def _get_price_total_and_subtotal( icmssn_range=self.icmssn_range_id, icms_origin=self.icms_origin, ind_final=self.ind_final, + inss_reduction_manual=self.inss_reduction_manual, ), )._get_price_total_and_subtotal( price_unit=price_unit or self.price_unit, @@ -410,6 +411,7 @@ def _get_price_total_and_subtotal_model( icmssn_range=self.env.context.get("icmssn_range"), icms_origin=self.env.context.get("icms_origin"), ind_final=self.env.context.get("ind_final"), + inss_reduction_manual=self.env.context.get("inss_reduction_manual"), ) result["price_subtotal"] = taxes_res["total_excluded"] diff --git a/l10n_br_account/models/account_tax.py b/l10n_br_account/models/account_tax.py index f7ef89882900..2feb976b10fa 100644 --- a/l10n_br_account/models/account_tax.py +++ b/l10n_br_account/models/account_tax.py @@ -42,6 +42,7 @@ def compute_all( icmssn_range=None, icms_origin=None, ind_final=FINAL_CUSTOMER_NO, + inss_reduction_manual=None, ): """Returns all information required to apply taxes (in self + their children in case of a tax goup). @@ -113,6 +114,7 @@ def compute_all( icmssn_range=icmssn_range, icms_origin=icms_origin or product.icms_origin, ind_final=ind_final, + inss_reduction_manual=inss_reduction_manual, ) taxes_results["amount_tax_included"] = fiscal_taxes_results["amount_included"] diff --git a/l10n_br_base/models/res_company.py b/l10n_br_base/models/res_company.py index 4f6f4ee34db0..e3f2d765eb67 100644 --- a/l10n_br_base/models/res_company.py +++ b/l10n_br_base/models/res_company.py @@ -42,12 +42,10 @@ def _inverse_cnpj_cpf(self): for company in self: company.partner_id.cnpj_cpf = company.cnpj_cpf - def _inverse_state(self): + def _inverse_state_tax_number(self): """Write the l10n_br specific functional fields.""" for company in self: - company.partner_id.write( - {"state_id": company.state_id.id, "inscr_est": company.inscr_est} - ) + company.partner_id.inscr_est = company.inscr_est def _inverse_state_tax_number_ids(self): """Write the l10n_br specific functional fields.""" @@ -97,7 +95,7 @@ def _inverse_suframa(self): inscr_est = fields.Char( compute="_compute_address", - inverse="_inverse_state", + inverse="_inverse_state_tax_number", ) state_tax_number_ids = fields.One2many( diff --git a/l10n_br_fiscal/models/document_fiscal_line_mixin.py b/l10n_br_fiscal/models/document_fiscal_line_mixin.py index 0e69c8004e50..10241e8f4e24 100644 --- a/l10n_br_fiscal/models/document_fiscal_line_mixin.py +++ b/l10n_br_fiscal/models/document_fiscal_line_mixin.py @@ -801,6 +801,8 @@ def _operation_domain(self): inss_reduction = fields.Float(string="INSS % Reduction") + inss_reduction_manual = fields.Float(string="INSS % Manual Reduction") + inss_value = fields.Monetary(string="INSS Value") inss_wh_tax_id = fields.Many2one( diff --git a/l10n_br_fiscal/models/document_fiscal_line_mixin_methods.py b/l10n_br_fiscal/models/document_fiscal_line_mixin_methods.py index 00bd458e65c5..88d1e40478e8 100644 --- a/l10n_br_fiscal/models/document_fiscal_line_mixin_methods.py +++ b/l10n_br_fiscal/models/document_fiscal_line_mixin_methods.py @@ -212,6 +212,7 @@ def _compute_taxes(self, taxes, cst=None): icms_origin=self.icms_origin, icms_cst_id=self.icms_cst_id, ind_final=self.ind_final, + inss_reduction_manual=self.inss_reduction_manual, ) def _prepare_br_fiscal_dict(self, default=False): @@ -831,3 +832,7 @@ def _add_fields_to_amount(self): @api.model def _rm_fields_to_amount(self): return ["icms_relief_value"] + + @api.onchange("inss_reduction_manual") + def _onchange_inss_reduction_manual(self): + self._onchange_fiscal_tax_ids() diff --git a/l10n_br_fiscal/models/tax.py b/l10n_br_fiscal/models/tax.py index cc01d03a99e4..cc65929fd481 100644 --- a/l10n_br_fiscal/models/tax.py +++ b/l10n_br_fiscal/models/tax.py @@ -17,6 +17,7 @@ TAX_BASE_TYPE, TAX_BASE_TYPE_PERCENT, TAX_BASE_TYPE_VALUE, + TAX_DOMAIN_INSS, ) from ..constants.icms import ( ICMS_BASE_TYPE, @@ -572,6 +573,49 @@ def _compute_ipi(self, tax, taxes_dict, **kwargs): return self._compute_tax(tax, taxes_dict, **kwargs) + @api.model + def _compute_inss(self, tax, taxes_dict, **kwargs): + tax_dict = taxes_dict.get(tax.tax_domain) + company = kwargs.get("company") + currency = kwargs.get("currency", company.currency_id) + fiscal_price = kwargs.get("fiscal_price", 0.00) + fiscal_quantity = kwargs.get("fiscal_quantity", 0.00) + + if tax.tax_domain == TAX_DOMAIN_INSS: + if kwargs.get("inss_reduction_manual"): + if kwargs.get("inss_reduction_manual") > 0: + if tax_dict["base_type"] == "percent": + # Compute initial Tax Base for base_type Percent + base = currency.round(fiscal_price * fiscal_quantity) + + if tax_dict["base_type"] == "quantity": + # Compute initial Tax Base for base_type Quantity + base = fiscal_quantity + + if tax_dict["base_type"] == "fixed": + # Compute initial Tax Base + base = currency.round( + tax_dict["value_amount"] * fiscal_quantity + ) + + # Compute Tax Base Reduction + base_reduction = base * abs( + kwargs.get("inss_reduction_manual") / 100 + ) + + tax_dict["remove_from_base"] += base_reduction + tax_dict["percent_reduction"] = base_reduction + + tax_dict.update(self._compute_tax(tax, taxes_dict, **kwargs)) + + taxes_dict[tax.tax_domain].update( + self._compute_tax_base( + tax, taxes_dict.get(tax.tax_domain), **kwargs + ) + ) + + return self._compute_tax(tax, taxes_dict, **kwargs) + @api.model def _compute_tax_sequence(self, taxes_dict, **kwargs): """Método para calcular a ordem que os impostos serão calculados. diff --git a/l10n_br_fiscal/tests/test_icms_regulation.py b/l10n_br_fiscal/tests/test_icms_regulation.py index 3bd3b02cfa3a..ff4865a01e19 100644 --- a/l10n_br_fiscal/tests/test_icms_regulation.py +++ b/l10n_br_fiscal/tests/test_icms_regulation.py @@ -21,6 +21,15 @@ def setUp(self): self.ncm_48191000_id = self.env.ref("l10n_br_fiscal.ncm_48191000") self.ncm_energia_id = self.env.ref("l10n_br_fiscal.ncm_27160000") + def test_icms_sc_sp_ind_final_yes_default(self): + tax_icms = self.find_icms_tax( + in_state_id=self.sc_state_id, + out_state_id=self.sp_state_id, + ncm_id=self.ncm_48191000_id, + ind_final=FINAL_CUSTOMER_YES, + ) + self.assertEqual(tax_icms.percent_amount, 12.00) + def test_icms_sc_sc_ind_final_yes_default(self): tax_icms = self.find_icms_tax( in_state_id=self.sc_state_id, @@ -48,19 +57,28 @@ def test_icms_sc_sc_ind_final_yes_ncm_energia(self): ) self.assertEqual(tax_icms.percent_amount, 25.00) - def test_icms_sc_sp_ind_final_yes_default(self): - tax_icms = self.find_icms_tax( - in_state_id=self.sc_state_id, - out_state_id=self.sp_state_id, - ncm_id=self.ncm_48191000_id, - ind_final=FINAL_CUSTOMER_YES, + def find_icms_tax(self, in_state_id, out_state_id, ncm_id, ind_final): + company_inscr_est = ( + "497.846.721" if out_state_id.code == "SC" else self.company.inscr_est + ) + partner_inscr_est = ( + "495.723.657" if in_state_id.code == "SC" else self.partner.inscr_est ) - self.assertEqual(tax_icms.percent_amount, 12.00) - def find_icms_tax(self, in_state_id, out_state_id, ncm_id, ind_final): + if partner_inscr_est != self.partner.inscr_est: + self.partner.inscr_est = False + self.partner.state_id = in_state_id + self.partner.inscr_est = partner_inscr_est + else: + self.partner.state_id = in_state_id + + if company_inscr_est != self.company.inscr_est: + self.company.inscr_est = False + self.company.state_id = out_state_id + self.company.inscr_est = company_inscr_est + else: + self.company.state_id = out_state_id - self.partner.state_id = in_state_id - self.company.state_id = out_state_id self.product.ncm_id = ncm_id tax_icms = self.icms_regulation.map_tax_icms( diff --git a/l10n_br_fiscal/views/document_fiscal_line_mixin_view.xml b/l10n_br_fiscal/views/document_fiscal_line_mixin_view.xml index 257156db3431..a852308aa2f9 100644 --- a/l10n_br_fiscal/views/document_fiscal_line_mixin_view.xml +++ b/l10n_br_fiscal/views/document_fiscal_line_mixin_view.xml @@ -925,6 +925,10 @@ /> + `_. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us smashing it by providing a detailed and welcomed +`feedback `_. + +Do not contact contributors directly about support or help with technical issues. + +Credits +======= + +Authors +~~~~~~~ + +* KMEE + +Contributors +~~~~~~~~~~~~ + +* `KMEE `__: + + * Luis Felipe Mileo + * Gabriel Cardoso de Faria + * Luiz Felipe Divino + +Maintainers +~~~~~~~~~~~ + +This module is maintained by the OCA. + +.. image:: https://odoo-community.org/logo.png + :alt: Odoo Community Association + :target: https://odoo-community.org + +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. + +.. |maintainer-gabrielcardoso21| image:: https://github.com/gabrielcardoso21.png?size=40px + :target: https://github.com/gabrielcardoso21 + :alt: gabrielcardoso21 +.. |maintainer-mileo| image:: https://github.com/mileo.png?size=40px + :target: https://github.com/mileo + :alt: mileo + +Current `maintainers `__: + +|maintainer-gabrielcardoso21| |maintainer-mileo| + +This module is part of the `OCA/l10n-brazil `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/l10n_br_fiscal_queue/__init__.py b/l10n_br_fiscal_queue/__init__.py new file mode 100644 index 000000000000..e12d83df011d --- /dev/null +++ b/l10n_br_fiscal_queue/__init__.py @@ -0,0 +1,4 @@ +# Copyright 2017 KMEE INFORMATICA LTDA +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from . import model diff --git a/l10n_br_fiscal_queue/__manifest__.py b/l10n_br_fiscal_queue/__manifest__.py new file mode 100644 index 000000000000..23158dce4697 --- /dev/null +++ b/l10n_br_fiscal_queue/__manifest__.py @@ -0,0 +1,23 @@ +# Copyright 2017 KMEE INFORMATICA LTDA +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +{ + "name": "Fiscal Queue", + "summary": """ + Permite o envio assicrono de documentos fiscais""", + "version": "14.0.1.0.0", + "license": "AGPL-3", + "author": "KMEE,Odoo Community Association (OCA)", + "maintainers": ["gabrielcardoso21", "mileo"], + "development_status": "Beta", + "website": "https://github.com/OCA/l10n-brazil", + "depends": [ + "queue_job", + "l10n_br_fiscal", + ], + "data": [ + "views/operation_view.xml", + "views/subsequent_operation_view.xml", + "data/queue_job.xml", + ], +} diff --git a/l10n_br_fiscal_queue/data/queue_job.xml b/l10n_br_fiscal_queue/data/queue_job.xml new file mode 100644 index 000000000000..2c115ecefcec --- /dev/null +++ b/l10n_br_fiscal_queue/data/queue_job.xml @@ -0,0 +1,31 @@ + + + + + + eDocument Job + + + + + + + _send_document_job + + + + + + + _generate_subsequent_document_job + + + + + diff --git a/l10n_br_fiscal_queue/model/__init__.py b/l10n_br_fiscal_queue/model/__init__.py new file mode 100644 index 000000000000..30aff2febb4f --- /dev/null +++ b/l10n_br_fiscal_queue/model/__init__.py @@ -0,0 +1,7 @@ +# Copyright 2017 KMEE INFORMATICA LTDA +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from . import document +from . import operation +from . import subsequent_document +from . import subsequent_operation diff --git a/l10n_br_fiscal_queue/model/document.py b/l10n_br_fiscal_queue/model/document.py new file mode 100644 index 000000000000..f54117ed5726 --- /dev/null +++ b/l10n_br_fiscal_queue/model/document.py @@ -0,0 +1,34 @@ +# Copyright 2017 KMEE INFORMATICA LTDA +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +import logging + +from odoo import _, models + +_logger = logging.getLogger(__name__) + + +class FiscalDocument(models.Model): + _inherit = "l10n_br_fiscal.document" + + def _send_document_job(self): + for record in self: + record._eletronic_document_send() + + def _document_send(self): + no_electronic = self.filtered(lambda d: not d.document_electronic) + no_electronic._no_eletronic_document_send() + electronic = self - no_electronic + + send_now = electronic.filtered( + lambda documento: documento.fiscal_operation_id.queue_document_send + == "send_now" + ) + send_later = electronic - send_now + + if send_now: + _logger.info(_("Sending fiscal document now: %s", send_now.ids)) + send_now._send_document_job() + if send_later: + _logger.info(_("Sending fiscal document later: %s", send_later.ids)) + send_later.with_delay()._send_document_job() diff --git a/l10n_br_fiscal_queue/model/operation.py b/l10n_br_fiscal_queue/model/operation.py new file mode 100644 index 000000000000..f8b62ca40f09 --- /dev/null +++ b/l10n_br_fiscal_queue/model/operation.py @@ -0,0 +1,22 @@ +# Copyright 2017 KMEE INFORMATICA LTDA +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +import logging + +from odoo import fields, models + +_logger = logging.getLogger(__name__) + + +class FiscalOperation(models.Model): + _inherit = "l10n_br_fiscal.operation" + + queue_document_send = fields.Selection( + selection=[ + ("send_now", "Send Immediately"), + ("with_delay", "Send Later"), + ], + string="Transmission moment", + default="send_now", + required=True, + ) diff --git a/l10n_br_fiscal_queue/model/subsequent_document.py b/l10n_br_fiscal_queue/model/subsequent_document.py new file mode 100644 index 000000000000..0b05f8559402 --- /dev/null +++ b/l10n_br_fiscal_queue/model/subsequent_document.py @@ -0,0 +1,30 @@ +# Copyright 2018 KMEE INFORMATICA LTDA +# Gabriel Cardoso de Faria +# License AGPL-3 or later (http://www.gnu.org/licenses/agpl) + +import logging + +from odoo import _, models + +_logger = logging.getLogger(__name__) + + +class SubsequentDocument(models.Model): + _inherit = "l10n_br_fiscal.subsequent.document" + + def _generate_subsequent_document_job(self): + self._generate_subsequent_document() + + def generate_subsequent_document(self): + _logger.info(_("Generating fiscal document %s", self.ids)) + + if self.operacao_subsequente_id.queue_document_send == "send_now": + _logger.info( + _("Generating fiscal document now: %s", self.documento_origem_id.ids) + ) + self._generate_subsequent_document_job() + else: + _logger.info( + _("Generating fiscal document later: %s", self.documento_origem_id.ids) + ) + self.with_delay()._generate_subsequent_document_job() diff --git a/l10n_br_fiscal_queue/model/subsequent_operation.py b/l10n_br_fiscal_queue/model/subsequent_operation.py new file mode 100644 index 000000000000..bac1a2b9a878 --- /dev/null +++ b/l10n_br_fiscal_queue/model/subsequent_operation.py @@ -0,0 +1,18 @@ +# Copyright 2017 KMEE INFORMATICA LTDA +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import fields, models + + +class SubsequentOperation(models.Model): + _inherit = "l10n_br_fiscal.subsequent.operation" + + queue_document_send = fields.Selection( + selection=[ + ("send_now", "Send Immediately"), + ("with_delay", "Send Later"), + ], + string="Generate Document", + default="send_now", + required=True, + ) diff --git a/l10n_br_fiscal_queue/readme/CONFIGURE.rst b/l10n_br_fiscal_queue/readme/CONFIGURE.rst new file mode 100644 index 000000000000..679032c3c176 --- /dev/null +++ b/l10n_br_fiscal_queue/readme/CONFIGURE.rst @@ -0,0 +1 @@ +Para utilizar este módulo você precisa alterar o a forma de envio no cadastro de operações fiscais diff --git a/l10n_br_fiscal_queue/readme/CONTRIBUTORS.rst b/l10n_br_fiscal_queue/readme/CONTRIBUTORS.rst new file mode 100644 index 000000000000..7e9681698d2f --- /dev/null +++ b/l10n_br_fiscal_queue/readme/CONTRIBUTORS.rst @@ -0,0 +1,5 @@ +* `KMEE `__: + + * Luis Felipe Mileo + * Gabriel Cardoso de Faria + * Luiz Felipe Divino diff --git a/l10n_br_fiscal_queue/readme/DESCRIPTION.rst b/l10n_br_fiscal_queue/readme/DESCRIPTION.rst new file mode 100644 index 000000000000..167f795f6386 --- /dev/null +++ b/l10n_br_fiscal_queue/readme/DESCRIPTION.rst @@ -0,0 +1 @@ +Permite o envio assincrono de documentos fiscais diff --git a/l10n_br_fiscal_queue/readme/HISTORY.rst b/l10n_br_fiscal_queue/readme/HISTORY.rst new file mode 100644 index 000000000000..b38e0f9b5611 --- /dev/null +++ b/l10n_br_fiscal_queue/readme/HISTORY.rst @@ -0,0 +1,14 @@ +14.0.1.0.0 (2022) +~~~~~~~~~~~~~~~~~ + +Migrate to OCA + +12.0.1.0.0 (2021) +~~~~~~~~~~~~~~~~~ + +Migrate to OCA + +10.0.1.0.0 (2017) +~~~~~~~~~~~~~~~~~ + +First Version diff --git a/l10n_br_fiscal_queue/readme/INSTALL.rst b/l10n_br_fiscal_queue/readme/INSTALL.rst new file mode 100644 index 000000000000..5f39c4a3d4ea --- /dev/null +++ b/l10n_br_fiscal_queue/readme/INSTALL.rst @@ -0,0 +1 @@ +Este módulo depende do módulo l10n_br_fiscal e queue_job. diff --git a/l10n_br_fiscal_queue/readme/USAGE.rst b/l10n_br_fiscal_queue/readme/USAGE.rst new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/l10n_br_fiscal_queue/static/description/icon.png b/l10n_br_fiscal_queue/static/description/icon.png new file mode 100644 index 000000000000..3a0328b516c4 Binary files /dev/null and b/l10n_br_fiscal_queue/static/description/icon.png differ diff --git a/l10n_br_fiscal_queue/static/description/index.html b/l10n_br_fiscal_queue/static/description/index.html new file mode 100644 index 000000000000..089f24e001c0 --- /dev/null +++ b/l10n_br_fiscal_queue/static/description/index.html @@ -0,0 +1,455 @@ + + + + + + +Fiscal Queue + + + +
+

Fiscal Queue

+ + +

Beta License: AGPL-3 OCA/l10n-brazil Translate me on Weblate Try me on Runbot

+

Permite o envio assincrono de documentos fiscais

+

Table of contents

+ +
+

Installation

+

To install this module, you need to do nothing install and configure queue_job

+
+
+

Configuration

+

Para utilizar este módulo você precisa alterar o a forma de envio no cadastro de operações fiscais

+
+
+

Changelog

+
+

12.0.1.0.0 (2021)

+

Migrate to OCA

+
+
+

10.0.1.0.0 (2017)

+

First Version

+
+
+
+

Bug Tracker

+

Bugs are tracked on GitHub Issues. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us smashing it by providing a detailed and welcomed +feedback.

+

Do not contact contributors directly about support or help with technical issues.

+
+
+

Credits

+
+

Authors

+
    +
  • KMEE
  • +
+
+
+

Contributors

+ +
+
+

Maintainers

+

This module is maintained by the OCA.

+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.

+

Current maintainers:

+

gabrielcardoso21 mileo

+

This module is part of the OCA/l10n-brazil project on GitHub.

+

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

+
+
+
+ + diff --git a/l10n_br_fiscal_queue/views/operation_view.xml b/l10n_br_fiscal_queue/views/operation_view.xml new file mode 100644 index 000000000000..6ab388d382f8 --- /dev/null +++ b/l10n_br_fiscal_queue/views/operation_view.xml @@ -0,0 +1,21 @@ + + + + + + l10n_br_fiscal.operation.inherited_form in (l10n_br_fiscal_queue) + l10n_br_fiscal.operation + + + + + + + + + diff --git a/l10n_br_fiscal_queue/views/subsequent_operation_view.xml b/l10n_br_fiscal_queue/views/subsequent_operation_view.xml new file mode 100644 index 000000000000..592910822b7e --- /dev/null +++ b/l10n_br_fiscal_queue/views/subsequent_operation_view.xml @@ -0,0 +1,21 @@ + + + + + + l10n_br_fiscal.subsequent.operation.inherited_form in (l10n_br_fiscal_queue) + l10n_br_fiscal.subsequent.operation + + + + + + + + + diff --git a/l10n_br_nfse/models/res_partner.py b/l10n_br_nfse/models/res_partner.py index c0101ea157b2..50c047954cca 100644 --- a/l10n_br_nfse/models/res_partner.py +++ b/l10n_br_nfse/models/res_partner.py @@ -33,12 +33,17 @@ def prepare_partner_tomador(self, country_id): else: email = None + if self.inscr_est not in ("isento", "isenta", "ISENTO", "ISENTA"): + tomador_inscricao_estadual = misc.punctuation_rm(self.inscr_est or "") + else: + tomador_inscricao_estadual = None + return { "cnpj": tomador_cnpj, "cpf": tomador_cpf, "email": email, "inscricao_municipal": misc.punctuation_rm(self.inscr_mun or "") or None, - "inscricao_estadual": misc.punctuation_rm(self.inscr_est or "") or None, + "inscricao_estadual": tomador_inscricao_estadual or None, "razao_social": str(self.legal_name[:60] or ""), "endereco": str(self.street_name or self.street or ""), "numero": self.street_number or "", diff --git a/l10n_br_nfse_ginfes/README.rst b/l10n_br_nfse_ginfes/README.rst new file mode 100644 index 000000000000..1b1c7f9ad5ce --- /dev/null +++ b/l10n_br_nfse_ginfes/README.rst @@ -0,0 +1,108 @@ +============== +NFS-e (Ginfes) +============== + +.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png + :target: https://odoo-community.org/page/development-status + :alt: Beta +.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png + :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 +.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fl10n--brazil-lightgray.png?logo=github + :target: https://github.com/OCA/l10n-brazil/tree/12.0/l10n_br_nfse_ginfes + :alt: OCA/l10n-brazil +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/l10n-brazil-12-0/l10n-brazil-12-0-l10n_br_nfse_ginfes + :alt: Translate me on Weblate +.. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png + :target: https://runbot.odoo-community.org/runbot/124/12.0 + :alt: Try me on Runbot + +|badge1| |badge2| |badge3| |badge4| |badge5| + +Esse módulo completa o documento criado pelo l10n_br_nfse para permite a criação e transmissão de Notas Fiscais de Serviço Eletrônicas (NFS-e) pelo sistema GINFES. + +**Table of contents** + +.. contents:: + :local: + +Installation +============ + +* Este módulo tem uma depedencia do pacote python erpbrasil.edoc +* Este módulo tem uma depedencia do pacote python erpbrasil.assinatura +* Este módulo tem uma depedencia do pacote python erpbrasil.transmissao +* Este módulo tem uma depedencia do pacote python erpbrasil.base +* Este módulo tem uma depedencia do pacote python nfselib + +Configuration +============= + +É apenas necessário a instalação e configuração do módulo l10n_br_nfse. + +Usage +===== + +Após ser criado uma Nota Fiscal de Serviço Eletrônicas (NFS-e) é possível confirmá-la e transmiti-la. + +Bug Tracker +=========== + +Bugs are tracked on `GitHub Issues `_. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us smashing it by providing a detailed and welcomed +`feedback `_. + +Do not contact contributors directly about support or help with technical issues. + +Credits +======= + +Authors +~~~~~~~ + +* KMEE + +Contributors +~~~~~~~~~~~~ + +* Luis Felipe Mileo +* Gabriel Cardoso de Faria +* Luis Otavio Malta Conceição + +Maintainers +~~~~~~~~~~~ + +This module is maintained by the OCA. + +.. image:: https://odoo-community.org/logo.png + :alt: Odoo Community Association + :target: https://odoo-community.org + +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. + +.. |maintainer-gabrielcardoso21| image:: https://github.com/gabrielcardoso21.png?size=40px + :target: https://github.com/gabrielcardoso21 + :alt: gabrielcardoso21 +.. |maintainer-mileo| image:: https://github.com/mileo.png?size=40px + :target: https://github.com/mileo + :alt: mileo +.. |maintainer-luismalta| image:: https://github.com/luismalta.png?size=40px + :target: https://github.com/luismalta + :alt: luismalta + +Current `maintainers `__: + +|maintainer-gabrielcardoso21| |maintainer-mileo| |maintainer-luismalta| + +This module is part of the `OCA/l10n-brazil `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/l10n_br_nfse_ginfes/__init__.py b/l10n_br_nfse_ginfes/__init__.py new file mode 100644 index 000000000000..0ee8b5073e26 --- /dev/null +++ b/l10n_br_nfse_ginfes/__init__.py @@ -0,0 +1,2 @@ +from . import models +from . import tests diff --git a/l10n_br_nfse_ginfes/__manifest__.py b/l10n_br_nfse_ginfes/__manifest__.py new file mode 100644 index 000000000000..a4e4ea10fb6d --- /dev/null +++ b/l10n_br_nfse_ginfes/__manifest__.py @@ -0,0 +1,27 @@ +# Copyright 2019 KMEE INFORMATICA LTDA +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +{ + "name": "NFS-e (Ginfes)", + "summary": """ + NFS-e (Ginfes)""", + "version": "14.0.1.0.0", + "license": "AGPL-3", + "author": "KMEE, Odoo Community Association (OCA)", + "maintainers": ["gabrielcardoso21", "mileo", "luismalta"], + "website": "https://github.com/OCA/l10n-brazil", + "development_status": "Beta", + "external_dependencies": { + "python": [ + "erpbrasil.edoc", + "erpbrasil.assinatura", + "erpbrasil.transmissao", + "erpbrasil.base>=2.3.0", + "nfselib.ginfes", + "xmldiff", + ], + }, + "depends": [ + "l10n_br_nfse", + ], +} diff --git a/l10n_br_nfse_ginfes/constants/ginfes.py b/l10n_br_nfse_ginfes/constants/ginfes.py new file mode 100644 index 000000000000..d152a2216747 --- /dev/null +++ b/l10n_br_nfse_ginfes/constants/ginfes.py @@ -0,0 +1,17 @@ +RECEPCIONAR_LOTE_RPS = ["RecepcionarLoteRpsV3", "RecepcionarLoteRps"] + +CONSULTAR_SITUACAO_LOTE_RPS = ["ConsultarSituacaoLoteRpsV3", "ConsultarSituacaoLoteRps"] + +CANCELAR_NFSE = ( + [ + "CancelarNfseV3", + "CancelarNfse", + ], +) + +CONSULTAR_NFSE_POR_RPS = ( + [ + "ConsultarNfsePorRpsV3", + "ConsultarNfsePorRps", + ], +) diff --git a/l10n_br_nfse_ginfes/i18n/l10n_br_nfse_ginfes.pot b/l10n_br_nfse_ginfes/i18n/l10n_br_nfse_ginfes.pot new file mode 100644 index 000000000000..0fd8321915ed --- /dev/null +++ b/l10n_br_nfse_ginfes/i18n/l10n_br_nfse_ginfes.pot @@ -0,0 +1,69 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * l10n_br_nfse_ginfes +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 12.0\n" +"Report-Msgid-Bugs-To: \n" +"Last-Translator: <>\n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: \n" + +#. module: l10n_br_nfse_ginfes +#: code:addons/l10n_br_nfse_ginfes/models/document.py:300 +#, python-format +msgid "Batch not yet processed" +msgstr "" + +#. module: l10n_br_nfse_ginfes +#: model:ir.model,name:l10n_br_nfse_ginfes.model_res_company +msgid "Companies" +msgstr "" + +#. module: l10n_br_nfse_ginfes +#: model:ir.model,name:l10n_br_nfse_ginfes.model_l10n_br_fiscal_document +msgid "Fiscal Document" +msgstr "" + +#. module: l10n_br_nfse_ginfes +#: selection:res.company,provedor_nfse:0 +msgid "Ginfes" +msgstr "" + +#. module: l10n_br_nfse_ginfes +#: selection:res.company,provedor_nfse:0 +msgid "ISSNet / Nota Control" +msgstr "" + +#. module: l10n_br_nfse_ginfes +#: model:ir.model.fields,field_description:l10n_br_nfse_ginfes.field_res_company__provedor_nfse +msgid "NFSe Provider" +msgstr "" + +#. module: l10n_br_nfse_ginfes +#: code:addons/l10n_br_nfse_ginfes/models/document.py:297 +#, python-format +msgid "Not received" +msgstr "" + +#. module: l10n_br_nfse_ginfes +#: selection:res.company,provedor_nfse:0 +msgid "Paulistana" +msgstr "" + +#. module: l10n_br_nfse_ginfes +#: code:addons/l10n_br_nfse_ginfes/models/document.py:303 +#, python-format +msgid "Processed with Error" +msgstr "" + +#. module: l10n_br_nfse_ginfes +#: code:addons/l10n_br_nfse_ginfes/models/document.py:306 +#, python-format +msgid "Successfully Processed" +msgstr "" + diff --git a/l10n_br_nfse_ginfes/i18n/pt_BR.po b/l10n_br_nfse_ginfes/i18n/pt_BR.po new file mode 100644 index 000000000000..93216b96446a --- /dev/null +++ b/l10n_br_nfse_ginfes/i18n/pt_BR.po @@ -0,0 +1,61 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * l10n_br_nfse_ginfes +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 12.0\n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: 2020-12-17 05:24+0000\n" +"Last-Translator: Marcel Savegnago \n" +"Language-Team: none\n" +"Language: pt_BR\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: nplurals=2; plural=n > 1;\n" +"X-Generator: Weblate 4.3.2\n" + +#. module: l10n_br_nfse_ginfes +#: model:ir.model,name:l10n_br_nfse_ginfes.model_res_company +msgid "Companies" +msgstr "Empresas" + +#. module: l10n_br_nfse_ginfes +#: model:ir.model,name:l10n_br_nfse_ginfes.model_l10n_br_fiscal_document +msgid "Fiscal Document" +msgstr "Documento Fiscal" + +#. module: l10n_br_nfse_ginfes +#: selection:res.company,provedor_nfse:0 +msgid "Ginfes" +msgstr "Ginfes" + +#. module: l10n_br_nfse_ginfes +#: code:addons/l10n_br_nfse_ginfes/models/document.py:307 +#, python-format +msgid "Lote ainda não processado" +msgstr "Lote ainda não processado" + +#. module: l10n_br_nfse_ginfes +#: model:ir.model.fields,field_description:l10n_br_nfse_ginfes.field_res_company__provedor_nfse +msgid "NFSe Provider" +msgstr "Provedor NFSe" + +#. module: l10n_br_nfse_ginfes +#: code:addons/l10n_br_nfse_ginfes/models/document.py:304 +#, python-format +msgid "Não Recebido" +msgstr "Não Recebido" + +#. module: l10n_br_nfse_ginfes +#: code:addons/l10n_br_nfse_ginfes/models/document.py:310 +#, python-format +msgid "Procesado com Erro" +msgstr "Procesado com Erro" + +#. module: l10n_br_nfse_ginfes +#: code:addons/l10n_br_nfse_ginfes/models/document.py:313 +#, python-format +msgid "Procesado com Sucesso" +msgstr "Procesado com Sucesso" diff --git a/l10n_br_nfse_ginfes/models/__init__.py b/l10n_br_nfse_ginfes/models/__init__.py new file mode 100644 index 000000000000..166dd3bd2493 --- /dev/null +++ b/l10n_br_nfse_ginfes/models/__init__.py @@ -0,0 +1,2 @@ +from . import document +from . import res_company diff --git a/l10n_br_nfse_ginfes/models/document.py b/l10n_br_nfse_ginfes/models/document.py new file mode 100644 index 000000000000..3752523db11c --- /dev/null +++ b/l10n_br_nfse_ginfes/models/document.py @@ -0,0 +1,394 @@ +# Copyright 2019 KMEE INFORMATICA LTDA +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + + +from nfselib.ginfes.v3_01.servico_enviar_lote_rps_envio import ( + EnviarLoteRpsEnvio, + ListaRpsType, + tcCpfCnpj, + tcDadosServico, + tcDadosTomador, + tcEndereco, + tcIdentificacaoPrestador, + tcIdentificacaoRps, + tcIdentificacaoTomador, + tcInfRps, + tcLoteRps, + tcRps, + tcValores, +) + +from odoo import _, models +from odoo.exceptions import UserError + +from odoo.addons.l10n_br_fiscal.constants.fiscal import ( + EVENT_ENV_HML, + EVENT_ENV_PROD, + MODELO_FISCAL_NFSE, + PROCESSADOR_OCA, + SITUACAO_EDOC_AUTORIZADA, + SITUACAO_EDOC_REJEITADA, +) + +from ..constants.ginfes import CONSULTAR_SITUACAO_LOTE_RPS, RECEPCIONAR_LOTE_RPS + + +def filter_oca_nfse(record): + if record.processador_edoc == PROCESSADOR_OCA and record.document_type_id.code in [ + MODELO_FISCAL_NFSE, + ]: + return True + return False + + +def filter_ginfes(record): + if record.company_id.provedor_nfse == "ginfes": + return True + return False + + +class Document(models.Model): + + _inherit = "l10n_br_fiscal.document" + + def _serialize(self, edocs): + edocs = super()._serialize(edocs) + for record in self.filtered(filter_oca_nfse).filtered(filter_ginfes): + edocs.append(record.serialize_nfse_ginfes()) + return edocs + + def _serialize_ginfes_dados_servico(self): + self.fiscal_line_ids.ensure_one() + dados = self._prepare_dados_servico() + return tcDadosServico( + Valores=tcValores( + ValorServicos=self.convert_type_nfselib( + tcValores, "ValorServicos", dados["valor_servicos"] + ), + ValorDeducoes=self.convert_type_nfselib( + tcValores, "ValorDeducoes", dados["valor_deducoes"] + ), + ValorPis=self.convert_type_nfselib( + tcValores, "ValorPis", dados["valor_pis"] + ), + ValorCofins=self.convert_type_nfselib( + tcValores, "ValorCofins", dados["valor_cofins"] + ), + ValorInss=self.convert_type_nfselib( + tcValores, "ValorInss", dados["valor_inss"] + ), + ValorIr=self.convert_type_nfselib( + tcValores, "ValorIr", dados["valor_ir"] + ), + ValorCsll=self.convert_type_nfselib( + tcValores, "ValorIr", dados["valor_csll"] + ), + IssRetido=self.convert_type_nfselib( + tcValores, "IssRetido", dados["iss_retido"] + ), + ValorIss=self.convert_type_nfselib( + tcValores, "ValorIss", dados["valor_iss"] + ), + ValorIssRetido=self.convert_type_nfselib( + tcValores, "ValorIssRetido", dados["valor_iss_retido"] + ), + OutrasRetencoes=self.convert_type_nfselib( + tcValores, "OutrasRetencoes", dados["outras_retencoes"] + ), + BaseCalculo=self.convert_type_nfselib( + tcValores, "BaseCalculo", dados["base_calculo"] + ), + Aliquota=self.convert_type_nfselib( + tcValores, "Aliquota", dados["aliquota"] + ), + ValorLiquidoNfse=self.convert_type_nfselib( + tcValores, "ValorLiquidoNfse", dados["valor_liquido_nfse"] + ), + ), + ItemListaServico=self.convert_type_nfselib( + tcDadosServico, "ItemListaServico", dados["item_lista_servico"] + ), + CodigoCnae=self.convert_type_nfselib( + tcDadosServico, "CodigoCnae", dados["codigo_cnae"] + ), + CodigoTributacaoMunicipio=self.convert_type_nfselib( + tcDadosServico, + "CodigoTributacaoMunicipio", + dados["codigo_tributacao_municipio"], + ), + Discriminacao=self.convert_type_nfselib( + tcDadosServico, "Discriminacao", dados["discriminacao"] + ), + CodigoMunicipio=self.convert_type_nfselib( + tcDadosServico, "CodigoMunicipio", dados["codigo_municipio"] + ), + ) + + def _serialize_ginfes_dados_tomador(self): + dados = self._prepare_dados_tomador() + return tcDadosTomador( + IdentificacaoTomador=tcIdentificacaoTomador( + CpfCnpj=tcCpfCnpj( + Cnpj=self.convert_type_nfselib(tcCpfCnpj, "Cnpj", dados["cnpj"]), + Cpf=self.convert_type_nfselib(tcCpfCnpj, "Cpf", dados["cpf"]), + ), + InscricaoMunicipal=self.convert_type_nfselib( + tcIdentificacaoTomador, + "InscricaoMunicipal", + dados["inscricao_municipal"], + ), + ), + RazaoSocial=self.convert_type_nfselib( + tcDadosTomador, "RazaoSocial", dados["razao_social"] + ), + Endereco=tcEndereco( + Endereco=self.convert_type_nfselib( + tcEndereco, "Endereco", dados["endereco"] + ), + Numero=self.convert_type_nfselib(tcEndereco, "Numero", dados["numero"]), + Complemento=self.convert_type_nfselib( + tcEndereco, "Complemento", dados["complemento"] + ), + Bairro=self.convert_type_nfselib(tcEndereco, "Bairro", dados["bairro"]), + CodigoMunicipio=self.convert_type_nfselib( + tcEndereco, "CodigoMunicipio", dados["codigo_municipio"] + ), + Uf=self.convert_type_nfselib(tcEndereco, "Uf", dados["uf"]), + Cep=self.convert_type_nfselib(tcEndereco, "Cep", dados["cep"]), + ) + or None, + ) + + def _serialize_ginfes_rps(self, dados): + + return tcRps( + InfRps=tcInfRps( + Id=dados["id"], + IdentificacaoRps=tcIdentificacaoRps( + Numero=self.convert_type_nfselib( + tcIdentificacaoRps, "Numero", dados["numero"] + ), + Serie=self.convert_type_nfselib( + tcIdentificacaoRps, "Serie", dados["serie"] + ), + Tipo=self.convert_type_nfselib( + tcIdentificacaoRps, "Tipo", dados["tipo"] + ), + ), + DataEmissao=self.convert_type_nfselib( + tcInfRps, "DataEmissao", dados["date_in_out"] + ), + NaturezaOperacao=self.convert_type_nfselib( + tcInfRps, "NaturezaOperacao", dados["natureza_operacao"] + ), + RegimeEspecialTributacao=self.convert_type_nfselib( + tcInfRps, + "RegimeEspecialTributacao", + dados["regime_especial_tributacao"], + ), + OptanteSimplesNacional=self.convert_type_nfselib( + tcInfRps, + "OptanteSimplesNacional", + dados["optante_simples_nacional"], + ), + IncentivadorCultural=self.convert_type_nfselib( + tcInfRps, "IncentivadorCultural", dados["incentivador_cultural"] + ), + Status=self.convert_type_nfselib(tcInfRps, "Status", dados["status"]), + RpsSubstituido=self.convert_type_nfselib( + tcInfRps, "RpsSubstituido", dados["rps_substitiuido"] + ), + Servico=self._serialize_ginfes_dados_servico(), + Prestador=tcIdentificacaoPrestador( + Cnpj=self.convert_type_nfselib( + tcIdentificacaoPrestador, "InscricaoMunicipal", dados["cnpj"] + ), + InscricaoMunicipal=self.convert_type_nfselib( + tcIdentificacaoPrestador, + "InscricaoMunicipal", + dados["inscricao_municipal"], + ), + ), + Tomador=self._serialize_ginfes_dados_tomador(), + IntermediarioServico=self.convert_type_nfselib( + tcInfRps, "IntermediarioServico", dados["intermediario_servico"] + ), + ConstrucaoCivil=self.convert_type_nfselib( + tcInfRps, "ConstrucaoCivil", dados["construcao_civil"] + ), + ) + ) + + def _serialize_ginfes_lote_rps(self): + dados = self._prepare_lote_rps() + return tcLoteRps( + Cnpj=self.convert_type_nfselib(tcLoteRps, "Cnpj", dados["cnpj"]), + InscricaoMunicipal=self.convert_type_nfselib( + tcLoteRps, "InscricaoMunicipal", dados["inscricao_municipal"] + ), + QuantidadeRps=1, + ListaRps=ListaRpsType(Rps=[self._serialize_ginfes_rps(dados)]), + ) + + def serialize_nfse_ginfes(self): + lote_rps = EnviarLoteRpsEnvio(LoteRps=self._serialize_ginfes_lote_rps()) + return lote_rps + + def cancel_document_ginfes(self): + for record in self.filtered(filter_oca_nfse).filtered(filter_ginfes): + processador = record._processador_erpbrasil_nfse() + processo = processador.cancela_documento( + doc_numero=int(record.document_number) + ) + + status, message = processador.analisa_retorno_cancelamento(processo) + + if not status: + raise UserError(_(message)) + + record.cancel_event_id = record.event_ids.create_event_save_xml( + company_id=record.company_id, + environment=( + EVENT_ENV_PROD if self.nfe_environment == "1" else EVENT_ENV_HML + ), + event_type="2", + xml_file=processo.envio_xml.decode("utf-8"), + document_id=record, + ) + + return status + + def _document_status(self): + for record in self.filtered(filter_oca_nfse): + processador = record._processador_erpbrasil_nfse() + processo = processador.consulta_nfse_rps( + rps_number=int(record.rps_number), + rps_serie=record.document_serie, + rps_type=int(record.rps_type), + ) + + return _( + processador.analisa_retorno_consulta( + processo, + record.document_number, + record.company_cnpj_cpf, + record.company_legal_name, + ) + ) + + @staticmethod + def _get_protocolo(record, processador, vals): + for edoc in record.serialize(): + processo = None + for p in processador.processar_documento(edoc): + processo = p + + if processo.webservice in RECEPCIONAR_LOTE_RPS: + if processo.resposta.Protocolo is None: + mensagem_completa = "" + if processo.resposta.ListaMensagemRetorno: + lista_msgs = processo.resposta.ListaMensagemRetorno + for mr in lista_msgs.MensagemRetorno: + + correcao = "" + if mr.Correcao: + correcao = mr.Correcao + + mensagem_completa += ( + mr.Codigo + + " - " + + mr.Mensagem + + " - Correção: " + + correcao + + "\n" + ) + vals["edoc_error_message"] = mensagem_completa + record._change_state(SITUACAO_EDOC_REJEITADA) + record.write(vals) + return + protocolo = processo.resposta.Protocolo + + if processo.webservice in CONSULTAR_SITUACAO_LOTE_RPS: + vals["status_code"] = processo.resposta.Situacao + + return vals, protocolo + + @staticmethod + def _set_response(record, processador, protocolo, vals): + processo = processador.consultar_lote_rps(protocolo) + + if processo.resposta: + mensagem_completa = "" + if processo.resposta.ListaMensagemRetorno: + lista_msgs = processo.resposta.ListaMensagemRetorno + for mr in lista_msgs.MensagemRetorno: + + correcao = "" + if mr.Correcao: + correcao = mr.Correcao + + mensagem_completa += ( + mr.Codigo + + " - " + + mr.Mensagem + + " - Correção: " + + correcao + + "\n" + ) + vals["edoc_error_message"] = mensagem_completa + if vals.get("status_code") == 3: + record._change_state(SITUACAO_EDOC_REJEITADA) + + if processo.resposta.ListaNfse: + xml_file = processo.retorno + for comp in processo.resposta.ListaNfse.CompNfse: + vals["document_number"] = comp.Nfse.InfNfse.Numero + vals["authorization_date"] = comp.Nfse.InfNfse.DataEmissao + vals["verify_code"] = comp.Nfse.InfNfse.CodigoVerificacao + record.authorization_event_id.set_done( + status_code=vals["status_code"], + response=vals["status_name"], + protocol_date=vals["authorization_date"], + protocol_number=protocolo, + file_response_xml=xml_file, + ) + record._change_state(SITUACAO_EDOC_AUTORIZADA) + + return vals + + def _eletronic_document_send(self): + super()._eletronic_document_send() + for record in self.filtered(filter_oca_nfse).filtered(filter_ginfes): + processador = record._processador_erpbrasil_nfse() + + protocolo = record.authorization_protocol + vals = dict() + + if not protocolo: + vals, protocolo = self._get_protocolo(record, processador, vals) + + else: + vals["status_code"] = 4 + + if vals.get("status_code") == 1: + vals["status_name"] = _("Not received") + + elif vals.get("status_code") == 2: + vals["status_name"] = _("Batch not yet processed") + + elif vals.get("status_code") == 3: + vals["status_name"] = _("Processed with Error") + + elif vals.get("status_code") == 4: + vals["status_name"] = _("Successfully Processed") + vals["authorization_protocol"] = protocolo + + if vals.get("status_code") in (3, 4): + vals = self._set_response(record, processador, protocolo, vals) + + record.write(vals) + return + + def _exec_before_SITUACAO_EDOC_CANCELADA(self, old_state, new_state): + super()._exec_before_SITUACAO_EDOC_CANCELADA(old_state, new_state) + return self.cancel_document_ginfes() diff --git a/l10n_br_nfse_ginfes/models/res_company.py b/l10n_br_nfse_ginfes/models/res_company.py new file mode 100644 index 000000000000..3c6445fa1429 --- /dev/null +++ b/l10n_br_nfse_ginfes/models/res_company.py @@ -0,0 +1,15 @@ +# Copyright 2020 - TODAY, Marcel Savegnago - Escodoo +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import fields, models + + +class ResCompany(models.Model): + + _inherit = "res.company" + + provedor_nfse = fields.Selection( + selection_add=[ + ("ginfes", "Ginfes"), + ] + ) diff --git a/l10n_br_nfse_ginfes/readme/CONFIGURE.rst b/l10n_br_nfse_ginfes/readme/CONFIGURE.rst new file mode 100644 index 000000000000..a827a70c7ee1 --- /dev/null +++ b/l10n_br_nfse_ginfes/readme/CONFIGURE.rst @@ -0,0 +1 @@ +É apenas necessário a instalação e configuração do módulo l10n_br_nfse. diff --git a/l10n_br_nfse_ginfes/readme/CONTRIBUTORS.rst b/l10n_br_nfse_ginfes/readme/CONTRIBUTORS.rst new file mode 100644 index 000000000000..72e4abef82ed --- /dev/null +++ b/l10n_br_nfse_ginfes/readme/CONTRIBUTORS.rst @@ -0,0 +1,3 @@ +* Luis Felipe Mileo +* Gabriel Cardoso de Faria +* Luis Otavio Malta Conceição diff --git a/l10n_br_nfse_ginfes/readme/DESCRIPTION.rst b/l10n_br_nfse_ginfes/readme/DESCRIPTION.rst new file mode 100644 index 000000000000..025d04716349 --- /dev/null +++ b/l10n_br_nfse_ginfes/readme/DESCRIPTION.rst @@ -0,0 +1 @@ +Esse módulo completa o documento criado pelo l10n_br_nfse para permite a criação e transmissão de Notas Fiscais de Serviço Eletrônicas (NFS-e) pelo sistema GINFES. diff --git a/l10n_br_nfse_ginfes/readme/INSTALL.rst b/l10n_br_nfse_ginfes/readme/INSTALL.rst new file mode 100644 index 000000000000..e2c08a6648de --- /dev/null +++ b/l10n_br_nfse_ginfes/readme/INSTALL.rst @@ -0,0 +1,5 @@ +* Este módulo tem uma depedencia do pacote python erpbrasil.edoc +* Este módulo tem uma depedencia do pacote python erpbrasil.assinatura +* Este módulo tem uma depedencia do pacote python erpbrasil.transmissao +* Este módulo tem uma depedencia do pacote python erpbrasil.base +* Este módulo tem uma depedencia do pacote python nfselib diff --git a/l10n_br_nfse_ginfes/readme/ROADMAP.rst b/l10n_br_nfse_ginfes/readme/ROADMAP.rst new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/l10n_br_nfse_ginfes/readme/USAGE.rst b/l10n_br_nfse_ginfes/readme/USAGE.rst new file mode 100644 index 000000000000..f217aefe6260 --- /dev/null +++ b/l10n_br_nfse_ginfes/readme/USAGE.rst @@ -0,0 +1 @@ +Após ser criado uma Nota Fiscal de Serviço Eletrônicas (NFS-e) é possível confirmá-la e transmiti-la. diff --git a/l10n_br_nfse_ginfes/static/description/icon.png b/l10n_br_nfse_ginfes/static/description/icon.png new file mode 100644 index 000000000000..3a0328b516c4 Binary files /dev/null and b/l10n_br_nfse_ginfes/static/description/icon.png differ diff --git a/l10n_br_nfse_ginfes/static/description/index.html b/l10n_br_nfse_ginfes/static/description/index.html new file mode 100644 index 000000000000..0dff68800e59 --- /dev/null +++ b/l10n_br_nfse_ginfes/static/description/index.html @@ -0,0 +1,444 @@ + + + + + + +NFS-e (Ginfes) + + + +
+

NFS-e (Ginfes)

+ + +

Beta License: AGPL-3 OCA/l10n-brazil Translate me on Weblate Try me on Runbot

+

Esse módulo completa o documento criado pelo l10n_br_nfse para permite a criação e transmissão de Notas Fiscais de Serviço Eletrônicas (NFS-e) pelo sistema GINFES.

+

Table of contents

+ +
+

Installation

+
    +
  • Este módulo tem uma depedencia do pacote python erpbrasil.edoc
  • +
  • Este módulo tem uma depedencia do pacote python erpbrasil.assinatura
  • +
  • Este módulo tem uma depedencia do pacote python erpbrasil.transmissao
  • +
  • Este módulo tem uma depedencia do pacote python erpbrasil.base
  • +
  • Este módulo tem uma depedencia do pacote python nfselib
  • +
+
+
+

Configuration

+

É apenas necessário a instalação e configuração do módulo l10n_br_nfse.

+
+
+

Usage

+

Após ser criado uma Nota Fiscal de Serviço Eletrônicas (NFS-e) é possível confirmá-la e transmiti-la.

+
+
+

Bug Tracker

+

Bugs are tracked on GitHub Issues. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us smashing it by providing a detailed and welcomed +feedback.

+

Do not contact contributors directly about support or help with technical issues.

+
+
+

Credits

+
+

Authors

+
    +
  • KMEE
  • +
+
+
+

Contributors

+ +
+
+

Maintainers

+

This module is maintained by the OCA.

+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.

+

Current maintainers:

+

gabrielcardoso21 mileo luismalta

+

This module is part of the OCA/l10n-brazil project on GitHub.

+

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

+
+
+
+ + diff --git a/l10n_br_nfse_ginfes/static/src/img/itajuba.png b/l10n_br_nfse_ginfes/static/src/img/itajuba.png new file mode 100644 index 000000000000..b327d17fa2af Binary files /dev/null and b/l10n_br_nfse_ginfes/static/src/img/itajuba.png differ diff --git a/l10n_br_nfse_ginfes/tests/__init__.py b/l10n_br_nfse_ginfes/tests/__init__.py new file mode 100644 index 000000000000..333821c2330f --- /dev/null +++ b/l10n_br_nfse_ginfes/tests/__init__.py @@ -0,0 +1 @@ +from . import test_fiscal_document_nfse_ginfes diff --git a/l10n_br_nfse_ginfes/tests/nfse/001_50_nfse.xml b/l10n_br_nfse_ginfes/tests/nfse/001_50_nfse.xml new file mode 100644 index 000000000000..fa03e595763c --- /dev/null +++ b/l10n_br_nfse_ginfes/tests/nfse/001_50_nfse.xml @@ -0,0 +1,71 @@ + + + 59594315000157 + 35172 + 1 + + + + + 50 + 001 + 1 + + 2020-06-04T13:58:46 + 1 + 1 + 1 + 2 + 1 + + + 100 + 0 + 0 + 0 + 0 + 0 + 0 + 2 + 5 + 0 + 0 + 0 + 0.05 + 100 + + 105 + 3101200 + 6311900 + [ODOO_DEV] Customized Odoo Development + 3132404 + + + 59594315000157 + 35172 + + + + + 81493979000189 + + + Cliente 1 SP + + Rua Samuel Morse + 135 + Brooklin + 3550308 + SP + 4576060 + + + + + + + diff --git a/l10n_br_nfse_ginfes/tests/test_fiscal_document_nfse_ginfes.py b/l10n_br_nfse_ginfes/tests/test_fiscal_document_nfse_ginfes.py new file mode 100644 index 000000000000..0443a5b6b122 --- /dev/null +++ b/l10n_br_nfse_ginfes/tests/test_fiscal_document_nfse_ginfes.py @@ -0,0 +1,71 @@ +# Copyright 2020 KMEE INFORMATICA LTDA +# Gabriel Cardoso de Faria +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +import logging +import os +from datetime import datetime + +from xmldiff import main + +from odoo.tools import config + +from odoo.addons.l10n_br_nfse.tests.test_fiscal_document_nfse_common import ( + TestFiscalDocumentNFSeCommon, +) + +from ... import l10n_br_nfse_ginfes + +_logger = logging.getLogger(__name__) + + +class TestFiscalDocumentNFSeGinfes(TestFiscalDocumentNFSeCommon): + def setUp(self): + super(TestFiscalDocumentNFSeGinfes, self).setUp() + + self.company.provedor_nfse = "ginfes" + + def test_nfse_ginfes(self): + """Test NFS-e same state.""" + + xml_path = os.path.join( + l10n_br_nfse_ginfes.__path__[0], "tests", "nfse", "001_50_nfse.xml" + ) + + self.nfse_same_state._onchange_document_serie_id() + self.nfse_same_state._onchange_fiscal_operation_id() + self.nfse_same_state._onchange_company_id() + self.nfse_same_state.rps_number = "50" + self.nfse_same_state.document_number = "50" + + for line in self.nfse_same_state.fiscal_line_ids: + line._onchange_product_id_fiscal() + line._onchange_commercial_quantity() + line._onchange_ncm_id() + line._onchange_fiscal_operation_id() + line._onchange_fiscal_operation_line_id() + line._onchange_fiscal_taxes() + + self.nfse_same_state.action_document_confirm() + + self.nfse_same_state.document_date = datetime.strptime( + "2020-06-04T11:58:46", "%Y-%m-%dT%H:%M:%S" + ) + self.nfse_same_state.date_in_out = datetime.strptime( + "2020-06-04T11:58:46", "%Y-%m-%dT%H:%M:%S" + ) + + self.nfse_same_state.with_context(lang="pt_BR")._document_export() + + output = os.path.join( + config["data_dir"], + "filestore", + self.cr.dbname, + self.nfse_same_state.send_file_id.store_fname, + ) + _logger.info("XML file saved at %s" % (output,)) + + diff = main.diff_files(xml_path, output) + _logger.info("Diff with expected XML (if any): %s" % (diff,)) + + assert len(diff) == 0 diff --git a/oca_dependencies.txt b/oca_dependencies.txt index 4fd1f883e415..a390b56674c0 100644 --- a/oca_dependencies.txt +++ b/oca_dependencies.txt @@ -7,6 +7,7 @@ account-reconcile mis-builder contract manufacture +queue purchase-workflow sale-workflow delivery-carrier diff --git a/requirements.txt b/requirements.txt index 6e321eb81535..c916d2d17f0f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -6,6 +6,7 @@ erpbrasil.edoc erpbrasil.edoc.pdf erpbrasil.transmissao nfelib +nfselib.ginfes nfselib.paulistana num2words phonenumbers @@ -14,3 +15,4 @@ pyyaml satcomum unidecode workalendar +xmldiff diff --git a/setup/l10n_br_fiscal_queue/odoo/addons/l10n_br_fiscal_queue b/setup/l10n_br_fiscal_queue/odoo/addons/l10n_br_fiscal_queue new file mode 120000 index 000000000000..bcb659724ca6 --- /dev/null +++ b/setup/l10n_br_fiscal_queue/odoo/addons/l10n_br_fiscal_queue @@ -0,0 +1 @@ +../../../../l10n_br_fiscal_queue \ No newline at end of file diff --git a/setup/l10n_br_fiscal_queue/setup.py b/setup/l10n_br_fiscal_queue/setup.py new file mode 100644 index 000000000000..28c57bb64031 --- /dev/null +++ b/setup/l10n_br_fiscal_queue/setup.py @@ -0,0 +1,6 @@ +import setuptools + +setuptools.setup( + setup_requires=['setuptools-odoo'], + odoo_addon=True, +) diff --git a/setup/l10n_br_nfse_ginfes/odoo/addons/l10n_br_nfse_ginfes b/setup/l10n_br_nfse_ginfes/odoo/addons/l10n_br_nfse_ginfes new file mode 120000 index 000000000000..f44df05d62df --- /dev/null +++ b/setup/l10n_br_nfse_ginfes/odoo/addons/l10n_br_nfse_ginfes @@ -0,0 +1 @@ +../../../../l10n_br_nfse_ginfes \ No newline at end of file diff --git a/setup/l10n_br_nfse_ginfes/setup.py b/setup/l10n_br_nfse_ginfes/setup.py new file mode 100644 index 000000000000..28c57bb64031 --- /dev/null +++ b/setup/l10n_br_nfse_ginfes/setup.py @@ -0,0 +1,6 @@ +import setuptools + +setuptools.setup( + setup_requires=['setuptools-odoo'], + odoo_addon=True, +)