diff --git a/.coveragerc b/.coveragerc index e78d3c15d07a..99185271192f 100644 --- a/.coveragerc +++ b/.coveragerc @@ -1,3 +1,5 @@ [run] omit = l10n_br_currency_rate_update/tests/test_currency_rate_update_bcb.py + l10n_br_fiscal/tests/test_ibpt_service.py + l10n_br_fiscal/tests/test_ibpt_product.py diff --git a/README.md b/README.md index 504d6e25fd37..38210570101a 100644 --- a/README.md +++ b/README.md @@ -21,45 +21,47 @@ Available addons ---------------- addon | version | maintainers | summary --- | --- | --- | --- -[l10n_br_account](l10n_br_account/) | 14.0.6.3.0 | [![renatonlima](https://github.com/renatonlima.png?size=30px)](https://github.com/renatonlima) [![rvalyi](https://github.com/rvalyi.png?size=30px)](https://github.com/rvalyi) | Brazilian Localization Account -[l10n_br_account_due_list](l10n_br_account_due_list/) | 14.0.1.0.2 | [![renatonlima](https://github.com/renatonlima.png?size=30px)](https://github.com/renatonlima) [![rvalyi](https://github.com/rvalyi.png?size=30px)](https://github.com/rvalyi) | Brazilian Account Due List -[l10n_br_account_nfe](l10n_br_account_nfe/) | 14.0.2.1.1 | [![antoniospneto](https://github.com/antoniospneto.png?size=30px)](https://github.com/antoniospneto) [![felipemotter](https://github.com/felipemotter.png?size=30px)](https://github.com/felipemotter) [![mbcosta](https://github.com/mbcosta.png?size=30px)](https://github.com/mbcosta) | Integration between l10n_br_account and l10n_br_nfe +[l10n_br_account](l10n_br_account/) | 14.0.8.0.2 | [![renatonlima](https://github.com/renatonlima.png?size=30px)](https://github.com/renatonlima) [![rvalyi](https://github.com/rvalyi.png?size=30px)](https://github.com/rvalyi) | Brazilian Localization Account +[l10n_br_account_due_list](l10n_br_account_due_list/) | 14.0.2.0.0 | [![renatonlima](https://github.com/renatonlima.png?size=30px)](https://github.com/renatonlima) [![rvalyi](https://github.com/rvalyi.png?size=30px)](https://github.com/rvalyi) | Brazilian Account Due List +[l10n_br_account_nfe](l10n_br_account_nfe/) | 14.0.2.1.2 | [![antoniospneto](https://github.com/antoniospneto.png?size=30px)](https://github.com/antoniospneto) [![felipemotter](https://github.com/felipemotter.png?size=30px)](https://github.com/felipemotter) [![mbcosta](https://github.com/mbcosta.png?size=30px)](https://github.com/mbcosta) | Integration between l10n_br_account and l10n_br_nfe [l10n_br_account_payment_brcobranca](l10n_br_account_payment_brcobranca/) | 14.0.4.0.0 | [![rvalyi](https://github.com/rvalyi.png?size=30px)](https://github.com/rvalyi) [![mbcosta](https://github.com/mbcosta.png?size=30px)](https://github.com/mbcosta) | L10n Br Account Payment BRCobranca -[l10n_br_account_payment_order](l10n_br_account_payment_order/) | 14.0.3.0.0 | [![mbcosta](https://github.com/mbcosta.png?size=30px)](https://github.com/mbcosta) | Brazilian Payment Order -[l10n_br_base](l10n_br_base/) | 14.0.3.3.0 | [![renatonlima](https://github.com/renatonlima.png?size=30px)](https://github.com/renatonlima) [![rvalyi](https://github.com/rvalyi.png?size=30px)](https://github.com/rvalyi) | Customization of base module for implementations in Brazil. +[l10n_br_account_payment_order](l10n_br_account_payment_order/) | 14.0.3.0.1 | [![mbcosta](https://github.com/mbcosta.png?size=30px)](https://github.com/mbcosta) | Brazilian Payment Order +[l10n_br_base](l10n_br_base/) | 14.0.3.4.1 | [![renatonlima](https://github.com/renatonlima.png?size=30px)](https://github.com/renatonlima) [![rvalyi](https://github.com/rvalyi.png?size=30px)](https://github.com/rvalyi) | Customization of base module for implementations in Brazil. [l10n_br_cnab_structure](l10n_br_cnab_structure/) | 14.0.1.0.0 | [![antoniospneto](https://github.com/antoniospneto.png?size=30px)](https://github.com/antoniospneto) [![felipemotter](https://github.com/felipemotter.png?size=30px)](https://github.com/felipemotter) | This module allows defining the structure for generating the CNAB file. Used to exchange information with Brazilian banks. +[l10n_br_cnpj_search](l10n_br_cnpj_search/) | 14.0.1.1.3 | | Integração com os Webservices da ReceitaWS e SerPro [l10n_br_coa](l10n_br_coa/) | 14.0.3.3.2 | [![renatonlima](https://github.com/renatonlima.png?size=30px)](https://github.com/renatonlima) [![mileo](https://github.com/mileo.png?size=30px)](https://github.com/mileo) | Base do Planos de Contas brasileiros [l10n_br_coa_generic](l10n_br_coa_generic/) | 14.0.2.2.1 | [![mileo](https://github.com/mileo.png?size=30px)](https://github.com/mileo) | Plano de Contas para empresas do Regime normal (Micro e pequenas empresas) [l10n_br_coa_simple](l10n_br_coa_simple/) | 14.0.2.2.1 | [![renatonlima](https://github.com/renatonlima.png?size=30px)](https://github.com/renatonlima) | Plano de Contas ITG 1000 para Microempresas e Empresa de Pequeno Porte [l10n_br_contract](l10n_br_contract/) | 14.0.2.2.0 | [![mileo](https://github.com/mileo.png?size=30px)](https://github.com/mileo) [![marcelsavegnago](https://github.com/marcelsavegnago.png?size=30px)](https://github.com/marcelsavegnago) | Customization of Contract module for implementations in Brazil. -[l10n_br_crm](l10n_br_crm/) | 14.0.1.0.2 | | Brazilian Localization CRM -[l10n_br_currency_rate_update](l10n_br_currency_rate_update/) | 14.0.1.0.1 | [![renatonlima](https://github.com/renatonlima.png?size=30px)](https://github.com/renatonlima) | Update exchange rates using OCA modules for Brazil -[l10n_br_delivery](l10n_br_delivery/) | 14.0.1.0.1 | [![renatonlima](https://github.com/renatonlima.png?size=30px)](https://github.com/renatonlima) [![mbcosta](https://github.com/mbcosta.png?size=30px)](https://github.com/mbcosta) | delivery module Brazilian Localization +[l10n_br_crm](l10n_br_crm/) | 14.0.1.0.3 | | Brazilian Localization CRM +[l10n_br_currency_rate_update](l10n_br_currency_rate_update/) | 14.0.1.0.2 | [![renatonlima](https://github.com/renatonlima.png?size=30px)](https://github.com/renatonlima) | Update exchange rates using OCA modules for Brazil +[l10n_br_delivery](l10n_br_delivery/) | 14.0.1.1.0 | [![renatonlima](https://github.com/renatonlima.png?size=30px)](https://github.com/renatonlima) [![mbcosta](https://github.com/mbcosta.png?size=30px)](https://github.com/mbcosta) | delivery module Brazilian Localization [l10n_br_delivery_nfe](l10n_br_delivery_nfe/) | 14.0.1.1.0 | [![mbcosta](https://github.com/mbcosta.png?size=30px)](https://github.com/mbcosta) | Brazilian Localization Delivery NFe -[l10n_br_fiscal](l10n_br_fiscal/) | 14.0.12.2.0 | [![renatonlima](https://github.com/renatonlima.png?size=30px)](https://github.com/renatonlima) | Brazilian fiscal core module. +[l10n_br_fiscal](l10n_br_fiscal/) | 14.0.13.0.1 | [![renatonlima](https://github.com/renatonlima.png?size=30px)](https://github.com/renatonlima) | Brazilian fiscal core module. [l10n_br_fiscal_closing](l10n_br_fiscal_closing/) | 14.0.1.1.2 | | Fechamento fiscal do periodo -[l10n_br_hr](l10n_br_hr/) | 14.0.1.2.0 | | Brazilian Localization HR +[l10n_br_hr](l10n_br_hr/) | 14.0.1.2.1 | | Brazilian Localization HR [l10n_br_mis_report](l10n_br_mis_report/) | 14.0.1.0.1 | [![mileo](https://github.com/mileo.png?size=30px)](https://github.com/mileo) | Templates de relatórios contábeis brasileiros: Balanço Patrimonial e DRE -[l10n_br_nfe](l10n_br_nfe/) | 14.0.8.2.0 | [![rvalyi](https://github.com/rvalyi.png?size=30px)](https://github.com/rvalyi) [![renatonlima](https://github.com/renatonlima.png?size=30px)](https://github.com/renatonlima) | Brazilian Eletronic Invoice NF-e +[l10n_br_nfe](l10n_br_nfe/) | 14.0.8.4.2 | [![rvalyi](https://github.com/rvalyi.png?size=30px)](https://github.com/rvalyi) [![renatonlima](https://github.com/renatonlima.png?size=30px)](https://github.com/renatonlima) | Brazilian Eletronic Invoice NF-e [l10n_br_nfe_spec](l10n_br_nfe_spec/) | 14.0.3.0.0 | [![rvalyi](https://github.com/rvalyi.png?size=30px)](https://github.com/rvalyi) | nfe spec [l10n_br_nfse](l10n_br_nfse/) | 14.0.1.13.0 | [![gabrielcardoso21](https://github.com/gabrielcardoso21.png?size=30px)](https://github.com/gabrielcardoso21) [![mileo](https://github.com/mileo.png?size=30px)](https://github.com/mileo) [![luismalta](https://github.com/luismalta.png?size=30px)](https://github.com/luismalta) [![marcelsavegnago](https://github.com/marcelsavegnago.png?size=30px)](https://github.com/marcelsavegnago) | NFS-e +[l10n_br_nfse_paulistana](l10n_br_nfse_paulistana/) | 14.0.1.0.0 | [![gabrielcardoso21](https://github.com/gabrielcardoso21.png?size=30px)](https://github.com/gabrielcardoso21) [![mileo](https://github.com/mileo.png?size=30px)](https://github.com/mileo) [![luismalta](https://github.com/luismalta.png?size=30px)](https://github.com/luismalta) | NFS-e (Nota Paulistana) [l10n_br_portal](l10n_br_portal/) | 14.0.1.0.2 | | Campos Brasileiros no Portal -[l10n_br_pos](l10n_br_pos/) | 14.0.1.1.0 | [![mileo](https://github.com/mileo.png?size=30px)](https://github.com/mileo) [![lfdivino](https://github.com/lfdivino.png?size=30px)](https://github.com/lfdivino) [![luismalta](https://github.com/luismalta.png?size=30px)](https://github.com/luismalta) [![ygcarvalh](https://github.com/ygcarvalh.png?size=30px)](https://github.com/ygcarvalh) | Ponto de venda adaptado a legislação Brasileira -[l10n_br_pos_cfe](l10n_br_pos_cfe/) | 14.0.1.1.0 | [![mileo](https://github.com/mileo.png?size=30px)](https://github.com/mileo) [![lfdivino](https://github.com/lfdivino.png?size=30px)](https://github.com/lfdivino) [![luismalta](https://github.com/luismalta.png?size=30px)](https://github.com/luismalta) [![ygcarvalh](https://github.com/ygcarvalh.png?size=30px)](https://github.com/ygcarvalh) | CF-e +[l10n_br_pos](l10n_br_pos/) | 14.0.1.2.0 | [![mileo](https://github.com/mileo.png?size=30px)](https://github.com/mileo) [![lfdivino](https://github.com/lfdivino.png?size=30px)](https://github.com/lfdivino) [![luismalta](https://github.com/luismalta.png?size=30px)](https://github.com/luismalta) [![ygcarvalh](https://github.com/ygcarvalh.png?size=30px)](https://github.com/ygcarvalh) | Ponto de venda adaptado a legislação Brasileira +[l10n_br_pos_cfe](l10n_br_pos_cfe/) | 14.0.1.2.0 | [![mileo](https://github.com/mileo.png?size=30px)](https://github.com/mileo) [![lfdivino](https://github.com/lfdivino.png?size=30px)](https://github.com/lfdivino) [![luismalta](https://github.com/luismalta.png?size=30px)](https://github.com/luismalta) [![ygcarvalh](https://github.com/ygcarvalh.png?size=30px)](https://github.com/ygcarvalh) | CF-e [l10n_br_product_contract](l10n_br_product_contract/) | 14.0.1.0.0 | [![mileo](https://github.com/mileo.png?size=30px)](https://github.com/mileo) [![marcelsavegnago](https://github.com/marcelsavegnago.png?size=30px)](https://github.com/marcelsavegnago) | Criação de contratos através dos Pedidos de Vendas -[l10n_br_purchase](l10n_br_purchase/) | 14.0.3.2.0 | [![renatonlima](https://github.com/renatonlima.png?size=30px)](https://github.com/renatonlima) [![rvalyi](https://github.com/rvalyi.png?size=30px)](https://github.com/rvalyi) | Brazilian Localization Purchase +[l10n_br_purchase](l10n_br_purchase/) | 14.0.3.2.1 | [![renatonlima](https://github.com/renatonlima.png?size=30px)](https://github.com/renatonlima) [![rvalyi](https://github.com/rvalyi.png?size=30px)](https://github.com/rvalyi) | Brazilian Localization Purchase [l10n_br_purchase_request](l10n_br_purchase_request/) | 14.0.1.0.1 | [![marcelsavegnago](https://github.com/marcelsavegnago.png?size=30px)](https://github.com/marcelsavegnago) | Purchase Request Brazilian Localization Purchase Request [l10n_br_purchase_stock](l10n_br_purchase_stock/) | 14.0.1.0.3 | | Brazilian Localization Purchase Stock -[l10n_br_repair](l10n_br_repair/) | 14.0.1.1.0 | [![marcelsavegnago](https://github.com/marcelsavegnago.png?size=30px)](https://github.com/marcelsavegnago) | Brazilian Localization Repair -[l10n_br_resource](l10n_br_resource/) | 14.0.1.0.1 | [![mileo](https://github.com/mileo.png?size=30px)](https://github.com/mileo) [![hendixcosta](https://github.com/hendixcosta.png?size=30px)](https://github.com/hendixcosta) [![lfdivino](https://github.com/lfdivino.png?size=30px)](https://github.com/lfdivino) | This module extend core resource to create important brazilian informations. Define a Brazilian calendar and some tools to compute dates used in financial and payroll modules -[l10n_br_sale](l10n_br_sale/) | 14.0.3.1.0 | [![renatonlima](https://github.com/renatonlima.png?size=30px)](https://github.com/renatonlima) | Brazilian Localization Sale +[l10n_br_repair](l10n_br_repair/) | 14.0.1.1.1 | [![marcelsavegnago](https://github.com/marcelsavegnago.png?size=30px)](https://github.com/marcelsavegnago) | Brazilian Localization Repair +[l10n_br_resource](l10n_br_resource/) | 14.0.1.0.2 | [![mileo](https://github.com/mileo.png?size=30px)](https://github.com/mileo) [![hendixcosta](https://github.com/hendixcosta.png?size=30px)](https://github.com/hendixcosta) [![lfdivino](https://github.com/lfdivino.png?size=30px)](https://github.com/lfdivino) | This module extend core resource to create important brazilian informations. Define a Brazilian calendar and some tools to compute dates used in financial and payroll modules +[l10n_br_sale](l10n_br_sale/) | 14.0.3.1.1 | [![renatonlima](https://github.com/renatonlima.png?size=30px)](https://github.com/renatonlima) | Brazilian Localization Sale [l10n_br_sale_invoice_plan](l10n_br_sale_invoice_plan/) | 14.0.1.0.2 | [![marcelsavegnago](https://github.com/marcelsavegnago.png?size=30px)](https://github.com/marcelsavegnago) | Brazilian Localization Sale Invoice Plan [l10n_br_sale_stock](l10n_br_sale_stock/) | 14.0.1.0.2 | [![renatonlima](https://github.com/renatonlima.png?size=30px)](https://github.com/renatonlima) [![mbcosta](https://github.com/mbcosta.png?size=30px)](https://github.com/mbcosta) | Brazilian Localization Sales and Warehouse [l10n_br_stock](l10n_br_stock/) | 14.0.2.0.2 | | Brazilian Localization Warehouse [l10n_br_stock_account](l10n_br_stock_account/) | 14.0.3.1.0 | | Brazilian Localization WMS Accounting [l10n_br_website_sale](l10n_br_website_sale/) | 14.0.1.0.1 | | Website sale localização brasileira. [l10n_br_website_sale_delivery](l10n_br_website_sale_delivery/) | 14.0.1.0.1 | [![marcelsavegnago](https://github.com/marcelsavegnago.png?size=30px)](https://github.com/marcelsavegnago) [![DiegoParadeda](https://github.com/DiegoParadeda.png?size=30px)](https://github.com/DiegoParadeda) | Implements Brazilian freight values for delivery. -[l10n_br_zip](l10n_br_zip/) | 14.0.1.0.3 | [![renatonlima](https://github.com/renatonlima.png?size=30px)](https://github.com/renatonlima) | Brazilian Localisation ZIP Codes +[l10n_br_zip](l10n_br_zip/) | 14.0.1.0.4 | [![renatonlima](https://github.com/renatonlima.png?size=30px)](https://github.com/renatonlima) | Brazilian Localisation ZIP Codes [payment_pagseguro](payment_pagseguro/) | 14.0.1.0.7 | | Payment Acquirer: PagSeguro Implementation [spec_driven_model](spec_driven_model/) | 14.0.4.0.0 | [![rvalyi](https://github.com/rvalyi.png?size=30px)](https://github.com/rvalyi) | Tools for specifications driven mixins (from xsd for instance) diff --git a/l10n_br_account/__manifest__.py b/l10n_br_account/__manifest__.py index fecb9d54b8a0..28619e304129 100644 --- a/l10n_br_account/__manifest__.py +++ b/l10n_br_account/__manifest__.py @@ -7,7 +7,7 @@ "license": "AGPL-3", "author": "Akretion, Odoo Community Association (OCA)", "website": "https://github.com/OCA/l10n-brazil", - "version": "14.0.6.3.0", + "version": "14.0.8.0.2", "development_status": "Beta", "maintainers": ["renatonlima", "rvalyi"], "depends": [ diff --git a/l10n_br_account/i18n/l10n_br_account.pot b/l10n_br_account/i18n/l10n_br_account.pot index a97645063e32..6d48cba3abfe 100644 --- a/l10n_br_account/i18n/l10n_br_account.pot +++ b/l10n_br_account/i18n/l10n_br_account.pot @@ -70,20 +70,6 @@ msgstr "" msgid "Active" msgstr "" -#. module: l10n_br_account -#: model:ir.model.fields,field_description:l10n_br_account.field_account_bank_statement_line__legal_name -#: model:ir.model.fields,field_description:l10n_br_account.field_account_move__legal_name -#: model:ir.model.fields,field_description:l10n_br_account.field_account_payment__legal_name -msgid "Adapted Legal Name" -msgstr "" - -#. module: l10n_br_account -#: model:ir.model.fields,field_description:l10n_br_account.field_account_bank_statement_line__ie -#: model:ir.model.fields,field_description:l10n_br_account.field_account_move__ie -#: model:ir.model.fields,field_description:l10n_br_account.field_account_payment__ie -msgid "Adapted State Tax Number" -msgstr "" - #. module: l10n_br_account #: model:ir.model.fields.selection,name:l10n_br_account.selection__account_move__fiscal_operation_type__all msgid "All" @@ -131,9 +117,6 @@ msgid "CNPJ" msgstr "" #. module: l10n_br_account -#: model:ir.model.fields,field_description:l10n_br_account.field_account_bank_statement_line__cnpj_cpf -#: model:ir.model.fields,field_description:l10n_br_account.field_account_move__cnpj_cpf -#: model:ir.model.fields,field_description:l10n_br_account.field_account_payment__cnpj_cpf #: model_terms:ir.ui.view,arch_db:l10n_br_account.fiscal_invoice_search #: model_terms:ir.ui.view,arch_db:l10n_br_account.invoice_search msgid "CNPJ/CPF" @@ -160,7 +143,11 @@ msgid "Companies" msgstr "" #. module: l10n_br_account +#: model:ir.model.fields,field_description:l10n_br_account.field_account_bank_statement_line__fiscal_company_id #: model:ir.model.fields,field_description:l10n_br_account.field_account_move__company_id +#: model:ir.model.fields,field_description:l10n_br_account.field_account_move__fiscal_company_id +#: model:ir.model.fields,field_description:l10n_br_account.field_account_payment__fiscal_company_id +#: model:ir.model.fields,field_description:l10n_br_account.field_l10n_br_fiscal_document__fiscal_company_id msgid "Company" msgstr "" @@ -276,6 +263,14 @@ msgstr "" msgid "Create a new NFS-e" msgstr "" +#. module: l10n_br_account +#: model:ir.model.fields,field_description:l10n_br_account.field_account_bank_statement_line__fiscal_currency_id +#: model:ir.model.fields,field_description:l10n_br_account.field_account_move__fiscal_currency_id +#: model:ir.model.fields,field_description:l10n_br_account.field_account_payment__fiscal_currency_id +#: model:ir.model.fields,field_description:l10n_br_account.field_l10n_br_fiscal_document__fiscal_currency_id +msgid "Currency" +msgstr "" + #. module: l10n_br_account #: model:ir.model.fields,field_description:l10n_br_account.field_account_bank_statement_line__date_in_out #: model:ir.model.fields,field_description:l10n_br_account.field_account_move__date_in_out @@ -344,14 +339,14 @@ msgid "Document Type" msgstr "" #. module: l10n_br_account -#: code:addons/l10n_br_account/models/account_invoice_line.py:0 +#: code:addons/l10n_br_account/models/account_move_line.py:0 #, python-format msgid "" "Document line dummy not found. Please contact your system administrator." msgstr "" #. module: l10n_br_account -#: code:addons/l10n_br_account/models/account_invoice.py:0 +#: code:addons/l10n_br_account/models/account_move.py:0 #, python-format msgid "" "Document without Return Fiscal Operation! \n" @@ -464,6 +459,13 @@ msgstr "" msgid "Force Fiscal Operation" msgstr "" +#. module: l10n_br_account +#: model:ir.model.fields,field_description:l10n_br_account.field_account_bank_statement_line__has_fiscal_dummy +#: model:ir.model.fields,field_description:l10n_br_account.field_account_move__has_fiscal_dummy +#: model:ir.model.fields,field_description:l10n_br_account.field_account_payment__has_fiscal_dummy +msgid "Has Fiscal Dummy" +msgstr "" + #. module: l10n_br_account #: model:ir.model.fields,field_description:l10n_br_account.field_account_move_line__icms_cst_code msgid "ICMS CST Code" @@ -516,6 +518,7 @@ msgstr "" #: model:ir.model.fields,field_description:l10n_br_account.field_account_bank_statement_line__incoterm_id #: model:ir.model.fields,field_description:l10n_br_account.field_account_move__incoterm_id #: model:ir.model.fields,field_description:l10n_br_account.field_account_payment__incoterm_id +#: model:ir.model.fields,field_description:l10n_br_account.field_l10n_br_fiscal_document__incoterm_id msgid "Incoterm" msgstr "" @@ -533,6 +536,7 @@ msgstr "" #: model:ir.model.fields,help:l10n_br_account.field_account_bank_statement_line__incoterm_id #: model:ir.model.fields,help:l10n_br_account.field_account_move__incoterm_id #: model:ir.model.fields,help:l10n_br_account.field_account_payment__incoterm_id +#: model:ir.model.fields,help:l10n_br_account.field_l10n_br_fiscal_document__incoterm_id msgid "" "International Commercial Terms are a series of predefined commercial terms " "used in international transactions." @@ -623,7 +627,7 @@ msgid "Legal Name" msgstr "" #. module: l10n_br_account -#: code:addons/l10n_br_account/models/account_invoice.py:0 +#: code:addons/l10n_br_account/models/account_move.py:0 #, python-format msgid "" "Line without Return Fiscal Operation! \n" @@ -652,6 +656,12 @@ msgstr "" msgid "NFS-e" msgstr "" +#. module: l10n_br_account +#: model:ir.model.fields,field_description:l10n_br_account.field_account_move_line__fiscal_name +#: model:ir.model.fields,field_description:l10n_br_account.field_l10n_br_fiscal_document_line__fiscal_name +msgid "Name" +msgstr "" + #. module: l10n_br_account #: model_terms:ir.actions.act_window,help:l10n_br_account.fiscal_invoice_all_action #: model_terms:ir.actions.act_window,help:l10n_br_account.fiscal_invoice_in_action @@ -694,7 +704,11 @@ msgid "PIS ST CST Code" msgstr "" #. module: l10n_br_account +#: model:ir.model.fields,field_description:l10n_br_account.field_account_bank_statement_line__fiscal_partner_id +#: model:ir.model.fields,field_description:l10n_br_account.field_account_move__fiscal_partner_id #: model:ir.model.fields,field_description:l10n_br_account.field_account_move__partner_id +#: model:ir.model.fields,field_description:l10n_br_account.field_account_payment__fiscal_partner_id +#: model:ir.model.fields,field_description:l10n_br_account.field_l10n_br_fiscal_document__fiscal_partner_id msgid "Partner" msgstr "" @@ -753,11 +767,37 @@ msgstr "" msgid "Partner Zip" msgstr "" +#. module: l10n_br_account +#: model:ir.model.fields,field_description:l10n_br_account.field_account_move_line__fiscal_price_unit +#: model:ir.model.fields,field_description:l10n_br_account.field_l10n_br_fiscal_document_line__fiscal_price_unit +msgid "Price Unit" +msgstr "" + #. module: l10n_br_account #: model:ir.model.fields,field_description:l10n_br_account.field_account_move__processador_edoc msgid "Processador documentos eletrônicos" msgstr "" +#. module: l10n_br_account +#: model:ir.model.fields,field_description:l10n_br_account.field_account_move_line__fiscal_product_id +#: model:ir.model.fields,field_description:l10n_br_account.field_l10n_br_fiscal_document_line__fiscal_product_id +msgid "Product" +msgstr "" + +#. module: l10n_br_account +#: model:ir.model.fields,field_description:l10n_br_account.field_account_move_line__fiscal_quantity +#: model:ir.model.fields,field_description:l10n_br_account.field_l10n_br_fiscal_document_line__fiscal_quantity +msgid "Quantity" +msgstr "" + +#. module: l10n_br_account +#: model:ir.model.fields,field_description:l10n_br_account.field_account_bank_statement_line__fiscal_partner_shipping_id +#: model:ir.model.fields,field_description:l10n_br_account.field_account_move__fiscal_partner_shipping_id +#: model:ir.model.fields,field_description:l10n_br_account.field_account_payment__fiscal_partner_shipping_id +#: model:ir.model.fields,field_description:l10n_br_account.field_l10n_br_fiscal_document__fiscal_partner_shipping_id +msgid "Shipping Address" +msgstr "" + #. module: l10n_br_account #: model:ir.model.fields,field_description:l10n_br_account.field_account_move__partner_inscr_est #: model_terms:ir.ui.view,arch_db:l10n_br_account.fiscal_invoice_search @@ -798,14 +838,25 @@ msgid "Templates for Taxes" msgstr "" #. module: l10n_br_account -#: model:ir.model.fields,help:l10n_br_account.field_account_bank_statement_line__legal_name +#: model:ir.model.fields,field_description:l10n_br_account.field_account_move_line__fiscal_uom_id +#: model:ir.model.fields,field_description:l10n_br_account.field_l10n_br_fiscal_document_line__fiscal_uom_id +msgid "UOM" +msgstr "" + +#. module: l10n_br_account #: model:ir.model.fields,help:l10n_br_account.field_account_move__company_legal_name -#: model:ir.model.fields,help:l10n_br_account.field_account_move__legal_name #: model:ir.model.fields,help:l10n_br_account.field_account_move__partner_legal_name -#: model:ir.model.fields,help:l10n_br_account.field_account_payment__legal_name msgid "Used in fiscal documents" msgstr "" +#. module: l10n_br_account +#: model:ir.model.fields,field_description:l10n_br_account.field_account_bank_statement_line__fiscal_user_id +#: model:ir.model.fields,field_description:l10n_br_account.field_account_move__fiscal_user_id +#: model:ir.model.fields,field_description:l10n_br_account.field_account_payment__fiscal_user_id +#: model:ir.model.fields,field_description:l10n_br_account.field_l10n_br_fiscal_document__fiscal_user_id +msgid "User" +msgstr "" + #. module: l10n_br_account #: model_terms:ir.ui.view,arch_db:l10n_br_account.fiscal_invoice_form msgid "Validate" @@ -822,7 +873,13 @@ msgid "WH Account Move Line" msgstr "" #. module: l10n_br_account -#: code:addons/l10n_br_account/models/account_invoice.py:0 +#: code:addons/l10n_br_account/models/account_move.py:0 +#, python-format +msgid "You can't set a document type to a fiscal dummy document." +msgstr "" + +#. module: l10n_br_account +#: code:addons/l10n_br_account/models/account_move.py:0 #, python-format msgid "" "You can't set this document number: {} to draft because this document is " @@ -836,7 +893,7 @@ msgid "You cannot delete a fiscal document which is not draft state." msgstr "" #. module: l10n_br_account -#: code:addons/l10n_br_account/models/account_invoice_line.py:0 +#: code:addons/l10n_br_account/models/account_move_line.py:0 #, python-format msgid "You cannot edit an invoice related to a withholding entry" msgstr "" diff --git a/l10n_br_account/i18n/pt_BR.po b/l10n_br_account/i18n/pt_BR.po index ff8f63cb0f6c..737cac5d23da 100644 --- a/l10n_br_account/i18n/pt_BR.po +++ b/l10n_br_account/i18n/pt_BR.po @@ -7,8 +7,8 @@ msgstr "" "Project-Id-Version: Odoo Server 12.0\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2020-04-07 09:02+0000\n" -"PO-Revision-Date: 2023-05-26 05:09+0000\n" -"Last-Translator: Antônio Neto \n" +"PO-Revision-Date: 2023-06-10 20:09+0000\n" +"Last-Translator: Adriano Prado \n" "Language-Team: \n" "Language: pt_BR\n" "MIME-Version: 1.0\n" @@ -74,20 +74,6 @@ msgstr "Contabilidade" msgid "Active" msgstr "Ativo" -#. module: l10n_br_account -#: model:ir.model.fields,field_description:l10n_br_account.field_account_bank_statement_line__legal_name -#: model:ir.model.fields,field_description:l10n_br_account.field_account_move__legal_name -#: model:ir.model.fields,field_description:l10n_br_account.field_account_payment__legal_name -msgid "Adapted Legal Name" -msgstr "Razão Social" - -#. module: l10n_br_account -#: model:ir.model.fields,field_description:l10n_br_account.field_account_bank_statement_line__ie -#: model:ir.model.fields,field_description:l10n_br_account.field_account_move__ie -#: model:ir.model.fields,field_description:l10n_br_account.field_account_payment__ie -msgid "Adapted State Tax Number" -msgstr "Inscrição Estadual" - #. module: l10n_br_account #: model:ir.model.fields.selection,name:l10n_br_account.selection__account_move__fiscal_operation_type__all msgid "All" @@ -106,7 +92,7 @@ msgstr "Montante Tributado" #. module: l10n_br_account #: model:ir.model.fields,field_description:l10n_br_account.field_account_move_line__amount_untaxed msgid "Amount Untaxed" -msgstr "" +msgstr "Total sem Impostos" #. module: l10n_br_account #: model_terms:ir.ui.view,arch_db:l10n_br_account.invoice_form @@ -135,9 +121,6 @@ msgid "CNPJ" msgstr "CNPJ" #. module: l10n_br_account -#: model:ir.model.fields,field_description:l10n_br_account.field_account_bank_statement_line__cnpj_cpf -#: model:ir.model.fields,field_description:l10n_br_account.field_account_move__cnpj_cpf -#: model:ir.model.fields,field_description:l10n_br_account.field_account_payment__cnpj_cpf #: model_terms:ir.ui.view,arch_db:l10n_br_account.fiscal_invoice_search #: model_terms:ir.ui.view,arch_db:l10n_br_account.invoice_search msgid "CNPJ/CPF" @@ -164,7 +147,11 @@ msgid "Companies" msgstr "Empresas" #. module: l10n_br_account +#: model:ir.model.fields,field_description:l10n_br_account.field_account_bank_statement_line__fiscal_company_id #: model:ir.model.fields,field_description:l10n_br_account.field_account_move__company_id +#: model:ir.model.fields,field_description:l10n_br_account.field_account_move__fiscal_company_id +#: model:ir.model.fields,field_description:l10n_br_account.field_account_payment__fiscal_company_id +#: model:ir.model.fields,field_description:l10n_br_account.field_l10n_br_fiscal_document__fiscal_company_id msgid "Company" msgstr "Empresa" @@ -280,13 +267,21 @@ msgstr "Criar uma nova NF-e" msgid "Create a new NFS-e" msgstr "Criar uma nova NFS-e" +#. module: l10n_br_account +#: model:ir.model.fields,field_description:l10n_br_account.field_account_bank_statement_line__fiscal_currency_id +#: model:ir.model.fields,field_description:l10n_br_account.field_account_move__fiscal_currency_id +#: model:ir.model.fields,field_description:l10n_br_account.field_account_payment__fiscal_currency_id +#: model:ir.model.fields,field_description:l10n_br_account.field_l10n_br_fiscal_document__fiscal_currency_id +msgid "Currency" +msgstr "Moeda" + #. module: l10n_br_account #: model:ir.model.fields,field_description:l10n_br_account.field_account_bank_statement_line__date_in_out #: model:ir.model.fields,field_description:l10n_br_account.field_account_move__date_in_out #: model:ir.model.fields,field_description:l10n_br_account.field_account_payment__date_in_out #: model:ir.model.fields,field_description:l10n_br_account.field_l10n_br_fiscal_document__date_in_out msgid "Date IN/OUT" -msgstr "" +msgstr "Data de Entrada/Saída" #. module: l10n_br_account #: model:ir.model.fields,field_description:l10n_br_account.field_l10n_br_fiscal_operation__deductible_taxes @@ -301,7 +296,7 @@ msgstr "Devolver" #. module: l10n_br_account #: model:ir.model.fields,field_description:l10n_br_account.field_account_move_line__discount msgid "Discount (%)" -msgstr "" +msgstr "Desconto (%)" #. module: l10n_br_account #: model:ir.model.fields,field_description:l10n_br_account.field_account_incoterms__display_name @@ -332,7 +327,7 @@ msgstr "Nome Exibido" #: model:ir.model.fields,field_description:l10n_br_account.field_account_payment__document_date #: model:ir.model.fields,field_description:l10n_br_account.field_l10n_br_fiscal_document__document_date msgid "Document Date" -msgstr "" +msgstr "Data do Documento" #. module: l10n_br_account #: model:ir.model.fields,field_description:l10n_br_account.field_account_bank_statement_line__fiscal_line_ids @@ -340,7 +335,7 @@ msgstr "" #: model:ir.model.fields,field_description:l10n_br_account.field_account_payment__fiscal_line_ids #: model:ir.model.fields,field_description:l10n_br_account.field_l10n_br_fiscal_document__fiscal_line_ids msgid "Document Lines" -msgstr "" +msgstr "Linhas de documentos" #. module: l10n_br_account #: model:ir.model.fields,field_description:l10n_br_account.field_account_move_line__document_type_id @@ -348,7 +343,7 @@ msgid "Document Type" msgstr "Tipo do Documento" #. module: l10n_br_account -#: code:addons/l10n_br_account/models/account_invoice_line.py:0 +#: code:addons/l10n_br_account/models/account_move_line.py:0 #, python-format msgid "" "Document line dummy not found. Please contact your system administrator." @@ -357,7 +352,7 @@ msgstr "" "administrador do sistema." #. module: l10n_br_account -#: code:addons/l10n_br_account/models/account_invoice.py:0 +#: code:addons/l10n_br_account/models/account_move.py:0 #, python-format msgid "" "Document without Return Fiscal Operation! \n" @@ -473,6 +468,13 @@ msgstr "Impostos Fiscais" msgid "Force Fiscal Operation" msgstr "Forçar Operação Fiscal" +#. module: l10n_br_account +#: model:ir.model.fields,field_description:l10n_br_account.field_account_bank_statement_line__has_fiscal_dummy +#: model:ir.model.fields,field_description:l10n_br_account.field_account_move__has_fiscal_dummy +#: model:ir.model.fields,field_description:l10n_br_account.field_account_payment__has_fiscal_dummy +msgid "Has Fiscal Dummy" +msgstr "" + #. module: l10n_br_account #: model:ir.model.fields,field_description:l10n_br_account.field_account_move_line__icms_cst_code msgid "ICMS CST Code" @@ -525,6 +527,7 @@ msgstr "Documento de Entrada" #: model:ir.model.fields,field_description:l10n_br_account.field_account_bank_statement_line__incoterm_id #: model:ir.model.fields,field_description:l10n_br_account.field_account_move__incoterm_id #: model:ir.model.fields,field_description:l10n_br_account.field_account_payment__incoterm_id +#: model:ir.model.fields,field_description:l10n_br_account.field_l10n_br_fiscal_document__incoterm_id msgid "Incoterm" msgstr "Incoterm" @@ -542,6 +545,7 @@ msgstr "Indica que este item de diário é uma linha de imposto" #: model:ir.model.fields,help:l10n_br_account.field_account_bank_statement_line__incoterm_id #: model:ir.model.fields,help:l10n_br_account.field_account_move__incoterm_id #: model:ir.model.fields,help:l10n_br_account.field_account_payment__incoterm_id +#: model:ir.model.fields,help:l10n_br_account.field_l10n_br_fiscal_document__incoterm_id msgid "" "International Commercial Terms are a series of predefined commercial terms " "used in international transactions." @@ -639,7 +643,7 @@ msgid "Legal Name" msgstr "Razão Social" #. module: l10n_br_account -#: code:addons/l10n_br_account/models/account_invoice.py:0 +#: code:addons/l10n_br_account/models/account_move.py:0 #, python-format msgid "" "Line without Return Fiscal Operation! \n" @@ -672,6 +676,12 @@ msgstr "NF-e" msgid "NFS-e" msgstr "NFS-e" +#. module: l10n_br_account +#: model:ir.model.fields,field_description:l10n_br_account.field_account_move_line__fiscal_name +#: model:ir.model.fields,field_description:l10n_br_account.field_l10n_br_fiscal_document_line__fiscal_name +msgid "Name" +msgstr "Nome" + #. module: l10n_br_account #: model_terms:ir.actions.act_window,help:l10n_br_account.fiscal_invoice_all_action #: model_terms:ir.actions.act_window,help:l10n_br_account.fiscal_invoice_in_action @@ -717,7 +727,11 @@ msgid "PIS ST CST Code" msgstr "Código CST do PIS ST" #. module: l10n_br_account +#: model:ir.model.fields,field_description:l10n_br_account.field_account_bank_statement_line__fiscal_partner_id +#: model:ir.model.fields,field_description:l10n_br_account.field_account_move__fiscal_partner_id #: model:ir.model.fields,field_description:l10n_br_account.field_account_move__partner_id +#: model:ir.model.fields,field_description:l10n_br_account.field_account_payment__fiscal_partner_id +#: model:ir.model.fields,field_description:l10n_br_account.field_l10n_br_fiscal_document__fiscal_partner_id msgid "Partner" msgstr "Parceiro" @@ -776,11 +790,37 @@ msgstr "Complemento da Rua do Parceiro" msgid "Partner Zip" msgstr "Cep do Parceiro" +#. module: l10n_br_account +#: model:ir.model.fields,field_description:l10n_br_account.field_account_move_line__fiscal_price_unit +#: model:ir.model.fields,field_description:l10n_br_account.field_l10n_br_fiscal_document_line__fiscal_price_unit +msgid "Price Unit" +msgstr "Preço Unitário" + #. module: l10n_br_account #: model:ir.model.fields,field_description:l10n_br_account.field_account_move__processador_edoc msgid "Processador documentos eletrônicos" msgstr "Processador documentos eletrônicos" +#. module: l10n_br_account +#: model:ir.model.fields,field_description:l10n_br_account.field_account_move_line__fiscal_product_id +#: model:ir.model.fields,field_description:l10n_br_account.field_l10n_br_fiscal_document_line__fiscal_product_id +msgid "Product" +msgstr "Produto" + +#. module: l10n_br_account +#: model:ir.model.fields,field_description:l10n_br_account.field_account_move_line__fiscal_quantity +#: model:ir.model.fields,field_description:l10n_br_account.field_l10n_br_fiscal_document_line__fiscal_quantity +msgid "Quantity" +msgstr "Quantidade" + +#. module: l10n_br_account +#: model:ir.model.fields,field_description:l10n_br_account.field_account_bank_statement_line__fiscal_partner_shipping_id +#: model:ir.model.fields,field_description:l10n_br_account.field_account_move__fiscal_partner_shipping_id +#: model:ir.model.fields,field_description:l10n_br_account.field_account_payment__fiscal_partner_shipping_id +#: model:ir.model.fields,field_description:l10n_br_account.field_l10n_br_fiscal_document__fiscal_partner_shipping_id +msgid "Shipping Address" +msgstr "Endereço de Entrega" + #. module: l10n_br_account #: model:ir.model.fields,field_description:l10n_br_account.field_account_move__partner_inscr_est #: model_terms:ir.ui.view,arch_db:l10n_br_account.fiscal_invoice_search @@ -821,14 +861,25 @@ msgid "Templates for Taxes" msgstr "Modelos para Impostos" #. module: l10n_br_account -#: model:ir.model.fields,help:l10n_br_account.field_account_bank_statement_line__legal_name +#: model:ir.model.fields,field_description:l10n_br_account.field_account_move_line__fiscal_uom_id +#: model:ir.model.fields,field_description:l10n_br_account.field_l10n_br_fiscal_document_line__fiscal_uom_id +msgid "UOM" +msgstr "UDM" + +#. module: l10n_br_account #: model:ir.model.fields,help:l10n_br_account.field_account_move__company_legal_name -#: model:ir.model.fields,help:l10n_br_account.field_account_move__legal_name #: model:ir.model.fields,help:l10n_br_account.field_account_move__partner_legal_name -#: model:ir.model.fields,help:l10n_br_account.field_account_payment__legal_name msgid "Used in fiscal documents" msgstr "Usado em documentos fiscais" +#. module: l10n_br_account +#: model:ir.model.fields,field_description:l10n_br_account.field_account_bank_statement_line__fiscal_user_id +#: model:ir.model.fields,field_description:l10n_br_account.field_account_move__fiscal_user_id +#: model:ir.model.fields,field_description:l10n_br_account.field_account_payment__fiscal_user_id +#: model:ir.model.fields,field_description:l10n_br_account.field_l10n_br_fiscal_document__fiscal_user_id +msgid "User" +msgstr "Usuário" + #. module: l10n_br_account #: model_terms:ir.ui.view,arch_db:l10n_br_account.fiscal_invoice_form msgid "Validate" @@ -845,7 +896,13 @@ msgid "WH Account Move Line" msgstr "Linha de Movimento Contábil RET" #. module: l10n_br_account -#: code:addons/l10n_br_account/models/account_invoice.py:0 +#: code:addons/l10n_br_account/models/account_move.py:0 +#, python-format +msgid "You can't set a document type to a fiscal dummy document." +msgstr "" + +#. module: l10n_br_account +#: code:addons/l10n_br_account/models/account_move.py:0 #, python-format msgid "" "You can't set this document number: {} to draft because this document is " @@ -863,12 +920,18 @@ msgstr "" "rascunho." #. module: l10n_br_account -#: code:addons/l10n_br_account/models/account_invoice_line.py:0 +#: code:addons/l10n_br_account/models/account_move_line.py:0 #, python-format msgid "You cannot edit an invoice related to a withholding entry" msgstr "" "Você não pode editar um documento relacionado a um lançamento de retenção" +#~ msgid "Adapted Legal Name" +#~ msgstr "Razão Social" + +#~ msgid "Adapted State Tax Number" +#~ msgstr "Inscrição Estadual" + #~ msgid "Document Code" #~ msgstr "Código do Documento" diff --git a/l10n_br_account/models/__init__.py b/l10n_br_account/models/__init__.py index 8a2abe6935cd..382a52699330 100644 --- a/l10n_br_account/models/__init__.py +++ b/l10n_br_account/models/__init__.py @@ -8,10 +8,9 @@ from . import fiscal_tax from . import fiscal_operation from . import fiscal_operation_line -from . import account_invoice -from . import account_invoice_line +from . import account_move +from . import account_move_line from . import fiscal_document from . import fiscal_document_line -from . import account_move from . import account_incoterms from . import res_company diff --git a/l10n_br_account/models/account_invoice.py b/l10n_br_account/models/account_invoice.py deleted file mode 100644 index bfe7e320d5a1..000000000000 --- a/l10n_br_account/models/account_invoice.py +++ /dev/null @@ -1,678 +0,0 @@ -# Copyright (C) 2009 - TODAY Renato Lima - Akretion -# Copyright (C) 2019 - TODAY Raphaël Valyi - Akretion -# Copyright (C) 2020 - TODAY Luis Felipe Mileo - KMEE -# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html - - -from odoo import _, api, fields, models -from odoo.exceptions import UserError - -from odoo.addons.l10n_br_fiscal.constants.fiscal import ( - DOCUMENT_ISSUER_COMPANY, - DOCUMENT_ISSUER_PARTNER, - FISCAL_IN_OUT_ALL, - FISCAL_OUT, - MODELO_FISCAL_NFE, - SITUACAO_EDOC_CANCELADA, - SITUACAO_EDOC_EM_DIGITACAO, -) - -MOVE_TO_OPERATION = { - "out_invoice": "out", - "in_invoice": "in", - "out_refund": "in", - "in_refund": "out", - "out_receipt": "out", - "in_receipt": "in", -} - -REFUND_TO_OPERATION = { - "out_invoice": "in", - "in_invoice": "out", - "out_refund": "out", - "in_refund": "in", -} - -FISCAL_TYPE_REFUND = { - "out": ["purchase_refund", "in_return"], - "in": ["sale_refund", "out_return"], -} - -MOVE_TAX_USER_TYPE = { - "out_invoice": "sale", - "in_invoice": "purchase", - "out_refund": "sale", - "in_refund": "purchase", -} - -SHADOWED_FIELDS = [ - "partner_id", - "company_id", - "currency_id", - "partner_shipping_id", - "user_id", -] - - -class AccountMove(models.Model): - _name = "account.move" - _inherit = [ - _name, - "l10n_br_fiscal.document.mixin.methods", - "l10n_br_fiscal.document.invoice.mixin", - ] - _inherits = {"l10n_br_fiscal.document": "fiscal_document_id"} - _order = "date DESC, name DESC" - - # some account.move records _inherits from an fiscal.document that is - # disabled with active=False (dummy record) in the l10n_br_fiscal_document table. - # To make the invoices still visible, we set active=True - # in the account_move table. - active = fields.Boolean( - default=True, - ) - - cnpj_cpf = fields.Char( - string="CNPJ/CPF", - related="partner_id.cnpj_cpf", - ) - - legal_name = fields.Char( - string="Adapted Legal Name", - related="partner_id.legal_name", - ) - - ie = fields.Char( - string="Adapted State Tax Number", - related="partner_id.inscr_est", - ) - - document_electronic = fields.Boolean( - related="document_type_id.electronic", - string="Electronic?", - ) - - fiscal_document_id = fields.Many2one( - comodel_name="l10n_br_fiscal.document", - string="Fiscal Document", - required=True, - copy=False, - ondelete="cascade", - ) - - fiscal_operation_type = fields.Selection( - selection=FISCAL_IN_OUT_ALL, - related=None, - compute="_compute_fiscal_operation_type", - ) - - # override the incoterm inherited by the fiscal document - # to have the same value as the native incoterm of the invoice. - incoterm_id = fields.Many2one(related="invoice_incoterm_id") - - def _compute_fiscal_operation_type(self): - for inv in self: - if inv.move_type == "entry": - # if it is a Journal Entry there is nothing to do. - inv.fiscal_operation_type = False - continue - if inv.fiscal_operation_id: - inv.fiscal_operation_type = ( - inv.fiscal_operation_id.fiscal_operation_type - ) - else: - inv.fiscal_operation_type = MOVE_TO_OPERATION[inv.move_type] - - def _get_amount_lines(self): - """Get object lines instaces used to compute fields""" - return self.mapped("invoice_line_ids") - - @api.model - def _shadowed_fields(self): - """Returns the list of shadowed fields that are synced - from the parent.""" - return SHADOWED_FIELDS - - def _prepare_shadowed_fields_dict(self, default=False): - self.ensure_one() - vals = self._convert_to_write(self.read(self._shadowed_fields())[0]) - if default: # in case you want to use new rather than write later - return {"default_%s" % (k,): vals[k] for k in vals.keys()} - return vals - - def _write_shadowed_fields(self): - for invoice in self: - if invoice.document_type_id: - shadowed_fiscal_vals = invoice._prepare_shadowed_fields_dict() - invoice.fiscal_document_id.write(shadowed_fiscal_vals) - - @api.model - def fields_view_get( - self, view_id=None, view_type="form", toolbar=False, submenu=False - ): - invoice_view = super().fields_view_get(view_id, view_type, toolbar, submenu) - if view_type == "form": - view = self.env["ir.ui.view"] - - if view_id == self.env.ref("l10n_br_account.fiscal_invoice_form").id: - invoice_line_form_id = self.env.ref( - "l10n_br_account.fiscal_invoice_line_form" - ).id - sub_form_view = self.env["account.move.line"].fields_view_get( - view_id=invoice_line_form_id, view_type="form" - )["arch"] - sub_form_node = self.env["account.move.line"].inject_fiscal_fields( - sub_form_view - ) - sub_arch, sub_fields = view.postprocess_and_fields( - sub_form_node, "account.move.line", False - ) - line_field_name = "invoice_line_ids" - invoice_view["fields"][line_field_name]["views"]["form"] = { - "fields": sub_fields, - "arch": sub_arch, - } - - else: - if invoice_view["fields"].get("invoice_line_ids"): - invoice_line_form_id = self.env.ref( - "l10n_br_account.invoice_form" - ).id - sub_form_view = invoice_view["fields"]["invoice_line_ids"]["views"][ - "form" - ]["arch"] - - sub_form_node = self.env["account.move.line"].inject_fiscal_fields( - sub_form_view - ) - sub_arch, sub_fields = view.postprocess_and_fields( - sub_form_node, "account.move.line", False - ) - line_field_name = "invoice_line_ids" - invoice_view["fields"][line_field_name]["views"]["form"] = { - "fields": sub_fields, - "arch": sub_arch, - } - - if invoice_view["fields"].get("line_ids"): - invoice_line_form_id = self.env.ref( - "l10n_br_account.invoice_form" - ).id - sub_form_view = invoice_view["fields"]["line_ids"]["views"]["tree"][ - "arch" - ] - - sub_form_node = self.env["account.move.line"].inject_fiscal_fields( - sub_form_view - ) - sub_arch, sub_fields = view.postprocess_and_fields( - sub_form_node, "account.move.line", False - ) - line_field_name = "line_ids" - invoice_view["fields"][line_field_name]["views"]["tree"] = { - "fields": sub_fields, - "arch": sub_arch, - } - - return invoice_view - - @api.depends( - "line_ids.matched_debit_ids.debit_move_id.move_id.payment_id.is_matched", - "line_ids.matched_debit_ids.debit_move_id.move_id.line_ids.amount_residual", - "line_ids.matched_debit_ids.debit_move_id.move_id.line_ids.amount_residual_currency", - "line_ids.matched_credit_ids.credit_move_id.move_id.payment_id.is_matched", - "line_ids.matched_credit_ids.credit_move_id.move_id.line_ids.amount_residual", - "line_ids.matched_credit_ids.credit_move_id.move_id.line_ids.amount_residual_currency", - "line_ids.debit", - "line_ids.credit", - "line_ids.currency_id", - "line_ids.amount_currency", - "line_ids.amount_residual", - "line_ids.amount_residual_currency", - "line_ids.payment_id.state", - "line_ids.full_reconcile_id", - "ind_final", - ) - def _compute_amount(self): - if self.company_id.country_id.code != "BR": - return super()._compute_amount() - for move in self: - for line in move.line_ids: - if ( - move.is_invoice(include_receipts=True) - and not line.exclude_from_invoice_tab - ): - line._update_taxes() - - result = super()._compute_amount() - for move in self: - if move.move_type == "entry" or move.is_outbound(): - sign = -1 - else: - sign = 1 - inv_line_ids = move.line_ids.filtered( - lambda l: not l.exclude_from_invoice_tab - ) - move.amount_untaxed = sum(inv_line_ids.mapped("amount_untaxed")) - move.amount_tax = sum(inv_line_ids.mapped("amount_tax")) - move.amount_untaxed_signed = sign * sum( - inv_line_ids.mapped("amount_untaxed") - ) - move.amount_tax_signed = sign * sum(inv_line_ids.mapped("amount_tax")) - - return result - - @api.onchange("ind_final") - def _onchange_ind_final(self): - """Trigger the recompute of the taxes when the ind_final is changed""" - for line in self.invoice_line_ids: - line._onchange_fiscal_operation_id() - return self._recompute_dynamic_lines(recompute_all_taxes=True) - - @api.model - def default_get(self, fields_list): - defaults = super().default_get(fields_list) - move_type = self.env.context.get("default_move_type", "out_invoice") - if not move_type == "entry": - defaults["fiscal_operation_type"] = MOVE_TO_OPERATION[move_type] - if defaults["fiscal_operation_type"] == FISCAL_OUT: - defaults["issuer"] = DOCUMENT_ISSUER_COMPANY - else: - defaults["issuer"] = DOCUMENT_ISSUER_PARTNER - return defaults - - @api.model - def _move_autocomplete_invoice_lines_create(self, vals_list): - new_vals_list = super( - AccountMove, self.with_context(lines_compute_amounts=True) - )._move_autocomplete_invoice_lines_create(vals_list) - for vals in new_vals_list: - if not vals.get("document_type_id"): - vals["fiscal_document_id"] = self.env.company.fiscal_dummy_id.id - return new_vals_list - - def _move_autocomplete_invoice_lines_values(self): - self.ensure_one() - if self._context.get("lines_compute_amounts"): - self.line_ids._compute_amounts() - return super()._move_autocomplete_invoice_lines_values() - - @api.model_create_multi - def create(self, vals_list): - invoice = super().create(vals_list) - invoice._write_shadowed_fields() - return invoice - - def write(self, values): - result = super().write(values) - self._write_shadowed_fields() - return result - - def unlink(self): - """Allows delete a draft or cancelled invoices""" - unlink_moves = self.env["account.move"] - unlink_documents = self.env["l10n_br_fiscal.document"] - for move in self: - if not move.exists(): - continue - if ( - move.fiscal_document_id - and move.fiscal_document_id.id != self.env.company.fiscal_dummy_id.id - ): - unlink_documents |= move.fiscal_document_id - unlink_moves |= move - result = super(AccountMove, unlink_moves).unlink() - unlink_documents.unlink() - self.clear_caches() - return result - - @api.returns("self", lambda value: value.id) - def copy(self, default=None): - default = default or {} - if self.document_type_id: - default["fiscal_line_ids"] = False - else: - default["fiscal_line_ids"] = self.line_ids[0] - return super().copy(default) - - @api.model - def _serialize_tax_grouping_key(self, grouping_dict): - return "-".join(str(v) for v in grouping_dict.values()) - - @api.model - def _compute_taxes_mapped(self, base_line): - - move = base_line.move_id - - if move.is_invoice(include_receipts=True): - handle_price_include = True - sign = -1 if move.is_inbound() else 1 - quantity = base_line.quantity - is_refund = move.move_type in ("out_refund", "in_refund") - price_unit_wo_discount = ( - sign * base_line.price_unit * (1 - (base_line.discount / 100.0)) - ) - else: - handle_price_include = False - quantity = 1.0 - tax_type = base_line.tax_ids[0].type_tax_use if base_line.tax_ids else None - is_refund = (tax_type == "sale" and base_line.debit) or ( - tax_type == "purchase" and base_line.credit - ) - price_unit_wo_discount = base_line.amount_currency - - balance_taxes_res = base_line.tax_ids._origin.with_context( - force_sign=move._get_tax_force_sign() - ).compute_all( - price_unit_wo_discount, - currency=base_line.currency_id, - quantity=quantity, - product=base_line.product_id, - partner=base_line.partner_id, - is_refund=is_refund, - handle_price_include=handle_price_include, - fiscal_taxes=base_line.fiscal_tax_ids, - operation_line=base_line.fiscal_operation_line_id, - ncm=base_line.ncm_id, - nbs=base_line.nbs_id, - nbm=base_line.nbm_id, - cest=base_line.cest_id, - discount_value=base_line.discount_value, - insurance_value=base_line.insurance_value, - other_value=base_line.other_value, - freight_value=base_line.freight_value, - fiscal_price=base_line.fiscal_price, - fiscal_quantity=base_line.fiscal_quantity, - uot_id=base_line.uot_id, - icmssn_range=base_line.icmssn_range_id, - icms_origin=base_line.icms_origin, - ind_final=base_line.ind_final, - ) - - return balance_taxes_res - - def _preprocess_taxes_map(self, taxes_map): - """Useful in case we want to pre-process taxes_map""" - - taxes_mapped = super()._preprocess_taxes_map(taxes_map=taxes_map) - - for line in self.line_ids.filtered( - lambda line: not line.tax_repartition_line_id - ): - if not line.tax_ids or not line.fiscal_tax_ids: - continue - - compute_all_vals = self._compute_taxes_mapped(line) - - for tax_vals in compute_all_vals["taxes"]: - grouping_dict = self._get_tax_grouping_key_from_base_line( - line, tax_vals - ) - grouping_key = self._serialize_tax_grouping_key(grouping_dict) - - tax_repartition_line = self.env["account.tax.repartition.line"].browse( - tax_vals["tax_repartition_line_id"] - ) - - if taxes_mapped[grouping_key]: - taxes_mapped[grouping_key]["amount"] += tax_vals["amount"] - taxes_mapped[grouping_key][ - "tax_base_amount" - ] += self._get_base_amount_to_display( - tax_vals["base"], tax_repartition_line, tax_vals["group"] - ) - - return taxes_mapped - - def _recompute_payment_terms_lines(self): - """Compute the dynamic payment term lines of the journal entry. - overwritten this method to change aml's field name. - """ - - # TODO - esse método é executado em um onchange, na emissão de um novo - # documento fiscal o numero do documento pode estar em branco - # atualizar esse dado ao validar a fatura, ou atribuir o número da NFe - # antes de salva-la. - result = super()._recompute_payment_terms_lines() - if self.document_number: - terms_lines = self.line_ids.filtered( - lambda l: l.account_id.user_type_id.type in ("receivable", "payable") - and l.move_id.document_type_id - ) - terms_lines.sorted(lambda line: line.date_maturity) - for idx, terms_line in enumerate(terms_lines): - # TODO TODO pegar o método do self.fiscal_document_id.with_context( - # fiscal_document_no_company=True - # )._compute_document_name() - terms_line.name = "{}/{}-{}".format( - self.document_number, idx + 1, len(terms_lines) - ) - return result - - # @api.model - # def invoice_line_move_line_get(self): - # # TODO FIXME migrate. No such method in Odoo 13+ - # move_lines_dict = super().invoice_line_move_line_get() - # new_mv_lines_dict = [] - # for line in move_lines_dict: - # invoice_line = self.line_ids.filtered(lambda l: l.id == line.get("invl_id")) - # - # if invoice_line.fiscal_operation_id: - # if invoice_line.fiscal_operation_id.deductible_taxes: - # line["price"] = invoice_line.price_total - # else: - # line["price"] = invoice_line.price_total - ( - # invoice_line.amount_tax_withholding - # + invoice_line.amount_tax_included - # ) - # - # if invoice_line.cfop_id: - # if invoice_line.cfop_id.finance_move: - # new_mv_lines_dict.append(line) - # else: - # new_mv_lines_dict.append(line) - # - # return new_mv_lines_dict - # - - @api.onchange("fiscal_operation_id") - def _onchange_fiscal_operation_id(self): - result = super()._onchange_fiscal_operation_id() - if self.fiscal_operation_id and self.fiscal_operation_id.journal_id: - self.journal_id = self.fiscal_operation_id.journal_id - return result - - def open_fiscal_document(self): - if self.env.context.get("move_type", "") == "out_invoice": - xmlid = "l10n_br_account.fiscal_invoice_out_action" - elif self.env.context.get("move_type", "") == "in_invoice": - xmlid = "l10n_br_account.fiscal_invoice_in_action" - else: - xmlid = "l10n_br_account.fiscal_invoice_all_action" - action = self.env["ir.actions.act_window"]._for_xml_id(xmlid) - form_view = [(self.env.ref("l10n_br_account.fiscal_invoice_form").id, "form")] - if "views" in action: - action["views"] = form_view + [ - (state, view) for state, view in action["views"] if view != "form" - ] - else: - action["views"] = form_view - action["res_id"] = self.id - return action - - def action_date_assign(self): - """Usamos esse método para definir a data de emissão do documento - fiscal e numeração do documento fiscal para ser usado nas linhas - dos lançamentos contábeis.""" - # TODO FIXME migrate. No such method in Odoo 13+ - result = super().action_date_assign() - for invoice in self: - if invoice.document_type_id: - if invoice.issuer == DOCUMENT_ISSUER_COMPANY: - invoice.fiscal_document_id._document_date() - invoice.fiscal_document_id._document_number() - return result - - def button_draft(self): - for i in self.filtered(lambda d: d.document_type_id): - if i.state_edoc == SITUACAO_EDOC_CANCELADA: - if i.issuer == DOCUMENT_ISSUER_COMPANY: - raise UserError( - _( - "You can't set this document number: {} to draft " - "because this document is cancelled in SEFAZ" - ).format(i.document_number) - ) - if i.state_edoc != SITUACAO_EDOC_EM_DIGITACAO: - i.fiscal_document_id.action_document_back2draft() - return super().button_draft() - - def action_document_send(self): - invoices = self.filtered(lambda d: d.document_type_id) - if invoices: - invoices.mapped("fiscal_document_id").action_document_send() - # FIXME: na migração para a v14 foi permitido o post antes do envio - # para destravar a migração, mas poderia ser cogitado de obrigar a - # transmissão antes do post novamente como na v12. - # for invoice in invoices: - # invoice.move_id.post(invoice=invoice) - - def action_document_cancel(self): - for i in self.filtered(lambda d: d.document_type_id): - return i.fiscal_document_id.action_document_cancel() - - def action_document_correction(self): - for i in self.filtered(lambda d: d.document_type_id): - return i.fiscal_document_id.action_document_correction() - - def action_document_invalidate(self): - for i in self.filtered(lambda d: d.document_type_id): - return i.fiscal_document_id.action_document_invalidate() - - def action_document_back2draft(self): - """Sets fiscal document to draft state and cancel and set to draft - the related invoice for both documents remain equivalent state.""" - for i in self.filtered(lambda d: d.document_type_id): - i.button_cancel() - i.button_draft() - - def action_post(self): - result = super().action_post() - - self.mapped("fiscal_document_id").filtered( - lambda d: d.document_type_id - ).action_document_confirm() - - # TODO FIXME - # Deixar a migração das funcionalidades do refund por último. - # Verificar se ainda haverá necessidade desse código. - - # for record in self.filtered(lambda i: i.refund_move_id): - # if record.state == "open": - # # Ao confirmar uma fatura/documento fiscal se é uma devolução - # # é feito conciliado com o documento de origem para abater - # # o valor devolvido pelo documento de refund - # to_reconcile_lines = self.env["account.move.line"] - # for line in record.move_id.line_ids: - # if line.account_id.id == record.account_id.id: - # to_reconcile_lines += line - # if line.reconciled: - # line.remove_move_reconcile() - # for line in record.refund_move_id.move_id.line_ids: - # if line.account_id.id == record.refund_move_id.account_id.id: - # to_reconcile_lines += line - - # to_reconcile_lines.filtered(lambda l: l.reconciled).reconcile() - - return result - - def view_xml(self): - self.ensure_one() - return self.fiscal_document_id.view_xml() - - def view_pdf(self): - self.ensure_one() - return self.fiscal_document_id.view_pdf() - - def action_send_email(self): - self.ensure_one() - return self.fiscal_document_id.action_send_email() - - @api.onchange("document_type_id") - def _onchange_document_type_id(self): - # We need to ensure that invoices without a fiscal document have the - # document_number blank, as all invoices without a fiscal document share this - # same field, they are linked to the same dummy fiscal document. - # Otherwise, in the tree view, this field will be displayed with the same value - # for all these invoices. - if not self.document_type_id: - self.document_number = "" - - def _reverse_moves(self, default_values_list=None, cancel=False): - new_moves = super()._reverse_moves( - default_values_list=default_values_list, cancel=cancel - ) - force_fiscal_operation_id = False - if self.env.context.get("force_fiscal_operation_id"): - force_fiscal_operation_id = self.env["l10n_br_fiscal.operation"].browse( - self.env.context.get("force_fiscal_operation_id") - ) - for record in new_moves.filtered(lambda i: i.document_type_id): - if ( - not force_fiscal_operation_id - and not record.fiscal_operation_id.return_fiscal_operation_id - ): - raise UserError( - _("""Document without Return Fiscal Operation! \n Force one!""") - ) - - record.fiscal_operation_id = ( - force_fiscal_operation_id - or record.fiscal_operation_id.return_fiscal_operation_id - ) - record._onchange_fiscal_operation_id() - - for line in record.invoice_line_ids: - if ( - not force_fiscal_operation_id - and not line.fiscal_operation_id.return_fiscal_operation_id - ): - raise UserError( - _( - """Line without Return Fiscal Operation! \n - Please force one! \n{}""".format( - line.name - ) - ) - ) - - line.fiscal_operation_id = ( - force_fiscal_operation_id - or line.fiscal_operation_id.return_fiscal_operation_id - ) - line._onchange_fiscal_operation_id() - - # Adds the related document to the NF-e. - # this is required for correct xml validation - if record.document_type_id and record.document_type_id.code in ( - MODELO_FISCAL_NFE - ): - record.fiscal_document_id._document_reference( - record.reversed_entry_id.fiscal_document_id - ) - - return new_moves - - # Migrar para v14.0 .. talvez nao precise mais disso. - # def _refund_cleanup_lines(self, lines): - # result = super()._refund_cleanup_lines(lines) - # for _a, _b, vals in result: - # if vals.get("fiscal_document_line_id"): - # vals.pop("fiscal_document_line_id") - - # for i, line in enumerate(lines): - # for name, _field in line._fields.items(): - # if name == "fiscal_tax_ids": - # result[i][2][name] = [(6, 0, line[name].ids)] - - # return result diff --git a/l10n_br_account/models/account_move.py b/l10n_br_account/models/account_move.py index 55acf279bca5..e392af3cc3ff 100644 --- a/l10n_br_account/models/account_move.py +++ b/l10n_br_account/models/account_move.py @@ -1,18 +1,656 @@ -# Copyright (C) 2021 - TODAY Renato Lima - Akretion +# Copyright (C) 2009 - TODAY Renato Lima - Akretion +# Copyright (C) 2019 - TODAY Raphaël Valyi - Akretion +# Copyright (C) 2020 - TODAY Luis Felipe Mileo - KMEE # License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html from dateutil.relativedelta import relativedelta -from odoo import models +from odoo import _, api, fields, models +from odoo.exceptions import UserError from odoo.addons.l10n_br_fiscal.constants.fiscal import ( DOCUMENT_ISSUER_COMPANY, + DOCUMENT_ISSUER_PARTNER, + FISCAL_IN_OUT_ALL, + FISCAL_OUT, + MODELO_FISCAL_NFE, SITUACAO_EDOC_AUTORIZADA, + SITUACAO_EDOC_CANCELADA, + SITUACAO_EDOC_EM_DIGITACAO, ) +MOVE_TO_OPERATION = { + "out_invoice": "out", + "in_invoice": "in", + "out_refund": "in", + "in_refund": "out", + "out_receipt": "out", + "in_receipt": "in", +} + +REFUND_TO_OPERATION = { + "out_invoice": "in", + "in_invoice": "out", + "out_refund": "out", + "in_refund": "in", +} + +FISCAL_TYPE_REFUND = { + "out": ["purchase_refund", "in_return"], + "in": ["sale_refund", "out_return"], +} + +MOVE_TAX_USER_TYPE = { + "out_invoice": "sale", + "in_invoice": "purchase", + "out_refund": "sale", + "in_refund": "purchase", +} + +SHADOWED_FIELDS = [ + "partner_id", + "company_id", + "currency_id", + "partner_shipping_id", + "user_id", +] + class AccountMove(models.Model): - _inherit = "account.move" + _name = "account.move" + _inherit = [ + _name, + "l10n_br_fiscal.document.mixin.methods", + "l10n_br_fiscal.document.move.mixin", + ] + _inherits = {"l10n_br_fiscal.document": "fiscal_document_id"} + _order = "date DESC, name DESC" + + # some account.move records _inherits from an fiscal.document that is + # disabled with active=False (dummy record) in the l10n_br_fiscal_document table. + # To make the invoices still visible, we set active=True + # in the account_move table. + active = fields.Boolean( + default=True, + ) + + document_electronic = fields.Boolean( + related="document_type_id.electronic", + string="Electronic?", + ) + + fiscal_document_id = fields.Many2one( + comodel_name="l10n_br_fiscal.document", + string="Fiscal Document", + required=True, + copy=False, + ondelete="cascade", + ) + + fiscal_operation_type = fields.Selection( + selection=FISCAL_IN_OUT_ALL, + related=None, + compute="_compute_fiscal_operation_type", + ) + + has_fiscal_dummy = fields.Boolean( + compute="_compute_has_fiscal_dummy", + ) + + def _compute_has_fiscal_dummy(self): + for rec in self: + rec.has_fiscal_dummy = ( + rec.fiscal_document_id.id == rec.company_id.fiscal_dummy_id.id + ) + + @api.constrains("fiscal_document_id", "document_type_id") + def _check_fiscal_document_type(self): + for rec in self: + has_fiscal_dummy = ( + rec.fiscal_document_id.id == rec.company_id.fiscal_dummy_id.id + ) + if has_fiscal_dummy and rec.document_type_id: + raise UserError( + _("You can't set a document type to a fiscal dummy document.") + ) + + def _compute_fiscal_operation_type(self): + for inv in self: + if inv.move_type == "entry": + # if it is a Journal Entry there is nothing to do. + inv.fiscal_operation_type = False + continue + if inv.fiscal_operation_id: + inv.fiscal_operation_type = ( + inv.fiscal_operation_id.fiscal_operation_type + ) + else: + inv.fiscal_operation_type = MOVE_TO_OPERATION[inv.move_type] + + def _get_amount_lines(self): + """Get object lines instaces used to compute fields""" + return self.mapped("invoice_line_ids") + + @api.model + def _shadowed_fields(self): + """Returns the list of shadowed fields that are synced + from the parent.""" + return SHADOWED_FIELDS + + @api.model + def _inject_shadowed_fields(self, vals_list): + for vals in vals_list: + for field in self._shadowed_fields(): + if vals.get(field): + vals["fiscal_%s" % (field,)] = vals[field] + + @api.model + def fields_view_get( + self, view_id=None, view_type="form", toolbar=False, submenu=False + ): + invoice_view = super().fields_view_get(view_id, view_type, toolbar, submenu) + if self.env.company.country_id.code != "BR": + return invoice_view + elif view_type == "form": + view = self.env["ir.ui.view"] + + if view_id == self.env.ref("l10n_br_account.fiscal_invoice_form").id: + invoice_line_form_id = self.env.ref( + "l10n_br_account.fiscal_invoice_line_form" + ).id + sub_form_view = self.env["account.move.line"].fields_view_get( + view_id=invoice_line_form_id, view_type="form" + )["arch"] + sub_form_node = self.env["account.move.line"].inject_fiscal_fields( + sub_form_view + ) + sub_arch, sub_fields = view.postprocess_and_fields( + sub_form_node, "account.move.line", False + ) + line_field_name = "invoice_line_ids" + invoice_view["fields"][line_field_name]["views"]["form"] = { + "fields": sub_fields, + "arch": sub_arch, + } + + else: + if invoice_view["fields"].get("invoice_line_ids"): + invoice_line_form_id = self.env.ref( + "l10n_br_account.invoice_form" + ).id + sub_form_view = invoice_view["fields"]["invoice_line_ids"]["views"][ + "form" + ]["arch"] + + sub_form_node = self.env["account.move.line"].inject_fiscal_fields( + sub_form_view + ) + sub_arch, sub_fields = view.postprocess_and_fields( + sub_form_node, "account.move.line", False + ) + line_field_name = "invoice_line_ids" + invoice_view["fields"][line_field_name]["views"]["form"] = { + "fields": sub_fields, + "arch": sub_arch, + } + + if invoice_view["fields"].get("line_ids"): + invoice_line_form_id = self.env.ref( + "l10n_br_account.invoice_form" + ).id + sub_form_view = invoice_view["fields"]["line_ids"]["views"]["tree"][ + "arch" + ] + + sub_form_node = self.env["account.move.line"].inject_fiscal_fields( + sub_form_view + ) + sub_arch, sub_fields = view.postprocess_and_fields( + sub_form_node, "account.move.line", False + ) + line_field_name = "line_ids" + invoice_view["fields"][line_field_name]["views"]["tree"] = { + "fields": sub_fields, + "arch": sub_arch, + } + + return invoice_view + + @api.depends( + "line_ids.matched_debit_ids.debit_move_id.move_id.payment_id.is_matched", + "line_ids.matched_debit_ids.debit_move_id.move_id.line_ids.amount_residual", + "line_ids.matched_debit_ids.debit_move_id.move_id.line_ids.amount_residual_currency", + "line_ids.matched_credit_ids.credit_move_id.move_id.payment_id.is_matched", + "line_ids.matched_credit_ids.credit_move_id.move_id.line_ids.amount_residual", + "line_ids.matched_credit_ids.credit_move_id.move_id.line_ids.amount_residual_currency", + "line_ids.debit", + "line_ids.credit", + "line_ids.currency_id", + "line_ids.amount_currency", + "line_ids.amount_residual", + "line_ids.amount_residual_currency", + "line_ids.payment_id.state", + "line_ids.full_reconcile_id", + "ind_final", + ) + def _compute_amount(self): + for move in self.filtered(lambda m: m.company_id.country_id.code == "BR"): + for line in move.line_ids: + if ( + move.is_invoice(include_receipts=True) + and not line.exclude_from_invoice_tab + ): + line._update_taxes() + + result = super()._compute_amount() + for move in self.filtered(lambda m: m.company_id.country_id.code == "BR"): + if move.move_type == "entry" or move.is_outbound(): + sign = -1 + else: + sign = 1 + inv_line_ids = move.line_ids.filtered( + lambda l: not l.exclude_from_invoice_tab + ) + move.amount_untaxed = sum(inv_line_ids.mapped("amount_untaxed")) + move.amount_tax = sum(inv_line_ids.mapped("amount_tax")) + move.amount_untaxed_signed = sign * sum( + inv_line_ids.mapped("amount_untaxed") + ) + move.amount_tax_signed = sign * sum(inv_line_ids.mapped("amount_tax")) + + return result + + @api.onchange("ind_final") + def _onchange_ind_final(self): + """Trigger the recompute of the taxes when the ind_final is changed""" + for line in self.invoice_line_ids: + line._onchange_fiscal_operation_id() + return self._recompute_dynamic_lines(recompute_all_taxes=True) + + @api.model + def default_get(self, fields_list): + defaults = super().default_get(fields_list) + move_type = self.env.context.get("default_move_type", "out_invoice") + if not move_type == "entry": + defaults["fiscal_operation_type"] = MOVE_TO_OPERATION[move_type] + if defaults["fiscal_operation_type"] == FISCAL_OUT: + defaults["issuer"] = DOCUMENT_ISSUER_COMPANY + else: + defaults["issuer"] = DOCUMENT_ISSUER_PARTNER + return defaults + + @api.model + def _move_autocomplete_invoice_lines_create(self, vals_list): + new_vals_list = super( + AccountMove, self.with_context(lines_compute_amounts=True) + )._move_autocomplete_invoice_lines_create(vals_list) + for vals in new_vals_list: + if not vals.get("document_type_id"): + vals["fiscal_document_id"] = self.env.company.fiscal_dummy_id.id + return new_vals_list + + def _move_autocomplete_invoice_lines_values(self): + self.ensure_one() + if self._context.get("lines_compute_amounts"): + self.line_ids._compute_amounts() + return super()._move_autocomplete_invoice_lines_values() + + @api.model_create_multi + def create(self, vals_list): + self._inject_shadowed_fields(vals_list) + invoice = super().create(vals_list) + return invoice + + def write(self, values): + self._inject_shadowed_fields([values]) + result = super().write(values) + return result + + def unlink(self): + """Allows delete a draft or cancelled invoices""" + unlink_moves = self.env["account.move"] + unlink_documents = self.env["l10n_br_fiscal.document"] + for move in self: + if not move.exists(): + continue + if ( + move.fiscal_document_id + and move.fiscal_document_id.id != self.env.company.fiscal_dummy_id.id + ): + unlink_documents |= move.fiscal_document_id + unlink_moves |= move + result = super(AccountMove, unlink_moves).unlink() + unlink_documents.unlink() + self.clear_caches() + return result + + @api.model + def _serialize_tax_grouping_key(self, grouping_dict): + return "-".join(str(v) for v in grouping_dict.values()) + + @api.model + def _compute_taxes_mapped(self, base_line): + + move = base_line.move_id + + if move.is_invoice(include_receipts=True): + handle_price_include = True + sign = -1 if move.is_inbound() else 1 + quantity = base_line.quantity + is_refund = move.move_type in ("out_refund", "in_refund") + price_unit_wo_discount = ( + sign * base_line.price_unit * (1 - (base_line.discount / 100.0)) + ) + else: + handle_price_include = False + quantity = 1.0 + tax_type = base_line.tax_ids[0].type_tax_use if base_line.tax_ids else None + is_refund = (tax_type == "sale" and base_line.debit) or ( + tax_type == "purchase" and base_line.credit + ) + price_unit_wo_discount = base_line.amount_currency + + balance_taxes_res = base_line.tax_ids._origin.with_context( + force_sign=move._get_tax_force_sign() + ).compute_all( + price_unit_wo_discount, + currency=base_line.currency_id, + quantity=quantity, + product=base_line.product_id, + partner=base_line.partner_id, + is_refund=is_refund, + handle_price_include=handle_price_include, + fiscal_taxes=base_line.fiscal_tax_ids, + operation_line=base_line.fiscal_operation_line_id, + ncm=base_line.ncm_id, + nbs=base_line.nbs_id, + nbm=base_line.nbm_id, + cest=base_line.cest_id, + discount_value=base_line.discount_value, + insurance_value=base_line.insurance_value, + other_value=base_line.other_value, + freight_value=base_line.freight_value, + fiscal_price=base_line.fiscal_price, + fiscal_quantity=base_line.fiscal_quantity, + uot_id=base_line.uot_id, + icmssn_range=base_line.icmssn_range_id, + icms_origin=base_line.icms_origin, + ind_final=base_line.ind_final, + ) + + return balance_taxes_res + + def _preprocess_taxes_map(self, taxes_map): + """Useful in case we want to pre-process taxes_map""" + + taxes_mapped = super()._preprocess_taxes_map(taxes_map=taxes_map) + + for line in self.line_ids.filtered( + lambda line: not line.tax_repartition_line_id + ): + if not line.tax_ids or not line.fiscal_tax_ids: + continue + + compute_all_vals = self._compute_taxes_mapped(line) + + for tax_vals in compute_all_vals["taxes"]: + grouping_dict = self._get_tax_grouping_key_from_base_line( + line, tax_vals + ) + grouping_key = self._serialize_tax_grouping_key(grouping_dict) + + tax_repartition_line = self.env["account.tax.repartition.line"].browse( + tax_vals["tax_repartition_line_id"] + ) + + if taxes_mapped[grouping_key]: + taxes_mapped[grouping_key]["amount"] += tax_vals["amount"] + taxes_mapped[grouping_key][ + "tax_base_amount" + ] += self._get_base_amount_to_display( + tax_vals["base"], tax_repartition_line, tax_vals["group"] + ) + + return taxes_mapped + + def _recompute_payment_terms_lines(self): + """Compute the dynamic payment term lines of the journal entry. + overwritten this method to change aml's field name. + """ + + # TODO - esse método é executado em um onchange, na emissão de um novo + # documento fiscal o numero do documento pode estar em branco + # atualizar esse dado ao validar a fatura, ou atribuir o número da NFe + # antes de salva-la. + result = super()._recompute_payment_terms_lines() + if self.document_number: + terms_lines = self.line_ids.filtered( + lambda l: l.account_id.user_type_id.type in ("receivable", "payable") + and l.move_id.document_type_id + ) + terms_lines.sorted(lambda line: line.date_maturity) + for idx, terms_line in enumerate(terms_lines): + # TODO TODO pegar o método do self.fiscal_document_id.with_context( + # fiscal_document_no_company=True + # )._compute_document_name() + terms_line.name = "{}/{}-{}".format( + self.document_number, idx + 1, len(terms_lines) + ) + return result + + # @api.model + # def invoice_line_move_line_get(self): + # # TODO FIXME migrate. No such method in Odoo 13+ + # move_lines_dict = super().invoice_line_move_line_get() + # new_mv_lines_dict = [] + # for line in move_lines_dict: + # invoice_line = self.line_ids.filtered(lambda l: l.id == line.get("invl_id")) + # + # if invoice_line.fiscal_operation_id: + # if invoice_line.fiscal_operation_id.deductible_taxes: + # line["price"] = invoice_line.price_total + # else: + # line["price"] = invoice_line.price_total - ( + # invoice_line.amount_tax_withholding + # + invoice_line.amount_tax_included + # ) + # + # if invoice_line.cfop_id: + # if invoice_line.cfop_id.finance_move: + # new_mv_lines_dict.append(line) + # else: + # new_mv_lines_dict.append(line) + # + # return new_mv_lines_dict + # + + @api.onchange("fiscal_operation_id") + def _onchange_fiscal_operation_id(self): + result = super()._onchange_fiscal_operation_id() + if self.fiscal_operation_id and self.fiscal_operation_id.journal_id: + self.journal_id = self.fiscal_operation_id.journal_id + return result + + def open_fiscal_document(self): + if self.env.context.get("move_type", "") == "out_invoice": + xmlid = "l10n_br_account.fiscal_invoice_out_action" + elif self.env.context.get("move_type", "") == "in_invoice": + xmlid = "l10n_br_account.fiscal_invoice_in_action" + else: + xmlid = "l10n_br_account.fiscal_invoice_all_action" + action = self.env["ir.actions.act_window"]._for_xml_id(xmlid) + form_view = [(self.env.ref("l10n_br_account.fiscal_invoice_form").id, "form")] + if "views" in action: + action["views"] = form_view + [ + (state, view) for state, view in action["views"] if view != "form" + ] + else: + action["views"] = form_view + action["res_id"] = self.id + return action + + def action_date_assign(self): + """Usamos esse método para definir a data de emissão do documento + fiscal e numeração do documento fiscal para ser usado nas linhas + dos lançamentos contábeis.""" + # TODO FIXME migrate. No such method in Odoo 13+ + result = super().action_date_assign() + for invoice in self: + if invoice.document_type_id: + if invoice.issuer == DOCUMENT_ISSUER_COMPANY: + invoice.fiscal_document_id._document_date() + invoice.fiscal_document_id._document_number() + return result + + def button_draft(self): + for i in self.filtered(lambda d: d.document_type_id): + if i.state_edoc == SITUACAO_EDOC_CANCELADA: + if i.issuer == DOCUMENT_ISSUER_COMPANY: + raise UserError( + _( + "You can't set this document number: {} to draft " + "because this document is cancelled in SEFAZ" + ).format(i.document_number) + ) + if i.state_edoc != SITUACAO_EDOC_EM_DIGITACAO: + i.fiscal_document_id.action_document_back2draft() + return super().button_draft() + + def action_document_send(self): + invoices = self.filtered(lambda d: d.document_type_id) + if invoices: + invoices.mapped("fiscal_document_id").action_document_send() + # FIXME: na migração para a v14 foi permitido o post antes do envio + # para destravar a migração, mas poderia ser cogitado de obrigar a + # transmissão antes do post novamente como na v12. + # for invoice in invoices: + # invoice.move_id.post(invoice=invoice) + + def action_document_cancel(self): + for i in self.filtered(lambda d: d.document_type_id): + return i.fiscal_document_id.action_document_cancel() + + def action_document_correction(self): + for i in self.filtered(lambda d: d.document_type_id): + return i.fiscal_document_id.action_document_correction() + + def action_document_invalidate(self): + for i in self.filtered(lambda d: d.document_type_id): + return i.fiscal_document_id.action_document_invalidate() + + def action_document_back2draft(self): + """Sets fiscal document to draft state and cancel and set to draft + the related invoice for both documents remain equivalent state.""" + for i in self.filtered(lambda d: d.document_type_id): + i.button_cancel() + i.button_draft() + + def action_post(self): + result = super().action_post() + + self.mapped("fiscal_document_id").filtered( + lambda d: d.document_type_id + ).action_document_confirm() + + # TODO FIXME + # Deixar a migração das funcionalidades do refund por último. + # Verificar se ainda haverá necessidade desse código. + + # for record in self.filtered(lambda i: i.refund_move_id): + # if record.state == "open": + # # Ao confirmar uma fatura/documento fiscal se é uma devolução + # # é feito conciliado com o documento de origem para abater + # # o valor devolvido pelo documento de refund + # to_reconcile_lines = self.env["account.move.line"] + # for line in record.move_id.line_ids: + # if line.account_id.id == record.account_id.id: + # to_reconcile_lines += line + # if line.reconciled: + # line.remove_move_reconcile() + # for line in record.refund_move_id.move_id.line_ids: + # if line.account_id.id == record.refund_move_id.account_id.id: + # to_reconcile_lines += line + + # to_reconcile_lines.filtered(lambda l: l.reconciled).reconcile() + + return result + + def view_xml(self): + self.ensure_one() + return self.fiscal_document_id.view_xml() + + def view_pdf(self): + self.ensure_one() + return self.fiscal_document_id.view_pdf() + + def action_send_email(self): + self.ensure_one() + return self.fiscal_document_id.action_send_email() + + @api.onchange("document_type_id") + def _onchange_document_type_id(self): + # We need to ensure that invoices without a fiscal document have the + # document_number blank, as all invoices without a fiscal document share this + # same field, they are linked to the same dummy fiscal document. + # Otherwise, in the tree view, this field will be displayed with the same value + # for all these invoices. + if not self.document_type_id: + self.document_number = "" + + def _reverse_moves(self, default_values_list=None, cancel=False): + new_moves = super()._reverse_moves( + default_values_list=default_values_list, cancel=cancel + ) + force_fiscal_operation_id = False + if self.env.context.get("force_fiscal_operation_id"): + force_fiscal_operation_id = self.env["l10n_br_fiscal.operation"].browse( + self.env.context.get("force_fiscal_operation_id") + ) + for record in new_moves.filtered(lambda i: i.document_type_id): + if ( + not force_fiscal_operation_id + and not record.fiscal_operation_id.return_fiscal_operation_id + ): + raise UserError( + _("""Document without Return Fiscal Operation! \n Force one!""") + ) + + record.fiscal_operation_id = ( + force_fiscal_operation_id + or record.fiscal_operation_id.return_fiscal_operation_id + ) + record._onchange_fiscal_operation_id() + + for line in record.invoice_line_ids: + if ( + not force_fiscal_operation_id + and not line.fiscal_operation_id.return_fiscal_operation_id + ): + raise UserError( + _( + """Line without Return Fiscal Operation! \n + Please force one! \n{}""".format( + line.name + ) + ) + ) + + line.fiscal_operation_id = ( + force_fiscal_operation_id + or line.fiscal_operation_id.return_fiscal_operation_id + ) + line._onchange_fiscal_operation_id() + + # Adds the related document to the NF-e. + # this is required for correct xml validation + if record.document_type_id and record.document_type_id.code in ( + MODELO_FISCAL_NFE + ): + record.fiscal_document_id._document_reference( + record.reversed_entry_id.fiscal_document_id + ) + + return new_moves def _prepare_wh_invoice(self, move_line, fiscal_group): wh_date_invoice = move_line.move_id.date diff --git a/l10n_br_account/models/account_invoice_line.py b/l10n_br_account/models/account_move_line.py similarity index 93% rename from l10n_br_account/models/account_invoice_line.py rename to l10n_br_account/models/account_move_line.py index 9649625d22b1..81e2aa9daf0f 100644 --- a/l10n_br_account/models/account_invoice_line.py +++ b/l10n_br_account/models/account_move_line.py @@ -7,23 +7,24 @@ from odoo.exceptions import UserError # These fields have the same name in account.move.line -# and l10n_br_fiscal.document.line.mixin. So they wouldn't get updated +# and l10n_br_fiscal.document.line. So they wouldn't get updated # by the _inherits system. An alternative would be changing their name # in l10n_br_fiscal but that would make the code unreadable and fiscal mixin # methods would fail to do what we expect from them in the Odoo objects # where they are injected. +# Fields that are related in l10n_br_fiscal.document.line like partner_id or company_id +# don't need to be written through the account.move.line write. SHADOWED_FIELDS = [ "name", - "partner_id", - "company_id", - "currency_id", "product_id", "uom_id", "quantity", "price_unit", - "discount_value", ] +ACCOUNTING_FIELDS = ("debit", "credit", "amount_currency") +BUSINESS_FIELDS = ("price_unit", "quantity", "discount", "tax_ids") + class AccountMoveLine(models.Model): _name = "account.move.line" @@ -139,17 +140,15 @@ def _shadowed_fields(self): from the parent.""" return SHADOWED_FIELDS - def _prepare_shadowed_fields_dict(self, default=False): - self.ensure_one() - vals = self._convert_to_write(self.read(self._shadowed_fields())[0]) - if default: # in case you want to use new rather than write later - return {"default_%s" % (k,): vals[k] for k in vals.keys()} - return vals + @api.model + def _inject_shadowed_fields(self, vals_list): + for vals in vals_list: + for field in self._shadowed_fields(): + if vals.get(field): + vals["fiscal_%s" % (field,)] = vals[field] @api.model_create_multi def create(self, vals_list): - ACCOUNTING_FIELDS = ("debit", "credit", "amount_currency") - BUSINESS_FIELDS = ("price_unit", "quantity", "discount", "tax_ids") dummy_doc = self.env.company.fiscal_dummy_id dummy_line = fields.first(dummy_doc.fiscal_line_ids) for values in vals_list: @@ -165,6 +164,7 @@ def create(self, vals_list): ) ) values["fiscal_document_line_id"] = dummy_line.id + continue # dummy doc line, we can skip all l10n-brazil logic values.update( self._update_fiscal_quantity( @@ -175,6 +175,7 @@ def create(self, vals_list): values.get("uot_id"), ) ) + values["document_id"] = fiscal_doc_id # pass through the _inherits system if ( move_id.is_invoice(include_receipts=True) @@ -212,20 +213,14 @@ def create(self, vals_list): cfop_id=cfop_id, ) ) - lines = super().create(vals_list) - - for line in lines.filtered(lambda l: l.fiscal_document_line_id != dummy_line): - shadowed_fiscal_vals = line._prepare_shadowed_fields_dict() - doc_id = line.move_id.fiscal_document_id.id - shadowed_fiscal_vals["document_id"] = doc_id - line.fiscal_document_line_id.write(shadowed_fiscal_vals) - - return lines + self._inject_shadowed_fields(vals_list) + return super().create(vals_list) def write(self, values): dummy_doc = self.env.company.fiscal_dummy_id dummy_line = fields.first(dummy_doc.fiscal_line_ids) non_dummy = self.filtered(lambda l: l.fiscal_document_line_id != dummy_line) + self._inject_shadowed_fields([values]) if values.get("move_id") and len(non_dummy) == len(self): # we can write the document_id in all lines values["document_id"] = ( @@ -249,13 +244,7 @@ def write(self, values): raise UserError( _("You cannot edit an invoice related to a withholding entry") ) - if line.fiscal_document_line_id != dummy_line: - shadowed_fiscal_vals = line._prepare_shadowed_fields_dict() - line.fiscal_document_line_id.write(shadowed_fiscal_vals) - ACCOUNTING_FIELDS = ("debit", "credit", "amount_currency") - BUSINESS_FIELDS = ("price_unit", "quantity", "discount", "tax_ids") - for line in self: cleaned_vals = line.move_id._cleanup_write_orm_values(line, values) if not cleaned_vals: continue @@ -311,6 +300,18 @@ def _get_fields_onchange_balance_model( price_subtotal, force_computation=False, ): + if self.env.company.country_id.code != "BR": + return super()._get_fields_onchange_balance_model( + quantity=quantity, + discount=discount, + amount_currency=amount_currency, + move_type=move_type, + currency=currency, + taxes=taxes, + price_subtotal=price_subtotal, + force_computation=force_computation, + ) + return {} def _get_price_total_and_subtotal( @@ -515,7 +516,7 @@ def _onchange_price_subtotal(self): # completely new onchange, even if the name is not totally consistent with the # fields declared in the api.onchange. if self.company_id.country_id.code != "BR": - return super(AccountMoveLine, self)._onchange_price_subtotal() + return super()._onchange_price_subtotal() for line in self: if not line.move_id.is_invoice(include_receipts=True): continue diff --git a/l10n_br_account/models/fiscal_document.py b/l10n_br_account/models/fiscal_document.py index ffd3352a6a8e..e7ccbb2eef83 100644 --- a/l10n_br_account/models/fiscal_document.py +++ b/l10n_br_account/models/fiscal_document.py @@ -23,10 +23,30 @@ class FiscalDocument(models.Model): string="Invoices", ) + # proxy fields to enable writing the related (shadowed) fields + # to the fiscal document from the account.move through the _inherits system + # despite they have the same names. + fiscal_partner_id = fields.Many2one(related="partner_id", readonly=False) + fiscal_company_id = fields.Many2one(related="company_id", readonly=False) + fiscal_currency_id = fields.Many2one(related="currency_id", readonly=False) + fiscal_partner_shipping_id = fields.Many2one( + related="partner_shipping_id", readonly=False + ) + fiscal_user_id = fields.Many2one(related="user_id", readonly=False) + + # commented out because of badly written TestInvoiceDiscount.test_date_in_out + # def write(self, vals): + # if self.document_type_id: + # return super().write(vals) + fiscal_line_ids = fields.One2many( copy=False, ) + # For some reason, perhaps limitation of _inhertis, + # the related directly in the account.move does not work correctly. + incoterm_id = fields.Many2one(related="move_ids.invoice_incoterm_id") + document_date = fields.Datetime( compute="_compute_document_date", inverse="_inverse_document_date", store=True ) diff --git a/l10n_br_account/models/fiscal_document_line.py b/l10n_br_account/models/fiscal_document_line.py index e38b1a9b0ca4..4f476bb41151 100644 --- a/l10n_br_account/models/fiscal_document_line.py +++ b/l10n_br_account/models/fiscal_document_line.py @@ -14,6 +14,15 @@ class FiscalDocumentLine(models.Model): string="Invoice Lines", ) + # proxy fields to enable writing the related (shadowed) fields + # to the fiscal doc line from the aml through the _inherits system + # despite they have the same names. + fiscal_name = fields.Text(related="name", readonly=False) + fiscal_product_id = fields.Many2one(related="product_id", readonly=False) + fiscal_uom_id = fields.Many2one(related="uom_id", readonly=False) + fiscal_quantity = fields.Float(related="quantity", readonly=False) + fiscal_price_unit = fields.Float(related="price_unit", readonly=False) + def modified(self, fnames, create=False, before=False): """ Modifying a dummy fiscal document (no document_type_id) line should not recompute diff --git a/l10n_br_account/tests/__init__.py b/l10n_br_account/tests/__init__.py index f69a28772f09..6b21a2abb38b 100644 --- a/l10n_br_account/tests/__init__.py +++ b/l10n_br_account/tests/__init__.py @@ -1,8 +1,10 @@ # License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html from . import test_account_taxes +from . import test_company_fiscal_dummy from . import test_customer_invoice_dummy -from . import test_supplier_invoice_dummy +from . import test_document_date from . import test_invoice_refund -from . import test_company_fiscal_dummy -from . import test_invoice_general_cases +from . import test_move_discount +from . import test_supplier_invoice_dummy +from . import test_multi_localizations_invoice diff --git a/l10n_br_account/tests/test_account_taxes.py b/l10n_br_account/tests/test_account_taxes.py index aca5b8a00a73..08eacac80927 100644 --- a/l10n_br_account/tests/test_account_taxes.py +++ b/l10n_br_account/tests/test_account_taxes.py @@ -1,19 +1,18 @@ # Copyright (C) 2020 Renato Lima - Akretion # License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html -from odoo.tests.common import TransactionCase +from odoo.tests import SavepointCase -class TestAccountTaxes(TransactionCase): - def setUp(self): - super().setUp() - - self.l10n_br_company = self.env["res.company"].create( +class TestAccountTaxes(SavepointCase): + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.l10n_br_company = cls.env["res.company"].create( {"name": "Empresa Teste do Plano de Contas Simplificado"} ) - - self.env.user.company_ids += self.l10n_br_company - self.env.company = self.l10n_br_company + cls.env.user.company_ids += cls.l10n_br_company + cls.env.company = cls.l10n_br_company def test_account_taxes(self): """Test if account taxes are related with fiscal taxes""" diff --git a/l10n_br_account/tests/test_company_fiscal_dummy.py b/l10n_br_account/tests/test_company_fiscal_dummy.py index db6502260157..5e5ce0cea982 100644 --- a/l10n_br_account/tests/test_company_fiscal_dummy.py +++ b/l10n_br_account/tests/test_company_fiscal_dummy.py @@ -2,14 +2,15 @@ from psycopg2 import IntegrityError -from odoo.tests.common import TransactionCase +from odoo.tests import SavepointCase from odoo.tools import mute_logger -class TestCompanyFiscalDummy(TransactionCase): - def setUp(self): - super().setUp() - self.company = self.env["res.company"].create( +class TestCompanyFiscalDummy(SavepointCase): + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.company = cls.env["res.company"].create( { "name": "Company Test", } diff --git a/l10n_br_account/tests/test_customer_invoice_dummy.py b/l10n_br_account/tests/test_customer_invoice_dummy.py index d06f44b9e9ce..c60fdfed1db9 100644 --- a/l10n_br_account/tests/test_customer_invoice_dummy.py +++ b/l10n_br_account/tests/test_customer_invoice_dummy.py @@ -4,6 +4,7 @@ import mock +from odoo.exceptions import UserError from odoo.models import NewId from odoo.tests import SavepointCase @@ -361,3 +362,52 @@ def test_line_ids_write(self): self.invoice_3.fiscal_document_id.id, "line.document_id should be equal invoice fiscal_document_id", ) + + def test_invoice_copy_with_dummy(self): + """ + Tests the functionality of copying an invoice while using a fiscal dummy. + It verifies that the new invoice isn't recognized as a fiscal document, + the same fiscal dummy is used, and that no new entries were created. + """ + + # Retrieve initial count of fiscal document lines + init_number_of_fiscal_doc_lines = self.env[ + "l10n_br_fiscal.document.line" + ].search_count([]) + + invoice_copy = self.invoice_1.copy() + + # Confirm that the copied invoice uses the fiscal dummy + self.assertFalse(self.invoice_1.document_type_id.exists()) + self.assertFalse(invoice_copy.document_type_id.exists()) + + # Check that no new fiscal document lines were created after copying the invoice + final_number_of_fiscal_doc_lines = self.env[ + "l10n_br_fiscal.document.line" + ].search_count([]) + self.assertEqual( + init_number_of_fiscal_doc_lines, final_number_of_fiscal_doc_lines + ) + + # Retrieve the dummy fiscal document line + dummy_fiscal_document_line = ( + self.invoice_1.company_id.fiscal_dummy_id.fiscal_line_ids[0] + ) + + # Check that all account move lines are associated with the fiscal dummy + for line in invoice_copy.line_ids: + self.assertEqual( + line.fiscal_document_line_id.id, dummy_fiscal_document_line.id + ) + + self.assertEqual(len(invoice_copy), 1) + + def test_has_fiscal_dummy(self): + fiscal_dummy = self.invoice_1.company_id.fiscal_dummy_id + self.assertEqual(fiscal_dummy.id, self.invoice_1.fiscal_document_id.id) + self.assertTrue(self.invoice_1.has_fiscal_dummy) + + def test_set_document_type_with_dummy(self): + self.assertTrue(self.invoice_1.has_fiscal_dummy) + with self.assertRaises(UserError): + self.invoice_1.document_type_id = self.env.ref("l10n_br_fiscal.document_55") diff --git a/l10n_br_account/tests/test_invoice_general_cases.py b/l10n_br_account/tests/test_document_date.py similarity index 65% rename from l10n_br_account/tests/test_invoice_general_cases.py rename to l10n_br_account/tests/test_document_date.py index 8332eb3cc5b1..e0c778deb992 100644 --- a/l10n_br_account/tests/test_invoice_general_cases.py +++ b/l10n_br_account/tests/test_document_date.py @@ -5,61 +5,62 @@ from pytz import UTC, timezone -from odoo.tests import TransactionCase +from odoo.tests import SavepointCase from odoo.addons.l10n_br_fiscal.constants.fiscal import DOCUMENT_ISSUER_PARTNER -class TestInvoiceDiscount(TransactionCase): - def setUp(self): - super().setUp() +class TestInvoiceDiscount(SavepointCase): + @classmethod + def setUpClass(cls): + super().setUpClass() - self.company = self.env.ref("l10n_br_base.empresa_lucro_presumido") + cls.company = cls.env.ref("l10n_br_base.empresa_lucro_presumido") # set default user company - companies = self.env["res.company"].search([]) - self.env.user.company_ids = [(6, 0, companies.ids)] - self.env.user.company_id = self.company + companies = cls.env["res.company"].search([]) + cls.env.user.company_ids = [(6, 0, companies.ids)] + cls.env.user.company_id = cls.company - self.invoice_account_id = self.env["account.account"].create( + cls.invoice_account_id = cls.env["account.account"].create( { - "company_id": self.company.id, - "user_type_id": self.env.ref("account.data_account_type_receivable").id, + "company_id": cls.company.id, + "user_type_id": cls.env.ref("account.data_account_type_receivable").id, "code": "RECTEST", "name": "Test receivable account", "reconcile": True, } ) - self.invoice_journal = self.env["account.journal"].create( + cls.invoice_journal = cls.env["account.journal"].create( { - "company_id": self.company.id, + "company_id": cls.company.id, "name": "Invoice Journal - (test)", "code": "INVTEST", "type": "sale", } ) - self.invoice_line_account_id = self.env["account.account"].create( + cls.invoice_line_account_id = cls.env["account.account"].create( { - "company_id": self.company.id, - "user_type_id": self.env.ref("account.data_account_type_revenue").id, + "company_id": cls.company.id, + "user_type_id": cls.env.ref("account.data_account_type_revenue").id, "code": "705070", "name": "Product revenue account (test)", } ) - self.fiscal_operation_id = self.env.ref("l10n_br_fiscal.fo_venda") - self.fiscal_operation_id.deductible_taxes = True + cls.fiscal_operation_id = cls.env.ref("l10n_br_fiscal.fo_venda") + cls.fiscal_operation_id.deductible_taxes = True - product_id = self.env.ref("product.product_product_7") + product_id = cls.env.ref("product.product_product_7") invoice_line_vals = [ ( 0, 0, { - "account_id": self.invoice_line_account_id.id, + "account_id": cls.invoice_line_account_id.id, "product_id": product_id.id, "quantity": 1, "price_unit": 1000.0, @@ -68,33 +69,25 @@ def setUp(self): ) ] - self.move_id = ( - self.env["account.move"] + cls.move_id = ( + cls.env["account.move"] .with_context({"check_move_validity": False}) .create( { - "company_id": self.company.id, - "document_serie_id": self.env.ref( + "company_id": cls.company.id, + "document_serie_id": cls.env.ref( "l10n_br_fiscal.empresa_lc_document_55_serie_1" ).id, - "journal_id": self.invoice_journal.id, - "invoice_user_id": self.env.user.id, - "fiscal_operation_id": self.fiscal_operation_id, + "journal_id": cls.invoice_journal.id, + "invoice_user_id": cls.env.user.id, + "fiscal_operation_id": cls.fiscal_operation_id, "move_type": "out_invoice", - "currency_id": self.company.currency_id.id, + "currency_id": cls.company.currency_id.id, "invoice_line_ids": invoice_line_vals, } ) ) - def test_discount(self): - self.assertEqual(self.move_id.invoice_line_ids.price_unit, 1000) - self.assertEqual(self.move_id.invoice_line_ids.quantity, 1) - self.assertEqual(self.move_id.invoice_line_ids.discount_value, 100) - self.assertEqual(self.move_id.invoice_line_ids.discount, 10) - self.move_id.invoice_line_ids._onchange_price_subtotal() - self.assertEqual(self.move_id.invoice_line_ids.price_subtotal, 900) - def test_document_date(self): self.move_id.issuer = DOCUMENT_ISSUER_PARTNER user_tz = timezone(self.env.user.tz or "UTC") diff --git a/l10n_br_account/tests/test_invoice_refund.py b/l10n_br_account/tests/test_invoice_refund.py index ed0c99d243f6..3f2ed7df2754 100644 --- a/l10n_br_account/tests/test_invoice_refund.py +++ b/l10n_br_account/tests/test_invoice_refund.py @@ -3,49 +3,50 @@ from odoo import fields from odoo.exceptions import UserError -from odoo.tests.common import TransactionCase +from odoo.tests import SavepointCase -class TestInvoiceRefund(TransactionCase): - def setUp(self): - super().setUp() +class TestInvoiceRefund(SavepointCase): + @classmethod + def setUpClass(cls): + super().setUpClass() - self.sale_account = self.env["account.account"].create( + cls.sale_account = cls.env["account.account"].create( dict( code="X1020", name="Product Refund Sales - (test)", - user_type_id=self.env.ref("account.data_account_type_revenue").id, + user_type_id=cls.env.ref("account.data_account_type_revenue").id, ) ) - self.refund_journal = self.env["account.journal"].create( + cls.refund_journal = cls.env["account.journal"].create( dict( name="Refund Journal - (test)", code="TREJ", type="sale", refund_sequence=True, - default_account_id=self.sale_account.id, + default_account_id=cls.sale_account.id, ) ) - self.reverse_vals = { + cls.reverse_vals = { "date": fields.Date.from_string("2019-02-01"), "reason": "no reason", "refund_method": "refund", - "journal_id": self.refund_journal.id, + "journal_id": cls.refund_journal.id, } - self.invoice = self.env["account.move"].create( + cls.invoice = cls.env["account.move"].create( dict( name="Test Refund Invoice", move_type="out_invoice", - invoice_payment_term_id=self.env.ref( + invoice_payment_term_id=cls.env.ref( "account.account_payment_term_advance" ).id, - partner_id=self.env.ref("l10n_br_base.res_partner_cliente1_sp").id, - journal_id=self.refund_journal.id, - document_type_id=self.env.ref("l10n_br_fiscal.document_55").id, - document_serie_id=self.env.ref( + partner_id=cls.env.ref("l10n_br_base.res_partner_cliente1_sp").id, + journal_id=cls.refund_journal.id, + document_type_id=cls.env.ref("l10n_br_fiscal.document_55").id, + document_serie_id=cls.env.ref( "l10n_br_fiscal.empresa_lc_document_55_serie_1" ).id, invoice_line_ids=[ @@ -53,30 +54,30 @@ def setUp(self): 0, 0, { - "product_id": self.env.ref("product.product_product_6").id, + "product_id": cls.env.ref("product.product_product_6").id, "quantity": 1.0, "price_unit": 100.0, - "account_id": self.env["account.account"] + "account_id": cls.env["account.account"] .search( [ ( "user_type_id", "=", - self.env.ref( + cls.env.ref( "account.data_account_type_revenue" ).id, ), ( "company_id", "=", - self.env.company.id, + cls.env.company.id, ), ], limit=1, ) .id, "name": "Refund Test", - "uom_id": self.env.ref("uom.product_uom_unit").id, + "uom_id": cls.env.ref("uom.product_uom_unit").id, }, ) ], diff --git a/l10n_br_account/tests/test_move_discount.py b/l10n_br_account/tests/test_move_discount.py new file mode 100644 index 000000000000..80f415d63351 --- /dev/null +++ b/l10n_br_account/tests/test_move_discount.py @@ -0,0 +1,90 @@ +# Copyright (C) 2023-Today - Engenere (). +# @author Felipe Motter Pereira + +from odoo.tests import TransactionCase + + +class TestInvoiceDiscount(TransactionCase): + def setUp(self): + super().setUp() + + self.company = self.env.ref("l10n_br_base.empresa_lucro_presumido") + + # set default user company + companies = self.env["res.company"].search([]) + self.env.user.company_ids = [(6, 0, companies.ids)] + self.env.user.company_id = self.company + + self.invoice_account_id = self.env["account.account"].create( + { + "company_id": self.company.id, + "user_type_id": self.env.ref("account.data_account_type_receivable").id, + "code": "RECTEST", + "name": "Test receivable account", + "reconcile": True, + } + ) + + self.invoice_journal = self.env["account.journal"].create( + { + "company_id": self.company.id, + "name": "Invoice Journal - (test)", + "code": "INVTEST", + "type": "sale", + } + ) + + self.invoice_line_account_id = self.env["account.account"].create( + { + "company_id": self.company.id, + "user_type_id": self.env.ref("account.data_account_type_revenue").id, + "code": "705070", + "name": "Product revenue account (test)", + } + ) + + self.fiscal_operation_id = self.env.ref("l10n_br_fiscal.fo_venda") + self.fiscal_operation_id.deductible_taxes = True + + product_id = self.env.ref("product.product_product_7") + + invoice_line_vals = [ + ( + 0, + 0, + { + "account_id": self.invoice_line_account_id.id, + "product_id": product_id.id, + "quantity": 1, + "price_unit": 1000.0, + "discount_value": 100.0, + }, + ) + ] + + self.move_id = ( + self.env["account.move"] + .with_context({"check_move_validity": False}) + .create( + { + "company_id": self.company.id, + "document_serie_id": self.env.ref( + "l10n_br_fiscal.empresa_lc_document_55_serie_1" + ).id, + "journal_id": self.invoice_journal.id, + "invoice_user_id": self.env.user.id, + "fiscal_operation_id": self.fiscal_operation_id, + "move_type": "out_invoice", + "currency_id": self.company.currency_id.id, + "invoice_line_ids": invoice_line_vals, + } + ) + ) + + def test_discount(self): + self.assertEqual(self.move_id.invoice_line_ids.price_unit, 1000) + self.assertEqual(self.move_id.invoice_line_ids.quantity, 1) + self.assertEqual(self.move_id.invoice_line_ids.discount_value, 100) + self.assertEqual(self.move_id.invoice_line_ids.discount, 10) + self.move_id.invoice_line_ids._onchange_price_subtotal() + self.assertEqual(self.move_id.invoice_line_ids.price_subtotal, 900) diff --git a/l10n_br_account/tests/test_multi_localizations_invoice.py b/l10n_br_account/tests/test_multi_localizations_invoice.py new file mode 100644 index 000000000000..629c4675a2f0 --- /dev/null +++ b/l10n_br_account/tests/test_multi_localizations_invoice.py @@ -0,0 +1,199 @@ +# Copyright (C) 2023 - TODAY Raphaël Valyi - Akretion +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +import logging + +from odoo.tests.common import OdooSuite, tagged + +_logger = logging.getLogger(__name__) + + +# flake8: noqa: B950 - line too long +def addTest(self, test): + """ + This monkey patch is required to avoid triggering all the tests from + TestAccountMoveOutInvoiceOnchanges when it is imported. + see https://stackoverflow.com/questions/69091760/how-can-i-import-a-testclass-properly-to-inherit-from-without-it-being-run-as-a + """ + if type(test).__name__ == "MultiLocalizationsInvoice": + if test._testMethodName.startswith("test_force_"): + # in our MultiLocalizationInvoice class tests should start + # with test_force_ to be enabled in the test suite. + return OdooSuite.addTest._original_method(self, test) + + elif type(test).__name__ != "TestAccountMoveOutInvoiceOnchanges": + return OdooSuite.addTest._original_method(self, test) + + +addTest._original_method = OdooSuite.addTest +OdooSuite.addTest = addTest + + +# flake8: noqa: E402 - module level import not at top of file +from odoo.addons.account.tests.test_account_move_out_invoice import ( + TestAccountMoveOutInvoiceOnchanges, +) + + +@tagged("post_install", "-at_install") +class MultiLocalizationsInvoice(TestAccountMoveOutInvoiceOnchanges): + """ + This is a simple test for ensuring l10n_br_account doesn't break the basic + account module behavior with customer invoices. + """ + + @classmethod + def setup_company_data(cls, company_name, chart_template=None, **kwargs): + usa = cls.env.ref("base.us").id # would be Brazil by default otherwise + return super().setup_company_data( + company_name, chart_template, country_id=usa, **kwargs + ) + + @classmethod + def setUpClass(cls, chart_template_ref=None): + super().setUpClass(chart_template_ref) + # FIXME the following line should not be required but as for + # now if we don't add this group, creating a refund will result + # in an attempt to create a l10n_br_fiscal.subsequent.document record. + cls.env.user.groups_id |= cls.env.ref("l10n_br_fiscal.group_manager") + + # The following tests list is taken with + # cat addons/account/tests/test_account_move_out_invoice.py | grep "def test_" + # then the following script will format the lines: + # for line in lines.splitlines(): + # print(line.replace("def test_", "def test_force_")) + # print(line.replace("def ", " super().").replace("(self):", "()") + "\n") + # + # ideally they should made to pass for a True multi-localizations compatibility + + def test_force_out_invoice_onchange_invoice_date(self): + super().test_out_invoice_onchange_invoice_date() + + def test_force_out_invoice_line_onchange_product_1(self): + super().test_out_invoice_line_onchange_product_1() + + def test_force_out_invoice_line_onchange_product_2_with_fiscal_pos_1(self): + super().test_out_invoice_line_onchange_product_2_with_fiscal_pos_1() + + def test_force_out_invoice_line_onchange_product_2_with_fiscal_pos_2(self): + super().test_out_invoice_line_onchange_product_2_with_fiscal_pos_2() + + # def test_force_out_invoice_line_onchange_business_fields_1(self): + # FIXME + # super().test_out_invoice_line_onchange_business_fields_1() + + # def test_force_out_invoice_line_onchange_accounting_fields_1(self): + # FIXME this test works with most of the l10n-brazil modules + # but fails because of _order = "date desc, date_maturity ASC, id desc" + # inside l10n_br_account_payment_order/models/account_move_line.py + # super().test_out_invoice_line_onchange_accounting_fields_1() + + def test_force_out_invoice_line_onchange_partner_1(self): + super().test_out_invoice_line_onchange_partner_1() + + def test_force_out_invoice_line_onchange_taxes_1(self): + super().test_out_invoice_line_onchange_taxes_1() + + def test_force_out_invoice_line_onchange_rounding_price_subtotal_1(self): + super().test_out_invoice_line_onchange_rounding_price_subtotal_1() + + def test_force_out_invoice_line_onchange_rounding_price_subtotal_2(self): + super().test_out_invoice_line_onchange_rounding_price_subtotal_2() + + def test_force_out_invoice_line_onchange_taxes_2_price_unit_tax_included(self): + super().test_out_invoice_line_onchange_taxes_2_price_unit_tax_included() + + def test_force_out_invoice_line_onchange_analytic(self): + super().test_out_invoice_line_onchange_analytic() + + def test_force_out_invoice_line_onchange_analytic_2(self): + super().test_out_invoice_line_onchange_analytic_2() + + def test_force_out_invoice_line_onchange_cash_rounding_1(self): + super().test_out_invoice_line_onchange_cash_rounding_1() + + def test_force_out_invoice_line_onchange_currency_1(self): + super().test_out_invoice_line_onchange_currency_1() + + # def test_force_out_invoice_line_tax_fixed_price_include_free_product(self): + # FIXME + # super().test_out_invoice_line_tax_fixed_price_include_free_product() + + # def test_force_out_invoice_line_taxes_fixed_price_include_free_product(self): + # FIXME + # super().test_out_invoice_line_taxes_fixed_price_include_free_product() + + def test_force_out_invoice_create_refund(self): + super().test_out_invoice_create_refund() + + def test_force_out_invoice_create_refund_multi_currency(self): + super().test_out_invoice_create_refund_multi_currency() + + def test_force_out_invoice_create_refund_auto_post(self): + super().test_out_invoice_create_refund_auto_post() + + def test_force_out_invoice_create_1(self): + super().test_out_invoice_create_1() + + def test_force_out_invoice_create_child_partner(self): + super().test_out_invoice_create_child_partner() + + def test_force_out_invoice_write_1(self): + super().test_out_invoice_write_1() + + def test_force_out_invoice_write_2(self): + super().test_out_invoice_write_2() + + def test_force_out_invoice_post_1(self): + super().test_out_invoice_post_1() + + def test_force_out_invoice_post_2(self): + super().test_out_invoice_post_2() + + def test_force_out_invoice_switch_out_refund_1(self): + super().test_out_invoice_switch_out_refund_1() + + def test_force_out_invoice_switch_out_refund_2(self): + super().test_out_invoice_switch_out_refund_2() + + def test_force_out_invoice_reverse_move_tags(self): + super().test_out_invoice_reverse_move_tags() + + def test_force_out_invoice_change_period_accrual_1(self): + super().test_out_invoice_change_period_accrual_1() + + def test_force_out_invoice_multi_date_change_period_accrual(self): + super().test_out_invoice_multi_date_change_period_accrual() + + def test_force_out_invoice_filter_zero_balance_lines(self): + super().test_out_invoice_filter_zero_balance_lines() + + def test_force_out_invoice_recomputation_receivable_lines(self): + super().test_out_invoice_recomputation_receivable_lines() + + def test_force_out_invoice_rounding_recomputation_receivable_lines(self): + super().test_out_invoice_rounding_recomputation_receivable_lines() + + def test_force_out_invoice_multi_company(self): + super().test_out_invoice_multi_company() + + def test_force_out_invoice_multiple_switch_payment_terms(self): + super().test_out_invoice_multiple_switch_payment_terms() + + def test_force_out_invoice_copy_custom_date(self): + super().test_out_invoice_copy_custom_date() + + def test_force_select_specific_product_account(self): + super().test_select_specific_product_account() + + def test_force_out_invoice_note_and_tax_partner_is_set(self): + super().test_out_invoice_note_and_tax_partner_is_set() + + def test_force_out_invoice_reverse_caba(self): + super().test_out_invoice_reverse_caba() + + def test_force_out_invoice_duplicate_currency_rate(self): + super().test_out_invoice_duplicate_currency_rate() + + def test_force_out_invoice_depreciated_account(self): + super().test_out_invoice_depreciated_account() diff --git a/l10n_br_account/views/account_invoice_view.xml b/l10n_br_account/views/account_invoice_view.xml index ad391fcdea33..b217eace55df 100644 --- a/l10n_br_account/views/account_invoice_view.xml +++ b/l10n_br_account/views/account_invoice_view.xml @@ -7,9 +7,9 @@ - - - + + + + @@ -85,10 +86,12 @@ '|', '&', '|', + '|', ('move_type', 'not in', ('out_invoice', 'out_refund', 'in_invoice', 'in_refund', 'out_receipt', 'in_receipt')), ('create_date', '=', True), ('document_type_id', '=', False), ('currency_id', '!=', %(base.BRL)d), + ('has_fiscal_dummy', '=', True), ], 'readonly': [('state', '!=', 'draft')] }" diff --git a/l10n_br_account/views/fiscal_invoice_view.xml b/l10n_br_account/views/fiscal_invoice_view.xml index 4ac8a5a00f42..e4d9f3e3d8f3 100644 --- a/l10n_br_account/views/fiscal_invoice_view.xml +++ b/l10n_br_account/views/fiscal_invoice_view.xml @@ -9,9 +9,9 @@ - - - + + + diff --git a/l10n_br_account_due_list/__manifest__.py b/l10n_br_account_due_list/__manifest__.py index 89fdbd51e0ba..27d548af13a4 100644 --- a/l10n_br_account_due_list/__manifest__.py +++ b/l10n_br_account_due_list/__manifest__.py @@ -8,7 +8,7 @@ "license": "AGPL-3", "author": "Akretion, Odoo Community Association (OCA)", "website": "https://github.com/OCA/l10n-brazil", - "version": "14.0.1.0.2", + "version": "14.0.2.0.0", "depends": ["account_due_list"], "data": [ "views/account_invoice_view.xml", diff --git a/l10n_br_account_due_list/models/__init__.py b/l10n_br_account_due_list/models/__init__.py index 21c1776694a3..9c0a42138541 100644 --- a/l10n_br_account_due_list/models/__init__.py +++ b/l10n_br_account_due_list/models/__init__.py @@ -1,3 +1 @@ -# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html - -from . import account_invoice +from . import account_move diff --git a/l10n_br_account_due_list/models/account_invoice.py b/l10n_br_account_due_list/models/account_move.py similarity index 100% rename from l10n_br_account_due_list/models/account_invoice.py rename to l10n_br_account_due_list/models/account_move.py diff --git a/l10n_br_account_nfe/__manifest__.py b/l10n_br_account_nfe/__manifest__.py index f0098c45824f..39f90075e7e3 100644 --- a/l10n_br_account_nfe/__manifest__.py +++ b/l10n_br_account_nfe/__manifest__.py @@ -13,7 +13,7 @@ "author": "Engenere," "Akretion," "Odoo Community Association (OCA)", "maintainers": ["antoniospneto", "felipemotter", "mbcosta"], "website": "https://github.com/OCA/l10n-brazil", - "version": "14.0.2.1.1", + "version": "14.0.2.1.2", "development_status": "Beta", "depends": [ "l10n_br_nfe", diff --git a/l10n_br_account_nfe/models/account_move_line.py b/l10n_br_account_nfe/models/account_move_line.py index 06d462c7826b..7c237cf01267 100644 --- a/l10n_br_account_nfe/models/account_move_line.py +++ b/l10n_br_account_nfe/models/account_move_line.py @@ -17,7 +17,6 @@ class AccountMoveLine(models.Model): _inherit = "account.move.line" def write(self, values): - result = super().write(values) MOVE_LINE_FIELDS = ["date_maturity", "name", "amount_currency"] if any(field in values.keys() for field in MOVE_LINE_FIELDS): diff --git a/l10n_br_account_nfe/models/document.py b/l10n_br_account_nfe/models/document.py index 6310b9eadaa1..91c08cf3dadd 100644 --- a/l10n_br_account_nfe/models/document.py +++ b/l10n_br_account_nfe/models/document.py @@ -95,8 +95,15 @@ def _compute_nfe40_detpag(self): or "", "nfe40_vPag": rec.amount_financial_total, } - rec.nfe40_detPag = [(2, detpag, 0) for detpag in rec.nfe40_detPag.ids] - rec.nfe40_detPag = [(0, 0, det_pag_vals)] + detpag_current = { + field: getattr(detpag, field, None) + for detpag in rec.nfe40_detPag + for field in det_pag_vals + } + if det_pag_vals != detpag_current: + + rec.nfe40_detPag = [(2, detpag, 0) for detpag in rec.nfe40_detPag.ids] + rec.nfe40_detPag = [(0, 0, det_pag_vals)] ################################ # Business Model Methods diff --git a/l10n_br_account_payment_order/__manifest__.py b/l10n_br_account_payment_order/__manifest__.py index 42527b9d2b84..7888b7ddc997 100644 --- a/l10n_br_account_payment_order/__manifest__.py +++ b/l10n_br_account_payment_order/__manifest__.py @@ -4,7 +4,7 @@ { "name": "Brazilian Payment Order", - "version": "14.0.3.0.0", + "version": "14.0.3.0.1", "license": "AGPL-3", "author": "KMEE, Akretion, Odoo Community Association (OCA)", "maintainers": ["mbcosta"], diff --git a/l10n_br_account_payment_order/models/account_move_line.py b/l10n_br_account_payment_order/models/account_move_line.py index 52dcdbcab951..f8a0cbd5c1e6 100644 --- a/l10n_br_account_payment_order/models/account_move_line.py +++ b/l10n_br_account_payment_order/models/account_move_line.py @@ -21,7 +21,7 @@ class AccountMoveLine(models.Model): # Data de Vencimentos/date_maturity senão ficam fora de ordem: # ex.: own_number 201 31/12/2020, own_number 202 18/11/2020 # Isso causa confusão pois a primeira parcela fica como sendo a segunda. - _order = "date desc, date_maturity ASC, id desc" + _order = "date desc, date_maturity asc, move_name desc, id" cnab_state = fields.Selection( selection=ESTADOS_CNAB, @@ -280,7 +280,6 @@ def _compute_journal_payment_mode(self): ) def reconcile(self): - res = super().reconcile() for record in self: # Verificar Casos de CNAB diff --git a/l10n_br_account_payment_order/models/l10n_br_cnab_change_methods.py b/l10n_br_account_payment_order/models/l10n_br_cnab_change_methods.py index bbd7d167306a..671822e0ac2c 100644 --- a/l10n_br_account_payment_order/models/l10n_br_cnab_change_methods.py +++ b/l10n_br_account_payment_order/models/l10n_br_cnab_change_methods.py @@ -160,17 +160,14 @@ def _msg_error_cnab_missing(self, payment_mode_name, missing): ) def _cnab_already_start(self): - result = False # Se existir uma Ordem já gerada, exportada ou concluída # significa que o processo desse CNAB já foi iniciado no Banco cnab_already_start = self.payment_line_ids.filtered( lambda t: t.order_id.state in ("generated", "uploaded", "done") ) - if cnab_already_start: result = True - return result def update_cnab_for_cancel_invoice(self): @@ -518,7 +515,6 @@ def _create_baixa(self, reason, **kwargs): ) def create_payment_outside_cnab(self, amount_payment): - if self.amount_residual == 0.0: reason_write_off = ( "Movement Instruction Code Updated for" diff --git a/l10n_br_base/__init__.py b/l10n_br_base/__init__.py index 8fcef54c0b53..e3fc13f3af66 100644 --- a/l10n_br_base/__init__.py +++ b/l10n_br_base/__init__.py @@ -26,7 +26,11 @@ def _auto_install_l10n_br_generic_module(env): # Load all l10n_br COA in Demo if not tools.config["without_demo"]: module_name_domain = [ - ("name", "in", ("l10n_br_coa_simple", "l10n_br_coa_generic")) + ( + "name", + "in", + ("l10n_br_coa_simple", "l10n_br_coa_generic", "l10n_generic_coa"), + ) ] module_ids = env["ir.module.module"].search( diff --git a/l10n_br_base/__manifest__.py b/l10n_br_base/__manifest__.py index 7f461f191d63..84eb48ea73e1 100644 --- a/l10n_br_base/__manifest__.py +++ b/l10n_br_base/__manifest__.py @@ -9,7 +9,7 @@ "author": "Akretion,Odoo Community Association (OCA)", "maintainers": ["renatonlima", "rvalyi"], "website": "https://github.com/OCA/l10n-brazil", - "version": "14.0.3.3.0", + "version": "14.0.3.4.1", "depends": ["base", "base_setup", "base_address_city", "base_address_extended"], "data": [ "security/ir.model.access.csv", diff --git a/l10n_br_base/tests/test_partner_bank.py b/l10n_br_base/tests/test_partner_bank.py index ad069cd5f08a..5bb6db938bd7 100644 --- a/l10n_br_base/tests/test_partner_bank.py +++ b/l10n_br_base/tests/test_partner_bank.py @@ -3,15 +3,16 @@ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). from odoo.exceptions import UserError -from odoo.tests import TransactionCase +from odoo.tests import SavepointCase -class PartnerBankTest(TransactionCase): - def setUp(self): - super().setUp() - self.partner_bank_model = self.env["res.partner.bank"] - self.partner_id = self.env.ref("l10n_br_base.res_partner_amd") - self.bank_id = self.env.ref("l10n_br_base.res_bank_001") +class PartnerBankTest(SavepointCase): + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.partner_bank_model = cls.env["res.partner.bank"] + cls.partner_id = cls.env.ref("l10n_br_base.res_partner_amd") + cls.bank_id = cls.env.ref("l10n_br_base.res_bank_001") def test_ok_transactional_acc_type(self): ok_bank_vals = { diff --git a/l10n_br_base/tests/test_valid_pix.py b/l10n_br_base/tests/test_valid_pix.py index a04ca1fddcac..5476dce59efc 100644 --- a/l10n_br_base/tests/test_valid_pix.py +++ b/l10n_br_base/tests/test_valid_pix.py @@ -5,17 +5,18 @@ from psycopg2 import IntegrityError from odoo.exceptions import ValidationError -from odoo.tests import TransactionCase +from odoo.tests import SavepointCase from odoo.tools import mute_logger -class ValidCreatePIXTest(TransactionCase): +class ValidCreatePIXTest(SavepointCase): """Test if ValidationError is raised well during create({})""" - def setUp(self): - super().setUp() - self.res_partner_pix_model = self.env["res.partner.pix"] - self.partner_id = self.env.ref("l10n_br_base.res_partner_amd") + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.res_partner_pix_model = cls.env["res.partner.pix"] + cls.partner_id = cls.env.ref("l10n_br_base.res_partner_amd") def test_invalid_pix_cnpj_too_big(self): pix_vals = { diff --git a/l10n_br_base/views/res_company_view.xml b/l10n_br_base/views/res_company_view.xml index 25b469ac588c..89f64b0dc072 100644 --- a/l10n_br_base/views/res_company_view.xml +++ b/l10n_br_base/views/res_company_view.xml @@ -8,6 +8,11 @@ res.company + + {'invisible': [('country_id', '=', %(base.br)d)]} + Usuários e Empresas > Empresas > Criar ou acesse Contatos > Criar +#. Preencha os campos obrigatórios, insira no campo de CNPJ o CNPJ que deseja buscar e clique na lupa ao lado do campo para buscar +#. O mesmo procedimento pode ser feito editando alguma empresa. + +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 +~~~~~~~~~~~~ + +* `KMEE `_: + + * Breno Oliveira Dias + +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. + +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_cnpj_search/__init__.py b/l10n_br_cnpj_search/__init__.py new file mode 100644 index 000000000000..31660d6a9650 --- /dev/null +++ b/l10n_br_cnpj_search/__init__.py @@ -0,0 +1,3 @@ +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from . import models diff --git a/l10n_br_cnpj_search/__manifest__.py b/l10n_br_cnpj_search/__manifest__.py new file mode 100644 index 000000000000..8279e1ce281e --- /dev/null +++ b/l10n_br_cnpj_search/__manifest__.py @@ -0,0 +1,24 @@ +# Copyright 2023 KMEE +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +{ + "name": "Brazilian Localization CNPJ Search", + "summary": """ + Integração com os Webservices da ReceitaWS e SerPro""", + "version": "14.0.1.1.3", + "license": "AGPL-3", + "author": "KMEE,Odoo Community Association (OCA)", + "website": "https://github.com/OCA/l10n-brazil", + "depends": [ + "l10n_br_zip", + "l10n_br_fiscal", + "contacts", + ], + "data": [ + "security/ir.model.access.csv", + "views/res_partner_view.xml", + "views/res_company_view.xml", + "views/res_config_settings_view.xml", + ], + "demo": [], +} diff --git a/l10n_br_cnpj_search/data/serpro_qualificacao.csv b/l10n_br_cnpj_search/data/serpro_qualificacao.csv new file mode 100644 index 000000000000..d095ed06f43e --- /dev/null +++ b/l10n_br_cnpj_search/data/serpro_qualificacao.csv @@ -0,0 +1,47 @@ +id,cod,desc +serpro_qualificacao_1,5,Administrador +serpro_qualificacao_2,8,Conselheiro de Administração +serpro_qualificacao_3,10,Diretor +serpro_qualificacao_4,16,Presidente +serpro_qualificacao_5,17,Procurador +serpro_qualificacao_6,18,Secretário +serpro_qualificacao_7,20,Sociedade Consorciada +serpro_qualificacao_8,21,Sociedade Filiada +serpro_qualificacao_9,22,Sócio +serpro_qualificacao_10,23,Sócio Capitalista +serpro_qualificacao_11,24,Sócio Comanditado +serpro_qualificacao_12,25,Sócio Comanditário +serpro_qualificacao_13,26,Sócio de Indústria +serpro_qualificacao_14,28,Sócio-Gerente +serpro_qualificacao_15,29,Sócio Incapaz ou Relat.Incapaz (exceto menor) +serpro_qualificacao_16,30,Sócio Menor (Assistido/Representado) +serpro_qualificacao_17,31,Sócio Ostensivo +serpro_qualificacao_18,33,Tesoureiro +serpro_qualificacao_19,37,Sócio Pessoa Jurídica Domiciliado no Exterior +serpro_qualificacao_20,38,Sócio Pessoa Física Residente ou Domiciliado no Exterior +serpro_qualificacao_21,47,Sócio Pessoa Física Residente no Brasil +serpro_qualificacao_22,48,Sócio Pessoa Jurídica Domiciliado no Brasil +serpro_qualificacao_23,49,Sócio-Administrador +serpro_qualificacao_24,52,Sócio com Capital +serpro_qualificacao_25,53,Sócio sem Capital +serpro_qualificacao_26,54,Fundador +serpro_qualificacao_27,55,Sócio Comanditado Residente no Exterior +serpro_qualificacao_28,56,Sócio Comanditário Pessoa Física Residente no Exterior +serpro_qualificacao_29,57,Sócio Comanditário Pessoa Física Residente no Exterior +serpro_qualificacao_30,58,Sócio Comanditário Incapaz +serpro_qualificacao_31,59,Produtor Rural +serpro_qualificacao_32,63,Cotas em Tesouraria +serpro_qualificacao_33,65,Titular Pessoa Física Residente ou Domiciliado no Brasil +serpro_qualificacao_34,66,Titular Pessoa Física Residente ou Domiciliado no Exterior +serpro_qualificacao_35,67,Titular Pessoa Física Incapaz ou Relativamente Incapaz (exceto menor) +serpro_qualificacao_37,68,Titular Pessoa Física Menor (Assistido/Representado) +serpro_qualificacao_38,70,Administrador Residente ou Domiciliado no Exterior +serpro_qualificacao_39,71,Conselheiro de Administração Residente ou Domiciliado no Exterior +serpro_qualificacao_40,72,Diretor Residente ou Domiciliado no Exterior +serpro_qualificacao_41,73,Presidente Residente ou Domiciliado no Exterior +serpro_qualificacao_42,74,Sócio-Administrador Residente ou Domiciliado no Exterior +serpro_qualificacao_43,75,Fundador Residente ou Domiciliado no Exterior +serpro_qualificacao_44,76,Protetor +serpro_qualificacao_45,77,Vice-Presidente +serpro_qualificacao_46,78,Titular Pessoa Jurídica Domiciliada no Brasil +serpro_qualificacao_47,79,Titular Pessoa Jurídica Domiciliada no Exterior diff --git a/l10n_br_cnpj_search/i18n/l10n_br_cnpj_search.pot b/l10n_br_cnpj_search/i18n/l10n_br_cnpj_search.pot new file mode 100644 index 000000000000..4497894dc2a0 --- /dev/null +++ b/l10n_br_cnpj_search/i18n/l10n_br_cnpj_search.pot @@ -0,0 +1,188 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * l10n_br_cnpj_search +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 14.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_cnpj_search +#: model_terms:ir.ui.view,arch_db:l10n_br_cnpj_search.l10n_br_cnpj_search_res_config_settings_form +msgid "Brazilian CNPJ service API" +msgstr "" + +#. module: l10n_br_cnpj_search +#: model_terms:ir.ui.view,arch_db:l10n_br_cnpj_search.l10n_br_cnpj_search_res_config_settings_form +msgid "" +"Brazilian CNPJ service API to search partner information using cnpj code." +msgstr "" + +#. module: l10n_br_cnpj_search +#: model:ir.model,name:l10n_br_cnpj_search.model_l10n_br_base_party_mixin +msgid "Brazilian partner and company data mixin" +msgstr "" + +#. module: l10n_br_cnpj_search +#: model:ir.model.fields.selection,name:l10n_br_cnpj_search.selection__res_config_settings__serpro_schema__basica +msgid "Básica" +msgstr "" + +#. module: l10n_br_cnpj_search +#: model_terms:ir.ui.view,arch_db:l10n_br_cnpj_search.l10n_br_cnpj_search_fiscal_company_form +msgid "CNPJ" +msgstr "" + +#. module: l10n_br_cnpj_search +#: model:ir.model.fields,field_description:l10n_br_cnpj_search.field_res_config_settings__cnpj_provider +msgid "CNPJ Search Provider" +msgstr "" + +#. module: l10n_br_cnpj_search +#: model:ir.model,name:l10n_br_cnpj_search.model_l10n_br_cnpj_search_webservice_abstract +msgid "CNPJ Webservice" +msgstr "" + +#. module: l10n_br_cnpj_search +#: model:ir.model,name:l10n_br_cnpj_search.model_res_company +msgid "Companies" +msgstr "" + +#. module: l10n_br_cnpj_search +#: model:ir.model.fields,field_description:l10n_br_cnpj_search.field_hr_employee_dependent__company_currency_id +#: model:ir.model.fields,field_description:l10n_br_cnpj_search.field_res_company__company_currency_id +#: model:ir.model.fields,field_description:l10n_br_cnpj_search.field_res_partner__company_currency_id +#: model:ir.model.fields,field_description:l10n_br_cnpj_search.field_res_users__company_currency_id +msgid "Company Currency" +msgstr "" + +#. module: l10n_br_cnpj_search +#: model:ir.model.fields,field_description:l10n_br_cnpj_search.field_res_company__company_type +msgid "Company Type" +msgstr "" + +#. module: l10n_br_cnpj_search +#: model:ir.model,name:l10n_br_cnpj_search.model_res_config_settings +msgid "Config Settings" +msgstr "" + +#. module: l10n_br_cnpj_search +#: model:ir.model,name:l10n_br_cnpj_search.model_res_partner +msgid "Contact" +msgstr "" + +#. module: l10n_br_cnpj_search +#: model:ir.model.fields,field_description:l10n_br_cnpj_search.field_l10n_br_base_party_mixin__display_name +#: model:ir.model.fields,field_description:l10n_br_cnpj_search.field_l10n_br_cnpj_search_webservice_abstract__display_name +#: model:ir.model.fields,field_description:l10n_br_cnpj_search.field_res_company__display_name +#: model:ir.model.fields,field_description:l10n_br_cnpj_search.field_res_config_settings__display_name +#: model:ir.model.fields,field_description:l10n_br_cnpj_search.field_res_partner__display_name +msgid "Display Name" +msgstr "" + +#. module: l10n_br_cnpj_search +#: model:ir.model.fields.selection,name:l10n_br_cnpj_search.selection__res_config_settings__serpro_schema__empresa +msgid "Empresa" +msgstr "" + +#. module: l10n_br_cnpj_search +#: model:ir.model.fields,field_description:l10n_br_cnpj_search.field_hr_employee_dependent__equity_capital +#: model:ir.model.fields,field_description:l10n_br_cnpj_search.field_res_company__equity_capital +#: model:ir.model.fields,field_description:l10n_br_cnpj_search.field_res_partner__equity_capital +#: model:ir.model.fields,field_description:l10n_br_cnpj_search.field_res_users__equity_capital +msgid "Equity Capital" +msgstr "" + +#. module: l10n_br_cnpj_search +#: model:ir.model.fields,field_description:l10n_br_cnpj_search.field_l10n_br_base_party_mixin__id +#: model:ir.model.fields,field_description:l10n_br_cnpj_search.field_l10n_br_cnpj_search_webservice_abstract__id +#: model:ir.model.fields,field_description:l10n_br_cnpj_search.field_res_company__id +#: model:ir.model.fields,field_description:l10n_br_cnpj_search.field_res_config_settings__id +#: model:ir.model.fields,field_description:l10n_br_cnpj_search.field_res_partner__id +msgid "ID" +msgstr "" + +#. module: l10n_br_cnpj_search +#: code:addons/l10n_br_cnpj_search/models/l10n_br_base_party_mixin.py:0 +#, python-format +msgid "" +"It is necessary to activate the option to validate de CNPJ to use this " +"functionality." +msgstr "" + +#. module: l10n_br_cnpj_search +#: model:ir.model.fields,field_description:l10n_br_cnpj_search.field_l10n_br_base_party_mixin____last_update +#: model:ir.model.fields,field_description:l10n_br_cnpj_search.field_l10n_br_cnpj_search_webservice_abstract____last_update +#: model:ir.model.fields,field_description:l10n_br_cnpj_search.field_res_company____last_update +#: model:ir.model.fields,field_description:l10n_br_cnpj_search.field_res_config_settings____last_update +#: model:ir.model.fields,field_description:l10n_br_cnpj_search.field_res_partner____last_update +msgid "Last Modified on" +msgstr "" + +#. module: l10n_br_cnpj_search +#: model:ir.model.fields,field_description:l10n_br_cnpj_search.field_hr_employee_dependent__legal_nature +#: model:ir.model.fields,field_description:l10n_br_cnpj_search.field_res_company__legal_nature +#: model:ir.model.fields,field_description:l10n_br_cnpj_search.field_res_partner__legal_nature +#: model:ir.model.fields,field_description:l10n_br_cnpj_search.field_res_users__legal_nature +msgid "Legal Nature" +msgstr "" + +#. module: l10n_br_cnpj_search +#: model:ir.model.fields,field_description:l10n_br_cnpj_search.field_res_company__mobile +msgid "Mobile" +msgstr "" + +#. module: l10n_br_cnpj_search +#: model_terms:ir.ui.view,arch_db:l10n_br_cnpj_search.l10n_br_cnpj_search_fiscal_company_form +#: model_terms:ir.ui.view,arch_db:l10n_br_cnpj_search.l10n_br_cnpj_search_fiscal_partner_form +msgid "Pesquisar CNPJ" +msgstr "" + +#. module: l10n_br_cnpj_search +#: code:addons/l10n_br_cnpj_search/models/l10n_br_base_party_mixin.py:0 +#, python-format +msgid "Por favor insira o CNPJ" +msgstr "" + +#. module: l10n_br_cnpj_search +#: model:ir.model.fields.selection,name:l10n_br_cnpj_search.selection__res_config_settings__serpro_schema__qsa +msgid "QSA" +msgstr "" + +#. module: l10n_br_cnpj_search +#: model:ir.model.fields.selection,name:l10n_br_cnpj_search.selection__res_config_settings__cnpj_provider__receitaws +msgid "ReceitaWS" +msgstr "" + +#. module: l10n_br_cnpj_search +#: model:ir.model.fields.selection,name:l10n_br_cnpj_search.selection__res_config_settings__cnpj_provider__serpro +msgid "SERPRO" +msgstr "" + +#. module: l10n_br_cnpj_search +#: model:ir.model.fields,field_description:l10n_br_cnpj_search.field_res_config_settings__serpro_schema +msgid "SERPRO Schema" +msgstr "" + +#. module: l10n_br_cnpj_search +#: model:ir.model.fields,field_description:l10n_br_cnpj_search.field_res_config_settings__serpro_token +msgid "SERPRO Token" +msgstr "" + +#. module: l10n_br_cnpj_search +#: model:ir.model.fields,field_description:l10n_br_cnpj_search.field_hr_employee_dependent__cnae_secondary_ids +#: model:ir.model.fields,field_description:l10n_br_cnpj_search.field_res_partner__cnae_secondary_ids +#: model:ir.model.fields,field_description:l10n_br_cnpj_search.field_res_users__cnae_secondary_ids +msgid "Secondary CNAE" +msgstr "" + +#. module: l10n_br_cnpj_search +#: model:ir.model.fields,field_description:l10n_br_cnpj_search.field_res_config_settings__serpro_trial +msgid "Use SERPRO Trial" +msgstr "" diff --git a/l10n_br_cnpj_search/models/__init__.py b/l10n_br_cnpj_search/models/__init__.py new file mode 100644 index 000000000000..555dfc6a7107 --- /dev/null +++ b/l10n_br_cnpj_search/models/__init__.py @@ -0,0 +1,9 @@ +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from . import l10n_br_base_party_mixin +from . import res_config_settings +from . import cnpj_webservice +from . import receitaws_webservice +from . import serpro_webservice +from . import res_partner +from . import res_company diff --git a/l10n_br_cnpj_search/models/cnpj_webservice.py b/l10n_br_cnpj_search/models/cnpj_webservice.py new file mode 100644 index 000000000000..3a2954910a3d --- /dev/null +++ b/l10n_br_cnpj_search/models/cnpj_webservice.py @@ -0,0 +1,121 @@ +# Copyright 2022 KMEE - Luis Felipe Mileo +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from erpbrasil.base.misc import punctuation_rm + +from odoo import _, api, models +from odoo.exceptions import ValidationError + + +class CNPJWebservice(models.AbstractModel): + """Each specific webservice can extend the model by adding + its own methods, using the webservice name (same as selection in config) + as a prefix for the new methods. + + Methods that should be added in a webservice-specific implementation: + - _get_api_url(self, cnpj) + - _get_api_headers(self) + - _validate(self, response) + - _import_data(self, data) + """ + + _name = "l10n_br_cnpj_search.webservice.abstract" + _description = "CNPJ Webservice" + + @api.model + def get_provider(self): + """Return selected provider in config""" + if ( + self.env["ir.config_parameter"] + .sudo() + .get_param("l10n_br_cnpj_search.cnpj_provider") + ): + return ( + self.env["ir.config_parameter"] + .sudo() + .get_param("l10n_br_cnpj_search.cnpj_provider") + ) + else: + return "receitaws" + + @api.model + def get_api_url(self, cnpj): + """Get webservice endpoint + + Params: + cnpj (str): Partner CNPJ. + """ + if hasattr(self, "%s_get_api_url" % self.get_provider()): + return getattr(self, "%s_get_api_url" % self.get_provider())(cnpj) + return False + + @api.model + def get_headers(self): + """Get webservice request headers""" + if hasattr(self, "%s_get_headers" % self.get_provider()): + return getattr(self, "%s_get_headers" % self.get_provider())() + return False + + @api.model + def validate(self, response): + """Validate webservice response. + + Returns: data (dict) + """ + if hasattr(self, "%s_validate" % self.get_provider()): + return getattr(self, "%s_validate" % self.get_provider())(response) + return False + + @api.model + def import_data(self, data): + """Import webservice response to Odoo + + Params: + data (dict): data with webservice response + + Returns: + values (dict): dict with res_partner fields and it's values + """ + if hasattr(self, "_%s_import_data" % self.get_provider()): + return getattr(self, "_%s_import_data" % self.get_provider())(data) + return False + + @api.model + def get_data(self, data, name, title=False, lower=False): + value = False + if data.get(name) != "": + value = data[name] + if lower: + value = value.lower() + elif title: + value = value.title() + + return value + + @api.model + def _get_cnpj_param(self, param_name): + return ( + self.env["ir.config_parameter"] + .sudo() + .get_param("l10n_br_cnpj_search." + param_name) + ) + + @api.model + def _validate(self, response): + if response.status_code != 200: + raise ValidationError(_("%s" % response.reason)) + + @api.model + def _get_cnae(self, raw_code): + code = punctuation_rm(raw_code) + cnae_id = False + + if code: + formatted_code = code[0:4] + "-" + code[4] + "/" + code[5:] + cnae_id = ( + self.env["l10n_br_fiscal.cnae"] + .search([("code", "=", formatted_code)]) + .id + ) + + return cnae_id diff --git a/l10n_br_cnpj_search/models/l10n_br_base_party_mixin.py b/l10n_br_cnpj_search/models/l10n_br_base_party_mixin.py new file mode 100644 index 000000000000..c921bdde0789 --- /dev/null +++ b/l10n_br_cnpj_search/models/l10n_br_base_party_mixin.py @@ -0,0 +1,45 @@ +# Copyright 2022 KMEE +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from erpbrasil.base.misc import punctuation_rm +from requests import get + +from odoo import _, api, models +from odoo.exceptions import UserError + + +class PartyMixin(models.AbstractModel): + _inherit = "l10n_br_base.party.mixin" + + def search_cnpj(self): + """Search CNPJ by the chosen API""" + if not self.cnpj_cpf: + raise UserError(_("Por favor insira o CNPJ")) + + if self.cnpj_validation_disabled(): + raise UserError( + _( + "It is necessary to activate the option to validate de CNPJ to use this " + + "functionality." + ) + ) + + cnpj_cpf = punctuation_rm(self.cnpj_cpf) + webservice = self.env["l10n_br_cnpj_search.webservice.abstract"] + response = get( + webservice.get_api_url(cnpj_cpf), headers=webservice.get_headers() + ) + + data = webservice.validate(response) + values = webservice.import_data(data) + values["company_type"] = "company" + self.write(values) + + @api.model + def cnpj_validation_disabled(self): + cnpj_validation_disabled = ( + self.env["ir.config_parameter"] + .sudo() + .get_param("l10n_br_base.disable_cpf_cnpj_validation") + ) + return cnpj_validation_disabled diff --git a/l10n_br_cnpj_search/models/receitaws_webservice.py b/l10n_br_cnpj_search/models/receitaws_webservice.py new file mode 100644 index 000000000000..bc3028b4387f --- /dev/null +++ b/l10n_br_cnpj_search/models/receitaws_webservice.py @@ -0,0 +1,119 @@ +# Copyright 2022 KMEE - Luis Felipe Mileo +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import _, api, models +from odoo.exceptions import ValidationError + +RECEITAWS_URL = "https://www.receitaws.com.br/v1/cnpj/" + + +class ReceitawsWebservice(models.AbstractModel): + _inherit = "l10n_br_cnpj_search.webservice.abstract" + + @api.model + def receitaws_get_api_url(self, cnpj): + return RECEITAWS_URL + cnpj + + @api.model + def receitaws_get_headers(self): + return {"Accept": "application/json"} + + @api.model + def receitaws_validate(self, response): + self._validate(response) + data = response.json() + if data.get("status") == "ERROR": + raise ValidationError(_(data.get("message"))) + + return data + + @api.model + def _receitaws_import_data(self, data): + legal_name = self.get_data(data, "nome", title=True) + fantasy_name = self.get_data(data, "fantasia", title=True) + phone, mobile = self._receitaws_get_phones(data) + state_id, city_id = self._get_state_city(data) + + res = { + "legal_name": legal_name, + "name": fantasy_name if fantasy_name else legal_name, + "email": self.get_data(data, "email", lower=True), + "street_name": self.get_data(data, "logradouro", title=True), + "street2": self.get_data(data, "complemento", title=True), + "district": self.get_data(data, "bairro", title=True), + "street_number": self.get_data(data, "numero"), + "zip": self.get_data(data, "cep"), + "legal_nature": self.get_data(data, "natureza_juridica"), + "phone": phone, + "mobile": mobile, + "state_id": state_id, + "city_id": city_id, + "equity_capital": self.get_data(data, "capital_social"), + "cnae_main_id": self._receitaws_get_cnae(data), + "cnae_secondary_ids": self._receitaws_get_secondary_cnae(data), + } + + return res + + @api.model + def _receitaws_get_phones(self, data): + """Get phones from data. + If there is more than one phone, the second is assigned to mobile.""" + phone = False + mobile = False + if data.get("telefone") != "": + phones = data["telefone"].split("/") + phone = phones[0] + if len(phones) > 1: + mobile = phones[1][1:] # Remove Empty space separation + + return [phone, mobile] + + @api.model + def _get_state_city(self, data): + state_id = False + city_id = False + if data.get("uf") != "": + state = self.env["res.country.state"].search( + [ + ("code", "=", data["uf"]), + ("country_id.code", "=", "BR"), + ], + limit=1, + ) + if state.id: + state_id = state.id + + if data.get("municipio") != "": + city = self.env["res.city"].search( + [ + ("name", "=ilike", data["municipio"].title()), + ("state_id.id", "=", state_id), + ] + ) + if len(city) == 1: + city_id = city.id + + return [state_id, city_id] + + @api.model + def _receitaws_get_cnae(self, data): + cnae_main = data.get("atividade_principal")[0] + cnae_code = self.get_data(cnae_main, "code") + + return self._get_cnae(cnae_code) + + @api.model + def _receitaws_get_secondary_cnae(self, data): + cnae_secondary = [] + for atividade in data.get("atividades_secundarias"): + unformated = self.get_data(atividade, "code").split(".") + formatted = "" + for nums in unformated: + for num in nums.split("-"): + formatted += num + + if self._get_cnae(formatted) is not False: + cnae_secondary.append(self._get_cnae(formatted)) + + return cnae_secondary diff --git a/l10n_br_cnpj_search/models/res_company.py b/l10n_br_cnpj_search/models/res_company.py new file mode 100644 index 000000000000..e38aa8fb05cf --- /dev/null +++ b/l10n_br_cnpj_search/models/res_company.py @@ -0,0 +1,27 @@ +# Copyright 2022 KMEE - Luis Felipe Mileo +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + + +from odoo import fields, models + + +class ResCompany(models.Model): + _inherit = "res.company" + + equity_capital = fields.Monetary(related="partner_id.equity_capital") + + mobile = fields.Char(related="partner_id.mobile") + + legal_nature = fields.Char(related="partner_id.legal_nature") + + company_currency_id = fields.Many2one( + "res.currency", + related="currency_id", + string="Company Currency", + readonly=True, + ) + + company_type = fields.Selection( + related="partner_id.company_type", + readonly=True, + ) diff --git a/l10n_br_cnpj_search/models/res_config_settings.py b/l10n_br_cnpj_search/models/res_config_settings.py new file mode 100644 index 000000000000..67a7aad59c10 --- /dev/null +++ b/l10n_br_cnpj_search/models/res_config_settings.py @@ -0,0 +1,39 @@ +# Copyright 2022 KMEE - Luis Felipe Mileo +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import fields, models + + +class ResConfigSettings(models.TransientModel): + _inherit = "res.config.settings" + + cnpj_provider = fields.Selection( + selection=[ + ("receitaws", "ReceitaWS"), + ("serpro", "SERPRO"), + ], + string="CNPJ Search Provider", + required=True, + default="receitaws", + config_parameter="l10n_br_cnpj_search.cnpj_provider", + ) + + serpro_token = fields.Char( + string="SERPRO Token", + config_parameter="l10n_br_cnpj_search.serpro_token", + ) + + serpro_trial = fields.Boolean( + string="Use SERPRO Trial", + config_parameter="l10n_br_cnpj_search.serpro_trial", + ) + + serpro_schema = fields.Selection( + selection=[ + ("basica", "Básica"), + ("qsa", "QSA"), + ("empresa", "Empresa"), + ], + string="SERPRO Schema", + config_parameter="l10n_br_cnpj_search.serpro_schema", + ) diff --git a/l10n_br_cnpj_search/models/res_partner.py b/l10n_br_cnpj_search/models/res_partner.py new file mode 100644 index 000000000000..d6b9a99806d7 --- /dev/null +++ b/l10n_br_cnpj_search/models/res_partner.py @@ -0,0 +1,31 @@ +# Copyright 2022 KMEE - Luis Felipe Mileo +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + + +from odoo import fields, models + + +class ResPartner(models.Model): + _inherit = "res.partner" + + equity_capital = fields.Monetary( + string="Equity Capital", currency_field="company_currency_id" + ) + + cnae_secondary_ids = fields.Many2many( + comodel_name="l10n_br_fiscal.cnae", + relation="res_partner_fiscal_cnae_rel", + column1="company_id", + column2="cnae_id", + domain="[('internal_type', '=', 'normal'), " "('id', '!=', cnae_main_id)]", + string="Secondary CNAE", + ) + + legal_nature = fields.Char(string="Legal Nature") + + company_currency_id = fields.Many2one( + "res.currency", + related="company_id.currency_id", + string="Company Currency", + readonly=True, + ) diff --git a/l10n_br_cnpj_search/models/serpro_webservice.py b/l10n_br_cnpj_search/models/serpro_webservice.py new file mode 100644 index 000000000000..f8c7e40b8d84 --- /dev/null +++ b/l10n_br_cnpj_search/models/serpro_webservice.py @@ -0,0 +1,166 @@ +# Copyright 2022 KMEE - Luis Felipe Mileo +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +import csv +import logging +from os.path import dirname + +from odoo import api, models +from odoo.exceptions import UserError + +_logger = logging.getLogger(__name__) + +SERPRO_URL = "https://gateway.apiserpro.serpro.gov.br" + +QUALIFICACAO_CSV = dirname(__file__) + "/../data/serpro_qualificacao.csv" + + +class SerproWebservice(models.AbstractModel): + _inherit = "l10n_br_cnpj_search.webservice.abstract" + + @api.model + def serpro_get_api_url(self, cnpj): + trial = self._get_cnpj_param("serpro_trial") + schema = self._get_cnpj_param("serpro_schema") + + if trial: + url = SERPRO_URL + f"/consulta-cnpj-df-trial/v2/{schema}/{cnpj}" + else: + url = SERPRO_URL + f"/v2/{schema}/{cnpj}" + + return url + + @api.model + def serpro_get_headers(self): + token = self._get_cnpj_param("serpro_token") + return {"Authorization": "Bearer " + token} + + @api.model + def serpro_validate(self, response): + self._validate(response) + data = response.json() + return data + + @api.model + def _serpro_import_data(self, data): + schema = self._get_cnpj_param("serpro_schema") + + legal_name = self.get_data(data, "nomeEmpresarial", title=True) + fantasy_name = self.get_data(data, "nomeFantasia", title=True) + name = fantasy_name if fantasy_name else legal_name + phone, mobile = self._serpro_get_phones(data) + address = data.get("endereco") + nature = data.get("naturezaJuridica") + cep = self.get_data(address, "cep") + + res = { + "legal_name": legal_name, + "name": name, + "email": self.get_data(data, "correioEletronico"), + "street_name": self.get_data(address, "logradouro", title=True), + "street2": self.get_data(address, "complemento", title=True), + "district": self.get_data(address, "bairro", title=True), + "street_number": self.get_data(address, "numero"), + "legal_nature": self.get_data(nature, "codigo", title=True) + + self.get_data(nature, "descricao", title=True), + "zip": cep, + "phone": phone, + "mobile": mobile, + "state_id": self._get_state_id(address), + "city_id": self._get_city_id(cep), + "equity_capital": self.get_data(data, "capitalSocial"), + "cnae_main_id": self._serpro_get_cnae(data), + } + + res.update(self._import_additional_info(data, schema)) + + return res + + @api.model + def _import_additional_info(self, data, schema): + if schema not in ["empresa", "qsa"]: + return {} + + partners = data.get("socios") + child_ids = [] + for partner in partners: + partner_name = self.get_data(partner, "nome", title=True) + partner_qualification = self._get_qualification(partner) + + values = { + "name": partner_name, + "function": partner_qualification, + "company_type": "person", + } + + if schema == "empresa": + partner_cpf = self.get_data(partner, "cpf") + values.update({"cnpj_cpf": partner_cpf}) + + partner_id = self.env["res.partner"].create(values).id + child_ids.append(partner_id) + + return { + "child_ids": [(6, 0, child_ids)], + } + + @api.model + def _get_qualification(self, partner): + qualification = self.get_data(partner, "qualificacao") + + with open(QUALIFICACAO_CSV) as csvfile: + reader = csv.reader(csvfile, delimiter=",") + next(reader) # Remove header + for row in reader: + if row[0] == qualification: + return row[1] + return "" + + @api.model + def _serpro_get_phones(self, data): + """Get phones from data. + If there is more than one phone, the second is assigned to mobile and the rest + is ignored.""" + phone = False + mobile = False + phones_data = data.get("telefones") + ddd = phones_data[0].get("ddd") + num = phones_data[0].get("numero") + phone = f"({ddd}) {num}" + if len(phones_data) == 2: + ddd = phones_data[1].get("ddd") + num = phones_data[1].get("numero") + mobile = f"({ddd}) {num}" + + return phone, mobile + + @api.model + def _get_state_id(self, address): + state_code = self.get_data(address, "uf") + + return ( + self.env["res.country.state"] + .search( + [("country_id.code", "=", "BR"), ("code", "=", state_code)], limit=1 + ) + .id + ) + + @api.model + def _get_city_id(self, cep): + # Get city from cep + # TODO Send message if address doesn't match CEP + try: + cep_values = self.env["l10n_br.zip"]._consultar_cep(cep) + except UserError as error: + _logger.warning(error.name) + return False + + return cep_values.get("city_id") + + @api.model + def _serpro_get_cnae(self, data): + cnae_main = data.get("cnaePrincipal") + cnae_code = self.get_data(cnae_main, "codigo") + + return self._get_cnae(cnae_code) diff --git a/l10n_br_cnpj_search/readme/CONFIGURE.rst b/l10n_br_cnpj_search/readme/CONFIGURE.rst new file mode 100644 index 000000000000..8d3da62d9c00 --- /dev/null +++ b/l10n_br_cnpj_search/readme/CONFIGURE.rst @@ -0,0 +1 @@ +Esta busca de informações a partir do cnpj é realizada com base no provedor configurado na aba de configurações, vale ressaltar que o provedor receitaws permite a realização de três consultas por minuto, enquanto que o SERPRO é pago e permite consultas ilimitadas em seus planos. diff --git a/l10n_br_cnpj_search/readme/CONTRIBUTORS.rst b/l10n_br_cnpj_search/readme/CONTRIBUTORS.rst new file mode 100644 index 000000000000..6388f6073d52 --- /dev/null +++ b/l10n_br_cnpj_search/readme/CONTRIBUTORS.rst @@ -0,0 +1,3 @@ +* `KMEE `_: + + * Breno Oliveira Dias diff --git a/l10n_br_cnpj_search/readme/DESCRIPTION.rst b/l10n_br_cnpj_search/readme/DESCRIPTION.rst new file mode 100644 index 000000000000..1a1ce5e55c21 --- /dev/null +++ b/l10n_br_cnpj_search/readme/DESCRIPTION.rst @@ -0,0 +1,33 @@ +Módulo que adiciona um botão para preencher automaticamente os campos de um partner a partir do seu CNPJ. +Seguem abaixo os campos que podem ser consultados de ambas as APIs utilizadas para conseguir informações a partir do CNPJ: + +ReceitaWS +------------------- + +.. figure:: ../static/description/receita.png + :alt: ReceitaWS campos parte 1 + :width: 80 % + :align: center + +.. figure:: ../static/description/receita1.png + :alt: ReceitaWS campos parte 2 + :width: 80 % + :align: center + +.. figure:: ../static/description/receita2.png + :alt: ReceitaWS campos parte 3 + :width: 80 % + :align: center + +SERPRO +---------------------------------------------- + +.. figure:: ../static/description/serpro.png + :alt: SERPRO + :width: 80 % + :align: center + +.. figure:: ../static/description/serpro1.png + :alt: SERPRO + :width: 80 % + :align: center diff --git a/l10n_br_cnpj_search/readme/HISTORY.rst b/l10n_br_cnpj_search/readme/HISTORY.rst new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/l10n_br_cnpj_search/readme/INSTALL.rst b/l10n_br_cnpj_search/readme/INSTALL.rst new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/l10n_br_cnpj_search/readme/ROADMAP.rst b/l10n_br_cnpj_search/readme/ROADMAP.rst new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/l10n_br_cnpj_search/readme/USAGE.rst b/l10n_br_cnpj_search/readme/USAGE.rst new file mode 100644 index 000000000000..3571e5a5fcf8 --- /dev/null +++ b/l10n_br_cnpj_search/readme/USAGE.rst @@ -0,0 +1,5 @@ +#. Acesse Configurações +#. Escolha um provedor para a busca +#. Acesse Configurações > Usuários e Empresas > Empresas > Criar ou acesse Contatos > Criar +#. Preencha os campos obrigatórios, insira no campo de CNPJ o CNPJ que deseja buscar e clique na lupa ao lado do campo para buscar +#. O mesmo procedimento pode ser feito editando alguma empresa. diff --git a/l10n_br_cnpj_search/security/ir.model.access.csv b/l10n_br_cnpj_search/security/ir.model.access.csv new file mode 100644 index 000000000000..85cd348545d4 --- /dev/null +++ b/l10n_br_cnpj_search/security/ir.model.access.csv @@ -0,0 +1,2 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +access_l10n_br_cnpj_search_webservice,access_l10n_br_cnpj_search_webservice,model_l10n_br_cnpj_search_webservice_abstract,base.group_user,1,1,1,1 diff --git a/l10n_br_cnpj_search/static/description/icon.png b/l10n_br_cnpj_search/static/description/icon.png new file mode 100644 index 000000000000..3a0328b516c4 Binary files /dev/null and b/l10n_br_cnpj_search/static/description/icon.png differ diff --git a/l10n_br_cnpj_search/static/description/index.html b/l10n_br_cnpj_search/static/description/index.html new file mode 100644 index 000000000000..2966f11d8c11 --- /dev/null +++ b/l10n_br_cnpj_search/static/description/index.html @@ -0,0 +1,460 @@ + + + + + + +Brazilian Localization CNPJ Search + + + + + + diff --git a/l10n_br_cnpj_search/static/description/receita.png b/l10n_br_cnpj_search/static/description/receita.png new file mode 100644 index 000000000000..3c5cbe065533 Binary files /dev/null and b/l10n_br_cnpj_search/static/description/receita.png differ diff --git a/l10n_br_cnpj_search/static/description/receita1.png b/l10n_br_cnpj_search/static/description/receita1.png new file mode 100644 index 000000000000..fb910a87fdbd Binary files /dev/null and b/l10n_br_cnpj_search/static/description/receita1.png differ diff --git a/l10n_br_cnpj_search/static/description/receita2.png b/l10n_br_cnpj_search/static/description/receita2.png new file mode 100644 index 000000000000..5f9b84936f0f Binary files /dev/null and b/l10n_br_cnpj_search/static/description/receita2.png differ diff --git a/l10n_br_cnpj_search/static/description/serpro.png b/l10n_br_cnpj_search/static/description/serpro.png new file mode 100644 index 000000000000..beae5f719f3a Binary files /dev/null and b/l10n_br_cnpj_search/static/description/serpro.png differ diff --git a/l10n_br_cnpj_search/static/description/serpro1.png b/l10n_br_cnpj_search/static/description/serpro1.png new file mode 100644 index 000000000000..e1e88610ad9d Binary files /dev/null and b/l10n_br_cnpj_search/static/description/serpro1.png differ diff --git a/l10n_br_cnpj_search/tests/__init__.py b/l10n_br_cnpj_search/tests/__init__.py new file mode 100644 index 000000000000..ad6912175ecf --- /dev/null +++ b/l10n_br_cnpj_search/tests/__init__.py @@ -0,0 +1,5 @@ +# Copyright 2022 KMEE +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from . import test_receitaws +from . import test_serpro diff --git a/l10n_br_cnpj_search/tests/common.py b/l10n_br_cnpj_search/tests/common.py new file mode 100644 index 000000000000..9892b172fd5f --- /dev/null +++ b/l10n_br_cnpj_search/tests/common.py @@ -0,0 +1,18 @@ +# Copyright 2022 KMEE +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo.tests import SavepointCase + + +class TestCnpjCommon(SavepointCase): + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.model = cls.env["res.partner"] + + def set_param(self, param_name, param_value): + ( + self.env["ir.config_parameter"] + .sudo() + .set_param("l10n_br_cnpj_search." + param_name, param_value) + ) diff --git a/l10n_br_cnpj_search/tests/test_receitaws.py b/l10n_br_cnpj_search/tests/test_receitaws.py new file mode 100644 index 000000000000..2757a82bc3b5 --- /dev/null +++ b/l10n_br_cnpj_search/tests/test_receitaws.py @@ -0,0 +1,57 @@ +# Copyright 2022 KMEE +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +import time # You can't send multiple requests at the same time in trial version + +from odoo.exceptions import ValidationError +from odoo.tests import tagged + +from odoo.addons.l10n_br_cnpj_search.tests.common import TestCnpjCommon + + +@tagged("post_install", "-at_install") +class TestReceitaWS(TestCnpjCommon): + def setUp(self): + super(TestReceitaWS, self).setUp() + + self.set_param("cnpj_provider", "receitaws") + + def test_receita_ws_success(self): + kilian = self.model.create({"name": "Kilian", "cnpj_cpf": "44.356.113/0001-08"}) + + kilian._onchange_cnpj_cpf() + kilian.search_cnpj() + + self.assertEqual(kilian.company_type, "company") + self.assertEqual(kilian.legal_name, "Kilian Macedo Melcher 08777131460") + self.assertEqual(kilian.name, "Kilian Macedo Melcher") + self.assertEqual(kilian.email, "kilian.melcher@gmail.com") + self.assertEqual(kilian.street_name, "Rua Luiza Bezerra Motta") + self.assertEqual(kilian.street2, "Bloco E;Apt 302") + self.assertEqual(kilian.street_number, "950") + self.assertEqual(kilian.zip, "58.410-410") + self.assertEqual(kilian.district, "Catole") + self.assertEqual(kilian.phone, "(83) 8665-0905") + self.assertEqual(kilian.state_id.code, "PB") + self.assertEqual(kilian.city_id.name, "Campina Grande") + self.assertEqual(kilian.equity_capital, 3000) + self.assertEqual(kilian.cnae_main_id.code, "4751-2/01") + + def test_receita_ws_fail(self): + invalido = self.model.create({"name": "invalido", "cnpj_cpf": "00000000000000"}) + invalido._onchange_cnpj_cpf() + + time.sleep(2) # Pause + with self.assertRaises(ValidationError): + invalido.search_cnpj() + + def test_receita_ws_multiple_phones(self): + isla = self.model.create({"name": "Isla", "cnpj_cpf": "92.666.056/0001-06"}) + isla._onchange_cnpj_cpf() + + time.sleep(2) # Pause + isla.search_cnpj() + + self.assertEqual(isla.name, "Isla Sementes Ltda.") + self.assertEqual(isla.phone, "(51) 9852-9561") + self.assertEqual(isla.mobile, "(51) 2136-6600") diff --git a/l10n_br_cnpj_search/tests/test_serpro.py b/l10n_br_cnpj_search/tests/test_serpro.py new file mode 100644 index 000000000000..66cba12e82aa --- /dev/null +++ b/l10n_br_cnpj_search/tests/test_serpro.py @@ -0,0 +1,321 @@ +# Copyright 2022 KMEE +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +import logging +import time # You can't send multiple requests at the same time in trial version +from datetime import datetime +from os import environ +from unittest import mock + +from decorator import decorate + +from odoo.exceptions import ValidationError +from odoo.tests import tagged + +from .common import TestCnpjCommon + +_logger = logging.getLogger(__name__) + + +def _not_every_day_test(method, self, modulo=7, remaining=0): + if datetime.now().day % modulo == remaining: + return method(self) + elif environ.get("CI_FORCE_serpro"): + return method(self) + else: + return lambda: _logger.info( + "Skipping test today because datetime.now().day %% %s != %s" + % (modulo, remaining) + ) + + +def not_every_day_test(method): + """ + Decorate test methods to query the SERPRO only + 1 day out of 7 and skip tests otherwise in order to prevent + errors in the API to disrupt the tests from l10n-br test suite. + The CI_FORCE_serpro env var can be set to force the test anyhow. + """ + return decorate(method, _not_every_day_test) + + +def mocked_requests_get(*args, **kwargs): + class MockResponse: + def __init__(self, json_data, status_code): + self.json_data = json_data + self.status_code = status_code + + def ok(self): + return True + + def json(self): + return self.json_data + + return MockResponse( + { + "ni": "34238864000168", + "tipoEstabelecimento": "1", + "nomeEmpresarial": "UHIEQKX WHNHIWD NH FIXKHUUWPHMVX NH NWNXU (UHIFIX)", + "nomeFantasia": "UHIFIX UHNH", + "situacaoCadastral": {"codigo": "2", "data": "2019-06-19", "motivo": ""}, + "naturezaJuridica": {"codigo": "2011", "descricao": "Empresa Pública"}, + "dataAbertura": "1967-06-30", + "cnaePrincipal": { + "codigo": "6204000", + "descricao": "Consultoria em tecnologia da informação", + }, + "endereco": { + "tipoLogradouro": "SETOR", + "logradouro": "NH BIWMNH WIHW MXIVH", + "numero": "Q.601", + "complemento": "LOTE V", + "cep": "70836900", + "bairro": "ASA NORTE", + "municipio": {"codigo": "9701", "descricao": "BRASILIA"}, + "uf": "DF", + "pais": {"codigo": "105", "descricao": "BRASIL"}, + }, + "municipioJurisdicao": {"codigo": "0110100", "descricao": "BRASÍLIA"}, + "telefones": [ + {"ddd": "61", "numero": "22222222"}, + {"ddd": "61", "numero": "33333333"}, + ], + "correioEletronico": "EMPRESA@EMPRESA.BR", + "capitalSocial": 0, + "porte": "05", + "situacaoEspecial": "", + "dataSituacaoEspecial": "", + "informacoesAdicionais": {}, + "socios": [ + { + "tipoSocio": "2", + "cpf": "07119488449", + "nome": "LUIZA ARAUJO DE OLIVEIRA", + "qualificacao": "49", + "dataInclusao": "2014-01-01", + "pais": {"codigo": "105", "descricao": "BRASIL"}, + "representanteLegal": { + "cpf": "00000000000", + "nome": "", + "qualificacao": "00", + }, + }, + { + "tipoSocio": "2", + "cpf": "23982012600", + "nome": "JOANA ALVES MUNDIM PENA", + "qualificacao": "49", + "dataInclusao": "2014-01-01", + "pais": {"codigo": "105", "descricao": "BRASIL"}, + "representanteLegal": { + "cpf": "00000000000", + "nome": "", + "qualificacao": "00", + }, + }, + { + "tipoSocio": "2", + "cpf": "13946994415", + "nome": "LUIZA BARBOSA BEZERRA", + "qualificacao": "49", + "dataInclusao": "2014-01-01", + "pais": {"codigo": "105", "descricao": "BRASIL"}, + "representanteLegal": { + "cpf": "00000000000", + "nome": "", + "qualificacao": "00", + }, + }, + { + "tipoSocio": "2", + "cpf": "00031298702", + "nome": "MARCELO ANTONIO BARROS DE CICCO", + "qualificacao": "49", + "dataInclusao": "2014-01-01", + "pais": {"codigo": "105", "descricao": "BRASIL"}, + "representanteLegal": { + "cpf": "00000000000", + "nome": "", + "qualificacao": "00", + }, + }, + { + "tipoSocio": "2", + "cpf": "76822320300", + "nome": "LUIZA ALDENORA", + "qualificacao": "49", + "dataInclusao": "2014-01-01", + "pais": {"codigo": "105", "descricao": "BRASIL"}, + "representanteLegal": { + "cpf": "00000000000", + "nome": "", + "qualificacao": "00", + }, + }, + ], + } + ) + + +@tagged("post_install", "-at_install") +class TestTestSerPro(TestCnpjCommon): + def setUp(self): + super(TestTestSerPro, self).setUp() + + self.set_param("cnpj_provider", "serpro") + self.set_param("serpro_token", "06aef429-a981-3ec5-a1f8-71d38d86481e") + self.set_param("serpro_trial", True) + self.set_param("serpro_schema", "basica") + + @mock.patch("requests.get", side_effect=mocked_requests_get) + def test_mock(self, mock_get): + self.model.search([("cnpj_cpf", "=", "34.238.864/0001-68")]).unlink() + self.set_param("serpro_schema", "empresa") + + dummy_empresa = self.model.create( + {"name": "Dummy Empresa", "cnpj_cpf": "34.238.864/0001-68"} + ) + + time.sleep(3) # Pause + dummy_empresa._onchange_cnpj_cpf() + dummy_empresa.search_cnpj() + self.assertEqual(dummy_empresa.name, "Uhifix Uhnh") + self.assertEqual(dummy_empresa.email, "EMPRESA@EMPRESA.BR") + self.assertEqual(dummy_empresa.street_name, "Nh Biwmnh Wihw Mxivh") + self.assertEqual(dummy_empresa.street2, "Lote V") + self.assertEqual(dummy_empresa.street_number, "Q.601") + self.assertEqual(dummy_empresa.zip, "70836900") + self.assertEqual(dummy_empresa.district, "Asa Norte") + self.assertEqual(dummy_empresa.phone, "(61) 22222222") + self.assertEqual(dummy_empresa.mobile, "(61) 33333333") + self.assertEqual(dummy_empresa.state_id.code, "DF") + self.assertEqual(dummy_empresa.equity_capital, 0) + self.assertEqual(dummy_empresa.cnae_main_id.code, "6204-0/00") + + @not_every_day_test + def test_serpro_basica(self): + dummy_basica = self.model.create( + {"name": "Dummy Basica", "cnpj_cpf": "34.238.864/0001-68"} + ) + time.sleep(3) + dummy_basica._onchange_cnpj_cpf() + dummy_basica.search_cnpj() + + self.assertEqual(dummy_basica.company_type, "company") + self.assertEqual( + dummy_basica.legal_name, + "Uhieqkx Whnhiwd Nh Fixkhuuwphmvx Nh Nwnxu (Uhifix)", + ) + self.assertEqual(dummy_basica.name, "Uhifix Uhnh") + self.assertEqual(dummy_basica.email, "EMPRESA@XXXXXX.BR") + self.assertEqual(dummy_basica.street_name, "Nh Biwmnh Wihw Mxivh") + self.assertEqual(dummy_basica.street2, "Lote V") + self.assertEqual(dummy_basica.street_number, "Q.601") + self.assertEqual(dummy_basica.zip, "70836900") + self.assertEqual(dummy_basica.district, "Asa Norte") + self.assertEqual(dummy_basica.phone, "(61) 22222222") + self.assertEqual(dummy_basica.mobile, "(61) 22222222") + self.assertEqual(dummy_basica.state_id.code, "DF") + self.assertEqual(dummy_basica.equity_capital, 0) + self.assertEqual(dummy_basica.cnae_main_id.code, "6204-0/00") + + @not_every_day_test + def test_serpro_not_found(self): + # Na versão Trial só há alguns registros de CNPJ cadastrados + invalid = self.model.create( + {"name": "invalid", "cnpj_cpf": "44.356.113/0001-08"} + ) + invalid._onchange_cnpj_cpf() + + time.sleep(3) # Pause + with self.assertRaises(ValidationError): + invalid.search_cnpj() + + def assert_socios(self, partner, expected_cnpjs): + socios = self.model.search_read( + [("id", "in", partner.child_ids.ids)], + fields=["name", "cnpj_cpf", "company_type"], + ) + + for s in socios: + s.pop("id") + + expected_socios = [ + { + "name": "Joana Alves Mundim Pena", + "cnpj_cpf": expected_cnpjs["Joana"], + "company_type": "person", + }, + { + "name": "Luiza Aldenora", + "cnpj_cpf": expected_cnpjs["Aldenora"], + "company_type": "person", + }, + { + "name": "Luiza Araujo De Oliveira", + "cnpj_cpf": expected_cnpjs["Araujo"], + "company_type": "person", + }, + { + "name": "Luiza Barbosa Bezerra", + "cnpj_cpf": expected_cnpjs["Barbosa"], + "company_type": "person", + }, + { + "name": "Marcelo Antonio Barros De Cicco", + "cnpj_cpf": expected_cnpjs["Marcelo"], + "company_type": "person", + }, + ] + + self.assertEqual(socios, expected_socios) + + @not_every_day_test + def test_serpro_empresa(self): + self.model.search([("cnpj_cpf", "=", "34.238.864/0001-68")]).write( + {"active": False} + ) + self.set_param("serpro_schema", "empresa") + + dummy_empresa = self.model.create( + {"name": "Dummy Empresa", "cnpj_cpf": "34.238.864/0001-68"} + ) + + time.sleep(3) # Pause + dummy_empresa._onchange_cnpj_cpf() + dummy_empresa.search_cnpj() + + expected_cnpjs = { + "Joana": "23982012600", + "Aldenora": "76822320300", + "Araujo": "07119488449", + "Barbosa": "13946994415", + "Marcelo": "00031298702", + } + + self.assert_socios(dummy_empresa, expected_cnpjs) + + @not_every_day_test + def test_serpro_qsa(self): + self.model.search([("cnpj_cpf", "=", "34.238.864/0001-68")]).write( + {"active": False} + ) + self.set_param("serpro_schema", "qsa") + + dummy_qsa = self.model.create( + {"name": "Dummy QSA", "cnpj_cpf": "34.238.864/0001-68"} + ) + + time.sleep(3) # Pause + dummy_qsa._onchange_cnpj_cpf() + dummy_qsa.search_cnpj() + + expected_cnpjs = { + "Joana": False, + "Aldenora": False, + "Araujo": False, + "Barbosa": False, + "Marcelo": False, + } + + self.assert_socios(dummy_qsa, expected_cnpjs) diff --git a/l10n_br_cnpj_search/views/res_company_view.xml b/l10n_br_cnpj_search/views/res_company_view.xml new file mode 100644 index 000000000000..fb2160df0259 --- /dev/null +++ b/l10n_br_cnpj_search/views/res_company_view.xml @@ -0,0 +1,34 @@ + + + + l10n_br_fiscal.company.form (in l10n_br_cnpj_search) + res.company + + 99 + + + + + + + + + + + + + + + + diff --git a/l10n_br_cnpj_search/views/res_config_settings_view.xml b/l10n_br_cnpj_search/views/res_config_settings_view.xml new file mode 100644 index 000000000000..b4271f2d8607 --- /dev/null +++ b/l10n_br_cnpj_search/views/res_config_settings_view.xml @@ -0,0 +1,66 @@ + + + + + + res.config.settings.form (in l10n_br_cnpj_search) + res.config.settings + + + +
+
+
+ Brazilian CNPJ service API +
+ Brazilian CNPJ service API to search partner information using cnpj code. +
+
+
+
+
+
+
+
+
+
+
+ + + + + diff --git a/l10n_br_cnpj_search/views/res_partner_view.xml b/l10n_br_cnpj_search/views/res_partner_view.xml new file mode 100644 index 000000000000..7855e7a6d53e --- /dev/null +++ b/l10n_br_cnpj_search/views/res_partner_view.xml @@ -0,0 +1,38 @@ + + + + + + l10n_br_fiscal.partner.form (in l10n_br_cnpj_search) + res.partner + 99 + + + + + + + + + +
+ +
+
+
+
+ +
diff --git a/l10n_br_crm/__manifest__.py b/l10n_br_crm/__manifest__.py index 2a4c4b11c940..352a63657b30 100644 --- a/l10n_br_crm/__manifest__.py +++ b/l10n_br_crm/__manifest__.py @@ -8,7 +8,7 @@ "license": "AGPL-3", "author": "Akretion, " "Odoo Community Association (OCA)", "website": "https://github.com/OCA/l10n-brazil", - "version": "14.0.1.0.2", + "version": "14.0.1.0.3", "depends": ["l10n_br_base", "crm"], "data": ["views/crm_lead_view.xml", "views/crm_quick_create_opportunity_form.xml"], "installable": True, diff --git a/l10n_br_crm/tests/test_crm_lead.py b/l10n_br_crm/tests/test_crm_lead.py index 14f8ab8b871c..477ad3692d7f 100644 --- a/l10n_br_crm/tests/test_crm_lead.py +++ b/l10n_br_crm/tests/test_crm_lead.py @@ -2,22 +2,23 @@ # Clément Mombereau # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). -from odoo.tests.common import TransactionCase +from odoo.tests import SavepointCase -class CrmLeadTest(TransactionCase): +class CrmLeadTest(SavepointCase): """Test basic operations on Lead""" - def setUp(self): - super(CrmLeadTest, self).setUp() + @classmethod + def setUpClass(cls): + super().setUpClass() # Create lead with simple details - self.crm_lead_company = self.env["crm.lead"].create( + cls.crm_lead_company = cls.env["crm.lead"].create( { "name": "Test Company Lead", "legal_name": "Teste Empresa", "cnpj": "56.647.352/0001-98", - "stage_id": self.env.ref("crm.stage_lead1").id, + "stage_id": cls.env.ref("crm.stage_lead1").id, "partner_name": "Test Partner", "inscr_est": "079.798.013.363", "inscr_mun": "99999999", @@ -25,33 +26,33 @@ def setUp(self): ) # Create lead for a person/contact - self.crm_lead_contact = self.env["crm.lead"].create( + cls.crm_lead_contact = cls.env["crm.lead"].create( { "name": "Test Contact", "cpf": "70531160505", "rg": "99.888.777-1", - "stage_id": self.env.ref("crm.stage_lead1").id, + "stage_id": cls.env.ref("crm.stage_lead1").id, "contact_name": "Test Contact", } ) # Create lead with a valid Inscr. Estadual - self.crm_lead_company_1 = self.env["crm.lead"].create( + cls.crm_lead_company_1 = cls.env["crm.lead"].create( { "name": "Test Company Lead IE", "legal_name": "Teste Empresa 1", "cnpj": "57.240.310/0001-09", - "stage_id": self.env.ref("crm.stage_lead1").id, + "stage_id": cls.env.ref("crm.stage_lead1").id, "partner_name": "Test Partner 1", "inscr_est": "041.092.540.590", "inscr_mun": "99999999", - "country_id": self.env.ref("base.br").id, - "state_id": self.env.ref("base.state_br_sp").id, + "country_id": cls.env.ref("base.br").id, + "state_id": cls.env.ref("base.state_br_sp").id, } ) # Create a Partner - self.partner_id_01 = self.env["res.partner"].create( + cls.partner_id_01 = cls.env["res.partner"].create( { "name": "Test Lead Partner", "legal_name": "Test Lead Partner", @@ -61,7 +62,7 @@ def setUp(self): "suframa": "99999999", "street_number": "1225", "district": "centro", - "city_id": self.env.ref("l10n_br_base.city_4205407").id, + "city_id": cls.env.ref("l10n_br_base.city_4205407").id, "is_company": True, } ) diff --git a/l10n_br_currency_rate_update/__manifest__.py b/l10n_br_currency_rate_update/__manifest__.py index 006b56c57d7d..90641ce24b66 100644 --- a/l10n_br_currency_rate_update/__manifest__.py +++ b/l10n_br_currency_rate_update/__manifest__.py @@ -4,7 +4,7 @@ { "name": "Currency Rate Update BR", "summary": "Update exchange rates using OCA modules for Brazil", - "version": "14.0.1.0.1", + "version": "14.0.1.0.2", "author": "Akretion, " "Odoo Community Association (OCA)", "maintainers": ["renatonlima"], "website": "https://github.com/OCA/l10n-brazil", diff --git a/l10n_br_currency_rate_update/tests/test_currency_rate_update_bcb.py b/l10n_br_currency_rate_update/tests/test_currency_rate_update_bcb.py index d5657b961045..9b67f07969fb 100644 --- a/l10n_br_currency_rate_update/tests/test_currency_rate_update_bcb.py +++ b/l10n_br_currency_rate_update/tests/test_currency_rate_update_bcb.py @@ -7,36 +7,33 @@ from unittest import mock from dateutil.relativedelta import relativedelta +from decorator import decorate from odoo import fields -from odoo.tests import OdooSuite, SavepointCase +from odoo.tests import SavepointCase _logger = logging.getLogger(__name__) -def addTest(self, test): +def _not_every_day_test(method, self, modulo=7, remaining=1): + if datetime.now().day % modulo == remaining or environ.get("CI_FORCE_IBPT"): + return method(self) + else: + return lambda: _logger.info( + "Skipping test today because datetime.now().day %% %s != %s" + % (modulo, remaining) + ) + + +def not_every_day_test(method): """ - Monkey patch OdooSuite to query the Banco Do Brasil only + Decorate test methods to query the Banco Do Brasil only 1 day out of 7 and skip tests otherwise. - Indeed the BCB webservice often returns errors and it sucks + Indeed the IBPT webservice often returns errors and it sucks to crash the entire l10n-brazil test suite because of this. - the CI_FORCE_BCB env var can be set to force the test anyhow. + the CI_FORCE_IBPT env var can be set to force the test anyhow. """ - if type(test).__name__ == "TestCurrencyRateUpdateBCB": - if ( - datetime.now().day % 7 == 0 - or environ.get("CI_FORCE_BCB") - or test._testMethodName in ["test_mock", "test_get_supported_currencies"] - ): - return OdooSuite.addTest._original_method(self, test) - else: - _logger.info("Skipping test because datetime.now().day % 7 != 0") - else: - return OdooSuite.addTest._original_method(self, test) - - -addTest._original_method = OdooSuite.addTest -OdooSuite.addTest = addTest + return decorate(method, _not_every_day_test) def mocked_requests_get(*args, **kwargs): @@ -132,15 +129,18 @@ def test_mock(self, mock_get): self.assertTrue(rates) self.CurrencyRate.search([("currency_id", "=", self.usd_currency.id)]).unlink() + @not_every_day_test def test_get_supported_currencies(self): currencies = self.bcb_provider._get_supported_currencies() self.assertTrue(currencies) + @not_every_day_test def test_update_BCB_today(self): """No checks are made since today may not be a banking day""" self.bcb_provider._update(self.today, self.today) self.CurrencyRate.search([("currency_id", "=", self.usd_currency.id)]).unlink() + @not_every_day_test def test_update_BCB_month(self): self.bcb_provider._update(self.today - relativedelta(months=1), self.today) @@ -151,6 +151,7 @@ def test_update_BCB_month(self): self.CurrencyRate.search([("currency_id", "=", self.usd_currency.id)]).unlink() + @not_every_day_test def test_update_BCB_year(self): self.bcb_provider._update(self.today - relativedelta(years=1), self.today) @@ -161,6 +162,7 @@ def test_update_BCB_year(self): self.CurrencyRate.search([("currency_id", "=", self.usd_currency.id)]).unlink() + @not_every_day_test def test_update_BCB_scheduled(self): self.bcb_provider.interval_type = "days" self.bcb_provider.interval_number = 14 @@ -174,6 +176,7 @@ def test_update_BCB_scheduled(self): self.CurrencyRate.search([("currency_id", "=", self.usd_currency.id)]).unlink() + @not_every_day_test def test_update_BCB_no_base_update(self): self.bcb_provider.interval_type = "days" self.bcb_provider.interval_number = 14 diff --git a/l10n_br_delivery/__manifest__.py b/l10n_br_delivery/__manifest__.py index e26bc4f44163..4626a72d6234 100644 --- a/l10n_br_delivery/__manifest__.py +++ b/l10n_br_delivery/__manifest__.py @@ -7,7 +7,7 @@ "author": "Akretion, Odoo Community Association (OCA)", "maintainers": ["renatonlima", "mbcosta"], "website": "https://github.com/OCA/l10n-brazil", - "version": "14.0.1.0.1", + "version": "14.0.1.1.0", "depends": [ "l10n_br_sale_stock", "delivery", diff --git a/l10n_br_delivery/i18n/l10n_br_delivery.pot b/l10n_br_delivery/i18n/l10n_br_delivery.pot index c3a1b4e89a7b..3c9ada9ec4aa 100644 --- a/l10n_br_delivery/i18n/l10n_br_delivery.pot +++ b/l10n_br_delivery/i18n/l10n_br_delivery.pot @@ -186,13 +186,27 @@ msgid "ID" msgstr "" #. module: l10n_br_delivery +#: model:ir.model.fields,field_description:l10n_br_delivery.field_account_bank_statement_line__fiscal_incoterm_id +#: model:ir.model.fields,field_description:l10n_br_delivery.field_account_bank_statement_line__incoterm_id +#: model:ir.model.fields,field_description:l10n_br_delivery.field_account_move__fiscal_incoterm_id +#: model:ir.model.fields,field_description:l10n_br_delivery.field_account_move__incoterm_id +#: model:ir.model.fields,field_description:l10n_br_delivery.field_account_payment__fiscal_incoterm_id +#: model:ir.model.fields,field_description:l10n_br_delivery.field_account_payment__incoterm_id +#: model:ir.model.fields,field_description:l10n_br_delivery.field_contract_contract__fiscal_incoterm_id #: model:ir.model.fields,field_description:l10n_br_delivery.field_contract_contract__incoterm_id +#: model:ir.model.fields,field_description:l10n_br_delivery.field_l10n_br_fiscal_document__fiscal_incoterm_id #: model:ir.model.fields,field_description:l10n_br_delivery.field_l10n_br_fiscal_document__incoterm_id +#: model:ir.model.fields,field_description:l10n_br_delivery.field_l10n_br_fiscal_document_mixin__fiscal_incoterm_id #: model:ir.model.fields,field_description:l10n_br_delivery.field_l10n_br_fiscal_document_mixin__incoterm_id +#: model:ir.model.fields,field_description:l10n_br_delivery.field_pos_order__fiscal_incoterm_id #: model:ir.model.fields,field_description:l10n_br_delivery.field_pos_order__incoterm_id +#: model:ir.model.fields,field_description:l10n_br_delivery.field_purchase_order__fiscal_incoterm_id #: model:ir.model.fields,field_description:l10n_br_delivery.field_purchase_order__incoterm_id +#: model:ir.model.fields,field_description:l10n_br_delivery.field_repair_order__fiscal_incoterm_id #: model:ir.model.fields,field_description:l10n_br_delivery.field_repair_order__incoterm_id +#: model:ir.model.fields,field_description:l10n_br_delivery.field_sale_order__fiscal_incoterm_id #: model:ir.model.fields,field_description:l10n_br_delivery.field_sale_order__incoterm_id +#: model:ir.model.fields,field_description:l10n_br_delivery.field_stock_picking__fiscal_incoterm_id #: model:ir.model.fields,field_description:l10n_br_delivery.field_stock_picking__incoterm_id msgid "Incoterm" msgstr "" @@ -208,13 +222,27 @@ msgid "Informação usada na emissão de Documentos Fiscais" msgstr "" #. module: l10n_br_delivery +#: model:ir.model.fields,help:l10n_br_delivery.field_account_bank_statement_line__fiscal_incoterm_id +#: model:ir.model.fields,help:l10n_br_delivery.field_account_bank_statement_line__incoterm_id +#: model:ir.model.fields,help:l10n_br_delivery.field_account_move__fiscal_incoterm_id +#: model:ir.model.fields,help:l10n_br_delivery.field_account_move__incoterm_id +#: model:ir.model.fields,help:l10n_br_delivery.field_account_payment__fiscal_incoterm_id +#: model:ir.model.fields,help:l10n_br_delivery.field_account_payment__incoterm_id +#: model:ir.model.fields,help:l10n_br_delivery.field_contract_contract__fiscal_incoterm_id #: model:ir.model.fields,help:l10n_br_delivery.field_contract_contract__incoterm_id +#: model:ir.model.fields,help:l10n_br_delivery.field_l10n_br_fiscal_document__fiscal_incoterm_id #: model:ir.model.fields,help:l10n_br_delivery.field_l10n_br_fiscal_document__incoterm_id +#: model:ir.model.fields,help:l10n_br_delivery.field_l10n_br_fiscal_document_mixin__fiscal_incoterm_id #: model:ir.model.fields,help:l10n_br_delivery.field_l10n_br_fiscal_document_mixin__incoterm_id +#: model:ir.model.fields,help:l10n_br_delivery.field_pos_order__fiscal_incoterm_id #: model:ir.model.fields,help:l10n_br_delivery.field_pos_order__incoterm_id +#: model:ir.model.fields,help:l10n_br_delivery.field_purchase_order__fiscal_incoterm_id #: model:ir.model.fields,help:l10n_br_delivery.field_purchase_order__incoterm_id +#: model:ir.model.fields,help:l10n_br_delivery.field_repair_order__fiscal_incoterm_id #: model:ir.model.fields,help:l10n_br_delivery.field_repair_order__incoterm_id +#: model:ir.model.fields,help:l10n_br_delivery.field_sale_order__fiscal_incoterm_id #: model:ir.model.fields,help:l10n_br_delivery.field_sale_order__incoterm_id +#: model:ir.model.fields,help:l10n_br_delivery.field_stock_picking__fiscal_incoterm_id #: model:ir.model.fields,help:l10n_br_delivery.field_stock_picking__incoterm_id msgid "" "International Commercial Terms are a series of predefined commercial terms " diff --git a/l10n_br_delivery/i18n/pt_BR.po b/l10n_br_delivery/i18n/pt_BR.po index 7bcf5c2b1729..f7a4860330f2 100644 --- a/l10n_br_delivery/i18n/pt_BR.po +++ b/l10n_br_delivery/i18n/pt_BR.po @@ -198,13 +198,27 @@ msgid "ID" msgstr "ID" #. module: l10n_br_delivery +#: model:ir.model.fields,field_description:l10n_br_delivery.field_account_bank_statement_line__fiscal_incoterm_id +#: model:ir.model.fields,field_description:l10n_br_delivery.field_account_bank_statement_line__incoterm_id +#: model:ir.model.fields,field_description:l10n_br_delivery.field_account_move__fiscal_incoterm_id +#: model:ir.model.fields,field_description:l10n_br_delivery.field_account_move__incoterm_id +#: model:ir.model.fields,field_description:l10n_br_delivery.field_account_payment__fiscal_incoterm_id +#: model:ir.model.fields,field_description:l10n_br_delivery.field_account_payment__incoterm_id +#: model:ir.model.fields,field_description:l10n_br_delivery.field_contract_contract__fiscal_incoterm_id #: model:ir.model.fields,field_description:l10n_br_delivery.field_contract_contract__incoterm_id +#: model:ir.model.fields,field_description:l10n_br_delivery.field_l10n_br_fiscal_document__fiscal_incoterm_id #: model:ir.model.fields,field_description:l10n_br_delivery.field_l10n_br_fiscal_document__incoterm_id +#: model:ir.model.fields,field_description:l10n_br_delivery.field_l10n_br_fiscal_document_mixin__fiscal_incoterm_id #: model:ir.model.fields,field_description:l10n_br_delivery.field_l10n_br_fiscal_document_mixin__incoterm_id +#: model:ir.model.fields,field_description:l10n_br_delivery.field_pos_order__fiscal_incoterm_id #: model:ir.model.fields,field_description:l10n_br_delivery.field_pos_order__incoterm_id +#: model:ir.model.fields,field_description:l10n_br_delivery.field_purchase_order__fiscal_incoterm_id #: model:ir.model.fields,field_description:l10n_br_delivery.field_purchase_order__incoterm_id +#: model:ir.model.fields,field_description:l10n_br_delivery.field_repair_order__fiscal_incoterm_id #: model:ir.model.fields,field_description:l10n_br_delivery.field_repair_order__incoterm_id +#: model:ir.model.fields,field_description:l10n_br_delivery.field_sale_order__fiscal_incoterm_id #: model:ir.model.fields,field_description:l10n_br_delivery.field_sale_order__incoterm_id +#: model:ir.model.fields,field_description:l10n_br_delivery.field_stock_picking__fiscal_incoterm_id #: model:ir.model.fields,field_description:l10n_br_delivery.field_stock_picking__incoterm_id #, fuzzy msgid "Incoterm" @@ -223,13 +237,27 @@ msgid "Informação usada na emissão de Documentos Fiscais" msgstr "Informação usada na emissão de Documentos Fiscais" #. module: l10n_br_delivery +#: model:ir.model.fields,help:l10n_br_delivery.field_account_bank_statement_line__fiscal_incoterm_id +#: model:ir.model.fields,help:l10n_br_delivery.field_account_bank_statement_line__incoterm_id +#: model:ir.model.fields,help:l10n_br_delivery.field_account_move__fiscal_incoterm_id +#: model:ir.model.fields,help:l10n_br_delivery.field_account_move__incoterm_id +#: model:ir.model.fields,help:l10n_br_delivery.field_account_payment__fiscal_incoterm_id +#: model:ir.model.fields,help:l10n_br_delivery.field_account_payment__incoterm_id +#: model:ir.model.fields,help:l10n_br_delivery.field_contract_contract__fiscal_incoterm_id #: model:ir.model.fields,help:l10n_br_delivery.field_contract_contract__incoterm_id +#: model:ir.model.fields,help:l10n_br_delivery.field_l10n_br_fiscal_document__fiscal_incoterm_id #: model:ir.model.fields,help:l10n_br_delivery.field_l10n_br_fiscal_document__incoterm_id +#: model:ir.model.fields,help:l10n_br_delivery.field_l10n_br_fiscal_document_mixin__fiscal_incoterm_id #: model:ir.model.fields,help:l10n_br_delivery.field_l10n_br_fiscal_document_mixin__incoterm_id +#: model:ir.model.fields,help:l10n_br_delivery.field_pos_order__fiscal_incoterm_id #: model:ir.model.fields,help:l10n_br_delivery.field_pos_order__incoterm_id +#: model:ir.model.fields,help:l10n_br_delivery.field_purchase_order__fiscal_incoterm_id #: model:ir.model.fields,help:l10n_br_delivery.field_purchase_order__incoterm_id +#: model:ir.model.fields,help:l10n_br_delivery.field_repair_order__fiscal_incoterm_id #: model:ir.model.fields,help:l10n_br_delivery.field_repair_order__incoterm_id +#: model:ir.model.fields,help:l10n_br_delivery.field_sale_order__fiscal_incoterm_id #: model:ir.model.fields,help:l10n_br_delivery.field_sale_order__incoterm_id +#: model:ir.model.fields,help:l10n_br_delivery.field_stock_picking__fiscal_incoterm_id #: model:ir.model.fields,help:l10n_br_delivery.field_stock_picking__incoterm_id msgid "" "International Commercial Terms are a series of predefined commercial terms " diff --git a/l10n_br_delivery/models/fiscal_document_mixin.py b/l10n_br_delivery/models/fiscal_document_mixin.py index e526b0a8d3c9..9a2268c44e08 100644 --- a/l10n_br_delivery/models/fiscal_document_mixin.py +++ b/l10n_br_delivery/models/fiscal_document_mixin.py @@ -20,6 +20,8 @@ def _get_default_incoterm(self): " transactions.", ) + fiscal_incoterm_id = fields.Many2one(related="incoterm_id") + carrier_id = fields.Many2one( comodel_name="delivery.carrier", string="Carrier", diff --git a/l10n_br_fiscal/__manifest__.py b/l10n_br_fiscal/__manifest__.py index fcc85bffdd4e..7dec27d519a5 100644 --- a/l10n_br_fiscal/__manifest__.py +++ b/l10n_br_fiscal/__manifest__.py @@ -10,7 +10,7 @@ "maintainers": ["renatonlima"], "website": "https://github.com/OCA/l10n-brazil", "development_status": "Production/Stable", - "version": "14.0.12.2.0", + "version": "14.0.13.0.1", "depends": [ "product", "l10n_br_base", diff --git a/l10n_br_fiscal/i18n/l10n_br_fiscal.pot b/l10n_br_fiscal/i18n/l10n_br_fiscal.pot index 90cb472976fd..11016d64d2ea 100644 --- a/l10n_br_fiscal/i18n/l10n_br_fiscal.pot +++ b/l10n_br_fiscal/i18n/l10n_br_fiscal.pot @@ -511,6 +511,11 @@ msgstr "" msgid "Accountant" msgstr "" +#. module: l10n_br_fiscal +#: model:ir.model.fields,field_description:l10n_br_fiscal.field_res_company__accounting_office +msgid "Accounting Office" +msgstr "" + #. module: l10n_br_fiscal #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_certificate__message_needaction #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_icms_regulation__message_needaction @@ -1340,7 +1345,7 @@ msgstr "" #. module: l10n_br_fiscal #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document__partner_cnpj_cpf -#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_invoice_mixin__partner_cnpj_cpf +#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_move_mixin__partner_cnpj_cpf #: model:ir.model.fields.selection,name:l10n_br_fiscal.selection__l10n_br_fiscal_document_related__cpfcnpj_type__cnpj msgid "CNPJ" msgstr "" @@ -1835,7 +1840,7 @@ msgstr "" #. module: l10n_br_fiscal #: model:ir.model.fields,help:l10n_br_fiscal.field_l10n_br_fiscal_document__partner_is_company -#: model:ir.model.fields,help:l10n_br_fiscal.field_l10n_br_fiscal_document_invoice_mixin__partner_is_company +#: model:ir.model.fields,help:l10n_br_fiscal.field_l10n_br_fiscal_document_move_mixin__partner_is_company #: model:ir.model.fields,help:l10n_br_fiscal.field_res_partner__is_company #: model:ir.model.fields,help:l10n_br_fiscal.field_res_users__is_company msgid "Check if the contact is a company, otherwise it is a person" @@ -2004,9 +2009,9 @@ msgstr "" #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_dfe__company_id #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document__company_id #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_email__company_id -#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_invoice_mixin__company_id #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_line__company_id #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_mixin__company_id +#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_move_mixin__company_id #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_serie__company_id #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_event__company_id #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_invalidate_number__company_id @@ -2027,97 +2032,97 @@ msgstr "" #. module: l10n_br_fiscal #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document__company_cnpj_cpf -#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_invoice_mixin__company_cnpj_cpf +#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_move_mixin__company_cnpj_cpf msgid "Company CNPJ" msgstr "" #. module: l10n_br_fiscal #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document__company_city_id -#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_invoice_mixin__company_city_id +#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_move_mixin__company_city_id msgid "Company City" msgstr "" #. module: l10n_br_fiscal #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document__company_country_id -#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_invoice_mixin__company_country_id +#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_move_mixin__company_country_id msgid "Company Country" msgstr "" #. module: l10n_br_fiscal #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document__company_district -#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_invoice_mixin__company_district +#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_move_mixin__company_district msgid "Company District" msgstr "" #. module: l10n_br_fiscal #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document__company_legal_name -#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_invoice_mixin__company_legal_name +#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_move_mixin__company_legal_name msgid "Company Legal Name" msgstr "" #. module: l10n_br_fiscal #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document__company_cnae_main_id -#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_invoice_mixin__company_cnae_main_id +#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_move_mixin__company_cnae_main_id msgid "Company Main CNAE" msgstr "" #. module: l10n_br_fiscal #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document__company_inscr_mun -#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_invoice_mixin__company_inscr_mun +#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_move_mixin__company_inscr_mun msgid "Company Municipal Tax Number" msgstr "" #. module: l10n_br_fiscal #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document__company_name -#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_invoice_mixin__company_name +#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_move_mixin__company_name msgid "Company Name" msgstr "" #. module: l10n_br_fiscal #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document__company_number -#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_invoice_mixin__company_number +#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_move_mixin__company_number msgid "Company Number" msgstr "" #. module: l10n_br_fiscal #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document__company_phone -#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_invoice_mixin__company_phone +#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_move_mixin__company_phone msgid "Company Phone" msgstr "" #. module: l10n_br_fiscal #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document__company_state_id -#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_invoice_mixin__company_state_id +#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_move_mixin__company_state_id msgid "Company State" msgstr "" #. module: l10n_br_fiscal #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document__company_inscr_est -#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_invoice_mixin__company_inscr_est +#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_move_mixin__company_inscr_est msgid "Company State Tax Number" msgstr "" #. module: l10n_br_fiscal #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document__company_street -#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_invoice_mixin__company_street +#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_move_mixin__company_street msgid "Company Street" msgstr "" #. module: l10n_br_fiscal #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document__company_street2 -#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_invoice_mixin__company_street2 +#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_move_mixin__company_street2 msgid "Company Street2" msgstr "" #. module: l10n_br_fiscal #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document__company_suframa -#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_invoice_mixin__company_suframa +#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_move_mixin__company_suframa msgid "Company Suframa" msgstr "" #. module: l10n_br_fiscal #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document__company_tax_framework -#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_invoice_mixin__company_tax_framework +#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_move_mixin__company_tax_framework #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_operation_line__company_tax_framework msgid "Company Tax Framework" msgstr "" @@ -2130,7 +2135,7 @@ msgstr "" #. module: l10n_br_fiscal #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document__company_zip -#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_invoice_mixin__company_zip +#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_move_mixin__company_zip msgid "Company ZIP" msgstr "" @@ -2219,7 +2224,7 @@ msgstr "" #. module: l10n_br_fiscal #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document__partner_ind_ie_dest -#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_invoice_mixin__partner_ind_ie_dest +#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_move_mixin__partner_ind_ie_dest #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_partner_profile__ind_ie_dest #: model:ir.model.fields,field_description:l10n_br_fiscal.field_res_partner__ind_ie_dest #: model:ir.model.fields,field_description:l10n_br_fiscal.field_res_users__ind_ie_dest @@ -2744,12 +2749,12 @@ msgstr "" #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_correction_wizard__display_name #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_electronic__display_name #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_email__display_name -#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_invoice_mixin__display_name #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_line__display_name #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_line_mixin__display_name #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_line_mixin_methods__display_name #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_mixin__display_name #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_mixin_methods__display_name +#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_move_mixin__display_name #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_related__display_name #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_serie__display_name #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_status_wizard__display_name @@ -3614,7 +3619,6 @@ msgid "Fiscal Profile" msgstr "" #. module: l10n_br_fiscal -#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_line__fiscal_quantity #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_line_mixin__fiscal_quantity msgid "Fiscal Quantity" msgstr "" @@ -4775,12 +4779,12 @@ msgstr "" #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_correction_wizard__id #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_electronic__id #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_email__id -#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_invoice_mixin__id #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_line__id #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_line_mixin__id #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_line_mixin_methods__id #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_mixin__id #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_mixin_methods__id +#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_move_mixin__id #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_related__id #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_serie__id #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_status_wizard__id @@ -5525,11 +5529,6 @@ msgstr "" msgid "Inverse Operation" msgstr "" -#. module: l10n_br_fiscal -#: model:ir.model,name:l10n_br_fiscal.model_l10n_br_fiscal_document_invoice_mixin -msgid "Invoice Document Fiscal Mixin" -msgstr "" - #. module: l10n_br_fiscal #: code:addons/l10n_br_fiscal/models/operation_dashboard.py:0 #, python-format @@ -5668,12 +5667,12 @@ msgstr "" #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_correction_wizard____last_update #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_electronic____last_update #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_email____last_update -#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_invoice_mixin____last_update #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_line____last_update #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_line_mixin____last_update #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_line_mixin_methods____last_update #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_mixin____last_update #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_mixin_methods____last_update +#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_move_mixin____last_update #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_related____last_update #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_serie____last_update #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_status_wizard____last_update @@ -5842,7 +5841,7 @@ msgstr "" #. module: l10n_br_fiscal #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document__partner_legal_name -#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_invoice_mixin__partner_legal_name +#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_move_mixin__partner_legal_name msgid "Legal Name" msgstr "" @@ -5958,7 +5957,7 @@ msgstr "" #. module: l10n_br_fiscal #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document__partner_cnae_main_id -#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_invoice_mixin__partner_cnae_main_id +#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_move_mixin__partner_cnae_main_id #: model:ir.model.fields,field_description:l10n_br_fiscal.field_res_company__cnae_main_id #: model:ir.model.fields,field_description:l10n_br_fiscal.field_res_partner__cnae_main_id #: model:ir.model.fields,field_description:l10n_br_fiscal.field_res_users__cnae_main_id @@ -6117,6 +6116,11 @@ msgstr "" msgid "Month" msgstr "" +#. module: l10n_br_fiscal +#: model:ir.model,name:l10n_br_fiscal.model_l10n_br_fiscal_document_move_mixin +msgid "Move Document Fiscal Mixin" +msgstr "" + #. module: l10n_br_fiscal #: model_terms:ir.ui.view,arch_db:l10n_br_fiscal.cfop_form msgid "Move Settings" @@ -6124,7 +6128,7 @@ msgstr "" #. module: l10n_br_fiscal #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document__partner_inscr_mun -#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_invoice_mixin__partner_inscr_mun +#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_move_mixin__partner_inscr_mun #: model:ir.model.fields,field_description:l10n_br_fiscal.field_res_partner__inscr_mun #: model:ir.model.fields,field_description:l10n_br_fiscal.field_res_users__inscr_mun msgid "Municipal Tax Number" @@ -7052,10 +7056,10 @@ msgstr "" #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document__partner_id #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_cancel_wizard__partner_id #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_correction_wizard__partner_id -#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_invoice_mixin__partner_id #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_line__partner_id #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_line_mixin__partner_id #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_mixin__partner_id +#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_move_mixin__partner_id #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_status_wizard__partner_id #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_event__partner_id #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_invalidate_number_wizard__partner_id @@ -7071,37 +7075,37 @@ msgstr "" #. module: l10n_br_fiscal #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document__partner_city_id -#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_invoice_mixin__partner_city_id +#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_move_mixin__partner_city_id msgid "Partner City" msgstr "" #. module: l10n_br_fiscal #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document__partner_country_id -#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_invoice_mixin__partner_country_id +#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_move_mixin__partner_country_id msgid "Partner Country" msgstr "" #. module: l10n_br_fiscal #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document__partner_district -#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_invoice_mixin__partner_district +#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_move_mixin__partner_district msgid "Partner District" msgstr "" #. module: l10n_br_fiscal #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document__partner_is_company -#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_invoice_mixin__partner_is_company +#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_move_mixin__partner_is_company msgid "Partner Is Company?" msgstr "" #. module: l10n_br_fiscal #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document__partner_name -#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_invoice_mixin__partner_name +#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_move_mixin__partner_name msgid "Partner Name" msgstr "" #. module: l10n_br_fiscal #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document__partner_number -#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_invoice_mixin__partner_number +#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_move_mixin__partner_number msgid "Partner Number" msgstr "" @@ -7119,7 +7123,7 @@ msgstr "" #. module: l10n_br_fiscal #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document__partner_phone -#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_invoice_mixin__partner_phone +#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_move_mixin__partner_phone msgid "Partner Phone" msgstr "" @@ -7137,19 +7141,19 @@ msgstr "" #. module: l10n_br_fiscal #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document__partner_state_id -#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_invoice_mixin__partner_state_id +#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_move_mixin__partner_state_id msgid "Partner State" msgstr "" #. module: l10n_br_fiscal #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document__partner_street -#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_invoice_mixin__partner_street +#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_move_mixin__partner_street msgid "Partner Street" msgstr "" #. module: l10n_br_fiscal #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document__partner_street2 -#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_invoice_mixin__partner_street2 +#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_move_mixin__partner_street2 msgid "Partner Street2" msgstr "" @@ -7160,7 +7164,7 @@ msgstr "" #. module: l10n_br_fiscal #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document__partner_zip -#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_invoice_mixin__partner_zip +#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_move_mixin__partner_zip msgid "Partner Zip" msgstr "" @@ -7261,7 +7265,7 @@ msgstr "" #. module: l10n_br_fiscal #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document__processador_edoc -#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_invoice_mixin__processador_edoc +#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_move_mixin__processador_edoc #: model:ir.model.fields,field_description:l10n_br_fiscal.field_res_company__processador_edoc msgid "Processador documentos eletrônicos" msgstr "" @@ -7485,6 +7489,7 @@ msgstr "" #. module: l10n_br_fiscal #: code:addons/l10n_br_fiscal/constants/fiscal.py:0 +#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_line__fiscal_quantity #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_line__quantity #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_line_mixin__quantity #: model:ir.model.fields.selection,name:l10n_br_fiscal.selection__l10n_br_fiscal_document_line__cofins_base_type__quantity @@ -8117,7 +8122,7 @@ msgstr "" #. module: l10n_br_fiscal #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document__partner_inscr_est -#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_invoice_mixin__partner_inscr_est +#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_move_mixin__partner_inscr_est #: model:ir.model.fields,field_description:l10n_br_fiscal.field_res_partner__inscr_est #: model:ir.model.fields,field_description:l10n_br_fiscal.field_res_users__inscr_est msgid "State Tax Number" @@ -8222,7 +8227,7 @@ msgstr "" #. module: l10n_br_fiscal #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document__partner_suframa -#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_invoice_mixin__partner_suframa +#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_move_mixin__partner_suframa msgid "Suframa" msgstr "" @@ -8396,8 +8401,8 @@ msgstr "" #. module: l10n_br_fiscal #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document__partner_tax_framework -#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_invoice_mixin__partner_tax_framework #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_line__tax_framework +#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_move_mixin__partner_tax_framework #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_partner_profile__tax_framework #: model:ir.model.fields,field_description:l10n_br_fiscal.field_res_company__tax_framework #: model:ir.model.fields,field_description:l10n_br_fiscal.field_res_partner__tax_framework @@ -8900,8 +8905,8 @@ msgstr "" #. module: l10n_br_fiscal #: model:ir.model.fields,help:l10n_br_fiscal.field_l10n_br_fiscal_document__company_legal_name #: model:ir.model.fields,help:l10n_br_fiscal.field_l10n_br_fiscal_document__partner_legal_name -#: model:ir.model.fields,help:l10n_br_fiscal.field_l10n_br_fiscal_document_invoice_mixin__company_legal_name -#: model:ir.model.fields,help:l10n_br_fiscal.field_l10n_br_fiscal_document_invoice_mixin__partner_legal_name +#: model:ir.model.fields,help:l10n_br_fiscal.field_l10n_br_fiscal_document_move_mixin__company_legal_name +#: model:ir.model.fields,help:l10n_br_fiscal.field_l10n_br_fiscal_document_move_mixin__partner_legal_name msgid "Used in fiscal documents" msgstr "" diff --git a/l10n_br_fiscal/i18n/pt_BR.po b/l10n_br_fiscal/i18n/pt_BR.po index 56089c26644d..7aa3b620c2c0 100644 --- a/l10n_br_fiscal/i18n/pt_BR.po +++ b/l10n_br_fiscal/i18n/pt_BR.po @@ -699,6 +699,11 @@ msgstr "Mudança de conta?" msgid "Accountant" msgstr "Contador" +#. module: l10n_br_fiscal +#: model:ir.model.fields,field_description:l10n_br_fiscal.field_res_company__accounting_office +msgid "Accounting Office" +msgstr "Escritório de Contabilidade" + #. module: l10n_br_fiscal #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_certificate__message_needaction #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_icms_regulation__message_needaction @@ -1538,7 +1543,7 @@ msgstr "CNAEs" #. module: l10n_br_fiscal #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document__partner_cnpj_cpf -#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_invoice_mixin__partner_cnpj_cpf +#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_move_mixin__partner_cnpj_cpf #: model:ir.model.fields.selection,name:l10n_br_fiscal.selection__l10n_br_fiscal_document_related__cpfcnpj_type__cnpj msgid "CNPJ" msgstr "CNPJ" @@ -2038,7 +2043,7 @@ msgstr "Cervejas, chopes, refrigerantes, águas e outras bebidas" #. module: l10n_br_fiscal #: model:ir.model.fields,help:l10n_br_fiscal.field_l10n_br_fiscal_document__partner_is_company -#: model:ir.model.fields,help:l10n_br_fiscal.field_l10n_br_fiscal_document_invoice_mixin__partner_is_company +#: model:ir.model.fields,help:l10n_br_fiscal.field_l10n_br_fiscal_document_move_mixin__partner_is_company #: model:ir.model.fields,help:l10n_br_fiscal.field_res_partner__is_company #: model:ir.model.fields,help:l10n_br_fiscal.field_res_users__is_company msgid "Check if the contact is a company, otherwise it is a person" @@ -2207,9 +2212,9 @@ msgstr "Empresas" #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_dfe__company_id #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document__company_id #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_email__company_id -#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_invoice_mixin__company_id #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_line__company_id #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_mixin__company_id +#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_move_mixin__company_id #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_serie__company_id #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_event__company_id #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_invalidate_number__company_id @@ -2230,97 +2235,97 @@ msgstr "Empresa" #. module: l10n_br_fiscal #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document__company_cnpj_cpf -#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_invoice_mixin__company_cnpj_cpf +#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_move_mixin__company_cnpj_cpf msgid "Company CNPJ" msgstr "CNPJ" #. module: l10n_br_fiscal #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document__company_city_id -#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_invoice_mixin__company_city_id +#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_move_mixin__company_city_id msgid "Company City" msgstr "Cidade da empresa" #. module: l10n_br_fiscal #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document__company_country_id -#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_invoice_mixin__company_country_id +#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_move_mixin__company_country_id msgid "Company Country" msgstr "País da Empresa" #. module: l10n_br_fiscal #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document__company_district -#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_invoice_mixin__company_district +#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_move_mixin__company_district msgid "Company District" msgstr "Bairro da Empresa" #. module: l10n_br_fiscal #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document__company_legal_name -#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_invoice_mixin__company_legal_name +#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_move_mixin__company_legal_name msgid "Company Legal Name" msgstr "Nome Legal da Empresa" #. module: l10n_br_fiscal #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document__company_cnae_main_id -#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_invoice_mixin__company_cnae_main_id +#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_move_mixin__company_cnae_main_id msgid "Company Main CNAE" msgstr "CNAE Principal da Empresa" #. module: l10n_br_fiscal #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document__company_inscr_mun -#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_invoice_mixin__company_inscr_mun +#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_move_mixin__company_inscr_mun msgid "Company Municipal Tax Number" msgstr "Número de Impostos Municipais da Empresa" #. module: l10n_br_fiscal #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document__company_name -#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_invoice_mixin__company_name +#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_move_mixin__company_name msgid "Company Name" msgstr "Nome da Empresa" #. module: l10n_br_fiscal #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document__company_number -#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_invoice_mixin__company_number +#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_move_mixin__company_number msgid "Company Number" msgstr "Número" #. module: l10n_br_fiscal #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document__company_phone -#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_invoice_mixin__company_phone +#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_move_mixin__company_phone msgid "Company Phone" msgstr "Telefone da Empresa" #. module: l10n_br_fiscal #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document__company_state_id -#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_invoice_mixin__company_state_id +#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_move_mixin__company_state_id msgid "Company State" msgstr "Estado da Empresa" #. module: l10n_br_fiscal #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document__company_inscr_est -#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_invoice_mixin__company_inscr_est +#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_move_mixin__company_inscr_est msgid "Company State Tax Number" msgstr "Número da Inscrição Estadual" #. module: l10n_br_fiscal #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document__company_street -#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_invoice_mixin__company_street +#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_move_mixin__company_street msgid "Company Street" msgstr "Rua da Empresa" #. module: l10n_br_fiscal #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document__company_street2 -#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_invoice_mixin__company_street2 +#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_move_mixin__company_street2 msgid "Company Street2" msgstr "Empresa Rua2" #. module: l10n_br_fiscal #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document__company_suframa -#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_invoice_mixin__company_suframa +#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_move_mixin__company_suframa msgid "Company Suframa" msgstr "Empresa Suframa" #. module: l10n_br_fiscal #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document__company_tax_framework -#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_invoice_mixin__company_tax_framework +#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_move_mixin__company_tax_framework #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_operation_line__company_tax_framework msgid "Company Tax Framework" msgstr "Marco fiscal da empresa" @@ -2333,7 +2338,7 @@ msgstr "Tipo de Empresa" #. module: l10n_br_fiscal #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document__company_zip -#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_invoice_mixin__company_zip +#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_move_mixin__company_zip msgid "Company ZIP" msgstr "Cep da Empresa" @@ -2422,7 +2427,7 @@ msgstr "Contato" #. module: l10n_br_fiscal #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document__partner_ind_ie_dest -#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_invoice_mixin__partner_ind_ie_dest +#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_move_mixin__partner_ind_ie_dest #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_partner_profile__ind_ie_dest #: model:ir.model.fields,field_description:l10n_br_fiscal.field_res_partner__ind_ie_dest #: model:ir.model.fields,field_description:l10n_br_fiscal.field_res_users__ind_ie_dest @@ -2951,12 +2956,12 @@ msgstr "Valor do desconto" #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_correction_wizard__display_name #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_electronic__display_name #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_email__display_name -#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_invoice_mixin__display_name #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_line__display_name #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_line_mixin__display_name #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_line_mixin_methods__display_name #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_mixin__display_name #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_mixin_methods__display_name +#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_move_mixin__display_name #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_related__display_name #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_serie__display_name #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_status_wizard__display_name @@ -3823,7 +3828,6 @@ msgid "Fiscal Profile" msgstr "Perfil Fiscal" #. module: l10n_br_fiscal -#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_line__fiscal_quantity #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_line_mixin__fiscal_quantity msgid "Fiscal Quantity" msgstr "Quantidade Fiscal" @@ -4985,12 +4989,12 @@ msgstr "Valor do Crédito de ICMSSN" #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_correction_wizard__id #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_electronic__id #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_email__id -#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_invoice_mixin__id #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_line__id #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_line_mixin__id #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_line_mixin_methods__id #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_mixin__id #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_mixin_methods__id +#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_move_mixin__id #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_related__id #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_serie__id #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_status_wizard__id @@ -5738,11 +5742,6 @@ msgstr "CFOP inverso" msgid "Inverse Operation" msgstr "Operação Inversa" -#. module: l10n_br_fiscal -#: model:ir.model,name:l10n_br_fiscal.model_l10n_br_fiscal_document_invoice_mixin -msgid "Invoice Document Fiscal Mixin" -msgstr "" - #. module: l10n_br_fiscal #: code:addons/l10n_br_fiscal/models/operation_dashboard.py:0 #, python-format @@ -5881,12 +5880,12 @@ msgstr "LATA" #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_correction_wizard____last_update #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_electronic____last_update #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_email____last_update -#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_invoice_mixin____last_update #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_line____last_update #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_line_mixin____last_update #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_line_mixin_methods____last_update #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_mixin____last_update #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_mixin_methods____last_update +#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_move_mixin____last_update #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_related____last_update #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_serie____last_update #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_status_wizard____last_update @@ -6055,7 +6054,7 @@ msgstr "Última Atualização em" #. module: l10n_br_fiscal #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document__partner_legal_name -#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_invoice_mixin__partner_legal_name +#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_move_mixin__partner_legal_name msgid "Legal Name" msgstr "Razão Social" @@ -6175,7 +6174,7 @@ msgstr "Anexo Principal" #. module: l10n_br_fiscal #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document__partner_cnae_main_id -#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_invoice_mixin__partner_cnae_main_id +#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_move_mixin__partner_cnae_main_id #: model:ir.model.fields,field_description:l10n_br_fiscal.field_res_company__cnae_main_id #: model:ir.model.fields,field_description:l10n_br_fiscal.field_res_partner__cnae_main_id #: model:ir.model.fields,field_description:l10n_br_fiscal.field_res_users__cnae_main_id @@ -6338,6 +6337,11 @@ msgstr "Mensagens" msgid "Month" msgstr "Mês" +#. module: l10n_br_fiscal +#: model:ir.model,name:l10n_br_fiscal.model_l10n_br_fiscal_document_move_mixin +msgid "Move Document Fiscal Mixin" +msgstr "" + #. module: l10n_br_fiscal #: model_terms:ir.ui.view,arch_db:l10n_br_fiscal.cfop_form msgid "Move Settings" @@ -6345,7 +6349,7 @@ msgstr "Configurações de movimento" #. module: l10n_br_fiscal #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document__partner_inscr_mun -#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_invoice_mixin__partner_inscr_mun +#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_move_mixin__partner_inscr_mun #: model:ir.model.fields,field_description:l10n_br_fiscal.field_res_partner__inscr_mun #: model:ir.model.fields,field_description:l10n_br_fiscal.field_res_users__inscr_mun msgid "Municipal Tax Number" @@ -7293,10 +7297,10 @@ msgstr "Tipo de Serviço Pai" #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document__partner_id #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_cancel_wizard__partner_id #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_correction_wizard__partner_id -#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_invoice_mixin__partner_id #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_line__partner_id #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_line_mixin__partner_id #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_mixin__partner_id +#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_move_mixin__partner_id #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_status_wizard__partner_id #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_event__partner_id #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_invalidate_number_wizard__partner_id @@ -7312,37 +7316,37 @@ msgstr "Parceiro" #. module: l10n_br_fiscal #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document__partner_city_id -#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_invoice_mixin__partner_city_id +#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_move_mixin__partner_city_id msgid "Partner City" msgstr "Cidade do Parceiro" #. module: l10n_br_fiscal #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document__partner_country_id -#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_invoice_mixin__partner_country_id +#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_move_mixin__partner_country_id msgid "Partner Country" msgstr "País Parceiro" #. module: l10n_br_fiscal #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document__partner_district -#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_invoice_mixin__partner_district +#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_move_mixin__partner_district msgid "Partner District" msgstr "Bairro do Parceiro" #. module: l10n_br_fiscal #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document__partner_is_company -#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_invoice_mixin__partner_is_company +#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_move_mixin__partner_is_company msgid "Partner Is Company?" msgstr "Parceiro é Empresa?" #. module: l10n_br_fiscal #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document__partner_name -#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_invoice_mixin__partner_name +#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_move_mixin__partner_name msgid "Partner Name" msgstr "Nome do Parceiro" #. module: l10n_br_fiscal #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document__partner_number -#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_invoice_mixin__partner_number +#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_move_mixin__partner_number msgid "Partner Number" msgstr "Número do parceiro" @@ -7360,7 +7364,7 @@ msgstr "Linha de Pedido do Parceiro (nItemPed)" #. module: l10n_br_fiscal #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document__partner_phone -#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_invoice_mixin__partner_phone +#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_move_mixin__partner_phone msgid "Partner Phone" msgstr "Telefone Parceiro" @@ -7378,19 +7382,19 @@ msgstr "Quantidade de Parceiros" #. module: l10n_br_fiscal #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document__partner_state_id -#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_invoice_mixin__partner_state_id +#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_move_mixin__partner_state_id msgid "Partner State" msgstr "Estado do Parceiro" #. module: l10n_br_fiscal #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document__partner_street -#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_invoice_mixin__partner_street +#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_move_mixin__partner_street msgid "Partner Street" msgstr "Rua do Parceiro" #. module: l10n_br_fiscal #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document__partner_street2 -#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_invoice_mixin__partner_street2 +#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_move_mixin__partner_street2 msgid "Partner Street2" msgstr "Rua2 do Parceiro" @@ -7401,7 +7405,7 @@ msgstr "Framework Fiscal do Parceiro" #. module: l10n_br_fiscal #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document__partner_zip -#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_invoice_mixin__partner_zip +#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_move_mixin__partner_zip msgid "Partner Zip" msgstr "Parceiro Zip" @@ -7502,7 +7506,7 @@ msgstr "Preço Unitário" #. module: l10n_br_fiscal #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document__processador_edoc -#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_invoice_mixin__processador_edoc +#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_move_mixin__processador_edoc #: model:ir.model.fields,field_description:l10n_br_fiscal.field_res_company__processador_edoc msgid "Processador documentos eletrônicos" msgstr "Processador documentos eletrônicos" @@ -7728,6 +7732,7 @@ msgstr "QUILATE" #. module: l10n_br_fiscal #: code:addons/l10n_br_fiscal/constants/fiscal.py:0 +#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_line__fiscal_quantity #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_line__quantity #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_line_mixin__quantity #: model:ir.model.fields.selection,name:l10n_br_fiscal.selection__l10n_br_fiscal_document_line__cofins_base_type__quantity @@ -8364,7 +8369,7 @@ msgstr "Estado" #. module: l10n_br_fiscal #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document__partner_inscr_est -#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_invoice_mixin__partner_inscr_est +#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_move_mixin__partner_inscr_est #: model:ir.model.fields,field_description:l10n_br_fiscal.field_res_partner__inscr_est #: model:ir.model.fields,field_description:l10n_br_fiscal.field_res_users__inscr_est msgid "State Tax Number" @@ -8473,7 +8478,7 @@ msgstr "Sufix" #. module: l10n_br_fiscal #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document__partner_suframa -#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_invoice_mixin__partner_suframa +#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_move_mixin__partner_suframa msgid "Suframa" msgstr "Suframa" @@ -8651,8 +8656,8 @@ msgstr "Domínio Tributário" #. module: l10n_br_fiscal #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document__partner_tax_framework -#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_invoice_mixin__partner_tax_framework #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_line__tax_framework +#: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_document_move_mixin__partner_tax_framework #: model:ir.model.fields,field_description:l10n_br_fiscal.field_l10n_br_fiscal_partner_profile__tax_framework #: model:ir.model.fields,field_description:l10n_br_fiscal.field_res_company__tax_framework #: model:ir.model.fields,field_description:l10n_br_fiscal.field_res_partner__tax_framework @@ -9160,8 +9165,8 @@ msgstr "" #. module: l10n_br_fiscal #: model:ir.model.fields,help:l10n_br_fiscal.field_l10n_br_fiscal_document__company_legal_name #: model:ir.model.fields,help:l10n_br_fiscal.field_l10n_br_fiscal_document__partner_legal_name -#: model:ir.model.fields,help:l10n_br_fiscal.field_l10n_br_fiscal_document_invoice_mixin__company_legal_name -#: model:ir.model.fields,help:l10n_br_fiscal.field_l10n_br_fiscal_document_invoice_mixin__partner_legal_name +#: model:ir.model.fields,help:l10n_br_fiscal.field_l10n_br_fiscal_document_move_mixin__company_legal_name +#: model:ir.model.fields,help:l10n_br_fiscal.field_l10n_br_fiscal_document_move_mixin__partner_legal_name msgid "Used in fiscal documents" msgstr "Usado em documentos fiscais" diff --git a/l10n_br_fiscal/models/__init__.py b/l10n_br_fiscal/models/__init__.py index bfc682311150..a7f2b0caa037 100644 --- a/l10n_br_fiscal/models/__init__.py +++ b/l10n_br_fiscal/models/__init__.py @@ -6,7 +6,7 @@ from . import document_workflow from . import document_fiscal_mixin_methods from . import document_fiscal_mixin -from . import document_fiscal_invoice_mixin +from . import document_move_mixin from . import document_fiscal_line_mixin_methods from . import document_fiscal_line_mixin from . import document_event diff --git a/l10n_br_fiscal/models/document.py b/l10n_br_fiscal/models/document.py index 23df4817943f..2f2df45d9128 100644 --- a/l10n_br_fiscal/models/document.py +++ b/l10n_br_fiscal/models/document.py @@ -50,7 +50,7 @@ class Document(models.Model): _inherit = [ "l10n_br_fiscal.document.mixin", "l10n_br_fiscal.document.electronic", - "l10n_br_fiscal.document.invoice.mixin", + "l10n_br_fiscal.document.move.mixin", ] _description = "Fiscal Document" _check_company_auto = True diff --git a/l10n_br_fiscal/models/document_fiscal_invoice_mixin.py b/l10n_br_fiscal/models/document_move_mixin.py similarity index 97% rename from l10n_br_fiscal/models/document_fiscal_invoice_mixin.py rename to l10n_br_fiscal/models/document_move_mixin.py index 709156792610..25b8c8596e20 100644 --- a/l10n_br_fiscal/models/document_fiscal_invoice_mixin.py +++ b/l10n_br_fiscal/models/document_move_mixin.py @@ -6,9 +6,9 @@ from ..constants.fiscal import DOCUMENT_ISSUER_COMPANY -class FiscalDocumentInvoiceMixin(models.AbstractModel): - _name = "l10n_br_fiscal.document.invoice.mixin" - _description = "Invoice Document Fiscal Mixin" +class DocumentMoveMixin(models.AbstractModel): + _name = "l10n_br_fiscal.document.move.mixin" + _description = "Move Document Fiscal Mixin" partner_id = fields.Many2one( comodel_name="res.partner", diff --git a/l10n_br_fiscal/models/res_company.py b/l10n_br_fiscal/models/res_company.py index fe11b9acf7a1..d49931d3e4f8 100644 --- a/l10n_br_fiscal/models/res_company.py +++ b/l10n_br_fiscal/models/res_company.py @@ -259,6 +259,10 @@ def _compute_simplifed_tax(self): accountant_id = fields.Many2one(comodel_name="res.partner", string="Accountant") + accounting_office = fields.Many2one( + comodel_name="res.partner", string="Accounting Office" + ) + technical_support_id = fields.Many2one( comodel_name="res.partner", string="Technical Support" ) diff --git a/l10n_br_fiscal/tests/__init__.py b/l10n_br_fiscal/tests/__init__.py index 16eac1423a88..ae9e51e00e7b 100644 --- a/l10n_br_fiscal/tests/__init__.py +++ b/l10n_br_fiscal/tests/__init__.py @@ -1,16 +1,18 @@ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). -from . import test_cnae -from . import test_service_type -from . import test_partner_profile -from . import test_certificate -from . import test_ibpt_product -from . import test_ibpt_service -from . import test_fiscal_tax -from . import test_workflow -from . import test_fiscal_document_generic -from . import test_subsequent_operation -from . import test_uom_uom -from . import test_fiscal_document_nfse -from . import test_icms_regulation -from . import test_ncm +from . import ( + test_certificate, + test_cnae, + test_fiscal_document_generic, + test_fiscal_document_nfse, + test_fiscal_tax, + test_ibpt_product, + test_ibpt_service, + test_icms_regulation, + test_ncm, + test_partner_profile, + test_service_type, + test_subsequent_operation, + test_uom_uom, + test_workflow, +) diff --git a/l10n_br_fiscal/tests/test_certificate.py b/l10n_br_fiscal/tests/test_certificate.py index d4f53cc87a0d..08b2794ea4d1 100644 --- a/l10n_br_fiscal/tests/test_certificate.py +++ b/l10n_br_fiscal/tests/test_certificate.py @@ -7,61 +7,63 @@ from odoo import fields from odoo.exceptions import ValidationError -from odoo.tests import common +from odoo.tests import SavepointCase from odoo.tools.misc import format_date -class TestCertificate(common.TransactionCase): - def setUp(self): - super().setUp() +class TestCertificate(SavepointCase): + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.company_model = cls.env["res.company"] + cls.certificate_model = cls.env["l10n_br_fiscal.certificate"] + cls.company = cls._create_compay() + cls._switch_user_company(cls.env.user, cls.company) - self.company_model = self.env["res.company"] - self.certificate_model = self.env["l10n_br_fiscal.certificate"] - self.company = self._create_compay() - self._switch_user_company(self.env.user, self.company) - - self.cert_country = "BR" - self.cert_issuer_a = "EMISSOR A TESTE" - self.cert_issuer_b = "EMISSOR B TESTE" - self.cert_subject_valid = "CERTIFICADO VALIDO TESTE" - self.cert_date_exp = fields.Datetime.today() + timedelta(days=365) - self.cert_subject_invalid = "CERTIFICADO INVALIDO TESTE" - self.cert_passwd = "123456" - self.cert_name = "{} - {} - {} - Valid: {}".format( + cls.cert_country = "BR" + cls.cert_issuer_a = "EMISSOR A TESTE" + cls.cert_issuer_b = "EMISSOR B TESTE" + cls.cert_subject_valid = "CERTIFICADO VALIDO TESTE" + cls.cert_date_exp = fields.Datetime.today() + timedelta(days=365) + cls.cert_subject_invalid = "CERTIFICADO INVALIDO TESTE" + cls.cert_passwd = "123456" + cls.cert_name = "{} - {} - {} - Valid: {}".format( "NF-E", "A1", - self.cert_subject_valid, - format_date(self.env, self.cert_date_exp), + cls.cert_subject_valid, + format_date(cls.env, cls.cert_date_exp), ) - self.certificate_valid = misc.create_fake_certificate_file( + cls.certificate_valid = misc.create_fake_certificate_file( valid=True, - passwd=self.cert_passwd, - issuer=self.cert_issuer_a, - country=self.cert_country, - subject=self.cert_subject_valid, + passwd=cls.cert_passwd, + issuer=cls.cert_issuer_a, + country=cls.cert_country, + subject=cls.cert_subject_valid, ) - self.certificate_invalid = misc.create_fake_certificate_file( + cls.certificate_invalid = misc.create_fake_certificate_file( valid=False, - passwd=self.cert_passwd, - issuer=self.cert_issuer_b, - country=self.cert_country, - subject=self.cert_subject_invalid, + passwd=cls.cert_passwd, + issuer=cls.cert_issuer_b, + country=cls.cert_country, + subject=cls.cert_subject_invalid, ) - def _create_compay(self): + @classmethod + def _create_compay(cls): """Creating a company""" - company = self.env["res.company"].create( + company = cls.env["res.company"].create( { "name": "Company Test Fiscal BR", "cnpj_cpf": "42.245.642/0001-09", - "country_id": self.env.ref("base.br").id, - "state_id": self.env.ref("base.state_br_sp").id, + "country_id": cls.env.ref("base.br").id, + "state_id": cls.env.ref("base.state_br_sp").id, } ) return company - def _switch_user_company(self, user, company): + @classmethod + def _switch_user_company(cls, user, company): """Add a company to the user's allowed & set to current.""" user.write( { diff --git a/l10n_br_fiscal/tests/test_fiscal_tax.py b/l10n_br_fiscal/tests/test_fiscal_tax.py index 6614b4f65bf9..063f7d9ac2fa 100644 --- a/l10n_br_fiscal/tests/test_fiscal_tax.py +++ b/l10n_br_fiscal/tests/test_fiscal_tax.py @@ -1,17 +1,14 @@ # Copyright 2020 Akretion - Renato Lima # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). -from odoo.tests import common +from odoo.tests import SavepointCase from odoo.tools import float_compare from ..constants.fiscal import FINAL_CUSTOMER_NO, FINAL_CUSTOMER_YES from ..constants.icms import ICMS_ORIGIN_DEFAULT -class TestFiscalTax(common.TransactionCase): - def setUp(self): - super().setUp() - +class TestFiscalTax(SavepointCase): def _check_compute_taxes_result(self, test_result, compute_result, currency): for tax_domain in test_result["taxes"]: for tax_field in test_result["taxes"][tax_domain]: diff --git a/l10n_br_fiscal/tests/test_ibpt.py b/l10n_br_fiscal/tests/test_ibpt.py index d2c6bce058de..2539b174f529 100644 --- a/l10n_br_fiscal/tests/test_ibpt.py +++ b/l10n_br_fiscal/tests/test_ibpt.py @@ -1,6 +1,11 @@ # Copyright 2019 Akretion - Renato Lima # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). +import logging +from datetime import datetime +from os import environ + +from decorator import decorate from erpbrasil.base import misc from odoo.tests import SavepointCase @@ -11,11 +16,72 @@ get_ibpt_service, ) +_logger = logging.getLogger(__name__) + + +def _not_every_day_test(method, self, modulo=7, remaining=0): + if datetime.now().day % modulo == remaining or environ.get("CI_FORCE_IBPT"): + return method(self) + else: + return lambda: _logger.info( + "Skipping test today because datetime.now().day %% %s != %s" + % (modulo, remaining) + ) + + +def not_every_day_test(method): + """ + Decorate test methods to query the IBPT only + 1 day out of 7 and skip tests otherwise. + Indeed the IBPT webservice often returns errors and it sucks + to crash the entire l10n-brazil test suite because of this. + the CI_FORCE_IBPT env var can be set to force the test anyhow. + """ + return decorate(method, _not_every_day_test) + + +def mocked_requests_get(*args, **kwargs): + class MockResponse: + def __init__(self, json_data, status_code): + self.json_data = json_data + self.status_code = status_code + + def ok(self): + return True + + def json(self): + return self.json_data + + # the same as rates during 2 days: + return MockResponse( + { + "Codigo": "85030010", + "UF": "ES", + "EX": 0, + "Descricao": "Partes de motores/geradores de pot<=75kva", + "Nacional": 16.67, + "Estadual": 25.0, + "Importado": 23.98, + "Municipal": 0.0, + "Tipo": "0", + "VigenciaInicio": "20/05/2023", + "VigenciaFim": "30/06/2023", + "Chave": "FADD79", + "Versao": "23.1.F", + "Fonte": "IBPT/empresometro.com.br", + "Valor": 0.0, + "ValorTributoNacional": 0.0, + "ValorTributoEstadual": 0.0, + "ValorTributoImportado": 0.0, + "ValorTributoMunicipal": 0.0, + }, + 200, + ) + class TestIbpt(SavepointCase): @classmethod def setUpClass(cls): - super().setUpClass() cls.company = cls._create_compay() cls._switch_user_company(cls.env.user, cls.company) @@ -37,7 +103,6 @@ def _switch_user_company(cls, user, company): @classmethod def _check_ibpt_api(cls, company, ncm_nbs): """Check if IBPT API Webservice is online""" - result = False try: config = DeOlhoNoImposto( @@ -81,13 +146,11 @@ def _create_compay(cls): @classmethod def _create_product_tmpl(cls, name, ncm): """Create products related with NCM""" - product = cls.product_tmpl_model.create({"name": name, "ncm_id": ncm.id}) return product @classmethod def _create_service_tmpl(cls, name, nbs): """Create services related with NBS""" - product = cls.product_tmpl_model.create({"name": name, "nbs_id": nbs.id}) return product diff --git a/l10n_br_fiscal/tests/test_ibpt_product.py b/l10n_br_fiscal/tests/test_ibpt_product.py index 48e0ca256b62..06791b1836c2 100644 --- a/l10n_br_fiscal/tests/test_ibpt_product.py +++ b/l10n_br_fiscal/tests/test_ibpt_product.py @@ -1,13 +1,14 @@ # Copyright 2019 Akretion - Renato Lima # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). -from .test_ibpt import TestIbpt +from unittest import mock + +from .test_ibpt import TestIbpt, mocked_requests_get, not_every_day_test class TestIbptProduct(TestIbpt): @classmethod def setUpClass(cls): - super().setUpClass() cls.ncm_85030010 = cls.env.ref("l10n_br_fiscal.ncm_85030010") cls.ncm_85014029 = cls.env.ref("l10n_br_fiscal.ncm_85014029") @@ -24,6 +25,18 @@ def setUpClass(cls): name="Product Test 3 - With NCM: 8501.40.29", ncm=cls.ncm_85014029 ) + @mock.patch("requests.get", side_effect=mocked_requests_get) + def test_mock(self, mock_get): + api_status = self.env.company.ibpt_api + self.env.company.ibpt_api = True # force to run the mocked query + self.ncm_85030010.action_ibpt_inquiry() + ncms = self.ncm_model.search( + [("id", "in", (self.ncm_85030010.id, self.ncm_85014029.id))] + ) + ncms._scheduled_update() + self.env.company.ibpt_api = api_status + + @not_every_day_test def test_update_ibpt_product(self): """Check tax estimate update""" @@ -40,6 +53,7 @@ def test_update_ibpt_product(self): [("ncm_id", "in", (self.ncm_85030010.id, self.ncm_85014029.id))] ).unlink() + @not_every_day_test def test_ncm_count_product_template(self): """Check product template relation with NCM""" @@ -49,6 +63,7 @@ def test_ncm_count_product_template(self): self.assertEqual(self.ncm_85030010.product_tmpl_qty, 2) self.assertEqual(self.ncm_85014029.product_tmpl_qty, 1) + @not_every_day_test def test_update_scheduled(self): """Check NCM update scheduled""" diff --git a/l10n_br_fiscal/tests/test_ibpt_service.py b/l10n_br_fiscal/tests/test_ibpt_service.py index 7715a717496d..32067e711c60 100644 --- a/l10n_br_fiscal/tests/test_ibpt_service.py +++ b/l10n_br_fiscal/tests/test_ibpt_service.py @@ -1,13 +1,14 @@ # Copyright 2019 Akretion - Renato Lima # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). -from .test_ibpt import TestIbpt +from unittest import mock + +from .test_ibpt import TestIbpt, mocked_requests_get, not_every_day_test class TestIbptService(TestIbpt): @classmethod def setUpClass(cls): - super().setUpClass() cls.nbs_115069000 = cls.env.ref("l10n_br_fiscal.nbs_115069000") cls.nbs_124043300 = cls.env.ref("l10n_br_fiscal.nbs_124043300") @@ -24,6 +25,14 @@ def setUpClass(cls): name="Product Test 3 - With NBS: 1.2404.33.00", nbs=cls.nbs_124043300 ) + @mock.patch("requests.get", side_effect=mocked_requests_get) + def test_mock(self, mock_get): + api_status = self.env.company.ibpt_api + self.env.company.ibpt_api = True # force to run the mocked query + self.nbs_115069000.action_ibpt_inquiry() + self.env.company.ibpt_api = api_status + + @not_every_day_test def test_update_ibpt_service(self): """Check tax estimate update""" @@ -40,6 +49,7 @@ def test_update_ibpt_service(self): [("nbs_id", "in", (self.nbs_115069000.id, self.nbs_124043300.id))] ).unlink() + @not_every_day_test def test_nbs_count_product_template(self): """Check product template relation with NBS""" @@ -49,6 +59,7 @@ def test_nbs_count_product_template(self): self.assertEqual(self.nbs_115069000.product_tmpl_qty, 2) self.assertEqual(self.nbs_124043300.product_tmpl_qty, 1) + @not_every_day_test def test_update_scheduled(self): """Check NBS update scheduled""" diff --git a/l10n_br_fiscal/tests/test_icms_regulation.py b/l10n_br_fiscal/tests/test_icms_regulation.py index 3bd3b02cfa3a..4ee62b52ee08 100644 --- a/l10n_br_fiscal/tests/test_icms_regulation.py +++ b/l10n_br_fiscal/tests/test_icms_regulation.py @@ -1,25 +1,24 @@ -from odoo.tests import tagged -from odoo.tests.common import TransactionCase +from odoo.tests import SavepointCase, tagged from ..constants.fiscal import FINAL_CUSTOMER_NO, FINAL_CUSTOMER_YES @tagged("icms") -class TestICMSRegulation(TransactionCase): - def setUp(self): - super().setUp() +class TestICMSRegulation(SavepointCase): + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.partner = cls.env.ref("l10n_br_base.res_partner_akretion") + cls.company = cls.env.ref("base.main_company") + cls.product = cls.env.ref("product.product_product_1") + cls.nbm = cls.env["l10n_br_fiscal.nbm"] + cls.icms_regulation = cls.env.ref("l10n_br_fiscal.tax_icms_regulation") - self.partner = self.env.ref("l10n_br_base.res_partner_akretion") - self.company = self.env.ref("base.main_company") - self.product = self.env.ref("product.product_product_1") - self.nbm = self.env["l10n_br_fiscal.nbm"] - self.icms_regulation = self.env.ref("l10n_br_fiscal.tax_icms_regulation") - - self.sc_state_id = self.env.ref("base.state_br_sc") - self.sp_state_id = self.env.ref("base.state_br_sp") - self.venda_operation_line_id = self.env.ref("l10n_br_fiscal.fo_venda_venda") - self.ncm_48191000_id = self.env.ref("l10n_br_fiscal.ncm_48191000") - self.ncm_energia_id = self.env.ref("l10n_br_fiscal.ncm_27160000") + cls.sc_state_id = cls.env.ref("base.state_br_sc") + cls.sp_state_id = cls.env.ref("base.state_br_sp") + cls.venda_operation_line_id = cls.env.ref("l10n_br_fiscal.fo_venda_venda") + cls.ncm_48191000_id = cls.env.ref("l10n_br_fiscal.ncm_48191000") + cls.ncm_energia_id = cls.env.ref("l10n_br_fiscal.ncm_27160000") def test_icms_sc_sc_ind_final_yes_default(self): tax_icms = self.find_icms_tax( diff --git a/l10n_br_fiscal/tests/test_ncm.py b/l10n_br_fiscal/tests/test_ncm.py index 4b4873cb6792..1f15c68ef336 100644 --- a/l10n_br_fiscal/tests/test_ncm.py +++ b/l10n_br_fiscal/tests/test_ncm.py @@ -3,26 +3,25 @@ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). from odoo.exceptions import AccessError -from odoo.tests.common import TransactionCase +from odoo.tests import SavepointCase -class TestNcm(TransactionCase): - def setUp(self): - super(TestNcm, self).setUp() +class TestNcm(SavepointCase): + @classmethod + def setUpClass(cls): + super().setUpClass() # Create a test user with manager rights - self.test_user_manager = self.env["res.users"].create( + cls.test_user_manager = cls.env["res.users"].create( { "name": "Test User Manager", "login": "test_user_manager", - "groups_id": [ - (6, 0, [self.env.ref("l10n_br_fiscal.group_manager").id]) - ], + "groups_id": [(6, 0, [cls.env.ref("l10n_br_fiscal.group_manager").id])], } ) # Create a test user without manager rights - self.test_user = self.env["res.users"].create( + cls.test_user = cls.env["res.users"].create( { "name": "Test User", "login": "test_user", @@ -30,7 +29,7 @@ def setUp(self): ) # Fetch an existing record - self.test_record = self.env.ref("l10n_br_fiscal.ncm_00000000") + cls.test_record = cls.env.ref("l10n_br_fiscal.ncm_00000000") def test_action_archive_manager(self): self.test_record.with_user(self.test_user_manager).action_archive() diff --git a/l10n_br_fiscal/views/res_company_view.xml b/l10n_br_fiscal/views/res_company_view.xml index cf2803c1a36c..e58de29a9ce6 100644 --- a/l10n_br_fiscal/views/res_company_view.xml +++ b/l10n_br_fiscal/views/res_company_view.xml @@ -18,7 +18,14 @@ - + + 2SVRS202305251555107Servico em Operacao422023-06-11T00:15:00-03:001""" + +response_envia_documento = """2SVRS202305251555103Lote recebido com sucesso422023-06-11T01:18:19-03:004230022021132321""" + +response_consulta_documento = """2SVRS202305251555217Rejeicao: NF-e nao consta na base de dados da SEFAZ422023-06-11T01:20:55-03:0042230675277525000259550010000364481754015406""" + +response_consulta_recibo = """2SVRS202305261028423002202113232100Lote processado422023-06-11T01:18:19-03:002SVRS202304261131352001595943150001575500100000000220627771692023-06-02T10:47:21-03:00423002202113232IoYUWXt2fIiRXb7UYRgl77c6Zlk=100Autorizado o uso da NF-e""" + +response_cancela_documento = """2SVRS202305251555101""" # TODO not a valid cancelamento + +response_inutilizacao = """2SVRS202305251555102422381583054000129551222023-06-15T21:50:40-03:00""" + + +def is_libreoffice_command_available(): + try: + subprocess.run(["libreoffice", "--version"], check=True) + return True + except subprocess.CalledProcessError: + return False + except FileNotFoundError: + return False + + +class FakeRetorno(object): + def __init__(self, text): + self.text = text + self.content = text.encode("utf-8") + + def raise_for_status(self): + pass + + +def mocked_post(*args, **kwargs): + if args[2] == "nfeStatusServicoNF": + fake_retorno = FakeRetorno(response_status) + return analisar_retorno_raw( + "nfeStatusServicoNF", + object(), + b"", + fake_retorno, + retConsStatServ, + ) + + elif args[2] == "nfeConsultaNF": + fake_retorno = FakeRetorno(response_consulta_documento) + return analisar_retorno_raw( + "nfeConsultaNF", + object(), + b"", + fake_retorno, + retConsReciNFe, + ) + + elif args[2] == "nfeAutorizacaoLote": + fake_retorno = FakeRetorno(response_envia_documento) + nfe_etree = args[0][2] + nfe = ( + etree.tostring(nfe_etree) + .decode("utf-8") + .replace(' xmlns="http://www.portalfiscal.inf.br/nfe"', "") + ) + FakeRetorno.nfe = nfe + envi_nfe = ( + '%s' + % (nfe,) + ) + return analisar_retorno_raw( + "nfeAutorizacaoLote", + etree.fromstring(envi_nfe), + b"", + fake_retorno, + retEnviNFe, + ) + + elif args[2] == "nfeRetAutorizacaoLote": + fake_retorno = FakeRetorno(response_consulta_recibo) + nfe = FakeRetorno.nfe + return analisar_retorno_raw( + "nfeAutorizacaoLote", + object(), + nfe.encode("utf-8"), + fake_retorno, + retConsReciNFe, + ) + + elif args[2] == "nfeRecepcaoEvento": + fake_retorno = FakeRetorno(response_cancela_documento) + nfe = FakeRetorno.nfe + return analisar_retorno_raw( + "nfeRecepcaoEvento", + object(), + nfe.encode("utf-8"), + fake_retorno, + retEnvEvento, + ) + + elif args[2] == "nfeInutilizacaoNF": + fake_retorno = FakeRetorno(response_inutilizacao) + return analisar_retorno_raw( + "nfeInutilizacaoNF", + object(), + b"", + fake_retorno, + retInutNFe, + ) + + +class TestNFeWebservices(TestNFeExport): + def setUp(self): + nfe_list = [ + { + "record_ref": "l10n_br_nfe.demo_nfe_natural_icms_18_red_51_11", + "xml_file": "NFe35200159594315000157550010000000022062777169.xml", + }, + ] + super().setUp(nfe_list) + + @mock.patch.object(DocumentoEletronico, "_post", side_effect=mocked_post) + def test_enviar_e_cancelar(self, _mock_post): + for nfe_data in self.nfe_list: + nfe = nfe_data["nfe"] + + if not is_libreoffice_command_available(): + with mock.patch.object(NFe, "make_pdf"): + nfe.action_document_send() + else: + # testing with the original make_pdf requires you have + # apt-get install locale + # locale-gen pt_BR.UTF-8 + # dpkg-reconfigure locales + # pip install "reportlab==3.5.54" + # apt-get install libreoffice + nfe.action_document_send() + + self.assertEqual(nfe.state_edoc, SITUACAO_EDOC_AUTORIZADA) + + cancel_wizard = ( + self.env["l10n_br_fiscal.document.cancel.wizard"] + .with_context(active_model="l10n_br_fiscal.document", active_id=nfe.id) + .create( + {"document_id": nfe.id, "justification": "Era apenas um teste."} + ) + ) + cancel_wizard.doit() + self.assertEqual(nfe.state_edoc, SITUACAO_EDOC_CANCELADA) + + @mock.patch.object(DocumentoEletronico, "_post", side_effect=mocked_post) + def test_inutilizar(self, mocked_post): + nfe = self.nfe_list[0]["nfe"] + inutilizar_wizard = ( + self.env["l10n_br_fiscal.invalidate.number.wizard"] + .with_context(active_model="l10n_br_fiscal.document", active_id=nfe.id) + .create({"document_id": nfe.id, "justification": "Era apenas um teste."}) + ) + inutilizar_wizard.doit() diff --git a/l10n_br_nfe/views/nfe_document_view.xml b/l10n_br_nfe/views/nfe_document_view.xml index c143736620c8..34e90d2a280c 100644 --- a/l10n_br_nfe/views/nfe_document_view.xml +++ b/l10n_br_nfe/views/nfe_document_view.xml @@ -113,6 +113,10 @@ + + + + diff --git a/l10n_br_nfse_paulistana/README.rst b/l10n_br_nfse_paulistana/README.rst new file mode 100644 index 000000000000..94aafa33e4c3 --- /dev/null +++ b/l10n_br_nfse_paulistana/README.rst @@ -0,0 +1,108 @@ +======================= +NFS-e (Nota Paulistana) +======================= + +.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! 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/14.0/l10n_br_nfse_paulistana + :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-14-0/l10n-brazil-14-0-l10n_br_nfse_paulistana + :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/14.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 Nota Paulistana. + +**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 +============= + +Após a instalação do módulo deve ser configurado na empresa o processador de documentos eletrônicos como erpbrasil.edoc. + +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_paulistana/__init__.py b/l10n_br_nfse_paulistana/__init__.py new file mode 100644 index 000000000000..0ee8b5073e26 --- /dev/null +++ b/l10n_br_nfse_paulistana/__init__.py @@ -0,0 +1,2 @@ +from . import models +from . import tests diff --git a/l10n_br_nfse_paulistana/__manifest__.py b/l10n_br_nfse_paulistana/__manifest__.py new file mode 100644 index 000000000000..e07972de8a0d --- /dev/null +++ b/l10n_br_nfse_paulistana/__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 (Nota Paulistana)", + "summary": """ + NFS-e (Nota Paulistana)""", + "version": "14.0.1.0.0", + "license": "AGPL-3", + "author": "KMEE, Odoo Community Association (OCA)", + "maintainers": ["gabrielcardoso21", "mileo", "luismalta"], + "development_status": "Beta", + "website": "https://github.com/OCA/l10n-brazil", + "external_dependencies": { + "python": [ + "erpbrasil.edoc", + "erpbrasil.assinatura", + "erpbrasil.transmissao", + "erpbrasil.base>=2.3.0", + "nfselib.paulistana", + "unidecode", + ], + }, + "depends": [ + "l10n_br_nfse", + ], +} diff --git a/l10n_br_nfse_paulistana/constants/paulistana.py b/l10n_br_nfse_paulistana/constants/paulistana.py new file mode 100644 index 000000000000..b9b9fb34df6d --- /dev/null +++ b/l10n_br_nfse_paulistana/constants/paulistana.py @@ -0,0 +1,5 @@ +ENVIO_LOTE_RPS = ["EnvioLoteRPS", "TesteEnvioLoteRPS"] + +CONSULTA_LOTE = [ + "ConsultaLote", +] diff --git a/l10n_br_nfse_paulistana/i18n/l10n_br_nfse_paulistana.pot b/l10n_br_nfse_paulistana/i18n/l10n_br_nfse_paulistana.pot new file mode 100644 index 000000000000..83883df7e643 --- /dev/null +++ b/l10n_br_nfse_paulistana/i18n/l10n_br_nfse_paulistana.pot @@ -0,0 +1,65 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * l10n_br_nfse_paulistana +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 14.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_paulistana +#: model:ir.model,name:l10n_br_nfse_paulistana.model_res_company +msgid "Companies" +msgstr "" + +#. module: l10n_br_nfse_paulistana +#: model:ir.model.fields,field_description:l10n_br_nfse_paulistana.field_l10n_br_fiscal_document__display_name +#: model:ir.model.fields,field_description:l10n_br_nfse_paulistana.field_res_company__display_name +msgid "Display Name" +msgstr "" + +#. module: l10n_br_nfse_paulistana +#: model:ir.model,name:l10n_br_nfse_paulistana.model_l10n_br_fiscal_document +msgid "Fiscal Document" +msgstr "" + +#. module: l10n_br_nfse_paulistana +#: model:ir.model.fields,field_description:l10n_br_nfse_paulistana.field_l10n_br_fiscal_document__id +#: model:ir.model.fields,field_description:l10n_br_nfse_paulistana.field_res_company__id +msgid "ID" +msgstr "" + +#. module: l10n_br_nfse_paulistana +#: model:ir.model.fields,field_description:l10n_br_nfse_paulistana.field_l10n_br_fiscal_document____last_update +#: model:ir.model.fields,field_description:l10n_br_nfse_paulistana.field_res_company____last_update +msgid "Last Modified on" +msgstr "" + +#. module: l10n_br_nfse_paulistana +#: model:ir.model.fields,field_description:l10n_br_nfse_paulistana.field_res_company__provedor_nfse +msgid "NFSe Provider" +msgstr "" + +#. module: l10n_br_nfse_paulistana +#: model:ir.model.fields.selection,name:l10n_br_nfse_paulistana.selection__res_company__provedor_nfse__paulistana +msgid "Paulistana" +msgstr "" + +#. module: l10n_br_nfse_paulistana +#: code:addons/l10n_br_nfse_paulistana/models/document.py:0 +#, python-format +msgid "Procesado com Erro" +msgstr "" + +#. module: l10n_br_nfse_paulistana +#: code:addons/l10n_br_nfse_paulistana/models/document.py:0 +#: code:addons/l10n_br_nfse_paulistana/models/document.py:0 +#, python-format +msgid "Procesado com Sucesso" +msgstr "" diff --git a/l10n_br_nfse_paulistana/models/__init__.py b/l10n_br_nfse_paulistana/models/__init__.py new file mode 100644 index 000000000000..166dd3bd2493 --- /dev/null +++ b/l10n_br_nfse_paulistana/models/__init__.py @@ -0,0 +1,2 @@ +from . import document +from . import res_company diff --git a/l10n_br_nfse_paulistana/models/document.py b/l10n_br_nfse_paulistana/models/document.py new file mode 100644 index 000000000000..52d97a7cbd59 --- /dev/null +++ b/l10n_br_nfse_paulistana/models/document.py @@ -0,0 +1,443 @@ +# Copyright 2019 KMEE INFORMATICA LTDA +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +import xml.etree.ElementTree as ET +from datetime import datetime + +from erpbrasil.base import misc +from nfselib.paulistana.v02.PedidoEnvioLoteRPS import ( + CabecalhoType, + PedidoEnvioLoteRPS, + tpChaveRPS, + tpCPFCNPJ, + tpEndereco, + tpRPS, +) +from unidecode import unidecode + +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.paulistana import CONSULTA_LOTE, ENVIO_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_paulistana(record): + if record.company_id.provedor_nfse == "paulistana": + return True + return False + + +class Document(models.Model): + + _inherit = "l10n_br_fiscal.document" + + def convert_type_nfselib(self, class_object, object_filed, value): + if value is None: + return value + + value_type = "" + for field in class_object().member_data_items_: + if field.name == object_filed: + value_type = field.child_attrs.get("type", "").replace("xs:", "") + break + + if value_type in ("int", "long", "byte", "nonNegativeInteger"): + return int(value) + elif value_type == "decimal": + return round(float(value), 2) + elif value_type == "string": + return str(value) + else: + return value + + def _serialize(self, edocs): + edocs = super(Document, self)._serialize(edocs) + for record in self.filtered(filter_oca_nfse).filtered(filter_paulistana): + edocs.append(record.serialize_nfse_paulistana()) + return edocs + + def serialize_nfse_paulistana(self): + dados_lote_rps = self._prepare_lote_rps() + dados_servico = self._prepare_dados_servico() + lote_rps = PedidoEnvioLoteRPS( + Cabecalho=self._serialize_cabecalho(dados_lote_rps), + RPS=[self._serialize_lote_rps(dados_lote_rps, dados_servico)], + ) + return lote_rps + + def _serialize_cabecalho(self, dados_lote_rps): + return CabecalhoType( + Versao=self.convert_type_nfselib(CabecalhoType, "Versao", 1), + CPFCNPJRemetente=tpCPFCNPJ( + CNPJ=self.convert_type_nfselib( + CabecalhoType, "tpCPFCNPJ", dados_lote_rps["cnpj"] + ) + ), + transacao=False, # TODO: Verficar origem do dado + dtInicio=self.convert_type_nfselib( + CabecalhoType, + "dtInicio", + dados_lote_rps["date_in_out"].split("T", 1)[0], + ), + dtFim=self.convert_type_nfselib( + CabecalhoType, "dtFim", dados_lote_rps["date_in_out"].split("T", 1)[0] + ), + QtdRPS=self.convert_type_nfselib(CabecalhoType, "QtdRPS", "1"), + ValorTotalServicos=self.convert_type_nfselib( + CabecalhoType, "ValorTotalServicos", dados_lote_rps["total_recebido"] + ), + ValorTotalDeducoes=self.convert_type_nfselib( + CabecalhoType, "ValorTotalDeducoes", dados_lote_rps["carga_tributaria"] + ), + ) + + def _serialize_lote_rps(self, dados_lote_rps, dados_servico): + dados_tomador = self._prepare_dados_tomador() + return tpRPS( + Assinatura=self.assinatura_rps( + dados_lote_rps, dados_servico, dados_tomador + ), + ChaveRPS=tpChaveRPS( + InscricaoPrestador=self.convert_type_nfselib( + tpChaveRPS, + "InscricaoPrestador", + dados_lote_rps["inscricao_municipal"].zfill(8), + ), + SerieRPS=self.convert_type_nfselib( + tpChaveRPS, "SerieRPS", dados_lote_rps["serie"] + ), + NumeroRPS=self.convert_type_nfselib( + tpChaveRPS, "NumeroRPS", dados_lote_rps["numero"] + ), + ), + TipoRPS=self._map_type_rps(dados_lote_rps["tipo"]), + DataEmissao=self.convert_type_nfselib( + tpRPS, "DataEmissao", dados_lote_rps["data_emissao"].split("T", 1)[0] + ), + StatusRPS=self.convert_type_nfselib(tpRPS, "StatusRPS", "N"), + TributacaoRPS=self.convert_type_nfselib( + tpRPS, + "TributacaoRPS", + self._map_taxation_rps(dados_lote_rps["natureza_operacao"]), + ), + ValorServicos=self.convert_type_nfselib( + tpRPS, "ValorServicos", dados_servico["valor_servicos"] + ), + ValorDeducoes=self.convert_type_nfselib( + tpRPS, "ValorDeducoes", dados_servico["valor_deducoes"] + ), + ValorPIS=self.convert_type_nfselib( + tpRPS, "ValorPIS", dados_servico["valor_pis"] + ), + ValorCOFINS=self.convert_type_nfselib( + tpRPS, "ValorCOFINS", dados_servico["valor_cofins"] + ), + ValorINSS=self.convert_type_nfselib( + tpRPS, "ValorINSS", dados_servico["valor_inss"] + ), + ValorIR=self.convert_type_nfselib( + tpRPS, "ValorIR", dados_servico["valor_ir"] + ), + ValorCSLL=self.convert_type_nfselib( + tpRPS, "ValorCSLL", dados_servico["valor_csll"] + ), + CodigoServico=self.convert_type_nfselib( + tpRPS, "CodigoServico", dados_servico["codigo_tributacao_municipio"] + ), + AliquotaServicos=self.convert_type_nfselib( + tpRPS, "AliquotaServicos", dados_servico["aliquota"] + ), + ISSRetido="true" if dados_servico["iss_retido"] == "1" else "false", + # FIXME: Hardcoded + CPFCNPJTomador=self.convert_type_nfselib( + tpRPS, + "CPFCNPJTomador", + tpCPFCNPJ(CNPJ=dados_tomador["cnpj"], CPF=dados_tomador["cpf"]), + ), + InscricaoMunicipalTomador=self.convert_type_nfselib( + tpRPS, "InscricaoMunicipalTomador", dados_tomador["inscricao_municipal"] + ), + InscricaoEstadualTomador=self.convert_type_nfselib( + tpRPS, "InscricaoEstadualTomador", dados_tomador["inscricao_estadual"] + ), + RazaoSocialTomador=self.convert_type_nfselib( + tpRPS, "RazaoSocialTomador", dados_tomador["razao_social"] + ), + EnderecoTomador=tpEndereco( + Logradouro=self.convert_type_nfselib( + tpEndereco, "Logradouro", dados_tomador["endereco"] + ), + NumeroEndereco=self.convert_type_nfselib( + tpEndereco, "NumeroEndereco", dados_tomador["numero"] + ), + ComplementoEndereco=self.convert_type_nfselib( + tpEndereco, "ComplementoEndereco", dados_tomador["complemento"] + ), + Bairro=self.convert_type_nfselib( + tpEndereco, "Bairro", dados_tomador["bairro"] + ), + Cidade=self.convert_type_nfselib( + tpEndereco, "Cidade", dados_tomador["codigo_municipio"] + ), + UF=self.convert_type_nfselib(tpEndereco, "UF", dados_tomador["uf"]), + CEP=self.convert_type_nfselib(tpEndereco, "CEP", dados_tomador["cep"]), + ), + EmailTomador=self.convert_type_nfselib( + tpRPS, "EmailTomador", dados_tomador["email"] + ), + Discriminacao=self.convert_type_nfselib( + tpRPS, + "Discriminacao", + unidecode( + dados_servico["discriminacao"] + + ( + "|%s|" % self.fiscal_additional_data.replace("\n", "|") + if self.fiscal_additional_data + else "" + ) + ), + ), + ValorCargaTributaria=self.convert_type_nfselib( + tpRPS, "ValorCargaTributaria", dados_lote_rps["carga_tributaria"] + ), + FonteCargaTributaria=self.convert_type_nfselib( + tpRPS, "FonteCargaTributaria", dados_lote_rps["total_recebido"] + ), + MunicipioPrestacao=self.convert_type_nfselib( + CabecalhoType, + "Versao", + self._map_provision_municipality( + dados_lote_rps["natureza_operacao"], + dados_servico["codigo_municipio"], + ), + ), + ) + + def _serialize_rps(self, dados): + return tpRPS( + InscricaoMunicipalTomador=self.convert_type_nfselib( + tpRPS, "InscricaoMunicipalTomador", dados["inscricao_municipal"] + ), + CPFCNPJTomador=tpCPFCNPJ( + Cnpj=self.convert_type_nfselib(tpCPFCNPJ, "Cnpj", dados["cnpj"]), + Cpf=self.convert_type_nfselib(tpCPFCNPJ, "Cpf", dados["cpf"]), + ), + RazaoSocialTomador=self.convert_type_nfselib( + tpRPS, "RazaoSocialTomador", dados["razao_social"] + ), + EnderecoTomador=tpEndereco( + Logradouro=self.convert_type_nfselib( + tpEndereco, "Logradouro", dados["endereco"] + ), + NumeroEndereco=self.convert_type_nfselib( + tpEndereco, "NumeroEndereco", dados["numero"] + ), + ComplementoEndereco=self.convert_type_nfselib( + tpEndereco, "ComplementoEndereco", dados["complemento"] + ), + Bairro=self.convert_type_nfselib(tpEndereco, "Bairro", dados["bairro"]), + Cidade=self.convert_type_nfselib( + tpEndereco, "Cidade", dados["codigo_municipio"] + ), + UF=self.convert_type_nfselib(tpEndereco, "UF", dados["uf"]), + CEP=self.convert_type_nfselib(tpEndereco, "CEP", dados["cep"]), + ) + or None, + ) + + def assinatura_rps(self, dados_lote_rps, dados_servico, dados_tomador): + assinatura = "" + + assinatura += dados_lote_rps["inscricao_municipal"].zfill(8) + assinatura += dados_lote_rps["serie"].ljust(5, " ") + assinatura += dados_lote_rps["numero"].zfill(12) + assinatura += datetime.strptime( + dados_lote_rps["data_emissao"], "%Y-%m-%dT%H:%M:%S" + ).strftime("%Y%m%d") + assinatura += self._map_taxation_rps(dados_lote_rps["natureza_operacao"]) + assinatura += "N" # Corrigir - Verificar status do RPS + assinatura += "N" + assinatura += ( + ("%.2f" % dados_lote_rps["total_recebido"]).replace(".", "").zfill(15) + ) + assinatura += ( + ("%.2f" % dados_lote_rps["carga_tributaria"]).replace(".", "").zfill(15) + ) + assinatura += dados_servico["codigo_tributacao_municipio"].zfill(5) + assinatura += "2" if dados_tomador["cnpj"] else "1" + assinatura += (dados_tomador["cnpj"] or dados_tomador["cpf"]).zfill(14) + # assinatura += '3' + # assinatura += ''.zfill(14) + # assinatura += 'N' + + return assinatura + + def _map_taxation_rps(self, operation_nature): + # FIXME: Lidar com diferença de tributado em São Paulo ou não + dict_taxation = { + "1": "T", + "2": "F", + "3": "A", + "4": "R", + "5": "X", + "6": "X", + } + + return dict_taxation[operation_nature] + + def _map_provision_municipality(self, operation_nature, municipal_code): + if operation_nature == "1": + return None + else: + return municipal_code + + def _map_type_rps(self, rps_type): + dict_type_rps = { + "1": "RPS", + "2": "RPS-M", + "3": "RPS-C", + } + + return dict_type_rps[rps_type] + + def _eletronic_document_send(self): + super(Document, self)._eletronic_document_send() + for record in self.filtered(filter_oca_nfse).filtered(filter_paulistana): + processador = record._processador_erpbrasil_nfse() + + protocolo = record.authorization_protocol + vals = dict() + + if not protocolo: + for edoc in record.serialize(): + processo = None + for p in processador.processar_documento(edoc): + processo = p + + retorno = ET.fromstring(processo.retorno) + if processo.webservice in ENVIO_LOTE_RPS: + + if retorno: + if processo.resposta.Cabecalho.Sucesso: + record._change_state(SITUACAO_EDOC_AUTORIZADA) + vals["status_name"] = _("Procesado com Sucesso") + vals["status_code"] = 4 + vals["edoc_error_message"] = "" + else: + mensagem_erro = "" + for erro in retorno.findall("Erro"): + codigo = erro.find("Codigo").text + descricao = erro.find("Descricao").text + mensagem_erro += ( + codigo + " - " + descricao + "\n" + ) + + vals["edoc_error_message"] = mensagem_erro + vals["status_name"] = _("Procesado com Erro") + vals["status_code"] = 3 + record._change_state(SITUACAO_EDOC_REJEITADA) + + if processo.webservice in CONSULTA_LOTE: + if processo.resposta.Cabecalho.Sucesso: + nfse = retorno.find(".//NFe") + # TODO: Verificar resposta do ConsultarLote + vals["document_number"] = nfse.find(".//NumeroNFe").text + vals["authorization_date"] = nfse.find( + ".//DataEmissaoRPS" + ).text + vals["verify_code"] = nfse.find( + ".//CodigoVerificacao" + ).text + record.authorization_event_id.set_done( + status_code=4, + response=vals["status_name"], + protocol_date=vals["authorization_date"], + protocol_number=protocolo, + file_response_xml=processo.retorno, + ) + + record.write(vals) + if record.status_code == "4": + record.make_pdf() + return + + def _document_status(self): + for record in self.filtered(filter_oca_nfse).filtered(filter_paulistana): + processador = record._processador_erpbrasil_nfse() + processo = processador.consulta_nfse_rps( + numero_rps=record.rps_number, + serie_rps=record.document_serie, + insc_prest=misc.punctuation_rm( + record.company_id.partner_id.inscr_mun or "" + ) + or None, + cnpj_prest=misc.punctuation_rm(record.company_id.partner_id.cnpj_cpf), + ) + consulta = processador.analisa_retorno_consulta(processo) + if isinstance(consulta, dict): + record.write( + { + "verify_code": consulta["codigo_verificacao"], + "document_number": consulta["numero"], + "authorization_date": consulta["data_emissao"], + } + ) + record.authorization_event_id.set_done( + status_code=4, + response=_("Procesado com Sucesso"), + protocol_date=consulta["data_emissao"], + protocol_number=record.authorization_protocol, + file_response_xml=processo.retorno, + ) + return _(consulta) + + def cancel_document_paulistana(self): + def doc_dict(record): + return { + "numero_nfse": record.document_number, + "codigo_verificacao": record.verify_code, + } + + for record in self.filtered(filter_oca_nfse).filtered(filter_paulistana): + processador = record._processador_erpbrasil_nfse() + processo = processador.cancela_documento(doc_numero=doc_dict(record)) + + status, message = processador.analisa_retorno_cancelamento_paulistana( + 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 record.nfse_environment == "1" else EVENT_ENV_HML + ), + event_type="2", + xml_file=processo.envio_xml, + document_id=record, + ) + + return status + + def _exec_before_SITUACAO_EDOC_CANCELADA(self, old_state, new_state): + super(Document, self)._exec_before_SITUACAO_EDOC_CANCELADA(old_state, new_state) + return self.cancel_document_paulistana() diff --git a/l10n_br_nfse_paulistana/models/res_company.py b/l10n_br_nfse_paulistana/models/res_company.py new file mode 100644 index 000000000000..325a6060cd3b --- /dev/null +++ b/l10n_br_nfse_paulistana/models/res_company.py @@ -0,0 +1,15 @@ +# Copyright 2020 KMEE INFORMATICA LTDA +# 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=[ + ("paulistana", "Paulistana"), + ] + ) diff --git a/l10n_br_nfse_paulistana/readme/CONFIGURE.rst b/l10n_br_nfse_paulistana/readme/CONFIGURE.rst new file mode 100644 index 000000000000..5d5408311049 --- /dev/null +++ b/l10n_br_nfse_paulistana/readme/CONFIGURE.rst @@ -0,0 +1 @@ +Após a instalação do módulo deve ser configurado na empresa o processador de documentos eletrônicos como erpbrasil.edoc. diff --git a/l10n_br_nfse_paulistana/readme/CONTRIBUTORS.rst b/l10n_br_nfse_paulistana/readme/CONTRIBUTORS.rst new file mode 100644 index 000000000000..72e4abef82ed --- /dev/null +++ b/l10n_br_nfse_paulistana/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_paulistana/readme/DESCRIPTION.rst b/l10n_br_nfse_paulistana/readme/DESCRIPTION.rst new file mode 100644 index 000000000000..04538c9c332d --- /dev/null +++ b/l10n_br_nfse_paulistana/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 Nota Paulistana. diff --git a/l10n_br_nfse_paulistana/readme/INSTALL.rst b/l10n_br_nfse_paulistana/readme/INSTALL.rst new file mode 100644 index 000000000000..e2c08a6648de --- /dev/null +++ b/l10n_br_nfse_paulistana/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_paulistana/readme/ROADMAP.rst b/l10n_br_nfse_paulistana/readme/ROADMAP.rst new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/l10n_br_nfse_paulistana/readme/USAGE.rst b/l10n_br_nfse_paulistana/readme/USAGE.rst new file mode 100644 index 000000000000..f217aefe6260 --- /dev/null +++ b/l10n_br_nfse_paulistana/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_paulistana/static/description/icon.png b/l10n_br_nfse_paulistana/static/description/icon.png new file mode 100644 index 000000000000..3a0328b516c4 Binary files /dev/null and b/l10n_br_nfse_paulistana/static/description/icon.png differ diff --git a/l10n_br_nfse_paulistana/static/description/index.html b/l10n_br_nfse_paulistana/static/description/index.html new file mode 100644 index 000000000000..be6dd24ef579 --- /dev/null +++ b/l10n_br_nfse_paulistana/static/description/index.html @@ -0,0 +1,444 @@ + + + + + + +NFS-e (Nota Paulistana) + + + +
+

NFS-e (Nota Paulistana)

+ + +

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 Nota Paulistana.

+

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

+

Após a instalação do módulo deve ser configurado na empresa o processador de documentos eletrônicos como erpbrasil.edoc.

+
+
+

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_paulistana/tests/__init__.py b/l10n_br_nfse_paulistana/tests/__init__.py new file mode 100644 index 000000000000..0a0998034687 --- /dev/null +++ b/l10n_br_nfse_paulistana/tests/__init__.py @@ -0,0 +1 @@ +from . import test_fiscal_document_nfse_paulistana diff --git a/l10n_br_nfse_paulistana/tests/nfse/paulistana.xml b/l10n_br_nfse_paulistana/tests/nfse/paulistana.xml new file mode 100644 index 000000000000..296266a9fd0c --- /dev/null +++ b/l10n_br_nfse_paulistana/tests/nfse/paulistana.xml @@ -0,0 +1,58 @@ + + + + 59594315000157 + + false + 2020-06-04 + 2020-06-04 + 1 + 100 + 0 + + + 00035172001 00000000005020200604TNN0000000000100000000000000000006311900281493979000189 + + 35172 + 001 + 50 + + RPS + 2020-06-04 + N + T + 100 + 0 + 0 + 0 + 0 + 0 + 0 + 6311900 + 0.05 + false + + 81493979000189 + + 460429771334 + Cliente 1 SP + + Rua Samuel Morse + 135 + Brooklin + 3550308 + SP + 4576060 + + cliente1@cliente1.com.br + [ODOO_DEV] Customized Odoo Development|Documento emitido por: Marc Demo| + 0 + 100.0 + + diff --git a/l10n_br_nfse_paulistana/tests/test_fiscal_document_nfse_paulistana.py b/l10n_br_nfse_paulistana/tests/test_fiscal_document_nfse_paulistana.py new file mode 100644 index 000000000000..6b3dbf260d46 --- /dev/null +++ b/l10n_br_nfse_paulistana/tests/test_fiscal_document_nfse_paulistana.py @@ -0,0 +1,70 @@ +# 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_paulistana + +_logger = logging.getLogger(__name__) + + +class TestFiscalDocumentNFSePaulistana(TestFiscalDocumentNFSeCommon): + def setUp(self): + super(TestFiscalDocumentNFSePaulistana, self).setUp() + self.company.provedor_nfse = "paulistana" + + def test_nfse_paulistana(self): + """Test NFS-e same state.""" + + xml_path = os.path.join( + l10n_br_nfse_paulistana.__path__[0], "tests", "nfse", "paulistana.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/l10n_br_pos/__manifest__.py b/l10n_br_pos/__manifest__.py index 96d5c9ff86a3..c741f0fe11c4 100644 --- a/l10n_br_pos/__manifest__.py +++ b/l10n_br_pos/__manifest__.py @@ -3,7 +3,7 @@ { "name": "Ponto de venda adaptado a legislação Brasileira", - "version": "14.0.1.1.0", + "version": "14.0.1.2.0", "author": "KMEE, Odoo Community Association (OCA)", "website": "https://github.com/OCA/l10n-brazil", "license": "AGPL-3", diff --git a/l10n_br_pos/static/src/js/Screens/OrderManagementScreen/OrderRow.js b/l10n_br_pos/static/src/js/Screens/OrderManagementScreen/OrderRow.js index 1ae7856a6a58..0c37907955cb 100644 --- a/l10n_br_pos/static/src/js/Screens/OrderManagementScreen/OrderRow.js +++ b/l10n_br_pos/static/src/js/Screens/OrderManagementScreen/OrderRow.js @@ -10,8 +10,8 @@ odoo.define("l10n_br_pos.OrderRow", function (require) { const OrderRow = require("point_of_sale.OrderRow"); const Registries = require("point_of_sale.Registries"); - const L10nBrPosOrderRow = (OrderRow) => - class extends OrderRow { + const L10nBrPosOrderRow = (OrderRow_screen = OrderRow) => + class extends OrderRow_screen { get document_serie_number() { if ( this.props.order.document_serie && diff --git a/l10n_br_pos/static/src/js/Screens/PaymentScreen/PaymentScreen.js b/l10n_br_pos/static/src/js/Screens/PaymentScreen/PaymentScreen.js index 4877ddedc895..b1e4772c3955 100644 --- a/l10n_br_pos/static/src/js/Screens/PaymentScreen/PaymentScreen.js +++ b/l10n_br_pos/static/src/js/Screens/PaymentScreen/PaymentScreen.js @@ -16,8 +16,8 @@ odoo.define("l10n_br_pos.PaymentScreen", function (require) { const {Gui} = require("point_of_sale.Gui"); const _t = core._t; - const L10nBrPosPaymentScreen = (PaymentScreen) => - class extends PaymentScreen { + const L10nBrPosPaymentScreen = (PaymentScreen_screen = PaymentScreen) => + class extends PaymentScreen_screen { check_valid_cpf_cnpj(order) { let result = true; const client = order.get_client(); diff --git a/l10n_br_pos/static/src/js/models.js b/l10n_br_pos/static/src/js/models.js index af47f91b038c..a211609bc86f 100644 --- a/l10n_br_pos/static/src/js/models.js +++ b/l10n_br_pos/static/src/js/models.js @@ -110,6 +110,7 @@ odoo.define("l10n_br_pos.models", function (require) { models.Order = models.Order.extend({ initialize: function (attributes, options) { // CORE METHODS + /* eslint complexity: ["error", 30] */ _super_order.initialize.apply(this, arguments, options); this.init_locked = true; @@ -358,7 +359,8 @@ odoo.define("l10n_br_pos.models", function (require) { return taxes; }, compute_message: function (templateString, taxes) { - /* Compute fiscal message */ + // Compute fiscal message + // eslint-disable-next-line no-new-func return new Function(`return \`${templateString}\`;`).call(this, taxes); }, _document_status_popup: function () { diff --git a/l10n_br_pos/static/src/js/util.js b/l10n_br_pos/static/src/js/util.js index c9eaea411f9c..d1d76e75cf79 100644 --- a/l10n_br_pos/static/src/js/util.js +++ b/l10n_br_pos/static/src/js/util.js @@ -1,115 +1,121 @@ odoo.define("l10n_br_pos.util", function () { "use strict"; - function validate_cnpj_cpf(value) { - var cnpj_cpf = value; - - cnpj_cpf = cnpj_cpf.trim(); - cnpj_cpf = cnpj_cpf.replace(/\./g, ""); - cnpj_cpf = cnpj_cpf.replace("-", ""); - cnpj_cpf = cnpj_cpf.replace("/", ""); - cnpj_cpf = cnpj_cpf.split(""); + function validate_cpf(cpf) { + let v1 = 0; + let v2 = 0; + let aux = false; - if (cnpj_cpf.length === 11) { - const cpf = cnpj_cpf; - let v1 = 0; - let v2 = 0; - let aux = false; - - for (let i = 1; cpf.length > i; i++) { - if (cpf[i - 1] !== cpf[i]) { - aux = true; - } + for (let i = 1; cpf.length > i; i++) { + if (cpf[i - 1] !== cpf[i]) { + aux = true; } + } - if (aux === false) { - return false; - } + if (aux === false) { + return false; + } - for (let i = 0, p = 10; cpf.length - 2 > i; i++, p--) { - v1 += cpf[i] * p; - } + for (let i = 0, p = 10; cpf.length - 2 > i; i++, p--) { + v1 += cpf[i] * p; + } - v1 = (v1 * 10) % 11; + v1 = (v1 * 10) % 11; - if (v1 == 10) { - v1 = 0; - } + if (v1 === 10) { + v1 = 0; + } - if (v1 != cpf[9]) { - return false; - } + if (v1 !== parseInt(cpf[9], 10)) { + return false; + } - for (let i = 0, p = 11; cpf.length - 1 > i; i++, p--) { - v2 += cpf[i] * p; - } + for (let i = 0, p = 11; cpf.length - 1 > i; i++, p--) { + v2 += cpf[i] * p; + } - v2 = (v2 * 10) % 11; + v2 = (v2 * 10) % 11; - if (v2 == 10) { - v2 = 0; - } + if (v2 === 10) { + v2 = 0; + } - if (v2 != cpf[10]) { - return false; - } - return true; - } else if (cnpj_cpf.length === 14) { - const cnpj = cnpj_cpf; - let v1 = 0; - let v2 = 0; - let aux = false; - - for (let i = 1; cnpj.length > i; i++) { - if (cnpj[i - 1] !== cnpj[i]) { - aux = true; - } - } + if (v2 !== parseInt(cpf[10], 10)) { + return false; + } + return true; + } - if (aux === false) { - return false; - } + function validate_cnpj(cnpj) { + let v1 = 0; + let v2 = 0; + let aux = false; - for (let i = 0, p1 = 5, p2 = 13; cnpj.length - 2 > i; i++, p1--, p2--) { - if (p1 >= 2) { - v1 += cnpj[i] * p1; - } else { - v1 += cnpj[i] * p2; - } + for (let i = 1; cnpj.length > i; i++) { + if (cnpj[i - 1] !== cnpj[i]) { + aux = true; } + } - v1 %= 11; + if (aux === false) { + return false; + } - if (v1 < 2) { - v1 = 0; + for (let i = 0, p1 = 5, p2 = 13; cnpj.length - 2 > i; i++, p1--, p2--) { + if (p1 >= 2) { + v1 += cnpj[i] * p1; } else { - v1 = 11 - v1; + v1 += cnpj[i] * p2; } + } - if (v1 != cnpj[12]) { - return false; - } + v1 %= 11; - for (let i = 0, p1 = 6, p2 = 14; cnpj.length - 1 > i; i++, p1--, p2--) { - if (p1 >= 2) { - v2 += cnpj[i] * p1; - } else { - v2 += cnpj[i] * p2; - } - } + if (v1 < 2) { + v1 = 0; + } else { + v1 = 11 - v1; + } - v2 %= 11; + if (v1 !== parseInt(cnpj[12], 10)) { + return false; + } - if (v2 < 2) { - v2 = 0; + for (let i = 0, p1 = 6, p2 = 14; cnpj.length - 1 > i; i++, p1--, p2--) { + if (p1 >= 2) { + v2 += cnpj[i] * p1; } else { - v2 = 11 - v2; + v2 += cnpj[i] * p2; } + } - if (v2 != cnpj[13]) { - return false; - } - return true; + v2 %= 11; + + if (v2 < 2) { + v2 = 0; + } else { + v2 = 11 - v2; + } + + if (v2 !== parseInt(cnpj[13], 10)) { + return false; + } + return true; + } + + function validate_cnpj_cpf(value) { + var cnpj_cpf = value; + + cnpj_cpf = cnpj_cpf.trim(); + cnpj_cpf = cnpj_cpf.replace(/\./g, ""); + cnpj_cpf = cnpj_cpf.replace("-", ""); + cnpj_cpf = cnpj_cpf.replace("/", ""); + cnpj_cpf = cnpj_cpf.split(""); + + if (cnpj_cpf.length === 11) { + return validate_cpf(cnpj_cpf); + } else if (cnpj_cpf.length === 14) { + return validate_cnpj(cnpj_cpf); } return cnpj_cpf.length === 0; } diff --git a/l10n_br_pos/tests/test_l10n_br_pos_config.py b/l10n_br_pos/tests/test_l10n_br_pos_config.py index 487368e80e71..8b54e9b5254d 100644 --- a/l10n_br_pos/tests/test_l10n_br_pos_config.py +++ b/l10n_br_pos/tests/test_l10n_br_pos_config.py @@ -1,9 +1,10 @@ # Copyright 2022 KMEE # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). -from odoo.tests.common import TransactionCase +from odoo.tests import TransactionCase +# for some reason conversion to SavepointCase fails class TestL10nBrPosConfig(TransactionCase): def setUp(self): super().setUp() diff --git a/l10n_br_pos/tests/test_l10n_br_pos_order.py b/l10n_br_pos/tests/test_l10n_br_pos_order.py index 8c2edd1f7a56..ee31e816638f 100644 --- a/l10n_br_pos/tests/test_l10n_br_pos_order.py +++ b/l10n_br_pos/tests/test_l10n_br_pos_order.py @@ -4,16 +4,17 @@ from datetime import datetime from odoo import fields -from odoo.tests.common import TransactionCase +from odoo.tests import SavepointCase -class TestL10nBrPosOrder(TransactionCase): - def setUp(self): - super().setUp() - self.env.company = self.env.ref("l10n_br_base.empresa_lucro_presumido") - self.pos_config = self.env.ref("l10n_br_pos.pos_config_presumido") - self.cash_payment_method = self.env.ref("l10n_br_pos.presumido_dinheiro") - self.led_lamp = self.env["product.product"].create( +class TestL10nBrPosOrder(SavepointCase): + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.env.company = cls.env.ref("l10n_br_base.empresa_lucro_presumido") + cls.pos_config = cls.env.ref("l10n_br_pos.pos_config_presumido") + cls.cash_payment_method = cls.env.ref("l10n_br_pos.presumido_dinheiro") + cls.led_lamp = cls.env["product.product"].create( { "name": "LED Lamp", "available_in_pos": True, diff --git a/l10n_br_pos/tests/test_l10n_br_pos_partner.py b/l10n_br_pos/tests/test_l10n_br_pos_partner.py index 90de275bc992..f36c07ef9823 100644 --- a/l10n_br_pos/tests/test_l10n_br_pos_partner.py +++ b/l10n_br_pos/tests/test_l10n_br_pos_partner.py @@ -1,13 +1,14 @@ # Copyright 2023 KMEE # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). -from odoo.tests.common import TransactionCase +from odoo.tests import SavepointCase -class TestL10nBrPosPartner(TransactionCase): - def setUp(self): - super().setUp() - self.pos_config = self.env.ref("point_of_sale.pos_config_main") +class TestL10nBrPosPartner(SavepointCase): + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.pos_config = cls.env.ref("point_of_sale.pos_config_main") def test_create_partner_from_ui_l10n_brazil(self): diff --git a/l10n_br_pos_cfe/__manifest__.py b/l10n_br_pos_cfe/__manifest__.py index e1b98dee7fbc..3d11d878656c 100644 --- a/l10n_br_pos_cfe/__manifest__.py +++ b/l10n_br_pos_cfe/__manifest__.py @@ -4,7 +4,7 @@ { "name": "L10n Br Pos Cfe", "summary": """CF-e""", - "version": "14.0.1.1.0", + "version": "14.0.1.2.0", "license": "AGPL-3", "author": "KMEE,Odoo Community Association (OCA)", "website": "https://github.com/OCA/l10n-brazil", diff --git a/l10n_br_pos_cfe/static/src/js/ChromeWidgets/ProxyStatus.js b/l10n_br_pos_cfe/static/src/js/ChromeWidgets/ProxyStatus.js index 69c6ac1e05ca..c0aa22b314fc 100644 --- a/l10n_br_pos_cfe/static/src/js/ChromeWidgets/ProxyStatus.js +++ b/l10n_br_pos_cfe/static/src/js/ChromeWidgets/ProxyStatus.js @@ -9,8 +9,8 @@ odoo.define("l10n_br_pos_cfe.ProxyStatus", function (require) { var Registries = require("point_of_sale.Registries"); var ProxyStatus = require("point_of_sale.ProxyStatus"); - const CFeProxyStatus = (ProxyStatus) => - class CFeProxyStatus extends ProxyStatus { + const CFeProxyStatus = (ProxyStatus_status = ProxyStatus) => + class CFeProxyStatus extends ProxyStatus_status { _setStatus(newStatus) { super._setStatus(newStatus); var warning = false; diff --git a/l10n_br_pos_cfe/static/src/js/OrderManagementScreen/ReprintReceiptScreen.js b/l10n_br_pos_cfe/static/src/js/OrderManagementScreen/ReprintReceiptScreen.js index e67563ea520f..3d651dca65ee 100644 --- a/l10n_br_pos_cfe/static/src/js/OrderManagementScreen/ReprintReceiptScreen.js +++ b/l10n_br_pos_cfe/static/src/js/OrderManagementScreen/ReprintReceiptScreen.js @@ -12,8 +12,10 @@ odoo.define("l10n_br_pos_cfe.ReprintReceiptScreen", function (require) { const ReprintReceiptScreen = require("point_of_sale.ReprintReceiptScreen"); const Registries = require("point_of_sale.Registries"); - const L10nBrPosCfeReprintReceiptScreen = (ReprintReceiptScreen) => - class extends ReprintReceiptScreen { + const L10nBrPosCfeReprintReceiptScreen = ( + ReprintReceiptScreen_screen = ReprintReceiptScreen + ) => + class extends ReprintReceiptScreen_screen { async printReceipt() { setTimeout(() => super.printReceipt(), 1000); } diff --git a/l10n_br_pos_cfe/static/src/js/PaymentScreen/PaymentScreen.js b/l10n_br_pos_cfe/static/src/js/PaymentScreen/PaymentScreen.js index ec1e791f9c9e..30957861ac1f 100644 --- a/l10n_br_pos_cfe/static/src/js/PaymentScreen/PaymentScreen.js +++ b/l10n_br_pos_cfe/static/src/js/PaymentScreen/PaymentScreen.js @@ -12,8 +12,8 @@ odoo.define("l10n_br_pos_cfe.PaymentScreen", function (require) { const PaymentScreen = require("point_of_sale.PaymentScreen"); const Registries = require("point_of_sale.Registries"); - const L10nBrPosCfePaymentScreen = (PaymentScreen) => - class extends PaymentScreen { + const L10nBrPosCfePaymentScreen = (PaymentScreen_screen = PaymentScreen) => + class extends PaymentScreen_screen { async validateOrder(isForceValidate) { if (this.env.pos.config.iface_fiscal_via_proxy) { super.validateOrder(isForceValidate); diff --git a/l10n_br_pos_cfe/static/src/js/ReceiptScreen/SatOrderReceipt.js b/l10n_br_pos_cfe/static/src/js/ReceiptScreen/SatOrderReceipt.js index 4ecff60f34fa..063f0b0fcf16 100644 --- a/l10n_br_pos_cfe/static/src/js/ReceiptScreen/SatOrderReceipt.js +++ b/l10n_br_pos_cfe/static/src/js/ReceiptScreen/SatOrderReceipt.js @@ -8,8 +8,8 @@ odoo.define("l10n_br_pos_cfe.SatOrderReceipt", function (require) { const round_pr = utils.round_precision; - const SatOrderReceipt = (OrderReceipt) => - class extends OrderReceipt { + const SatOrderReceipt = (OrderReceipt_screen = OrderReceipt) => + class extends OrderReceipt_screen { get isCanceled() { return this.props.order.state_edoc === "cancelada"; } diff --git a/l10n_br_pos_cfe/static/src/js/devices.js b/l10n_br_pos_cfe/static/src/js/devices.js index 5e74208a6500..7dc3573b6720 100644 --- a/l10n_br_pos_cfe/static/src/js/devices.js +++ b/l10n_br_pos_cfe/static/src/js/devices.js @@ -19,8 +19,8 @@ odoo.define("l10n_br_pos_cfe.devices", function (require) { var self = this; this.on("change:status", this, function (eh, status) { - status = status.newValue; - if (status.status === "connected" && self.fiscal_device) { + var new_status = status.newValue; + if (new_status.status === "connected" && self.fiscal_device) { self.fiscal_device.send_order(); // TODO: Criar uma abstração na fila para processar todas as ações. } diff --git a/l10n_br_pos_cfe/static/src/js/fiscal_cfe.js b/l10n_br_pos_cfe/static/src/js/fiscal_cfe.js index afa0696bfcf1..4bb751606f1e 100644 --- a/l10n_br_pos_cfe/static/src/js/fiscal_cfe.js +++ b/l10n_br_pos_cfe/static/src/js/fiscal_cfe.js @@ -59,18 +59,18 @@ odoo.define("l10n_br_pos_cfe.FiscalDocumentCFe", function (require) { let sendFiscalResultParsed = null; while (this.fiscal_queue.length > 0) { - var order = this.fiscal_queue.shift(); + const order_to_send = this.fiscal_queue.shift(); if ( - order.document_session_number && + order_to_send.document_session_number && this.pos.last_document_session_number === - order.document_session_number + order_to_send.document_session_number ) { // TODO: Melhorar esse método, consultando os dados da sessão em questão. return this.fiscalDocumentResultGenerator.IoTActionError( _t("Documento já transmitido.") ); } - var order_json = order.export_for_printing(); + var order_json = order_to_send.export_for_printing(); try { sendFiscalResult = await this.send_order_job(order_json); diff --git a/l10n_br_pos_cfe/static/src/js/models.js b/l10n_br_pos_cfe/static/src/js/models.js index 3c8f58928d6e..20a3b48a3e13 100644 --- a/l10n_br_pos_cfe/static/src/js/models.js +++ b/l10n_br_pos_cfe/static/src/js/models.js @@ -35,10 +35,8 @@ odoo.define("l10n_br_pos_cfe.models", function (require) { var _super_order = models.Order.prototype; models.Order = models.Order.extend({ - /** - * TODO: Verificar o que não deve ser copiado através do clone - * TODO: Verificar o funcionamento do export_as_JSON - */ + // TODO: Verificar o que não deve ser copiado através do clone + // TODO: Verificar o funcionamento do export_as_JSON _prepare_fiscal_json: function (json) { _super_order._prepare_fiscal_json.apply(this, arguments); diff --git a/l10n_br_purchase/__manifest__.py b/l10n_br_purchase/__manifest__.py index 659ef8a6e76d..7d66ebd17079 100644 --- a/l10n_br_purchase/__manifest__.py +++ b/l10n_br_purchase/__manifest__.py @@ -8,7 +8,7 @@ "author": "Akretion, Odoo Community Association (OCA)", "maintainers": ["renatonlima", "rvalyi"], "website": "https://github.com/OCA/l10n-brazil", - "version": "14.0.3.2.0", + "version": "14.0.3.2.1", "depends": ["purchase", "l10n_br_account"], "data": [ # Security diff --git a/l10n_br_purchase/tests/test_l10n_br_purchase.py b/l10n_br_purchase/tests/test_l10n_br_purchase.py index 7647cb8ffd26..62ee2b3ab2e8 100644 --- a/l10n_br_purchase/tests/test_l10n_br_purchase.py +++ b/l10n_br_purchase/tests/test_l10n_br_purchase.py @@ -20,7 +20,6 @@ class L10nBrPurchaseBaseTest(SavepointCase): @classmethod def setUpClass(cls): super().setUpClass() - cls.main_company = cls.env.ref("base.main_company") cls.company = cls.env.ref("l10n_br_base.empresa_lucro_presumido") cls.po_products = cls.env.ref("l10n_br_purchase.lp_po_only_products") # cls.po_services = cls.env.ref( @@ -433,7 +432,6 @@ def test_l10n_br_purchase_products(self): ) self._invoice_purchase_order(self.po_products) - self._change_user_company(self.main_company) def test_fields_view_get(self): """Test Purchase Order fields_view_get.""" @@ -536,4 +534,3 @@ def test_fields_freight_insurance_other_costs(self): 13.34, "Unexpected value for the field Other Values in Purchase Order.", ) - self._change_user_company(self.main_company) diff --git a/l10n_br_repair/__manifest__.py b/l10n_br_repair/__manifest__.py index 912c9eed2e07..fdef7700c297 100644 --- a/l10n_br_repair/__manifest__.py +++ b/l10n_br_repair/__manifest__.py @@ -5,7 +5,7 @@ "name": "Brazilian Localization Repair", "summary": """ Brazilian Localization Repair""", - "version": "14.0.1.1.0", + "version": "14.0.1.1.1", "category": "Localisation", "license": "AGPL-3", "author": "Escodoo, " "Odoo Community Association (OCA)", diff --git a/l10n_br_repair/tests/test_l10n_br_repair.py b/l10n_br_repair/tests/test_l10n_br_repair.py index 840d14d214d4..3b1364ec3c76 100644 --- a/l10n_br_repair/tests/test_l10n_br_repair.py +++ b/l10n_br_repair/tests/test_l10n_br_repair.py @@ -19,7 +19,6 @@ class L10nBrRepairBaseTest(SavepointCase): @classmethod def setUpClass(cls): super().setUpClass() - cls.main_company = cls.env.ref("base.main_company") cls.company = cls.env.ref("base.main_company") cls.so_products = cls.env.ref("l10n_br_repair.main_so_only_products") cls.so_services = cls.env.ref("l10n_br_repair.main_so_only_services") @@ -338,8 +337,6 @@ def test_l10n_br_repair_products(self): self.assertEqual(action_created_invoice["type"], "ir.actions.act_window") self.assertEqual(action_created_invoice["view_mode"], "form") - self._change_user_company(self.company) - def test_l10n_br_repair_services(self): """Test brazilian Repair Order with only Services.""" self._change_user_company(self.company) @@ -460,8 +457,6 @@ def test_l10n_br_repair_services(self): self.assertEqual(action_created_invoice["type"], "ir.actions.act_window") self.assertEqual(action_created_invoice["view_mode"], "form") - self._change_user_company(self.company) - def test_l10n_br_repair_products_services(self): """Test brazilian Repair Order with Product and Services.""" self._change_user_company(self.company) @@ -701,8 +696,6 @@ def test_l10n_br_repair_products_services(self): self.assertEqual(action_created_invoice["type"], "ir.actions.act_window") self.assertEqual(action_created_invoice["view_mode"], "tree,form") - self._change_user_company(self.main_company) - def test_action_views(self): act1 = self.so_services.action_created_invoice() self.assertTrue(act1) diff --git a/l10n_br_resource/__manifest__.py b/l10n_br_resource/__manifest__.py index f40a07f6b690..2ae44e331ea6 100644 --- a/l10n_br_resource/__manifest__.py +++ b/l10n_br_resource/__manifest__.py @@ -7,7 +7,7 @@ This module extend core resource to create important brazilian informations. Define a Brazilian calendar and some tools to compute dates used in financial and payroll modules""", - "version": "14.0.1.0.1", + "version": "14.0.1.0.2", "license": "AGPL-3", "author": "KMEE,Odoo Community Association (OCA)", "website": "https://github.com/OCA/l10n-brazil", diff --git a/l10n_br_resource/tests/test_resource_calendar.py b/l10n_br_resource/tests/test_resource_calendar.py index c15d04ee460f..0040768d60c0 100644 --- a/l10n_br_resource/tests/test_resource_calendar.py +++ b/l10n_br_resource/tests/test_resource_calendar.py @@ -5,6 +5,7 @@ from odoo import fields +# for some reason conversion to SavepointCase fails class TestResourceCalendar(test_common.SingleTransactionCase): def setUp(self): super(TestResourceCalendar, self).setUp() diff --git a/l10n_br_resource/tests/test_resource_calendar_2.py b/l10n_br_resource/tests/test_resource_calendar_2.py index ed5b11281acf..cc5394824b71 100644 --- a/l10n_br_resource/tests/test_resource_calendar_2.py +++ b/l10n_br_resource/tests/test_resource_calendar_2.py @@ -1,25 +1,25 @@ from datetime import date, datetime -from odoo.tests import common +from odoo.tests import SavepointCase -class TestResourceCalendar(common.TransactionCase): - def setUp(self): - super(TestResourceCalendar, self).setUp() - self.calendar = self.env["resource.calendar"].create({"name": "Test Calendar"}) +class TestResourceCalendar(SavepointCase): + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.calendar = cls.env["resource.calendar"].create({"name": "Test Calendar"}) - self.env["resource.calendar.leaves"].create( + cls.env["resource.calendar.leaves"].create( { "name": "Christmas", "date_from": date(2023, 12, 25), "date_to": date(2023, 12, 25), "leave_type": "F", - "calendar_id": self.calendar.id, + "calendar_id": cls.calendar.id, } ) def test_data_eh_feriado(self): - holiday_date = datetime(2023, 12, 25) result = self.calendar.data_eh_feriado(holiday_date) expected_result = True @@ -73,7 +73,6 @@ def test_data_eh_feriado_bancario(self): ) def test_data_eh_feriado_emendado(self): - reference_data = datetime(2023, 9, 7, 15, 0, 0) expected_result = False diff --git a/l10n_br_sale/__manifest__.py b/l10n_br_sale/__manifest__.py index f445d008d560..f04d35c9b661 100644 --- a/l10n_br_sale/__manifest__.py +++ b/l10n_br_sale/__manifest__.py @@ -7,7 +7,7 @@ "license": "AGPL-3", "author": "Akretion, " "Odoo Community Association (OCA)", "website": "https://github.com/OCA/l10n-brazil", - "version": "14.0.3.1.0", + "version": "14.0.3.1.1", "depends": ["sale_management", "l10n_br_account"], "data": [ # Data diff --git a/l10n_br_sale/tests/test_l10n_br_sale.py b/l10n_br_sale/tests/test_l10n_br_sale.py index 99148240f333..f723e8150309 100644 --- a/l10n_br_sale/tests/test_l10n_br_sale.py +++ b/l10n_br_sale/tests/test_l10n_br_sale.py @@ -390,7 +390,6 @@ def test_l10n_br_sale_products(self): ) self._invoice_sale_order(self.so_products) - self._change_user_company(self.main_company) def test_l10n_br_sale_services(self): """Test brazilian Sale Order with only Services.""" @@ -507,7 +506,6 @@ def test_l10n_br_sale_services(self): ) self._invoice_sale_order(self.so_services) - self._change_user_company(self.main_company) def test_l10n_br_sale_product_service(self): """Test brazilian Sale Order with Product and Service.""" @@ -521,7 +519,6 @@ def test_l10n_br_sale_product_service(self): self.so_product_service._create_invoices(final=True) # Devem existir duas Faturas/Documentos Fiscais self.assertEqual(2, self.so_product_service.invoice_count) - self._change_user_company(self.main_company) def test_fields_freight_insurance_other_costs(self): """Test fields Freight, Insurance and Other Costs when @@ -615,4 +612,3 @@ def test_fields_freight_insurance_other_costs(self): 11.43, "Unexpected value for the field Other Values in Sale line.", ) - self._change_user_company(self.main_company) diff --git a/l10n_br_zip/__manifest__.py b/l10n_br_zip/__manifest__.py index f5158fd7daec..63e8311ab039 100644 --- a/l10n_br_zip/__manifest__.py +++ b/l10n_br_zip/__manifest__.py @@ -9,7 +9,7 @@ "author": "Akretion, " "Odoo Community Association (OCA)", "maintainers": ["renatonlima"], "website": "https://github.com/OCA/l10n-brazil", - "version": "14.0.1.0.3", + "version": "14.0.1.0.4", "depends": ["l10n_br_base"], "data": [ "views/l10n_br_zip_view.xml", diff --git a/l10n_br_zip/tests/test_l10n_br_zip_res_company.py b/l10n_br_zip/tests/test_l10n_br_zip_res_company.py index cc20a933bd69..7942432897e3 100644 --- a/l10n_br_zip/tests/test_l10n_br_zip_res_company.py +++ b/l10n_br_zip/tests/test_l10n_br_zip_res_company.py @@ -4,37 +4,38 @@ from unittest import mock -from odoo.tests.common import TransactionCase +from odoo.tests import SavepointCase _module_ns = "odoo.addons.l10n_br_zip" _provider_class = _module_ns + ".models.l10n_br_zip" + ".L10nBrZip" -class L10nBRZipTest(TransactionCase): - def setUp(self): - super(L10nBRZipTest, self).setUp() +class L10nBRZipTest(SavepointCase): + @classmethod + def setUpClass(cls): + super().setUpClass() - self.zip_obj = self.env["l10n_br.zip"] - self.zip_1 = self.zip_obj.create( + cls.zip_obj = cls.env["l10n_br.zip"] + cls.zip_1 = cls.zip_obj.create( dict( zip_code="01310923", - city_id=self.env.ref("l10n_br_base.city_3550308").id, - state_id=self.env.ref("base.state_br_sp").id, - country_id=self.env.ref("base.br").id, + city_id=cls.env.ref("l10n_br_base.city_3550308").id, + state_id=cls.env.ref("base.state_br_sp").id, + country_id=cls.env.ref("base.br").id, street_name="Avenida Paulista 1842", street_type="Avenida", district="Bela Vista", ) ) - self.company = self.env.ref("base.main_company") - self.company_1 = self.env["res.company"].create( + cls.company = cls.env.ref("base.main_company") + cls.company_1 = cls.env["res.company"].create( dict( name="teste", street_name="paulista", district="Bela Vista", - country_id=self.env.ref("base.br").id, - state_id=self.env.ref("base.state_br_sp").id, - city_id=self.env.ref("l10n_br_base.city_3550308").id, + country_id=cls.env.ref("base.br").id, + state_id=cls.env.ref("base.state_br_sp").id, + city_id=cls.env.ref("l10n_br_base.city_3550308").id, ) ) diff --git a/l10n_br_zip/tests/test_l10n_br_zip_res_partner.py b/l10n_br_zip/tests/test_l10n_br_zip_res_partner.py index 41532d0eaed7..fc26864f2587 100644 --- a/l10n_br_zip/tests/test_l10n_br_zip_res_partner.py +++ b/l10n_br_zip/tests/test_l10n_br_zip_res_partner.py @@ -4,37 +4,38 @@ from unittest import mock -from odoo.tests.common import TransactionCase +from odoo.tests import SavepointCase _module_ns = "odoo.addons.l10n_br_zip" _provider_class = _module_ns + ".models.l10n_br_zip" + ".L10nBrZip" -class L10nBRZipTest(TransactionCase): - def setUp(self): - super().setUp() +class L10nBRZipTest(SavepointCase): + @classmethod + def setUpClass(cls): + super().setUpClass() - self.zip_obj = self.env["l10n_br.zip"] - self.zip_1 = self.zip_obj.create( + cls.zip_obj = cls.env["l10n_br.zip"] + cls.zip_1 = cls.zip_obj.create( dict( zip_code="01310923", - city_id=self.env.ref("l10n_br_base.city_3550308").id, - state_id=self.env.ref("base.state_br_sp").id, - country_id=self.env.ref("base.br").id, + city_id=cls.env.ref("l10n_br_base.city_3550308").id, + state_id=cls.env.ref("base.state_br_sp").id, + country_id=cls.env.ref("base.br").id, street_name="Avenida Paulista 1842", street_type="Avenida", district="Bela Vista", ) ) - self.res_partner = self.env.ref("l10n_br_base.res_partner_akretion") - self.res_partner_1 = self.env["res.partner"].create( + cls.res_partner = cls.env.ref("l10n_br_base.res_partner_akretion") + cls.res_partner_1 = cls.env["res.partner"].create( dict( name="teste", street_name="paulista", district="Bela Vista", - country_id=self.env.ref("base.br").id, - state_id=self.env.ref("base.state_br_sp").id, - city_id=self.env.ref("l10n_br_base.city_3550308").id, + country_id=cls.env.ref("base.br").id, + state_id=cls.env.ref("base.state_br_sp").id, + city_id=cls.env.ref("l10n_br_base.city_3550308").id, ) ) diff --git a/requirements.txt b/requirements.txt index d3bcb385e820..6e321eb81535 100644 --- a/requirements.txt +++ b/requirements.txt @@ -6,6 +6,7 @@ erpbrasil.edoc erpbrasil.edoc.pdf erpbrasil.transmissao nfelib +nfselib.paulistana num2words phonenumbers pycep_correios diff --git a/setup/_metapackage/VERSION.txt b/setup/_metapackage/VERSION.txt index 1253f71d4bad..df58c584067e 100644 --- a/setup/_metapackage/VERSION.txt +++ b/setup/_metapackage/VERSION.txt @@ -1 +1 @@ -14.0.20230412.0 \ No newline at end of file +14.0.20230605.0 \ No newline at end of file diff --git a/setup/_metapackage/setup.py b/setup/_metapackage/setup.py index 3b8faf487f8e..84cd8ba65824 100644 --- a/setup/_metapackage/setup.py +++ b/setup/_metapackage/setup.py @@ -15,6 +15,7 @@ 'odoo14-addon-l10n_br_account_payment_order', 'odoo14-addon-l10n_br_base', 'odoo14-addon-l10n_br_cnab_structure', + 'odoo14-addon-l10n_br_cnpj_search', 'odoo14-addon-l10n_br_coa', 'odoo14-addon-l10n_br_coa_generic', 'odoo14-addon-l10n_br_coa_simple', @@ -30,6 +31,7 @@ 'odoo14-addon-l10n_br_nfe', 'odoo14-addon-l10n_br_nfe_spec', 'odoo14-addon-l10n_br_nfse', + 'odoo14-addon-l10n_br_nfse_paulistana', 'odoo14-addon-l10n_br_portal', 'odoo14-addon-l10n_br_pos', 'odoo14-addon-l10n_br_pos_cfe', diff --git a/setup/l10n_br_cnpj_search/odoo/addons/l10n_br_cnpj_search b/setup/l10n_br_cnpj_search/odoo/addons/l10n_br_cnpj_search new file mode 120000 index 000000000000..536236282f13 --- /dev/null +++ b/setup/l10n_br_cnpj_search/odoo/addons/l10n_br_cnpj_search @@ -0,0 +1 @@ +../../../../l10n_br_cnpj_search \ No newline at end of file diff --git a/setup/l10n_br_cnpj_search/setup.py b/setup/l10n_br_cnpj_search/setup.py new file mode 100644 index 000000000000..28c57bb64031 --- /dev/null +++ b/setup/l10n_br_cnpj_search/setup.py @@ -0,0 +1,6 @@ +import setuptools + +setuptools.setup( + setup_requires=['setuptools-odoo'], + odoo_addon=True, +) diff --git a/setup/l10n_br_nfse_paulistana/odoo/addons/l10n_br_nfse_paulistana b/setup/l10n_br_nfse_paulistana/odoo/addons/l10n_br_nfse_paulistana new file mode 120000 index 000000000000..7f52f3facca5 --- /dev/null +++ b/setup/l10n_br_nfse_paulistana/odoo/addons/l10n_br_nfse_paulistana @@ -0,0 +1 @@ +../../../../l10n_br_nfse_paulistana \ No newline at end of file diff --git a/setup/l10n_br_nfse_paulistana/setup.py b/setup/l10n_br_nfse_paulistana/setup.py new file mode 100644 index 000000000000..28c57bb64031 --- /dev/null +++ b/setup/l10n_br_nfse_paulistana/setup.py @@ -0,0 +1,6 @@ +import setuptools + +setuptools.setup( + setup_requires=['setuptools-odoo'], + odoo_addon=True, +)