diff --git a/l10n_br_purchase_stock/README.rst b/l10n_br_purchase_stock/README.rst new file mode 100644 index 000000000000..0818c28d3bd5 --- /dev/null +++ b/l10n_br_purchase_stock/README.rst @@ -0,0 +1,193 @@ +===================================== +Brazilian Localization Purchase Stock +===================================== + +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:6b8a3d1ec5a68c3c9dde7b00462bd37b3236e4cf733c65001c6537ce5272020f + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |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/16.0/l10n_br_purchase_stock + :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-16-0/l10n-brazil-16-0-l10n_br_purchase_stock + :alt: Translate me on Weblate +.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png + :target: https://runboat.odoo-community.org/builds?repo=OCA/l10n-brazil&target_branch=16.0 + :alt: Try me on Runboat + +|badge1| |badge2| |badge3| |badge4| |badge5| + +O módulo estende o +`l10n_br_stock_account `__ +para ser usado em **Compras** incluindo a possibilidade de criar a +Fatura de um Pedido de Compra a partir da Ordem de Recebimento/Stock +Picking, ao definir a **Política de Faturamento de Compras** como: + +- **Pedido de Compra / Purchase Order**, mantém o comportamento padrão e + a criação da Fatura será feita a partir do **Pedido de + Compra/purchase.order** + +- **Ordem de Recebimento / Stock Picking**, desabilita a criação de + Faturas a partir do **Pedido de Compra** para os casos onde o 'Tipo do + Produto' é 'Produto', mas no caso de ser 'Serviço' ainda será possível + criar a partir do **Pedido de Compra** + +A implementação foi feita para que a Fatura criada a partir do +'stock.picking' seja a mais similar possível com a que é criada a partir +do 'purchase.order', e ao usar os métodos do módulo **purchase** para +obter os Dados usados na criação de uma nova Fatura isso também acaba +evitando a necessidade de 'glue modules' (pequenos módulos criados +apenas para evitar dependencias indiretas), então quando qualquer módulo +incluir um novo campo na Fatura criada a partir do Pedido de Compra esse +novo campo também deverá ser incluido na Fatura criada a partir do +'stock.picking', por exemplo o módulo +`account_payment_purchase `__. + +**Table of contents** + +.. contents:: + :local: + +Installation +============ + +O módulo depende do: + +- l10n_br_purchase +- l10n_br_stock_account + +Configuration +============= + +Defina a **Politica de Faturamento de Compras**, se a Fatura deverá ser +criada a partir do **Pedido de Compra/purchase.order** ou da **Ordem de +Recebimento/stock.picking** isso pode ser feito em: + +**Definições > Usuários e Empresas > Empresas** Selecione a Empresa, aba +**Fiscal** e depois na aba **Compras** + +ou + +**Compras > Configuração > Definições** Na seção **Faturamento** + +Usage +===== + +Se a **Política de Faturamento de Compras** estiver definida como +**Ordem de Recebimento/Stock Picking** é possível criar tanto uma Fatura +para uma Ordem de Recebimento quanto uma Fatura para diversas Ordens de +Recebimentos: + +- Caso uma Fatura para uma Ordem de Recebimento + +Na **Ordem de Recebimento**, referente ao **Pedido de Compra**, depois +de **Validar** essa Ordem deverá aparecer o botão **Criar Fatura** onde +ao clicar será possível criar a Fatura, nesse caso o campo **Grupo** +estará **Coleta**. + +- Caso uma Fatura para diversas Ordens de Recebimento + +Por estender o **l10n_br_stock_account** é possível criar uma **Fatura +Agrupada**, para isso é preciso ir na 'Visão Lista/Tree View' selecionar +as **Ordens de Recebimentos**, criadas a partir de diversos **Pedidos de +Compras** e que possuem o mesmo **Parceiro/Fornecedor**, clicar no botão +**Ação** em seguida **Criar rascunho das faturas** e no campo **Grupo** +selecionar **Parceiro** para assim criar apenas uma Fatura ou seja N +Pedidos de Compras com N Ordens de Recebimento de um mesmo Fornecedor +podem ter apenas uma Fatura/Documento Fiscal relacionado. + +Known issues / Roadmap +====================== + + + +Changelog +========= + +16.0.1.0.0 (2025-01-22) +----------------------- + +- [MIG] Migração para a v16 + +14.0.1.0.0 (2022-09-16) +----------------------- + +- [MIG] Migração para a v14 + +12.0.1.0.0 (2020-04-27) +----------------------- + +- [REF] Separado o módulo l10n_br_purchase em dois l10n_br_purchase e + l10n_br_purchase_stock. + +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 to smash it by providing a detailed and welcomed +`feedback `_. + +Do not contact contributors directly about support or help with technical issues. + +Credits +======= + +Authors +------- + +* Akretion + +Contributors +------------ + +- `Akretion `__: + + - Renato Lima + - Magno Costa + +Other credits +------------- + +The development of this module has been financially supported by: + +- AKRETION LTDA - `www.akretion.com `__ + +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-renatonlima| image:: https://github.com/renatonlima.png?size=40px + :target: https://github.com/renatonlima + :alt: renatonlima +.. |maintainer-mbcosta| image:: https://github.com/mbcosta.png?size=40px + :target: https://github.com/mbcosta + :alt: mbcosta + +Current `maintainers `__: + +|maintainer-renatonlima| |maintainer-mbcosta| + +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_purchase_stock/__init__.py b/l10n_br_purchase_stock/__init__.py new file mode 100644 index 000000000000..7084110eaedd --- /dev/null +++ b/l10n_br_purchase_stock/__init__.py @@ -0,0 +1,5 @@ +# Copyright (C) 2009 Renato Lima - Akretion +# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html + +from . import models +from . import wizards diff --git a/l10n_br_purchase_stock/__manifest__.py b/l10n_br_purchase_stock/__manifest__.py new file mode 100644 index 000000000000..45ccfb7e75e1 --- /dev/null +++ b/l10n_br_purchase_stock/__manifest__.py @@ -0,0 +1,27 @@ +# Copyright (C) 2009 Renato Lima - Akretion +# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html + +{ + "name": "Brazilian Localization Purchase Stock", + "license": "AGPL-3", + "category": "Localisation", + "author": "Akretion, Odoo Community Association (OCA)", + "maintainers": ["renatonlima", "mbcosta"], + "website": "https://github.com/OCA/l10n-brazil", + "version": "16.0.1.0.0", + "depends": [ + "l10n_br_purchase", + "l10n_br_stock_account", + ], + "data": [ + # Views + "views/purchase_order.xml", + "views/res_config_settings.xml", + "views/res_company_view.xml", + ], + "demo": [ + "demo/purchase_order.xml", + ], + "installable": True, + "auto_install": True, +} diff --git a/l10n_br_purchase_stock/demo/purchase_order.xml b/l10n_br_purchase_stock/demo/purchase_order.xml new file mode 100644 index 000000000000..d5760ef7634d --- /dev/null +++ b/l10n_br_purchase_stock/demo/purchase_order.xml @@ -0,0 +1,433 @@ + + + + + + stock_picking + + + + + + + + + + + + + + + Main l10n_br_purchase_stock + + + draft + + + + TESTE - TERMOS E CONDIÇÕES + TESTE - CUSTOMER ADDITIONAL DATA + TESTE - FISCAL ADDITIONAL DATA + 0 + + + + + + + + + Office Chair Black + + 4 + + 250 + + + + 999999 + 001 + Teste - Additional Data + 10 + 10 + 10 + + + + + + + + + Gaveta Preta + + 2 + + 500 + + + + 999999 + 002 + Teste - Additional Data + 10 + 10 + 10 + + + + + + + + + + TEST SECTION 1 + line_section + 0 + + + + + + TEST NOTE 1 + line_note + 0 + + + + + + 2 + + 500 + + + + 999999 + 003 + Teste - Additional Data + 10 + 10 + 10 + + + + + + + + + Main l10n_br_purchase_stock - teste agrupamento + + + draft + + + + TESTE - TERMOS E CONDIÇÕES + TESTE - CUSTOMER ADDITIONAL DATA + TESTE - FISCAL ADDITIONAL DATA + 0 + + + + + + + + + Office Chair Black + + 4 + + 250 + + + + 0000001 + 001 + Teste - Additional Data + 10 + 10 + 10 + + + + + + + + + Gaveta Preta + + 2 + + 500 + + + + 0000001 + 002 + Teste - Additional Data + 10 + 10 + 10 + + + + + + + + + + TEST SECTION 2 + line_section + 0 + + + + + + TEST NOTE 2 + line_note + 0 + + + + + + 2 + + 500 + + + + 999999 + 003 + Teste - Additional Data + 10 + 10 + 10 + + + + + + + + + + l10n_br_purchase_stock - somente produtos + + + draft + + + + TESTE - TERMOS E CONDIÇÕES + TESTE - CUSTOMER ADDITIONAL DATA + TESTE - FISCAL ADDITIONAL DATA + 0 + + + + + + + + + Office Chair Black + + 4 + + 250 + + + + 999999 + 001 + Teste - Additional Data + 10 + 10 + 10 + 0 + + + + + + + + + Gaveta Preta + + 2 + + 500 + + + + 999999 + 002 + Teste - Additional Data + 10 + 10 + 10 + + + + + + + + + + purchase + + + + Main l10n_br_purchase_stock - Serviços + + + draft + + + + 0 + + + + + + + + + Desenvolvimento Odoo + + 1 + + 250 + + + + 0000001 + 001 + + + + + + + + + Main l10n_br_purchase_stock - Serviço e Produto + + + draft + + + + 0 + + + + + + + + + Desenvolvimento Odoo + + 1 + + 250 + + + + 0000001 + 001 + + + + + + + + + Gaveta Preta + + 2 + + 500 + + + + 0000001 + 002 + Teste - Additional Data + 10 + 10 + 10 + + + + + + + diff --git a/l10n_br_purchase_stock/i18n/l10n_br_purchase_stock.pot b/l10n_br_purchase_stock/i18n/l10n_br_purchase_stock.pot new file mode 100644 index 000000000000..0d6f2d1b56f2 --- /dev/null +++ b/l10n_br_purchase_stock/i18n/l10n_br_purchase_stock.pot @@ -0,0 +1,137 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * l10n_br_purchase_stock +# +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_purchase_stock +#: model_terms:ir.ui.view,arch_db:l10n_br_purchase_stock.l10n_br_purchase_res_config_settings_form +msgid "" +"" +msgstr "" + +#. module: l10n_br_purchase_stock +#: model:ir.model.fields,field_description:l10n_br_purchase_stock.field_purchase_order__button_create_invoice_invisible +msgid "Button Create Invoice Invisible" +msgstr "" + +#. module: l10n_br_purchase_stock +#: model:ir.model,name:l10n_br_purchase_stock.model_res_company +msgid "Companies" +msgstr "" + +#. module: l10n_br_purchase_stock +#: model:ir.model,name:l10n_br_purchase_stock.model_res_config_settings +msgid "Config Settings" +msgstr "" + +#. module: l10n_br_purchase_stock +#: model:ir.model.fields,field_description:l10n_br_purchase_stock.field_res_company__purchase_fiscal_operation_id +msgid "Default Fiscal Operation for Purchase" +msgstr "" + +#. module: l10n_br_purchase_stock +#: model:ir.model.fields,field_description:l10n_br_purchase_stock.field_purchase_order__display_name +#: model:ir.model.fields,field_description:l10n_br_purchase_stock.field_purchase_order_line__display_name +#: model:ir.model.fields,field_description:l10n_br_purchase_stock.field_res_company__display_name +#: model:ir.model.fields,field_description:l10n_br_purchase_stock.field_res_config_settings__display_name +#: model:ir.model.fields,field_description:l10n_br_purchase_stock.field_stock_invoice_onshipping__display_name +#: model:ir.model.fields,field_description:l10n_br_purchase_stock.field_stock_move__display_name +#: model:ir.model.fields,field_description:l10n_br_purchase_stock.field_stock_picking__display_name +#: model:ir.model.fields,field_description:l10n_br_purchase_stock.field_stock_rule__display_name +msgid "Display Name" +msgstr "" + +#. module: l10n_br_purchase_stock +#: model:ir.model.fields,field_description:l10n_br_purchase_stock.field_purchase_order__id +#: model:ir.model.fields,field_description:l10n_br_purchase_stock.field_purchase_order_line__id +#: model:ir.model.fields,field_description:l10n_br_purchase_stock.field_res_company__id +#: model:ir.model.fields,field_description:l10n_br_purchase_stock.field_res_config_settings__id +#: model:ir.model.fields,field_description:l10n_br_purchase_stock.field_stock_invoice_onshipping__id +#: model:ir.model.fields,field_description:l10n_br_purchase_stock.field_stock_move__id +#: model:ir.model.fields,field_description:l10n_br_purchase_stock.field_stock_picking__id +#: model:ir.model.fields,field_description:l10n_br_purchase_stock.field_stock_rule__id +msgid "ID" +msgstr "" + +#. module: l10n_br_purchase_stock +#: model_terms:ir.ui.view,arch_db:l10n_br_purchase_stock.l10n_br_purchase_res_config_settings_form +msgid "" +"If enabled, activates 3-way matching on vendor bills : the items must be " +"received in order to pay the invoice." +msgstr "" + +#. module: l10n_br_purchase_stock +#: model:ir.model.fields,field_description:l10n_br_purchase_stock.field_purchase_order____last_update +#: model:ir.model.fields,field_description:l10n_br_purchase_stock.field_purchase_order_line____last_update +#: model:ir.model.fields,field_description:l10n_br_purchase_stock.field_res_company____last_update +#: model:ir.model.fields,field_description:l10n_br_purchase_stock.field_res_config_settings____last_update +#: model:ir.model.fields,field_description:l10n_br_purchase_stock.field_stock_invoice_onshipping____last_update +#: model:ir.model.fields,field_description:l10n_br_purchase_stock.field_stock_move____last_update +#: model:ir.model.fields,field_description:l10n_br_purchase_stock.field_stock_picking____last_update +#: model:ir.model.fields,field_description:l10n_br_purchase_stock.field_stock_rule____last_update +msgid "Last Modified on" +msgstr "" + +#. module: l10n_br_purchase_stock +#: model:ir.model.fields,field_description:l10n_br_purchase_stock.field_purchase_order__purchase_invoicing_policy +#: model:ir.model.fields,field_description:l10n_br_purchase_stock.field_res_company__purchase_invoicing_policy +#: model:ir.model.fields,field_description:l10n_br_purchase_stock.field_res_config_settings__purchase_invoicing_policy +msgid "Purchase Invoicing Policy" +msgstr "" + +#. module: l10n_br_purchase_stock +#: code:addons/l10n_br_purchase_stock/models/res_company.py:0 +#: model:ir.model,name:l10n_br_purchase_stock.model_purchase_order +#: model:ir.model.fields.selection,name:l10n_br_purchase_stock.selection__res_company__purchase_invoicing_policy__purchase_order +#, python-format +msgid "Purchase Order" +msgstr "" + +#. module: l10n_br_purchase_stock +#: model:ir.model,name:l10n_br_purchase_stock.model_purchase_order_line +msgid "Purchase Order Line" +msgstr "" + +#. module: l10n_br_purchase_stock +#: model_terms:ir.ui.view,arch_db:l10n_br_purchase_stock.l10n_br_purchase_res_config_settings_form +msgid "Select invoiced based on order or picking" +msgstr "" + +#. module: l10n_br_purchase_stock +#: model:ir.model,name:l10n_br_purchase_stock.model_stock_invoice_onshipping +msgid "Stock Invoice Onshipping" +msgstr "" + +#. module: l10n_br_purchase_stock +#: model:ir.model,name:l10n_br_purchase_stock.model_stock_move +msgid "Stock Move" +msgstr "" + +#. module: l10n_br_purchase_stock +#: code:addons/l10n_br_purchase_stock/models/res_company.py:0 +#: model:ir.model.fields.selection,name:l10n_br_purchase_stock.selection__res_company__purchase_invoicing_policy__stock_picking +#, python-format +msgid "Stock Picking" +msgstr "" + +#. module: l10n_br_purchase_stock +#: model:ir.model,name:l10n_br_purchase_stock.model_stock_rule +msgid "Stock Rule" +msgstr "" + +#. module: l10n_br_purchase_stock +#: model:ir.model,name:l10n_br_purchase_stock.model_stock_picking +msgid "Transfer" +msgstr "" diff --git a/l10n_br_purchase_stock/i18n/pt_BR.po b/l10n_br_purchase_stock/i18n/pt_BR.po new file mode 100644 index 000000000000..b4ec80aff271 --- /dev/null +++ b/l10n_br_purchase_stock/i18n/pt_BR.po @@ -0,0 +1,371 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * purchase_stock +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 12.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2021-02-02 02:25+0000\n" +"PO-Revision-Date: 2024-05-20 22:42+0000\n" +"Last-Translator: Rodrigo Macedo \n" +"Language-Team: \n" +"Language: pt_BR\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: nplurals=2; plural=n > 1;\n" +"X-Generator: Weblate 4.17\n" + +#. module: l10n_br_purchase_stock +#: model_terms:ir.ui.view,arch_db:l10n_br_purchase_stock.l10n_br_purchase_res_config_settings_form +msgid "" +"" +msgstr "" +"" + +#. module: l10n_br_purchase_stock +#: model:ir.model.fields,field_description:l10n_br_purchase_stock.field_purchase_order__button_create_invoice_invisible +msgid "Button Create Invoice Invisible" +msgstr "Botão Criar fatura invisível" + +#. module: l10n_br_purchase_stock +#: model:ir.model,name:l10n_br_purchase_stock.model_res_company +msgid "Companies" +msgstr "Empresas" + +#. module: l10n_br_purchase_stock +#: model:ir.model,name:l10n_br_purchase_stock.model_res_config_settings +msgid "Config Settings" +msgstr "Ajustes de Configurações" + +#. module: l10n_br_purchase_stock +#: model:ir.model.fields,field_description:l10n_br_purchase_stock.field_res_company__purchase_fiscal_operation_id +msgid "Default Fiscal Operation for Purchase" +msgstr "Operação Fiscal Padrão para Compra" + +#. module: l10n_br_purchase_stock +#: model:ir.model.fields,field_description:l10n_br_purchase_stock.field_purchase_order__display_name +#: model:ir.model.fields,field_description:l10n_br_purchase_stock.field_purchase_order_line__display_name +#: model:ir.model.fields,field_description:l10n_br_purchase_stock.field_res_company__display_name +#: model:ir.model.fields,field_description:l10n_br_purchase_stock.field_res_config_settings__display_name +#: model:ir.model.fields,field_description:l10n_br_purchase_stock.field_stock_invoice_onshipping__display_name +#: model:ir.model.fields,field_description:l10n_br_purchase_stock.field_stock_move__display_name +#: model:ir.model.fields,field_description:l10n_br_purchase_stock.field_stock_picking__display_name +#: model:ir.model.fields,field_description:l10n_br_purchase_stock.field_stock_rule__display_name +msgid "Display Name" +msgstr "Nome Exibido" + +#. module: l10n_br_purchase_stock +#: model:ir.model.fields,field_description:l10n_br_purchase_stock.field_purchase_order__id +#: model:ir.model.fields,field_description:l10n_br_purchase_stock.field_purchase_order_line__id +#: model:ir.model.fields,field_description:l10n_br_purchase_stock.field_res_company__id +#: model:ir.model.fields,field_description:l10n_br_purchase_stock.field_res_config_settings__id +#: model:ir.model.fields,field_description:l10n_br_purchase_stock.field_stock_invoice_onshipping__id +#: model:ir.model.fields,field_description:l10n_br_purchase_stock.field_stock_move__id +#: model:ir.model.fields,field_description:l10n_br_purchase_stock.field_stock_picking__id +#: model:ir.model.fields,field_description:l10n_br_purchase_stock.field_stock_rule__id +msgid "ID" +msgstr "ID" + +#. module: l10n_br_purchase_stock +#: model_terms:ir.ui.view,arch_db:l10n_br_purchase_stock.l10n_br_purchase_res_config_settings_form +msgid "" +"If enabled, activates 3-way matching on vendor bills : the items must be " +"received in order to pay the invoice." +msgstr "" +"Se ativado, ativa a conciliação de 3 vias nas faturas de fornecedores: os " +"itens devem ser recebidos para pagar a fatura." + +#. module: l10n_br_purchase_stock +#: model:ir.model.fields,field_description:l10n_br_purchase_stock.field_purchase_order____last_update +#: model:ir.model.fields,field_description:l10n_br_purchase_stock.field_purchase_order_line____last_update +#: model:ir.model.fields,field_description:l10n_br_purchase_stock.field_res_company____last_update +#: model:ir.model.fields,field_description:l10n_br_purchase_stock.field_res_config_settings____last_update +#: model:ir.model.fields,field_description:l10n_br_purchase_stock.field_stock_invoice_onshipping____last_update +#: model:ir.model.fields,field_description:l10n_br_purchase_stock.field_stock_move____last_update +#: model:ir.model.fields,field_description:l10n_br_purchase_stock.field_stock_picking____last_update +#: model:ir.model.fields,field_description:l10n_br_purchase_stock.field_stock_rule____last_update +msgid "Last Modified on" +msgstr "Última Modificação em" + +#. module: l10n_br_purchase_stock +#: model:ir.model.fields,field_description:l10n_br_purchase_stock.field_purchase_order__purchase_invoicing_policy +#: model:ir.model.fields,field_description:l10n_br_purchase_stock.field_res_company__purchase_invoicing_policy +#: model:ir.model.fields,field_description:l10n_br_purchase_stock.field_res_config_settings__purchase_invoicing_policy +msgid "Purchase Invoicing Policy" +msgstr "" + +#. module: l10n_br_purchase_stock +#: code:addons/l10n_br_purchase_stock/models/res_company.py:0 +#: model:ir.model,name:l10n_br_purchase_stock.model_purchase_order +#: model:ir.model.fields.selection,name:l10n_br_purchase_stock.selection__res_company__purchase_invoicing_policy__purchase_order +#, python-format +msgid "Purchase Order" +msgstr "Pedido de Compra" + +#. module: l10n_br_purchase_stock +#: model:ir.model,name:l10n_br_purchase_stock.model_purchase_order_line +msgid "Purchase Order Line" +msgstr "Linha de Pedido de Compra" + +#. module: l10n_br_purchase_stock +#: model_terms:ir.ui.view,arch_db:l10n_br_purchase_stock.l10n_br_purchase_res_config_settings_form +msgid "Select invoiced based on order or picking" +msgstr "Selecione faturas com base no pedido ou na separação de mercadorias" + +#. module: l10n_br_purchase_stock +#: model:ir.model,name:l10n_br_purchase_stock.model_stock_invoice_onshipping +msgid "Stock Invoice Onshipping" +msgstr "Faturamento ao Enviar" + +#. module: l10n_br_purchase_stock +#: model:ir.model,name:l10n_br_purchase_stock.model_stock_move +msgid "Stock Move" +msgstr "Movimento de Estoque" + +#. module: l10n_br_purchase_stock +#: code:addons/l10n_br_purchase_stock/models/res_company.py:0 +#: model:ir.model.fields.selection,name:l10n_br_purchase_stock.selection__res_company__purchase_invoicing_policy__stock_picking +#, python-format +msgid "Stock Picking" +msgstr "Separação de Estoque" + +#. module: l10n_br_purchase_stock +#: model:ir.model,name:l10n_br_purchase_stock.model_stock_rule +msgid "Stock Rule" +msgstr "Regra de Estoque" + +#. module: l10n_br_purchase_stock +#: model:ir.model,name:l10n_br_purchase_stock.model_stock_picking +msgid "Transfer" +msgstr "Transferir" + +#~ msgid "Purchase Create Invoice Policy" +#~ msgstr "Política de Criação de Faturas de Compra" + +#~ msgid "Purchase Orders" +#~ msgstr "Ordens de compra" + +#~ msgid "Shipping address:" +#~ msgstr "Endereço de envio:" + +#~ msgid "Action" +#~ msgstr "Ação" + +#~ msgid "Buy" +#~ msgstr "Comprar" + +#~ msgid "Buy rule" +#~ msgstr "Regra de Compra" + +#~ msgid "Buy to Resupply" +#~ msgstr "Compre para reabastecer" + +#~ msgid "Created Purchase Order Line" +#~ msgstr "Linha de pedidos de compra criada" + +#~ msgid "Deliver To" +#~ msgstr "Entregar para" + +#~ msgid "" +#~ "Depending on the modules installed, this will allow you to define the " +#~ "route of the product: whether it will be bought, manufactured, MTO, etc." +#~ msgstr "" +#~ "Dependendo dos módulos instalados, isso permitirá definir a rota do " +#~ "produto: se ele será comprado, fabricado, MTO, etc." + +#~ msgid "Destination Location Type" +#~ msgstr "Tipo de localização de destino" + +#~ msgid "Downstream Moves" +#~ msgstr "Movimentos Downstream" + +#~ msgid "Exception(s) occurred on the purchase order(s):" +#~ msgstr "Exceções(s) ocorreram na ordem de compra:" + +#~ msgid "Exception(s):" +#~ msgstr "Exceções:" + +#~ msgid "Incoming Products" +#~ msgstr "Recebimentos de Produtos" + +#~ msgid "Incoming Shipments" +#~ msgstr "Remessas Recebidas" + +#~ msgid "" +#~ "International Commercial Terms are a series of predefined commercial " +#~ "terms used in international transactions." +#~ msgstr "" +#~ "International Commercial Terms são uma série de termos comerciais pré-" +#~ "definidos utilizados em transações internacionais." + +#~ msgid "Invoice" +#~ msgstr "Fatura" + +#~ msgid "Invoice Line" +#~ msgstr "Linha da Fatura" + +#~ msgid "Is Shipped" +#~ msgstr "É enviado" + +#~ msgid "Is the Sale Module Installed" +#~ msgstr "O módulo de venda está instalado" + +#~ msgid "Logistics" +#~ msgstr "Logística" + +#~ msgid "Lot/Serial" +#~ msgstr "Lote/Serie" + +#~ msgid "Manual actions may be needed." +#~ msgstr "Ações manuais podem ser necessárias." + +#~ msgid "" +#~ "Margin of error for vendor lead times. When the system generates Purchase " +#~ "Orders for reordering products,they will be scheduled that many days " +#~ "earlier to cope with unexpected vendor delays." +#~ msgstr "" +#~ "Margem de erro para tempo de entrega de fornecedor. Quando o sistema " +#~ "gerar Ordens de Compra para recomprar produtos, eles serão agendados " +#~ "nesses número de dias anteriormente para levar em conta demoras " +#~ "inesperadas de fornecedores." + +#~ msgid "Minimum Inventory Rule" +#~ msgstr "Regra de Estoque Mínimo" + +#~ msgid "Move forward expected delivery dates by" +#~ msgstr "Avance as datas de entrega previstas por" + +#~ msgid "Next transfer(s) impacted:" +#~ msgstr "Próximas transferências afetadas:" + +#~ msgid "Orderpoint" +#~ msgstr "Ponto de pedido" + +#~ msgid "Picking count" +#~ msgstr "Resultado da Separação" + +#~ msgid "Procurement Group" +#~ msgstr "Grupo de Aquisição" + +#~ msgid "Product Template" +#~ msgstr "Modelo de Produto" + +#~ msgid "Pull & Push" +#~ msgstr "Puxe empurre" + +#~ msgid "Pull From" +#~ msgstr "Puxar de" + +#~ msgid "Purchase Orders" +#~ msgstr "Pedidos de Compra" + +#~ msgid "Purchase Report" +#~ msgstr "Relatório de Compra" + +#~ msgid "Purchase order count" +#~ msgstr "Contagem Ordem de Compra" + +#~ msgid "Push To" +#~ msgstr "Empurre para" + +#~ msgid "Receipt" +#~ msgstr "Recebimento" + +#~ msgid "Receive Products" +#~ msgstr "Receber Produtos" + +#~ msgid "Receptions" +#~ msgstr "Recebimentos" + +#~ msgid "Request your vendors to deliver to your customers" +#~ msgstr "Solicite que seus fornecedores entreguem aos seus clientes" + +#~ msgid "Reservation" +#~ msgstr "Reserva" + +#~ msgid "Routes" +#~ msgstr "Rotas" + +#~ msgid "Schedule receivings earlier to avoid delays" +#~ msgstr "Agende recebimentos com antecedência para evitar atrasos" + +#~ msgid "Stock Moves" +#~ msgstr "Movimentos de Estoque" + +#~ msgid "Stock rule report" +#~ msgstr "Relatório de regra de estoque" + +#~ msgid "Technical field used to display the Drop Ship Address" +#~ msgstr "Campo técnico usado para exibir o endereço de envio direto" + +#~ msgid "" +#~ "The quantities on your purchase order indicate less than billed. You " +#~ "should ask for a refund. " +#~ msgstr "" +#~ "As quantidades em seu pedido de compra indicam menos do que faturado. " +#~ "Você deve pedir um reembolso." + +#~ msgid "" +#~ "This adds a dropshipping route to apply on products in order to request " +#~ "your vendors to deliver to your customers. A product to dropship will " +#~ "generate a purchase request for quotation once the sales order confirmed. " +#~ "This is a on-demand flow. The requested delivery address will be the " +#~ "customer delivery address and not your warehouse." +#~ msgstr "" +#~ "Isso adiciona uma rota de dropshipping para aplicar em produtos a fim de " +#~ "solicitar que seus fornecedores entreguem aos seus clientes. Um produto " +#~ "para dropship gerará uma solicitação de compra para cotação assim que o " +#~ "pedido de venda for confirmado. Este é um fluxo sob demanda. O endereço " +#~ "de entrega solicitado será o endereço de entrega do cliente e não o seu " +#~ "armazém." + +#~ msgid "This will determine operation type of incoming shipment" +#~ msgstr "Isso determinará o tipo de operação da remessa de entrada" + +#~ msgid "" +#~ "Unable to cancel purchase order %s as some receptions have already been " +#~ "done." +#~ msgstr "" +#~ "ão é possível cancelar o pedido %s como algumas recepções já feitas." + +#~ msgid "Warehouse" +#~ msgstr "Armazém" + +#~ msgid "When products are bought, they can be delivered to this warehouse" +#~ msgstr "" +#~ "Quando os produtos são comprados , eles podem ser entregues nesse armazém" + +#~ msgid "" +#~ "When products are needed in %s,
a request for quotation is " +#~ "created to fulfill the need." +#~ msgstr "" +#~ "Quando os produtos são necessários %s,
uma solicitação de " +#~ "cotação é criada para atender à necessidade." + +#~ msgid "" +#~ "You cannot decrease the ordered quantity below the received quantity.\n" +#~ "Create a return first." +#~ msgstr "" +#~ "Você não pode diminuir a quantidade solicitada abaixo da quantidade " +#~ "recebida.\n" +#~ "Crie uma devolução primeiro." + +#~ msgid "You must set a Vendor Location for this partner %s" +#~ msgstr "" +#~ "Você deve definir uma localização de fornecedor para este parceiro %s" + +#~ msgid "days" +#~ msgstr "dias" + +#~ msgid "of" +#~ msgstr "de" + +#~ msgid "ordered instead of" +#~ msgstr "ordenado em vez de" diff --git a/l10n_br_purchase_stock/models/__init__.py b/l10n_br_purchase_stock/models/__init__.py new file mode 100644 index 000000000000..5706a41f13e4 --- /dev/null +++ b/l10n_br_purchase_stock/models/__init__.py @@ -0,0 +1,7 @@ +from . import res_company +from . import purchase_order +from . import purchase_order_line +from . import stock_rule +from . import res_config_settings +from . import stock_move +from . import stock_picking diff --git a/l10n_br_purchase_stock/models/purchase_order.py b/l10n_br_purchase_stock/models/purchase_order.py new file mode 100644 index 000000000000..5101527ab2a6 --- /dev/null +++ b/l10n_br_purchase_stock/models/purchase_order.py @@ -0,0 +1,51 @@ +# Copyright (C) 2009 Renato Lima - Akretion, Gabriel C. Stabel +# Copyright (C) 2012 Raphaël Valyi - Akretion +# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html + +from odoo import api, fields, models + + +class PurchaseOrder(models.Model): + _inherit = "purchase.order" + + purchase_invoicing_policy = fields.Selection( + related="company_id.purchase_invoicing_policy", + ) + + # Make Invisible Invoice Button + button_create_invoice_invisible = fields.Boolean( + compute="_compute_get_button_create_invoice_invisible" + ) + + @api.depends("state", "invoice_status") + def _compute_get_button_create_invoice_invisible(self): + for record in self: + button_create_invoice_invisible = False + + # Somente depois do Pedido confirmado o botão pode aparecer + if ( + record.state not in ("purchase", "done") + or record.invoice_status != "to invoice" + or not record.order_line + ): + button_create_invoice_invisible = True + else: + if record.purchase_invoicing_policy == "stock_picking": + # A criação de Fatura de Serviços deve ser possível via Pedido + if not any( + line.product_id.type == "service" for line in record.order_line + ): + button_create_invoice_invisible = True + + record.button_create_invoice_invisible = button_create_invoice_invisible + + @api.model + def _prepare_picking(self): + values = super()._prepare_picking() + if self.fiscal_operation_id: + values.update(self._prepare_br_fiscal_dict()) + else: + values["fiscal_operation_id"] = False + if self.company_id.purchase_invoicing_policy == "stock_picking": + values["invoice_state"] = "2binvoiced" + return values diff --git a/l10n_br_purchase_stock/models/purchase_order_line.py b/l10n_br_purchase_stock/models/purchase_order_line.py new file mode 100644 index 000000000000..054aa9c06add --- /dev/null +++ b/l10n_br_purchase_stock/models/purchase_order_line.py @@ -0,0 +1,37 @@ +# Copyright (C) 2009 Renato Lima - Akretion, Gabriel C. Stabel +# Copyright (C) 2012 Raphaël Valyi - Akretion +# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html + +from odoo import api, models + + +class PurchaseOrderLine(models.Model): + _inherit = "purchase.order.line" + + def _prepare_stock_moves(self, picking): + """Prepare the stock moves data for one order line. + This function returns a list of + dictionary ready to be used in stock.move's create() + """ + values = super()._prepare_stock_moves(picking) + for v in values: + if self.order_id.fiscal_operation_id: + v.update(self._prepare_br_fiscal_dict()) + if self.order_id.purchase_invoicing_policy == "stock_picking": + v["invoice_state"] = "2binvoiced" + return values + + @api.model + def _prepare_purchase_order_line_from_procurement( + self, product_id, product_qty, product_uom, company_id, values, po + ): + res = super()._prepare_purchase_order_line_from_procurement( + product_id, product_qty, product_uom, company_id, values, po + ) + if values.get("move_dest_ids"): + for move in values.get("move_dest_ids"): + move.fiscal_operation_id = ( + move.fiscal_operation_id.inverse_fiscal_operation_id.id + ) + + return res diff --git a/l10n_br_purchase_stock/models/res_company.py b/l10n_br_purchase_stock/models/res_company.py new file mode 100644 index 000000000000..cb05f0f29615 --- /dev/null +++ b/l10n_br_purchase_stock/models/res_company.py @@ -0,0 +1,22 @@ +# Copyright (C) 2009 Renato Lima - Akretion +# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html + +from odoo import _, fields, models + + +class Company(models.Model): + _inherit = "res.company" + + purchase_fiscal_operation_id = fields.Many2one( + comodel_name="l10n_br_fiscal.operation", + string="Default Fiscal Operation for Purchase", + domain=[("state", "=", "approved"), ("fiscal_type", "=", "purchase")], + ) + + purchase_invoicing_policy = fields.Selection( + selection=[ + ("purchase_order", _("Purchase Order")), + ("stock_picking", _("Stock Picking")), + ], + default="purchase_order", + ) diff --git a/l10n_br_purchase_stock/models/res_config_settings.py b/l10n_br_purchase_stock/models/res_config_settings.py new file mode 100644 index 000000000000..afc09d2dda55 --- /dev/null +++ b/l10n_br_purchase_stock/models/res_config_settings.py @@ -0,0 +1,13 @@ +# Copyright (C) 2021 Renato Lima - Akretion +# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html + +from odoo import fields, models + + +class ResConfigSettings(models.TransientModel): + _inherit = "res.config.settings" + + purchase_invoicing_policy = fields.Selection( + related="company_id.purchase_invoicing_policy", + readonly=False, + ) diff --git a/l10n_br_purchase_stock/models/stock_move.py b/l10n_br_purchase_stock/models/stock_move.py new file mode 100644 index 000000000000..1870d2fab133 --- /dev/null +++ b/l10n_br_purchase_stock/models/stock_move.py @@ -0,0 +1,20 @@ +# @ 2021 Akretion - www.akretion.com.br - +# Magno Costa +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from odoo import models + + +class StockMove(models.Model): + _inherit = "stock.move" + + def _get_price_unit_invoice(self, inv_type, partner, qty=1): + result = super()._get_price_unit_invoice(inv_type, partner, qty) + # Caso tenha Purchase Line já vem desagrupado aqui devido ao KEY + if len(self) == 1: + # Caso venha apenas uma linha porem sem + # purchase_line_id é preciso ignora-la + if self.purchase_line_id and self.purchase_line_id.price_unit != result: + result = self.purchase_line_id.price_unit + + return result diff --git a/l10n_br_purchase_stock/models/stock_picking.py b/l10n_br_purchase_stock/models/stock_picking.py new file mode 100644 index 000000000000..930d561ce36f --- /dev/null +++ b/l10n_br_purchase_stock/models/stock_picking.py @@ -0,0 +1,23 @@ +# Copyright (C) 2024-Today - Akretion (). +# @author Magno Costa +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import models + + +class StockPicking(models.Model): + _inherit = "stock.picking" + + def _get_default_fiscal_operation(self): + fiscal_operation = super()._get_default_fiscal_operation() + if self.purchase_id: + if self.purchase_id.fiscal_operation_id: + # Evita a inconsistência de ter o Pedido de Compras com uma + # OP Fiscal e a Ordem de Seleção outra, quando o campo + # invoice_state é alterado, o usuário pode alterar o campo + # mas dessa forma forçamos a decisão de não usar a mesma + # do Pedido. + if fiscal_operation != self.purchase_id.fiscal_operation_id: + fiscal_operation = self.purchase_id.fiscal_operation_id + + return fiscal_operation diff --git a/l10n_br_purchase_stock/models/stock_rule.py b/l10n_br_purchase_stock/models/stock_rule.py new file mode 100644 index 000000000000..9187a6405907 --- /dev/null +++ b/l10n_br_purchase_stock/models/stock_rule.py @@ -0,0 +1,24 @@ +# Copyright (C) 2020 Renato Lima - Akretion +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import models + + +class StockRule(models.Model): + _inherit = "stock.rule" + + def _run_buy(self, procurements): + result = super()._run_buy(procurements) + + for procurement, _rule in procurements: + purchases = self.env["purchase.order"].search( + [("origin", "=", procurement.origin), ("state", "=", "draft")] + ) + for purchase in purchases: + if purchase.fiscal_operation_id: + for line in purchase.order_line: + price_unit = line.price_unit + line._onchange_product_id_fiscal() + line.price_unit = price_unit + + return result diff --git a/l10n_br_purchase_stock/pyproject.toml b/l10n_br_purchase_stock/pyproject.toml new file mode 100644 index 000000000000..4231d0cccb3d --- /dev/null +++ b/l10n_br_purchase_stock/pyproject.toml @@ -0,0 +1,3 @@ +[build-system] +requires = ["whool"] +build-backend = "whool.buildapi" diff --git a/l10n_br_purchase_stock/readme/CONFIGURE.md b/l10n_br_purchase_stock/readme/CONFIGURE.md new file mode 100644 index 000000000000..8e383fe2ae35 --- /dev/null +++ b/l10n_br_purchase_stock/readme/CONFIGURE.md @@ -0,0 +1,10 @@ +Defina a **Politica de Faturamento de Compras**, se a Fatura deverá ser criada a partir do **Pedido de Compra/purchase.order** ou da **Ordem de Recebimento/stock.picking** +isso pode ser feito em: + +**Definições > Usuários e Empresas > Empresas** +Selecione a Empresa, aba **Fiscal** e depois na aba **Compras** + +ou + +**Compras > Configuração > Definições** +Na seção **Faturamento** diff --git a/l10n_br_purchase_stock/readme/CONTRIBUTORS.md b/l10n_br_purchase_stock/readme/CONTRIBUTORS.md new file mode 100644 index 000000000000..bba4146b5ca0 --- /dev/null +++ b/l10n_br_purchase_stock/readme/CONTRIBUTORS.md @@ -0,0 +1,3 @@ +- [Akretion](https://www.akretion.com/pt-BR): + - Renato Lima \<\> + - Magno Costa \<\> diff --git a/l10n_br_purchase_stock/readme/CREDITS.md b/l10n_br_purchase_stock/readme/CREDITS.md new file mode 100644 index 000000000000..b9eb4c0c361d --- /dev/null +++ b/l10n_br_purchase_stock/readme/CREDITS.md @@ -0,0 +1,3 @@ +The development of this module has been financially supported by: + +- AKRETION LTDA - www.akretion.com diff --git a/l10n_br_purchase_stock/readme/DESCRIPTION.md b/l10n_br_purchase_stock/readme/DESCRIPTION.md new file mode 100644 index 000000000000..49b877931e63 --- /dev/null +++ b/l10n_br_purchase_stock/readme/DESCRIPTION.md @@ -0,0 +1,7 @@ +O módulo estende o [l10n_br_stock_account](https://github.com/OCA/l10n-brazil/tree/16.0/l10n_br_stock_account) para ser usado em **Compras** incluindo a possibilidade de criar a Fatura de um Pedido de Compra a partir da Ordem de Recebimento/Stock Picking, ao definir a **Política de Faturamento de Compras** como: + +* **Pedido de Compra / Purchase Order**, mantém o comportamento padrão e a criação da Fatura será feita a partir do **Pedido de Compra/purchase.order** + +* **Ordem de Recebimento / Stock Picking**, desabilita a criação de Faturas a partir do **Pedido de Compra** para os casos onde o 'Tipo do Produto' é 'Produto', mas no caso de ser 'Serviço' ainda será possível criar a partir do **Pedido de Compra** + +A implementação foi feita para que a Fatura criada a partir do 'stock.picking' seja a mais similar possível com a que é criada a partir do 'purchase.order', e ao usar os métodos do módulo **purchase** para obter os Dados usados na criação de uma nova Fatura isso também acaba evitando a necessidade de 'glue modules' (pequenos módulos criados apenas para evitar dependencias indiretas), então quando qualquer módulo incluir um novo campo na Fatura criada a partir do Pedido de Compra esse novo campo também deverá ser incluido na Fatura criada a partir do 'stock.picking', por exemplo o módulo [account_payment_purchase](https://github.com/OCA/bank-payment/tree/16.0/account_payment_purchase). diff --git a/l10n_br_purchase_stock/readme/HISTORY.md b/l10n_br_purchase_stock/readme/HISTORY.md new file mode 100644 index 000000000000..ba06de28c97e --- /dev/null +++ b/l10n_br_purchase_stock/readme/HISTORY.md @@ -0,0 +1,12 @@ +## 16.0.1.0.0 (2025-01-22) + +- \[MIG\] Migração para a v16 + +## 14.0.1.0.0 (2022-09-16) + +- \[MIG\] Migração para a v14 + +## 12.0.1.0.0 (2020-04-27) + +- \[REF\] Separado o módulo l10n_br_purchase em dois l10n_br_purchase e + l10n_br_purchase_stock. diff --git a/l10n_br_purchase_stock/readme/INSTALL.md b/l10n_br_purchase_stock/readme/INSTALL.md new file mode 100644 index 000000000000..cfbb1f3f798e --- /dev/null +++ b/l10n_br_purchase_stock/readme/INSTALL.md @@ -0,0 +1,4 @@ +O módulo depende do: + +- l10n_br_purchase +- l10n_br_stock_account diff --git a/l10n_br_purchase_stock/readme/ROADMAP.md b/l10n_br_purchase_stock/readme/ROADMAP.md new file mode 100644 index 000000000000..8b137891791f --- /dev/null +++ b/l10n_br_purchase_stock/readme/ROADMAP.md @@ -0,0 +1 @@ + diff --git a/l10n_br_purchase_stock/readme/USAGE.md b/l10n_br_purchase_stock/readme/USAGE.md new file mode 100644 index 000000000000..17a14eb841b3 --- /dev/null +++ b/l10n_br_purchase_stock/readme/USAGE.md @@ -0,0 +1,9 @@ +Se a **Política de Faturamento de Compras** estiver definida como **Ordem de Recebimento/Stock Picking** é possível criar tanto uma Fatura para uma Ordem de Recebimento quanto uma Fatura para diversas Ordens de Recebimentos: + +* Caso uma Fatura para uma Ordem de Recebimento + +Na **Ordem de Recebimento**, referente ao **Pedido de Compra**, depois de **Validar** essa Ordem deverá aparecer o botão **Criar Fatura** onde ao clicar será possível criar a Fatura, nesse caso o campo **Grupo** estará **Coleta**. + +* Caso uma Fatura para diversas Ordens de Recebimento + +Por estender o **l10n_br_stock_account** é possível criar uma **Fatura Agrupada**, para isso é preciso ir na 'Visão Lista/Tree View' selecionar as **Ordens de Recebimentos**, criadas a partir de diversos **Pedidos de Compras** e que possuem o mesmo **Parceiro/Fornecedor**, clicar no botão **Ação** em seguida **Criar rascunho das faturas** e no campo **Grupo** selecionar **Parceiro** para assim criar apenas uma Fatura ou seja N Pedidos de Compras com N Ordens de Recebimento de um mesmo Fornecedor podem ter apenas uma Fatura/Documento Fiscal relacionado. diff --git a/l10n_br_purchase_stock/static/description/icon.png b/l10n_br_purchase_stock/static/description/icon.png new file mode 100644 index 000000000000..77d28aa00cf3 Binary files /dev/null and b/l10n_br_purchase_stock/static/description/icon.png differ diff --git a/l10n_br_purchase_stock/static/description/index.html b/l10n_br_purchase_stock/static/description/index.html new file mode 100644 index 000000000000..48d88e52498f --- /dev/null +++ b/l10n_br_purchase_stock/static/description/index.html @@ -0,0 +1,538 @@ + + + + + +Brazilian Localization Purchase Stock + + + +
+

Brazilian Localization Purchase Stock

+ + +

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

+

O módulo estende o +l10n_br_stock_account +para ser usado em Compras incluindo a possibilidade de criar a +Fatura de um Pedido de Compra a partir da Ordem de Recebimento/Stock +Picking, ao definir a Política de Faturamento de Compras como:

+
    +
  • Pedido de Compra / Purchase Order, mantém o comportamento padrão e +a criação da Fatura será feita a partir do Pedido de +Compra/purchase.order
  • +
  • Ordem de Recebimento / Stock Picking, desabilita a criação de +Faturas a partir do Pedido de Compra para os casos onde o ‘Tipo do +Produto’ é ‘Produto’, mas no caso de ser ‘Serviço’ ainda será possível +criar a partir do Pedido de Compra
  • +
+

A implementação foi feita para que a Fatura criada a partir do +‘stock.picking’ seja a mais similar possível com a que é criada a partir +do ‘purchase.order’, e ao usar os métodos do módulo purchase para +obter os Dados usados na criação de uma nova Fatura isso também acaba +evitando a necessidade de ‘glue modules’ (pequenos módulos criados +apenas para evitar dependencias indiretas), então quando qualquer módulo +incluir um novo campo na Fatura criada a partir do Pedido de Compra esse +novo campo também deverá ser incluido na Fatura criada a partir do +‘stock.picking’, por exemplo o módulo +account_payment_purchase.

+

Table of contents

+ +
+

Installation

+

O módulo depende do:

+
    +
  • l10n_br_purchase
  • +
  • l10n_br_stock_account
  • +
+
+
+

Configuration

+

Defina a Politica de Faturamento de Compras, se a Fatura deverá ser +criada a partir do Pedido de Compra/purchase.order ou da Ordem de +Recebimento/stock.picking isso pode ser feito em:

+

Definições > Usuários e Empresas > Empresas Selecione a Empresa, aba +Fiscal e depois na aba Compras

+

ou

+

Compras > Configuração > Definições Na seção Faturamento

+
+
+

Usage

+

Se a Política de Faturamento de Compras estiver definida como +Ordem de Recebimento/Stock Picking é possível criar tanto uma Fatura +para uma Ordem de Recebimento quanto uma Fatura para diversas Ordens de +Recebimentos:

+
    +
  • Caso uma Fatura para uma Ordem de Recebimento
  • +
+

Na Ordem de Recebimento, referente ao Pedido de Compra, depois +de Validar essa Ordem deverá aparecer o botão Criar Fatura onde +ao clicar será possível criar a Fatura, nesse caso o campo Grupo +estará Coleta.

+
    +
  • Caso uma Fatura para diversas Ordens de Recebimento
  • +
+

Por estender o l10n_br_stock_account é possível criar uma Fatura +Agrupada, para isso é preciso ir na ‘Visão Lista/Tree View’ selecionar +as Ordens de Recebimentos, criadas a partir de diversos Pedidos de +Compras e que possuem o mesmo Parceiro/Fornecedor, clicar no botão +Ação em seguida Criar rascunho das faturas e no campo Grupo +selecionar Parceiro para assim criar apenas uma Fatura ou seja N +Pedidos de Compras com N Ordens de Recebimento de um mesmo Fornecedor +podem ter apenas uma Fatura/Documento Fiscal relacionado.

+
+ +
+

Changelog

+
+

16.0.1.0.0 (2025-01-22)

+
    +
  • [MIG] Migração para a v16
  • +
+
+
+

14.0.1.0.0 (2022-09-16)

+
    +
  • [MIG] Migração para a v14
  • +
+
+
+

12.0.1.0.0 (2020-04-27)

+
    +
  • [REF] Separado o módulo l10n_br_purchase em dois l10n_br_purchase e +l10n_br_purchase_stock.
  • +
+
+
+
+

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 to smash it by providing a detailed and welcomed +feedback.

+

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

+
+
+

Credits

+
+

Authors

+
    +
  • Akretion
  • +
+
+ +
+

Other credits

+

The development of this module has been financially supported by:

+ +
+
+

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:

+

renatonlima mbcosta

+

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_purchase_stock/tests/__init__.py b/l10n_br_purchase_stock/tests/__init__.py new file mode 100644 index 000000000000..50af8bf70be7 --- /dev/null +++ b/l10n_br_purchase_stock/tests/__init__.py @@ -0,0 +1,4 @@ +from . import test_l10n_br_purchase_stock +from . import test_l10n_br_purchase_stock_sn +from . import test_l10n_br_purchase_stock_lp +from . import test_stock_rule diff --git a/l10n_br_purchase_stock/tests/test_l10n_br_purchase_stock.py b/l10n_br_purchase_stock/tests/test_l10n_br_purchase_stock.py new file mode 100644 index 000000000000..56928826248c --- /dev/null +++ b/l10n_br_purchase_stock/tests/test_l10n_br_purchase_stock.py @@ -0,0 +1,475 @@ +# @ 2019 Akretion - www.akretion.com.br - +# Magno Costa +# Renato Lima +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from odoo.tests import Form + +from odoo.addons.l10n_br_stock_account.tests.common import TestBrPickingInvoicingCommon + + +class L10nBrPurchaseStockBase(TestBrPickingInvoicingCommon): + def setUp(self): + super().setUp() + + def test_grouping_pickings(self): + """ + Test the invoice generation grouped by partner/product with 2 + picking and 2 moves per picking. + """ + purchase_1 = self.env.ref("l10n_br_purchase_stock.main_po_only_products_1") + purchase_1.button_confirm() + picking_1 = purchase_1.picking_ids + self.assertEqual( + picking_1.invoice_state, "2binvoiced", "Error to inform Invoice State." + ) + + self.picking_move_state(picking_1) + + self.assertEqual(picking_1.state, "done") + + self.assertEqual( + purchase_1.invoice_status, + "to invoice", + "Error in compute field invoice_status," + " before create invoice by Picking.", + ) + + purchase_2 = self.env.ref("l10n_br_purchase_stock.main_po_only_products_2") + purchase_2.button_confirm() + + picking_2 = purchase_2.picking_ids + self.picking_move_state(picking_2) + + pickings = picking_1 | picking_2 + invoice = self.create_invoice_wizard(pickings) + # Fatura Agrupada + self.assertEqual(len(invoice), 1) + self.assertEqual(picking_1.invoice_state, "invoiced") + self.assertEqual(picking_2.invoice_state, "invoiced") + + self.assertIn(invoice, picking_1.invoice_ids) + self.assertIn(picking_1, invoice.picking_ids) + self.assertIn(invoice, picking_2.invoice_ids) + self.assertIn(picking_2, invoice.picking_ids) + + # Validar o price_unit usado + for inv_line in invoice.invoice_line_ids.filtered(lambda ln: ln.product_id): + # TODO: A forma de instalação dos modulos feita no CI + # falha o browse aqui + # l10n_br_stock_account/models/stock_invoice_onshipping.py:105 + # isso não acontece no caso da empresa de Lucro Presumido + # ou quando é feito o teste apenas instalando os modulos + # l10n_br_account e em seguida o l10n_br_stock_account + # self.assertTrue( + # inv_line.tax_ids, + # "Error to map Purchase Tax in invoice.line.", + # ) + # Preço usado na Linha da Invoice deve ser o mesmo + # informado no Pedido de Compra + self.assertEqual(inv_line.price_unit, inv_line.purchase_line_id.price_unit) + # Valida presença dos campos principais para o mapeamento Fiscal + self.assertTrue(inv_line.fiscal_operation_id, "Missing Fiscal Operation.") + self.assertTrue( + inv_line.fiscal_operation_line_id, "Missing Fiscal Operation Line." + ) + + # Section Lines + section_lines = invoice.invoice_line_ids.filtered( + lambda ln: ln.display_type == "line_section" + ) + self.assertEqual(len(section_lines), 2) + # Note Lines + note_lines = invoice.invoice_line_ids.filtered( + lambda ln: ln.display_type == "line_note" + ) + self.assertEqual(len(note_lines), 2) + + if hasattr(invoice, "document_serie"): + invoice.document_serie = "1" + invoice.document_number = "123" + + # Confirmando a Fatura + invoice.action_post() + self.assertEqual(invoice.state, "posted", "Invoice should be in state Posted") + # Validar Atualização da Quantidade Faturada + for line in purchase_1.order_line: + # Apenas a linha de Produto tem a qtd faturada dobrada + if line.product_id.type == "product": + # A quantidade Faturada deve ser igual + # a Quantidade do Produto + self.assertEqual(line.product_uom_qty, line.qty_invoiced) + + # Teste de Retorno + picking_devolution = self.return_picking_wizard(picking_1) + self.assertEqual(picking_devolution.invoice_state, "2binvoiced") + self.assertTrue( + picking_devolution.fiscal_operation_id, "Missing Fiscal Operation." + ) + for line in picking_devolution.move_ids: + self.assertEqual(line.invoice_state, "2binvoiced") + # Valida presença dos campos principais para o mapeamento Fiscal + self.assertTrue(line.fiscal_operation_id, "Missing Fiscal Operation.") + self.assertTrue( + line.fiscal_operation_line_id, "Missing Fiscal Operation Line." + ) + self.picking_move_state(picking_devolution) + self.assertEqual(picking_devolution.state, "done", "Change state fail.") + + invoice_devolution = self.create_invoice_wizard(picking_devolution) + # Confirmando a Fatura + invoice_devolution.action_post() + self.assertEqual( + invoice_devolution.state, "posted", "Invoice should be in state Posted" + ) + # Validar Atualização da Quantidade Faturada + for line in purchase_1.order_line: + # Apenas a linha de Produto tem a qtd faturada dobrada + if line.product_id.type == "product": + # A quantidade Faturada deve ser zero devido a Devolução + self.assertEqual(0.0, line.qty_invoiced) + + def test_purchase_order_lucro_presumido(self): + """Test Purchase Order for company Lucro Presumido.""" + + self._change_user_company(self.env.ref("l10n_br_base.empresa_lucro_presumido")) + + purchase = self.env.ref( + "l10n_br_purchase_stock.lucro_presumido_po_only_products_1" + ) + purchase.button_confirm() + picking = purchase.picking_ids + + picking.set_to_be_invoiced() + # Testa o caso onde o valor no Picking é diferente do Pedido + # TODO: Validar quando esse caso pode ocorrer para saber + # se é necessário manter ou não o código no stock_move.py + for move in picking.move_ids_without_package: + move.price_unit = 123.0 + self.assertEqual( + picking.invoice_state, "2binvoiced", "Error to inform Invoice State." + ) + + self.picking_move_state(picking) + self.assertEqual(picking.state, "done") + + self.assertEqual( + purchase.invoice_status, + "to invoice", + "Error in compute field invoice_status," + " before create invoice by Picking.", + ) + + invoice = self.create_invoice_wizard(picking) + + # Validar o price_unit usado + for inv_line in invoice.invoice_line_ids.filtered(lambda ln: ln.product_id): + # TODO: A forma de instalação dos modulos feita no CI + # falha o browse aqui + # l10n_br_stock_account/models/stock_invoice_onshipping.py:105 + # isso não acontece no caso da empresa de Lucro Presumido + # ou quando é feito o teste apenas instalando os modulos + # l10n_br_account e em seguida o l10n_br_stock_account + self.assertTrue( + inv_line.tax_ids, + "Error to map Purchase Tax in invoice.line.", + ) + # Preço usado na Linha da Invoice deve ser o mesmo + # informado no Pedido de Compra + # TODO: Por algum motivo o Preço da Linha do Pedido de Compra fica + # diferente da Linha da Fatura, mas somente no caso da empresa + # Lucro Presumido + # File "/odoo/external-src/l10n-brazil-MIG-l10n_br_purchase_stock/ + # l10n_br_purchase_stock/tests/test_l10n_br_purchase_stock.py", + # line 263, in test_purchase_order_lucro_presumido + # self.assertEqual(inv_line.price_unit, + # inv_line.purchase_line_id.price_unit) + # AssertionError: 82.53 != 100.0 + # self.assertEqual(inv_line.price_unit, + # inv_line.purchase_line_id.price_unit) + # Valida presença dos campos principais para o mapeamento Fiscal + self.assertTrue(inv_line.fiscal_operation_id, "Missing Fiscal Operation.") + self.assertTrue( + inv_line.fiscal_operation_line_id, "Missing Fiscal Operation Line." + ) + + if hasattr(invoice, "document_serie"): + invoice.document_serie = "1" + invoice.document_number = "1234" + + # Confirmando a Fatura + invoice.action_post() + self.assertEqual(invoice.state, "posted", "Invoice should be in state Posted") + + def test_button_create_bill_in_view(self): + """ + Test Field to make Button Create Bill invisible. + """ + purchase_products = self.env.ref( + "l10n_br_purchase_stock.main_po_only_products_2" + ) + # Caso do Pedido de Compra em Rascunho + self.assertTrue( + purchase_products.button_create_invoice_invisible, + "Field to make invisible the Button Create Bill should be" + " invisible when Purchase Order is not in state Purchase or Done.", + ) + # Caso somente com Produtos + purchase_products.button_confirm() + self.assertTrue( + purchase_products.button_create_invoice_invisible, + "Field to make invisible the button Create Bill should be" + " invisible when Purchase Order has only products.", + ) + + # Caso Somente Serviços + purchase_only_service = self.env.ref( + "l10n_br_purchase_stock.main_po_only_service_stock" + ) + purchase_only_service.button_confirm() + self.assertFalse( + purchase_only_service.button_create_invoice_invisible, + "Field to make invisible the Button Create Bill should be" + " False when the Purchase Order has only Services.", + ) + # Caso Serviço e Produto + purchase_service_product = self.env.ref( + "l10n_br_purchase_stock.main_po_service_product_stock" + ) + purchase_service_product.button_confirm() + self.assertFalse( + purchase_only_service.button_create_invoice_invisible, + "Field to make invisible the Button Create Bill should be" + " False when the Purchase Order has Service and Product.", + ) + + def test_compatible_with_international_case(self): + """Test of compatible with international case or without Fiscal Operation, + create Invoice but not for Brazil.""" + po_international = self.env.ref("purchase.purchase_order_2") + po_international.fiscal_operation_id = False + po_international.with_context(tracking_disable=True).button_confirm() + picking = po_international.picking_ids + # Método onchange_invoice_state acaba trazendo a 'Operação Fiscal Padrão' + picking.fiscal_operation_id = False + self.picking_move_state(picking) + picking.fiscal_operation_id = False + self.assertEqual(picking.state, "done") + invoice = self.create_invoice_wizard(picking) + invoice.action_post() + for invoice in po_international.invoice_ids: + # Caso Internacional não deve ter Documento Fiscal associado + self.assertFalse( + invoice.fiscal_document_id, + "International case should not has Fiscal Document.", + ) + # Teste Retorno + picking_devolution = self.return_picking_wizard(picking) + self.picking_move_state(picking_devolution) + picking_devolution.fiscal_operation_id = False + invoice_devolution = self.create_invoice_wizard(picking_devolution) + self.assertFalse( + invoice_devolution.fiscal_document_id, + "International case should not has Fiscal Document.", + ) + + def test_purchase_with_partner_to_invoice(self): + """ + Test Purchase Order with different Partner to Invoice. + """ + # Caso do Pedido criado com um Partner que possui um Partner to Invoice + purchase = self.env.ref("l10n_br_purchase.main_company-purchase_2") + purchase.with_context(tracking_disable=True).button_confirm() + self.assertEqual(purchase.state, "purchase", "Error to confirm Purchase Order.") + + picking = purchase.picking_ids + picking.set_to_be_invoiced() + self.assertEqual( + picking.invoice_state, "2binvoiced", "Error to inform Invoice State." + ) + + self.picking_move_state(picking) + self.assertEqual(picking.state, "done") + + self.assertEqual( + purchase.invoice_status, + "to invoice", + "Error in compute field invoice_status," + " before create invoice by Picking.", + ) + invoice = self.create_invoice_wizard(picking) + invoice.action_post() + + self.assertTrue( + invoice.fiscal_document_id, + "Fiscal Document missing for Purchase.", + ) + partner_invoice = self.env["res.partner"].browse( + purchase.partner_id.address_get(["invoice"]).get("invoice") + ) + self.assertEqual( + invoice.partner_id, + partner_invoice, + "The Invoice should be created with the Partner to Invoice", + ) + self.assertNotEqual( + invoice.partner_id, + purchase.partner_id, + "The Invoice should be created with the Partner to Invoice", + ) + # TODO: No l10n_br_purchase quando a Fatura é criada pelo + # Pedido de Compra, deveria criar a Fatura com o + # Endereço de Entrega/partner_shipping_id preenchido com o + # Partner do Pedido como ocorre aqui? + self.assertEqual( + invoice.partner_shipping_id, + picking.partner_id, + "The Invoice should be created with Partner to Shipping.", + ) + + # Caso onde o Pedido é criado com o Partner to Invoice + purchase_2 = self.env.ref("l10n_br_purchase.main_company-purchase_3") + purchase_2.with_context(tracking_disable=True).button_confirm() + self.assertEqual( + purchase_2.state, "purchase", "Error to confirm Purchase Order." + ) + + picking_2 = purchase_2.picking_ids + picking_2.set_to_be_invoiced() + + self.picking_move_state(picking_2) + self.assertEqual(picking.state, "done") + self.assertEqual( + picking_2.invoice_state, "2binvoiced", "Error to inform Invoice State." + ) + self.assertEqual( + purchase_2.invoice_status, + "to invoice", + "Error in compute field invoice_status," + " before create invoice by Picking.", + ) + + invoice = self.create_invoice_wizard(picking_2) + invoice.action_post() + + self.assertEqual( + invoice.partner_id, + purchase_2.partner_id, + "The Partner in Purchase and Invoice should be the same.", + ) + self.assertEqual( + invoice.partner_shipping_id, + picking_2.partner_id, + "The Invoice should be created with Partner to Shipping.", + ) + self.assertEqual( + invoice.partner_shipping_id, + invoice.partner_id, + "The Invoice Partner and Partner to Shipping should be the same.", + ) + + def test_purchase_with_partner_to_shipping(self): + """Test brazilian Purchase Order with Partner to Shipping.""" + + # Caso do Pedido criado com o Contato de Entrega/Partner to Delivery + purchase = self.env.ref("l10n_br_purchase.main_company-purchase_4") + purchase.with_context(tracking_disable=True).button_confirm() + self.assertEqual(purchase.state, "purchase", "Error to confirm Purchase Order.") + + picking = purchase.picking_ids + picking.set_to_be_invoiced() + self.assertEqual( + picking.invoice_state, "2binvoiced", "Error to inform Invoice State." + ) + self.picking_move_state(picking) + self.assertEqual(picking.state, "done") + invoice = self.create_invoice_wizard(picking) + invoice.action_post() + + self.assertTrue( + invoice.fiscal_document_id, + "Fiscal Document missing for Purchase.", + ) + partner_delivery = self.env["res.partner"].browse( + purchase.partner_id.address_get(["delivery"]).get("delivery") + ) + self.assertEqual( + invoice.partner_shipping_id, + partner_delivery, + "The Invoice should be created with the Partner to Delivery", + ) + self.assertNotEqual( + invoice.partner_id, + purchase.partner_id, + "The Invoice should be created with the Partner to Invoice", + ) + # TODO: No l10n_br_purchase quando a Fatura é criada pelo + # Pedido de Compra, deveria criar a Fatura com o + # Endereço de Entrega/partner_shipping_id preenchido com o + # Partner do Pedido como ocorre aqui? + self.assertEqual( + invoice.partner_shipping_id, + picking.partner_id, + "The Invoice should be created with Partner to Shipping.", + ) + + # Caso do Pedido com um Partner que tem um contato como endereço de entrega + purchase_2 = self.env.ref("l10n_br_purchase.main_company-purchase_5") + purchase_2.with_context(tracking_disable=True).button_confirm() + self.assertEqual( + purchase_2.state, "purchase", "Error to confirm Purchase Order." + ) + + picking_2 = purchase_2.picking_ids + picking_2.set_to_be_invoiced() + + self.assertEqual( + picking_2.invoice_state, "2binvoiced", "Error to inform Invoice State." + ) + + self.picking_move_state(picking_2) + self.assertEqual(picking.state, "done") + self.assertEqual( + purchase_2.invoice_status, + "to invoice", + "Error in compute field invoice_status," + " before create invoice by Picking.", + ) + + invoice = self.create_invoice_wizard(picking_2) + invoice.action_post() + + self.assertEqual( + invoice.partner_id, + purchase_2.partner_id, + "The Partner in Purchase and Invoice should be the same.", + ) + self.assertEqual( + invoice.partner_shipping_id, + picking_2.partner_id, + "The Invoice should be created with Partner to Shipping.", + ) + self.assertEqual( + invoice.partner_shipping_id, + invoice.partner_id, + "The Invoice Partner and Partner to Shipping should be the same.", + ) + + def test_form_stock_picking(self): + """Test Stock Picking with Form""" + purchase = self.env.ref("l10n_br_purchase_stock.main_po_only_products_1") + purchase.button_confirm() + picking = purchase.picking_ids + self.picking_move_state(picking) + picking_form = Form(picking) + + # Alterando a OP Fiscal apenas para forçar a diferença + picking.company_id.stock_in_fiscal_operation_id = False + + # Apesar do metodo onchange retornar uma OP Fiscal padrão, + # quando existe um Pedido de Venda associado deve usar retornar + # a mesma OP Fiscal do Pedido. + picking_form.invoice_state = "none" + picking_form.invoice_state = "2binvoiced" + self.assertEqual(purchase.fiscal_operation_id, picking.fiscal_operation_id) + picking_form.save() diff --git a/l10n_br_purchase_stock/tests/test_l10n_br_purchase_stock_lp.py b/l10n_br_purchase_stock/tests/test_l10n_br_purchase_stock_lp.py new file mode 100644 index 000000000000..dd0d8fb62157 --- /dev/null +++ b/l10n_br_purchase_stock/tests/test_l10n_br_purchase_stock_lp.py @@ -0,0 +1,11 @@ +# @ 2019 Akretion - www.akretion.com.br - +# Magno Costa +# Renato Lima +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from .test_l10n_br_purchase_stock import L10nBrPurchaseStockBase + + +class L10nBrPurchaseStockBase(L10nBrPurchaseStockBase): + def setUp(self): + super().setUp() diff --git a/l10n_br_purchase_stock/tests/test_l10n_br_purchase_stock_sn.py b/l10n_br_purchase_stock/tests/test_l10n_br_purchase_stock_sn.py new file mode 100644 index 000000000000..dd0d8fb62157 --- /dev/null +++ b/l10n_br_purchase_stock/tests/test_l10n_br_purchase_stock_sn.py @@ -0,0 +1,11 @@ +# @ 2019 Akretion - www.akretion.com.br - +# Magno Costa +# Renato Lima +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from .test_l10n_br_purchase_stock import L10nBrPurchaseStockBase + + +class L10nBrPurchaseStockBase(L10nBrPurchaseStockBase): + def setUp(self): + super().setUp() diff --git a/l10n_br_purchase_stock/tests/test_stock_rule.py b/l10n_br_purchase_stock/tests/test_stock_rule.py new file mode 100644 index 000000000000..f073c95c76a0 --- /dev/null +++ b/l10n_br_purchase_stock/tests/test_stock_rule.py @@ -0,0 +1,85 @@ +# Copyright (C) 2023-Today - Akretion (). +# @author Magno Costa +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo.tests.common import TransactionCase + + +class StockRuleTest(TransactionCase): + """Test Stock Rule""" + + @classmethod + def setUpClass(cls): + super().setUpClass() + + def test_stock_rule_purchase(self): + """ + Test Stock Rule related to Purchase + """ + warehouse_1 = self.env["stock.warehouse"].search( + [("company_id", "=", self.env.user.id)], limit=1 + ) + warehouse_1 = warehouse_1.write({"reception_steps": "two_steps"}) + self.env.ref("purchase_stock.route_warehouse0_buy") + orderpoint = self.env["stock.warehouse.orderpoint"].create( + { + "product_id": self.env.ref("product.product_product_12").id, + "product_min_qty": 1000.0, + } + ) + + # Run scheduler + self.env["procurement.group"].run_scheduler() + + orderpoint.action_replenish() + orderpoint.action_replenish_auto() + po_line = self.env["purchase.order.line"].search( + [("orderpoint_id", "=", orderpoint.id)] + ) + self.assertTrue(po_line.fiscal_operation_id, "Missing Fiscal Operation.") + po_line._onchange_fiscal_operation_id() + po_line._onchange_fiscal_operation_line_id() + self.assertTrue( + po_line.fiscal_operation_line_id, + "Missing Fiscal Operation Line in Purchase Order.", + ) + po_order = po_line.order_id + self.assertTrue( + po_order.fiscal_operation_id, "Missing Fiscal Operation in Purchase Order." + ) + po_order.button_confirm() + picking = po_order.picking_ids + self.assertTrue( + picking.fiscal_operation_id, "Missing Fiscal Operation in Picking." + ) + for line in picking.move_ids: + self.assertEqual(line.invoice_state, "2binvoiced") + # Valida presença dos campos principais para o mapeamento Fiscal + line._onchange_fiscal_operation_id() + line._onchange_fiscal_operation_line_id() + self.assertTrue( + line.fiscal_operation_id, "Missing Fiscal Operation in Stock Move." + ) + + self.assertTrue( + line.fiscal_operation_line_id, + "Missing Fiscal Operation Line in Stock Move.", + ) + + picking.action_confirm() + # Necessario para testar + # l10n_br_purchase_stock/models/stock_rule.py Linhas 18-23 + product_mto = self.env.ref("product.product_product_16") + product_mto.route_ids |= self.env.ref("stock.route_warehouse0_mto") + orderpoint_2 = self.env["stock.warehouse.orderpoint"].create( + { + "product_id": product_mto.id, + "product_min_qty": 150.0, + "route_id": self.env.ref("stock.route_warehouse0_mto").id, + } + ) + + # Run scheduler + self.env["procurement.group"].run_scheduler() + orderpoint_2.action_replenish() + orderpoint_2.action_replenish_auto() diff --git a/l10n_br_purchase_stock/views/purchase_order.xml b/l10n_br_purchase_stock/views/purchase_order.xml new file mode 100644 index 000000000000..d53d656fb5f5 --- /dev/null +++ b/l10n_br_purchase_stock/views/purchase_order.xml @@ -0,0 +1,38 @@ + + + + + l10n_br_purchase_stock.order.form + purchase.order + + 120 + + + + + + + {'invisible': [('button_create_invoice_invisible', '=', True)]} + + + + + {'invisible': [('button_create_invoice_invisible', '=', True)]} + + + + + + + + diff --git a/l10n_br_purchase_stock/views/res_company_view.xml b/l10n_br_purchase_stock/views/res_company_view.xml new file mode 100644 index 000000000000..d60cf2bd0b66 --- /dev/null +++ b/l10n_br_purchase_stock/views/res_company_view.xml @@ -0,0 +1,15 @@ + + + + + purchase.create.inv.policy.res.company.form + res.company + + + + + + + + + diff --git a/l10n_br_purchase_stock/views/res_config_settings.xml b/l10n_br_purchase_stock/views/res_config_settings.xml new file mode 100644 index 000000000000..2fe951328702 --- /dev/null +++ b/l10n_br_purchase_stock/views/res_config_settings.xml @@ -0,0 +1,48 @@ + + + + + l10n_br_purchase.res.config.settings.form + res.config.settings + + + +
+
+
+
+
+ + + + + diff --git a/l10n_br_purchase_stock/wizards/__init__.py b/l10n_br_purchase_stock/wizards/__init__.py new file mode 100644 index 000000000000..403769a0b38a --- /dev/null +++ b/l10n_br_purchase_stock/wizards/__init__.py @@ -0,0 +1 @@ +from . import stock_invocing_onshipping diff --git a/l10n_br_purchase_stock/wizards/stock_invocing_onshipping.py b/l10n_br_purchase_stock/wizards/stock_invocing_onshipping.py new file mode 100644 index 000000000000..5eb362d428f2 --- /dev/null +++ b/l10n_br_purchase_stock/wizards/stock_invocing_onshipping.py @@ -0,0 +1,227 @@ +# @ 2021 Akretion - www.akretion.com.br - +# Magno Costa +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from odoo import models + +from odoo.addons.l10n_br_fiscal.constants.fiscal import DOCUMENT_ISSUER_PARTNER + + +class StockInvoiceOnshipping(models.TransientModel): + _inherit = "stock.invoice.onshipping" + + def _build_invoice_values_from_pickings(self, pickings): + """ + Build dict to create a new invoice from given pickings + :param pickings: stock.picking recordset + :return: dict + """ + invoice, values = super()._build_invoice_values_from_pickings(pickings) + + purchase_pickings = pickings.filtered(lambda pk: pk.purchase_id) + if purchase_pickings and self._get_invoice_type() != "in_refund": + # Case more than one Purchase Order the fields below will be join + # the others will be overwritting, as done in purchase module, + # one more field include here Note + payment_refs = set() + refs = set() + # Include Note/Narration + narration = set() + for picking in purchase_pickings: + # Campos informados em qualquer caso + purchase = picking.purchase_id + + # Campo purchase_id store=false + # values["purchase_id"] = purchase.id + if picking.fiscal_operation_id: + values["issuer"] = DOCUMENT_ISSUER_PARTNER + + # Refund case don't get values from Purchase Dict + # TODO: Should get any value? + purchase_values = purchase._prepare_invoice() + + # Fields to Join + # origins.add(purchase_values["invoice_origin"]) + payment_refs.add(purchase_values["payment_reference"]) + refs.add(purchase_values["ref"]) + narration.add(purchase_values["narration"]) + + # Original dict from purchase module. + + # Fields to get from original dict: + # - "ref": self.partner_ref or "", + # - "narration": self.notes, + # - "currency_id": self.currency_id.id, + # - "invoice_user_id": self.user_id and self.user_id.id + # or self.env.user.id, + # - "payment_reference": self.partner_ref or "", + # - "partner_bank_id": partner_bank_id.id, + # - "invoice_payment_term_id": self.payment_term_id.id, + + # Fields to remove from Original Dict + vals_to_remove = { + "move_type", + "partner_id", + "fiscal_position_id", + "invoice_origin", + "invoice_line_ids", + "company_id", + # Another fields + "__last_update", + "display_name", + } + + purchase_values_rm = { + k: purchase_values[k] for k in set(purchase_values) - vals_to_remove + } + values.update(purchase_values_rm) + + # Fields to join + if len(purchase_pickings) > 1: + values.update( + { + "ref": ", ".join(refs)[:2000], + # In this case Origin get Pickings Names + # "invoice_origin": ", ".join(origins), + "payment_reference": len(payment_refs) == 1 + and payment_refs.pop() + or False, + "narration": ", ".join(narration), + } + ) + + return invoice, values + + def _get_move_key(self, move): + """ + Get the key based on the given move + :param move: stock.move recordset + :return: key + """ + key = super()._get_move_key(move) + if move.purchase_line_id: + # Field purchase_line_id in account.move is Many2one + key = key + (move.purchase_line_id,) + + return key + + def _get_invoice_line_values(self, moves, invoice_values, invoice): + """ + Create invoice line values from given moves + :param moves: stock.move + :param invoice: account.invoice + :return: dict + """ + + values = super()._get_invoice_line_values(moves, invoice_values, invoice) + # Devido ao KEY com purchase_line_id aqui + # vem somente um registro + purchase_moves = moves.filtered(lambda ln: ln.purchase_line_id) + if purchase_moves: + # Campos informados em qualquer caso + purchase_line = purchase_moves.purchase_line_id + values["purchase_line_id"] = purchase_line.id + # Same make above, get fields informed in + # original of Purchase Line dict: + purchase_line_values = purchase_line._prepare_account_move_line() + values["analytic_distribution"] = purchase_line_values.get( + "analytic_distribution" + ) + + # Refund case don't get values from Purchase Line Dict + # TODO: Should get any value? + if self._get_invoice_type() != "in_refund": + # Fields to get: + # "display_type": self.display_type or 'product' + # "sequence": self.sequence, + # * 'N field' included in _prepare_account_move_line method + # by another module + + # Fields to remove: + vals_to_remove = { + # Fields from Move has priority + "name", + "product_id", + "product_uom_id", + "quantity", + "price_unit", + "tax_ids", + # Already get + "purchase_line_id", + "anlytic_distribution", + # another fields + "__last_update", + "display_name", + } + + purchase_line_values_rm = { + k: purchase_line_values[k] + for k in set(purchase_line_values) - vals_to_remove + } + values.update(purchase_line_values_rm) + + return values + + def _create_invoice(self, invoice_values): + """Override this method if you need to change any values of the + invoice and the lines before the invoice creation + :param invoice_values: dict with the invoice and its lines + :return: invoice + """ + purchase = self.env["purchase.order"].browse(invoice_values.get("purchase_id")) + pickings = self._load_pickings() + purchase_pickings = pickings.filtered(lambda pk: pk.purchase_id) + if not purchase_pickings or self._get_invoice_type() == "in_refund": + return super()._create_invoice(invoice_values) + + # Check Other Purchase Lines + section_note_lines = self.env["purchase.order.line"] + # Resequencing + invoice_item_sequence = 10 + invoice_item_seq_dict = {} + for picking in purchase_pickings.sorted(key=lambda p: p.name): + purchase = picking.purchase_id + # Resequencing + for line in purchase.order_line: + invoice_item_seq_dict[line.id] = invoice_item_sequence + invoice_item_sequence += 1 + + # Section and Note Lines + section_note_lines |= purchase.order_line.filtered( + lambda ln: ln.display_type in ("line_section", "line_note") + ) + + for line in section_note_lines: + line_vals = line._prepare_account_move_line() + invoice_values["invoice_line_ids"].append((0, 0, line_vals)) + + # Resequence + for ln in invoice_values["invoice_line_ids"]: + if ln[0] != 5: + if ln[2] and ln[2].get("purchase_line_id"): + ln[2].update( + { + "sequence": invoice_item_seq_dict.get( + ln[2].get("purchase_line_id") + ) + } + ) + + # 3) Create invoices. + moves = self.env["account.move"] + AccountMove = self.env["account.move"].with_context( + default_move_type="in_invoice" + ) + # for vals in invoice_vals_list: + moves |= AccountMove.with_company(self.env.company).create(invoice_values) + + # 4) Some moves might actually be refunds: convert them if the + # total amount is negative + # We do this after the moves have been created since we need taxes, + # etc. to know if the total + # is actually negative or not + moves.filtered( + lambda m: m.currency_id.round(m.amount_total) < 0 + ).action_switch_invoice_into_refund_credit_note() + + return moves