diff --git a/README.md b/README.md index 6547653d704..0c38bfca3ee 100644 --- a/README.md +++ b/README.md @@ -24,8 +24,10 @@ addon | version | maintainers | summary [partner_supplierinfo_smartbutton](partner_supplierinfo_smartbutton/) | 19.0.1.0.0 | victoralmau | Access supplied products from the vendor [procurement_purchase_no_grouping](procurement_purchase_no_grouping/) | 19.0.1.0.0 | | Procurement Purchase No Grouping [product_supplier_code_purchase](product_supplier_code_purchase/) | 19.0.1.0.0 | | This module adds to the purchase order line the supplier code defined in the product. +[purchase_advance_payment](purchase_advance_payment/) | 19.0.1.0.0 | LoisRForgeFlow | Allow to add advance payments on purchase orders [purchase_allowed_product](purchase_allowed_product/) | 19.0.1.0.0 | | This module allows to select only products that can be supplied by the vendor [purchase_default_terms_conditions](purchase_default_terms_conditions/) | 19.0.1.0.0 | | This module allows purchase default terms & conditions +[purchase_delivery_split_date](purchase_delivery_split_date/) | 19.0.1.0.0 | | Allows Purchase Order you confirm to generate one Incoming Shipment for each expected date indicated in the Purchase Order Lines [purchase_force_invoiced](purchase_force_invoiced/) | 19.0.1.0.0 | | Allows to force the billing status of the purchase order to "Invoiced" [purchase_line_reassign](purchase_line_reassign/) | 19.0.1.0.0 | | Purchase Line Reassign [purchase_no_rfq](purchase_no_rfq/) | 19.0.1.0.0 | legalsylvain | Purchase Order - No Request For Quotation @@ -35,8 +37,10 @@ addon | version | maintainers | summary [purchase_order_type](purchase_order_type/) | 19.0.1.0.0 | | Purchase Order Type [purchase_order_weight_volume](purchase_order_weight_volume/) | 19.0.1.0.0 | ilyasProgrammer | Display purchase order weight and volume [purchase_partner_incoterm](purchase_partner_incoterm/) | 19.0.1.0.0 | TDu bealdav | Add a an incoterm field for supplier and use it on purchase order +[purchase_reception_status](purchase_reception_status/) | 19.0.1.0.0 | alexis-via | Add reception status on purchase orders (OCA logic) [purchase_request](purchase_request/) | 19.0.1.0.1 | | Use this module to have notification of requirements of materials and/or external services and keep track of such requirements. [purchase_stock_price_unit_sync](purchase_stock_price_unit_sync/) | 19.0.1.0.0 | | Update cost price in stock moves already done +[purchase_tag](purchase_tag/) | 19.0.1.0.0 | | Allows to add multiple tags to purchase orders [//]: # (end addons) diff --git a/purchase_advance_payment/README.rst b/purchase_advance_payment/README.rst new file mode 100644 index 00000000000..7d55e16e3cb --- /dev/null +++ b/purchase_advance_payment/README.rst @@ -0,0 +1,119 @@ +.. image:: https://odoo-community.org/readme-banner-image + :target: https://odoo-community.org/get-involved?utm_source=readme + :alt: Odoo Community Association + +======================== +Purchase Advance Payment +======================== + +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:4bd242f99f8df504d450b7931a3f4cd6f43301dc8bb99a4f52fea0daa8d55232 + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |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/license-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%2Fpurchase--workflow-lightgray.png?logo=github + :target: https://github.com/OCA/purchase-workflow/tree/19.0/purchase_advance_payment + :alt: OCA/purchase-workflow +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/purchase-workflow-19-0/purchase-workflow-19-0-purchase_advance_payment + :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/purchase-workflow&target_branch=19.0 + :alt: Try me on Runboat + +|badge1| |badge2| |badge3| |badge4| |badge5| + +The module allows to add advance payments on purchase orders and Request +for Quotation. The advance payments are allowed even before confirmation +and before starting the billing process. + +**Table of contents** + +.. contents:: + :local: + +Usage +===== + +To use this module, you need to: + +- Go to a purchase order. +- Click on "Pay Purchase Advance". +- Select the Journal and specify the amount of the advanced payment. +- "Make Advance Payment". + +When generating the invoice, the system displays the advanced payments, +select those you want to add to the invoice. + +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 +------- + +* Forgeflow + +Contributors +------------ + +- Mateu Griful + +- Lois Rilo + +- `Trobz `__: + + - Son Ho + +- `Heliconia Solutions Pvt. Ltd. `__ + + - Bhavesh Heliconia + +Other credits +------------- + +The migration of this module from 15.0 to 16.0 was financially supported +by Camptocamp + +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-LoisRForgeFlow| image:: https://github.com/LoisRForgeFlow.png?size=40px + :target: https://github.com/LoisRForgeFlow + :alt: LoisRForgeFlow + +Current `maintainer `__: + +|maintainer-LoisRForgeFlow| + +This module is part of the `OCA/purchase-workflow `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/purchase_advance_payment/__init__.py b/purchase_advance_payment/__init__.py new file mode 100644 index 00000000000..9b4296142f4 --- /dev/null +++ b/purchase_advance_payment/__init__.py @@ -0,0 +1,2 @@ +from . import models +from . import wizard diff --git a/purchase_advance_payment/__manifest__.py b/purchase_advance_payment/__manifest__.py new file mode 100644 index 00000000000..8612f5b272e --- /dev/null +++ b/purchase_advance_payment/__manifest__.py @@ -0,0 +1,23 @@ +# Copyright (C) 2021 ForgeFlow S.L. +# License AGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html) + +{ + "name": "Purchase Advance Payment", + "version": "19.0.1.0.0", + "author": "Forgeflow, Odoo Community Association (OCA)", + "website": "https://github.com/OCA/purchase-workflow", + "category": "Purchase", + "maintainers": ["LoisRForgeFlow"], + "license": "AGPL-3", + "summary": "Allow to add advance payments on purchase orders", + "depends": ["purchase"], + "data": [ + "data/ir_config_parameter.xml", + "wizard/purchase_advance_payment_wizard_view.xml", + "views/res_config_settings_views.xml", + "views/purchase_view.xml", + "security/ir.model.access.csv", + "views/account_payment_views.xml", + ], + "installable": True, +} diff --git a/purchase_advance_payment/data/ir_config_parameter.xml b/purchase_advance_payment/data/ir_config_parameter.xml new file mode 100644 index 00000000000..dcf05f855c3 --- /dev/null +++ b/purchase_advance_payment/data/ir_config_parameter.xml @@ -0,0 +1,7 @@ + + + + purchase_advance_payment.auto_post_advance_payments + True + + diff --git a/purchase_advance_payment/i18n/es.po b/purchase_advance_payment/i18n/es.po new file mode 100644 index 00000000000..5a3560eaf52 --- /dev/null +++ b/purchase_advance_payment/i18n/es.po @@ -0,0 +1,276 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * purchase_advance_payment +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 16.0\n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: 2023-07-20 10:15+0000\n" +"Last-Translator: Ivorra78 \n" +"Language-Team: none\n" +"Language: es\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: purchase_advance_payment +#: model:ir.model,name:purchase_advance_payment.model_account_voucher_wizard_purchase +msgid "Account Voucher Wizard Purchase" +msgstr "Asistente de compra de vales de cuenta" + +#. module: purchase_advance_payment +#: model:ir.actions.act_window,name:purchase_advance_payment.action_view_account_voucher_wizard +#: model_terms:ir.ui.view,arch_db:purchase_advance_payment.view_account_voucher_wizard +msgid "Advance Payment" +msgstr "Pago por Adelantado" + +#. module: purchase_advance_payment +#: model:ir.model.fields,field_description:purchase_advance_payment.field_purchase_order__advance_payment_status +msgid "Advance Payment Status" +msgstr "Estado del pago anticipado" + +#. module: purchase_advance_payment +#: model:ir.actions.act_window,name:purchase_advance_payment.action_account_advance_payments_payable +#: model:ir.ui.menu,name:purchase_advance_payment.menu_account_advance_payments_payable +msgid "Advance Payments" +msgstr "" + +#. module: purchase_advance_payment +#: model_terms:ir.ui.view,arch_db:purchase_advance_payment.purchase_order_kpis_tree +#: model_terms:ir.ui.view,arch_db:purchase_advance_payment.purchase_order_view_tree +msgid "Amount Residual" +msgstr "Importe residual" + +#. module: purchase_advance_payment +#: model:ir.model.fields,field_description:purchase_advance_payment.field_account_voucher_wizard_purchase__amount_total +msgid "Amount Total" +msgstr "Importe total" + +#. module: purchase_advance_payment +#: model:ir.model.fields,field_description:purchase_advance_payment.field_account_voucher_wizard_purchase__amount_advance +msgid "Amount advanced" +msgstr "Importe anticipado" + +#. module: purchase_advance_payment +#: model_terms:ir.ui.view,arch_db:purchase_advance_payment.view_account_voucher_wizard +msgid "Amount in Order Currency" +msgstr "Importe en la moneda del pedido" + +#. module: purchase_advance_payment +#. odoo-python +#: code:addons/purchase_advance_payment/wizard/purchase_advance_payment_wizard.py:0 +msgid "Amount of advance is greater than residual amount on purchase" +msgstr "El importe anticipado es mayor que el importe pendiente del pedido" + +#. module: purchase_advance_payment +#. odoo-python +#: code:addons/purchase_advance_payment/wizard/purchase_advance_payment_wizard.py:0 +msgid "Amount of advance must be positive." +msgstr "El importe del anticipo debe ser positivo." + +#. module: purchase_advance_payment +#: model:ir.model.fields,field_description:purchase_advance_payment.field_res_config_settings__auto_post_purchase_advance_payments +msgid "Auto Post Purchase Advance Payments" +msgstr "" + +#. module: purchase_advance_payment +#: model:ir.model.fields,field_description:purchase_advance_payment.field_res_config_settings__auto_reconcile_purchase_advance_payments +msgid "Auto Reconcile Purchase Advance Payments" +msgstr "" + +#. module: purchase_advance_payment +#: model_terms:ir.ui.view,arch_db:purchase_advance_payment.res_config_settings_view_form_purchase +msgid "Automatically reconcile payments upon bill confirmation." +msgstr "" + +#. module: purchase_advance_payment +#: model:ir.model.fields,field_description:purchase_advance_payment.field_account_voucher_wizard_purchase__available_payment_method_line_ids +msgid "Available Payment Method Line" +msgstr "" + +#. module: purchase_advance_payment +#: model_terms:ir.ui.view,arch_db:purchase_advance_payment.view_account_voucher_wizard +msgid "Cancel" +msgstr "Cancelar" + +#. module: purchase_advance_payment +#: model:ir.model.fields,field_description:purchase_advance_payment.field_account_voucher_wizard_purchase__create_uid +msgid "Created by" +msgstr "Creado por" + +#. module: purchase_advance_payment +#: model:ir.model.fields,field_description:purchase_advance_payment.field_account_voucher_wizard_purchase__create_date +msgid "Created on" +msgstr "Creado el" + +#. module: purchase_advance_payment +#: model:ir.model.fields,field_description:purchase_advance_payment.field_account_voucher_wizard_purchase__currency_amount +msgid "Curr. amount" +msgstr "Importe en divisa" + +#. module: purchase_advance_payment +#: model:ir.model.fields,field_description:purchase_advance_payment.field_account_voucher_wizard_purchase__currency_id +#: model_terms:ir.ui.view,arch_db:purchase_advance_payment.view_account_voucher_wizard +msgid "Currency" +msgstr "Moneda" + +#. module: purchase_advance_payment +#: model:ir.model.fields,field_description:purchase_advance_payment.field_account_voucher_wizard_purchase__date +msgid "Date" +msgstr "Fecha" + +#. module: purchase_advance_payment +#: model:ir.model.fields,field_description:purchase_advance_payment.field_account_voucher_wizard_purchase__display_name +msgid "Display Name" +msgstr "Nombre mostrado" + +#. module: purchase_advance_payment +#: model:ir.model.fields,field_description:purchase_advance_payment.field_account_voucher_wizard_purchase__id +msgid "ID" +msgstr "ID" + +#. module: purchase_advance_payment +#: model_terms:ir.ui.view,arch_db:purchase_advance_payment.res_config_settings_view_form_purchase +msgid "If enabled, advance payments created from POs are automatically posted." +msgstr "" + +#. module: purchase_advance_payment +#: model_terms:ir.ui.view,arch_db:purchase_advance_payment.res_config_settings_view_form_purchase +msgid "" +"If enabled, advance payments created from POs are automatically reconciled " +"upon bill confirmation." +msgstr "" + +#. module: purchase_advance_payment +#: model:ir.model.fields,field_description:purchase_advance_payment.field_account_voucher_wizard_purchase__journal_id +msgid "Journal" +msgstr "Diario" + +#. module: purchase_advance_payment +#: model:ir.model.fields,field_description:purchase_advance_payment.field_account_voucher_wizard_purchase__journal_currency_id +msgid "Journal Currency" +msgstr "Moneda del diario" + +#. module: purchase_advance_payment +#: model:ir.model,name:purchase_advance_payment.model_account_move +msgid "Journal Entry" +msgstr "" + +#. module: purchase_advance_payment +#: model:ir.model.fields,field_description:purchase_advance_payment.field_account_voucher_wizard_purchase__write_uid +msgid "Last Updated by" +msgstr "última modificación por" + +#. module: purchase_advance_payment +#: model:ir.model.fields,field_description:purchase_advance_payment.field_account_voucher_wizard_purchase__write_date +msgid "Last Updated on" +msgstr "Última actualización el" + +#. module: purchase_advance_payment +#: model_terms:ir.ui.view,arch_db:purchase_advance_payment.view_account_voucher_wizard +msgid "Make advance payment" +msgstr "Realizar anticipo" + +#. module: purchase_advance_payment +#: model:ir.model.fields.selection,name:purchase_advance_payment.selection__purchase_order__advance_payment_status__not_paid +msgid "Not Paid" +msgstr "No pagado" + +#. module: purchase_advance_payment +#: model_terms:ir.ui.view,arch_db:purchase_advance_payment.view_account_payment_search +msgid "Not Reconciled" +msgstr "" + +#. module: purchase_advance_payment +#: model:ir.model.fields,field_description:purchase_advance_payment.field_account_voucher_wizard_purchase__order_id +msgid "Order" +msgstr "Pedido" + +#. module: purchase_advance_payment +#: model_terms:ir.ui.view,arch_db:purchase_advance_payment.view_account_voucher_wizard +msgid "Order Due Amount" +msgstr "Importe pendiente del pedido" + +#. module: purchase_advance_payment +#: model:ir.model.fields.selection,name:purchase_advance_payment.selection__purchase_order__advance_payment_status__paid +msgid "Paid" +msgstr "Pagado" + +#. module: purchase_advance_payment +#: model:ir.model.fields.selection,name:purchase_advance_payment.selection__purchase_order__advance_payment_status__partial +msgid "Partially Paid" +msgstr "Pagado parcialmente" + +#. module: purchase_advance_payment +#: model:ir.model.fields,field_description:purchase_advance_payment.field_purchase_order__account_payment_ids +#: model_terms:ir.ui.view,arch_db:purchase_advance_payment.purchase_order_form +msgid "Pay purchase advanced" +msgstr "Pagar anticipo de compra" + +#. module: purchase_advance_payment +#: model:ir.model.fields,field_description:purchase_advance_payment.field_account_voucher_wizard_purchase__payment_method_line_id +msgid "Payment Method" +msgstr "Método de pago" + +#. module: purchase_advance_payment +#: model_terms:ir.ui.view,arch_db:purchase_advance_payment.view_account_supplier_payment_tree +msgid "Payment Sent?" +msgstr "" + +#. module: purchase_advance_payment +#: model_terms:ir.ui.view,arch_db:purchase_advance_payment.purchase_order_form +msgid "Payment advances" +msgstr "Anticipos" + +#. module: purchase_advance_payment +#: model:ir.model.fields,field_description:purchase_advance_payment.field_purchase_order__payment_line_ids +msgid "Payment move lines" +msgstr "Líneas de asiento de pago" + +#. module: purchase_advance_payment +#: model:ir.model,name:purchase_advance_payment.model_account_payment +msgid "Payments" +msgstr "Pagos" + +#. module: purchase_advance_payment +#: model:ir.model,name:purchase_advance_payment.model_res_config_settings +msgid "Procurement purchase grouping settings" +msgstr "" + +#. module: purchase_advance_payment +#: model:ir.model.fields,field_description:purchase_advance_payment.field_account_payment__purchase_id +msgid "Purchase" +msgstr "Compra" + +#. module: purchase_advance_payment +#: model:ir.model,name:purchase_advance_payment.model_purchase_order +#: model_terms:ir.ui.view,arch_db:purchase_advance_payment.view_account_payment_form +msgid "Purchase Order" +msgstr "Pedido de compra" + +#. module: purchase_advance_payment +#: model:ir.model.fields,field_description:purchase_advance_payment.field_account_voucher_wizard_purchase__payment_ref +msgid "Ref." +msgstr "Ref." + +#. module: purchase_advance_payment +#: model:ir.model.fields,field_description:purchase_advance_payment.field_purchase_order__amount_residual +msgid "Residual amount" +msgstr "Importe pendiente" + +#. module: purchase_advance_payment +#: model_terms:ir.ui.view,arch_db:purchase_advance_payment.view_account_payment_search +msgid "Vendor Advance Payments" +msgstr "" + +#~ msgid "Operation" +#~ msgstr "Operación" + +#~ msgid "Order Currency" +#~ msgstr "Moneda del pedido" + +#~ msgid "Last Modified on" +#~ msgstr "última modificación el" diff --git a/purchase_advance_payment/i18n/fr.po b/purchase_advance_payment/i18n/fr.po new file mode 100644 index 00000000000..ab2542e8d02 --- /dev/null +++ b/purchase_advance_payment/i18n/fr.po @@ -0,0 +1,272 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * purchase_advance_payment +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 14.0\n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: 2024-10-22 15:06+0000\n" +"Last-Translator: epanisset \n" +"Language-Team: none\n" +"Language: fr\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 5.6.2\n" + +#. module: purchase_advance_payment +#: model:ir.model,name:purchase_advance_payment.model_account_voucher_wizard_purchase +msgid "Account Voucher Wizard Purchase" +msgstr "Wizard de justificatif comptable d'achat" + +#. module: purchase_advance_payment +#: model:ir.actions.act_window,name:purchase_advance_payment.action_view_account_voucher_wizard +#: model_terms:ir.ui.view,arch_db:purchase_advance_payment.view_account_voucher_wizard +msgid "Advance Payment" +msgstr "Paiement anticipé" + +#. module: purchase_advance_payment +#: model:ir.model.fields,field_description:purchase_advance_payment.field_purchase_order__advance_payment_status +msgid "Advance Payment Status" +msgstr "Statut Paiement Anticipé" + +#. module: purchase_advance_payment +#: model:ir.actions.act_window,name:purchase_advance_payment.action_account_advance_payments_payable +#: model:ir.ui.menu,name:purchase_advance_payment.menu_account_advance_payments_payable +msgid "Advance Payments" +msgstr "Paiements anticipés" + +#. module: purchase_advance_payment +#: model_terms:ir.ui.view,arch_db:purchase_advance_payment.purchase_order_kpis_tree +#: model_terms:ir.ui.view,arch_db:purchase_advance_payment.purchase_order_view_tree +msgid "Amount Residual" +msgstr "Montant Résiduel" + +#. module: purchase_advance_payment +#: model:ir.model.fields,field_description:purchase_advance_payment.field_account_voucher_wizard_purchase__amount_total +msgid "Amount Total" +msgstr "Montant Total" + +#. module: purchase_advance_payment +#: model:ir.model.fields,field_description:purchase_advance_payment.field_account_voucher_wizard_purchase__amount_advance +msgid "Amount advanced" +msgstr "Montant Avancé" + +#. module: purchase_advance_payment +#: model_terms:ir.ui.view,arch_db:purchase_advance_payment.view_account_voucher_wizard +msgid "Amount in Order Currency" +msgstr "Montant en devise" + +#. module: purchase_advance_payment +#. odoo-python +#: code:addons/purchase_advance_payment/wizard/purchase_advance_payment_wizard.py:0 +msgid "Amount of advance is greater than residual amount on purchase" +msgstr "Le montant de l'avance est supérieur au montant résiduel sur l'achat" + +#. module: purchase_advance_payment +#. odoo-python +#: code:addons/purchase_advance_payment/wizard/purchase_advance_payment_wizard.py:0 +msgid "Amount of advance must be positive." +msgstr "Le montant de l'avance doit être positif." + +#. module: purchase_advance_payment +#: model:ir.model.fields,field_description:purchase_advance_payment.field_res_config_settings__auto_post_purchase_advance_payments +msgid "Auto Post Purchase Advance Payments" +msgstr "Validation automatique des paiements anticipés" + +#. module: purchase_advance_payment +#: model:ir.model.fields,field_description:purchase_advance_payment.field_res_config_settings__auto_reconcile_purchase_advance_payments +msgid "Auto Reconcile Purchase Advance Payments" +msgstr "" + +#. module: purchase_advance_payment +#: model_terms:ir.ui.view,arch_db:purchase_advance_payment.res_config_settings_view_form_purchase +msgid "Automatically reconcile payments upon bill confirmation." +msgstr "" + +#. module: purchase_advance_payment +#: model:ir.model.fields,field_description:purchase_advance_payment.field_account_voucher_wizard_purchase__available_payment_method_line_ids +msgid "Available Payment Method Line" +msgstr "Méthodes de Paiement Disponibles" + +#. module: purchase_advance_payment +#: model_terms:ir.ui.view,arch_db:purchase_advance_payment.view_account_voucher_wizard +msgid "Cancel" +msgstr "Annuler" + +#. module: purchase_advance_payment +#: model:ir.model.fields,field_description:purchase_advance_payment.field_account_voucher_wizard_purchase__create_uid +msgid "Created by" +msgstr "Créé par" + +#. module: purchase_advance_payment +#: model:ir.model.fields,field_description:purchase_advance_payment.field_account_voucher_wizard_purchase__create_date +msgid "Created on" +msgstr "Créé le" + +#. module: purchase_advance_payment +#: model:ir.model.fields,field_description:purchase_advance_payment.field_account_voucher_wizard_purchase__currency_amount +msgid "Curr. amount" +msgstr "Montant en devise" + +#. module: purchase_advance_payment +#: model:ir.model.fields,field_description:purchase_advance_payment.field_account_voucher_wizard_purchase__currency_id +#: model_terms:ir.ui.view,arch_db:purchase_advance_payment.view_account_voucher_wizard +msgid "Currency" +msgstr "Devise" + +#. module: purchase_advance_payment +#: model:ir.model.fields,field_description:purchase_advance_payment.field_account_voucher_wizard_purchase__date +msgid "Date" +msgstr "Date" + +#. module: purchase_advance_payment +#: model:ir.model.fields,field_description:purchase_advance_payment.field_account_voucher_wizard_purchase__display_name +msgid "Display Name" +msgstr "Nom Affiché" + +#. module: purchase_advance_payment +#: model:ir.model.fields,field_description:purchase_advance_payment.field_account_voucher_wizard_purchase__id +msgid "ID" +msgstr "ID" + +#. module: purchase_advance_payment +#: model_terms:ir.ui.view,arch_db:purchase_advance_payment.res_config_settings_view_form_purchase +msgid "If enabled, advance payments created from POs are automatically posted." +msgstr "" +"Si activé, les paiements anticipés créés à partir des commandes d'achat sont " +"automatiquement comptabilisés." + +#. module: purchase_advance_payment +#: model_terms:ir.ui.view,arch_db:purchase_advance_payment.res_config_settings_view_form_purchase +msgid "" +"If enabled, advance payments created from POs are automatically reconciled " +"upon bill confirmation." +msgstr "" + +#. module: purchase_advance_payment +#: model:ir.model.fields,field_description:purchase_advance_payment.field_account_voucher_wizard_purchase__journal_id +msgid "Journal" +msgstr "Journal" + +#. module: purchase_advance_payment +#: model:ir.model.fields,field_description:purchase_advance_payment.field_account_voucher_wizard_purchase__journal_currency_id +msgid "Journal Currency" +msgstr "Devise du Journal" + +#. module: purchase_advance_payment +#: model:ir.model,name:purchase_advance_payment.model_account_move +msgid "Journal Entry" +msgstr "" + +#. module: purchase_advance_payment +#: model:ir.model.fields,field_description:purchase_advance_payment.field_account_voucher_wizard_purchase__write_uid +msgid "Last Updated by" +msgstr "Dernière modification par" + +#. module: purchase_advance_payment +#: model:ir.model.fields,field_description:purchase_advance_payment.field_account_voucher_wizard_purchase__write_date +msgid "Last Updated on" +msgstr "Dernière modification le" + +#. module: purchase_advance_payment +#: model_terms:ir.ui.view,arch_db:purchase_advance_payment.view_account_voucher_wizard +msgid "Make advance payment" +msgstr "Faire un paiement anticipé" + +#. module: purchase_advance_payment +#: model:ir.model.fields.selection,name:purchase_advance_payment.selection__purchase_order__advance_payment_status__not_paid +msgid "Not Paid" +msgstr "Non Payé" + +#. module: purchase_advance_payment +#: model_terms:ir.ui.view,arch_db:purchase_advance_payment.view_account_payment_search +msgid "Not Reconciled" +msgstr "Non Réconcilié" + +#. module: purchase_advance_payment +#: model:ir.model.fields,field_description:purchase_advance_payment.field_account_voucher_wizard_purchase__order_id +msgid "Order" +msgstr "Commande" + +#. module: purchase_advance_payment +#: model_terms:ir.ui.view,arch_db:purchase_advance_payment.view_account_voucher_wizard +msgid "Order Due Amount" +msgstr "Montant dû sur la Commande" + +#. module: purchase_advance_payment +#: model:ir.model.fields.selection,name:purchase_advance_payment.selection__purchase_order__advance_payment_status__paid +msgid "Paid" +msgstr "Payé" + +#. module: purchase_advance_payment +#: model:ir.model.fields.selection,name:purchase_advance_payment.selection__purchase_order__advance_payment_status__partial +msgid "Partially Paid" +msgstr "Partiellement Payé" + +#. module: purchase_advance_payment +#: model:ir.model.fields,field_description:purchase_advance_payment.field_purchase_order__account_payment_ids +#: model_terms:ir.ui.view,arch_db:purchase_advance_payment.purchase_order_form +msgid "Pay purchase advanced" +msgstr "Payer le paiement en avance" + +#. module: purchase_advance_payment +#: model:ir.model.fields,field_description:purchase_advance_payment.field_account_voucher_wizard_purchase__payment_method_line_id +msgid "Payment Method" +msgstr "Méthode de Paiement" + +#. module: purchase_advance_payment +#: model_terms:ir.ui.view,arch_db:purchase_advance_payment.view_account_supplier_payment_tree +msgid "Payment Sent?" +msgstr "Paiement Envoyé ?" + +#. module: purchase_advance_payment +#: model_terms:ir.ui.view,arch_db:purchase_advance_payment.purchase_order_form +msgid "Payment advances" +msgstr "Paiements anticipés" + +#. module: purchase_advance_payment +#: model:ir.model.fields,field_description:purchase_advance_payment.field_purchase_order__payment_line_ids +msgid "Payment move lines" +msgstr "Lignes de Paiement" + +#. module: purchase_advance_payment +#: model:ir.model,name:purchase_advance_payment.model_account_payment +msgid "Payments" +msgstr "Paiements" + +#. module: purchase_advance_payment +#: model:ir.model,name:purchase_advance_payment.model_res_config_settings +msgid "Procurement purchase grouping settings" +msgstr "Paramètres de regroupement des achats" + +#. module: purchase_advance_payment +#: model:ir.model.fields,field_description:purchase_advance_payment.field_account_payment__purchase_id +msgid "Purchase" +msgstr "Achat" + +#. module: purchase_advance_payment +#: model:ir.model,name:purchase_advance_payment.model_purchase_order +#: model_terms:ir.ui.view,arch_db:purchase_advance_payment.view_account_payment_form +msgid "Purchase Order" +msgstr "Commande d'Achat" + +#. module: purchase_advance_payment +#: model:ir.model.fields,field_description:purchase_advance_payment.field_account_voucher_wizard_purchase__payment_ref +msgid "Ref." +msgstr "Réf." + +#. module: purchase_advance_payment +#: model:ir.model.fields,field_description:purchase_advance_payment.field_purchase_order__amount_residual +msgid "Residual amount" +msgstr "Montant Résiduel" + +#. module: purchase_advance_payment +#: model_terms:ir.ui.view,arch_db:purchase_advance_payment.view_account_payment_search +msgid "Vendor Advance Payments" +msgstr "Paiements anticipés aux fournisseurs" + +#~ msgid "Not Sent" +#~ msgstr "Non Envoyé" diff --git a/purchase_advance_payment/i18n/it.po b/purchase_advance_payment/i18n/it.po new file mode 100644 index 00000000000..7e1817676c2 --- /dev/null +++ b/purchase_advance_payment/i18n/it.po @@ -0,0 +1,280 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * purchase_advance_payment +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 17.0\n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: 2025-07-30 12:43+0000\n" +"Last-Translator: mymage \n" +"Language-Team: none\n" +"Language: it\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 5.10.4\n" + +#. module: purchase_advance_payment +#: model:ir.model,name:purchase_advance_payment.model_account_voucher_wizard_purchase +msgid "Account Voucher Wizard Purchase" +msgstr "Conto buono procedura guidata acquisto" + +#. module: purchase_advance_payment +#: model:ir.actions.act_window,name:purchase_advance_payment.action_view_account_voucher_wizard +#: model_terms:ir.ui.view,arch_db:purchase_advance_payment.view_account_voucher_wizard +msgid "Advance Payment" +msgstr "Pagamento anticipato" + +#. module: purchase_advance_payment +#: model:ir.model.fields,field_description:purchase_advance_payment.field_purchase_order__advance_payment_status +msgid "Advance Payment Status" +msgstr "Stato pagamento anticipato" + +#. module: purchase_advance_payment +#: model:ir.actions.act_window,name:purchase_advance_payment.action_account_advance_payments_payable +#: model:ir.ui.menu,name:purchase_advance_payment.menu_account_advance_payments_payable +msgid "Advance Payments" +msgstr "Anticipo pagamenti" + +#. module: purchase_advance_payment +#: model_terms:ir.ui.view,arch_db:purchase_advance_payment.purchase_order_kpis_tree +#: model_terms:ir.ui.view,arch_db:purchase_advance_payment.purchase_order_view_tree +msgid "Amount Residual" +msgstr "Importo residuo" + +#. module: purchase_advance_payment +#: model:ir.model.fields,field_description:purchase_advance_payment.field_account_voucher_wizard_purchase__amount_total +msgid "Amount Total" +msgstr "Importo totale" + +#. module: purchase_advance_payment +#: model:ir.model.fields,field_description:purchase_advance_payment.field_account_voucher_wizard_purchase__amount_advance +msgid "Amount advanced" +msgstr "Importo anticipato" + +#. module: purchase_advance_payment +#: model_terms:ir.ui.view,arch_db:purchase_advance_payment.view_account_voucher_wizard +msgid "Amount in Order Currency" +msgstr "Importo nella valuta dell'ordine" + +#. module: purchase_advance_payment +#. odoo-python +#: code:addons/purchase_advance_payment/wizard/purchase_advance_payment_wizard.py:0 +msgid "Amount of advance is greater than residual amount on purchase" +msgstr "L'importo dell'anticipo è superiore all'importo residuo in vendita" + +#. module: purchase_advance_payment +#. odoo-python +#: code:addons/purchase_advance_payment/wizard/purchase_advance_payment_wizard.py:0 +msgid "Amount of advance must be positive." +msgstr "L'importo dell'anticipo deve essere positivo." + +#. module: purchase_advance_payment +#: model:ir.model.fields,field_description:purchase_advance_payment.field_res_config_settings__auto_post_purchase_advance_payments +msgid "Auto Post Purchase Advance Payments" +msgstr "Inserimento automatico anticipo pagamento acquisto" + +#. module: purchase_advance_payment +#: model:ir.model.fields,field_description:purchase_advance_payment.field_res_config_settings__auto_reconcile_purchase_advance_payments +msgid "Auto Reconcile Purchase Advance Payments" +msgstr "Riconciliazione automatica dei pagamenti anticipati per gli acquisti" + +#. module: purchase_advance_payment +#: model_terms:ir.ui.view,arch_db:purchase_advance_payment.res_config_settings_view_form_purchase +msgid "Automatically reconcile payments upon bill confirmation." +msgstr "Riconcilia automaticamente i pagamenti alla conferma della fattura." + +#. module: purchase_advance_payment +#: model:ir.model.fields,field_description:purchase_advance_payment.field_account_voucher_wizard_purchase__available_payment_method_line_ids +msgid "Available Payment Method Line" +msgstr "Riga metodo di pagamento disponibile" + +#. module: purchase_advance_payment +#: model_terms:ir.ui.view,arch_db:purchase_advance_payment.view_account_voucher_wizard +msgid "Cancel" +msgstr "Annulla" + +#. module: purchase_advance_payment +#: model:ir.model.fields,field_description:purchase_advance_payment.field_account_voucher_wizard_purchase__create_uid +msgid "Created by" +msgstr "Creato da" + +#. module: purchase_advance_payment +#: model:ir.model.fields,field_description:purchase_advance_payment.field_account_voucher_wizard_purchase__create_date +msgid "Created on" +msgstr "Creato il" + +#. module: purchase_advance_payment +#: model:ir.model.fields,field_description:purchase_advance_payment.field_account_voucher_wizard_purchase__currency_amount +msgid "Curr. amount" +msgstr "Importo valuta" + +#. module: purchase_advance_payment +#: model:ir.model.fields,field_description:purchase_advance_payment.field_account_voucher_wizard_purchase__currency_id +#: model_terms:ir.ui.view,arch_db:purchase_advance_payment.view_account_voucher_wizard +msgid "Currency" +msgstr "Valuta" + +#. module: purchase_advance_payment +#: model:ir.model.fields,field_description:purchase_advance_payment.field_account_voucher_wizard_purchase__date +msgid "Date" +msgstr "Data" + +#. module: purchase_advance_payment +#: model:ir.model.fields,field_description:purchase_advance_payment.field_account_voucher_wizard_purchase__display_name +msgid "Display Name" +msgstr "Nome visualizzato" + +#. module: purchase_advance_payment +#: model:ir.model.fields,field_description:purchase_advance_payment.field_account_voucher_wizard_purchase__id +msgid "ID" +msgstr "ID" + +#. module: purchase_advance_payment +#: model_terms:ir.ui.view,arch_db:purchase_advance_payment.res_config_settings_view_form_purchase +msgid "If enabled, advance payments created from POs are automatically posted." +msgstr "" +"Se abilitata, gli anticipi pagamenti creati dai POS sono inseriti " +"automaticamente." + +#. module: purchase_advance_payment +#: model_terms:ir.ui.view,arch_db:purchase_advance_payment.res_config_settings_view_form_purchase +msgid "" +"If enabled, advance payments created from POs are automatically reconciled " +"upon bill confirmation." +msgstr "" +"Se abilitata, i pagamenti anticipati creati dagli ordini di acquisto vengono " +"automaticamente riconciliati al momento della conferma della fattura." + +#. module: purchase_advance_payment +#: model:ir.model.fields,field_description:purchase_advance_payment.field_account_voucher_wizard_purchase__journal_id +msgid "Journal" +msgstr "Registro" + +#. module: purchase_advance_payment +#: model:ir.model.fields,field_description:purchase_advance_payment.field_account_voucher_wizard_purchase__journal_currency_id +msgid "Journal Currency" +msgstr "Valuta registro" + +#. module: purchase_advance_payment +#: model:ir.model,name:purchase_advance_payment.model_account_move +msgid "Journal Entry" +msgstr "Registrazione contabile" + +#. module: purchase_advance_payment +#: model:ir.model.fields,field_description:purchase_advance_payment.field_account_voucher_wizard_purchase__write_uid +msgid "Last Updated by" +msgstr "Ultimo aggiornamento di" + +#. module: purchase_advance_payment +#: model:ir.model.fields,field_description:purchase_advance_payment.field_account_voucher_wizard_purchase__write_date +msgid "Last Updated on" +msgstr "Ultimo aggiornamento il" + +#. module: purchase_advance_payment +#: model_terms:ir.ui.view,arch_db:purchase_advance_payment.view_account_voucher_wizard +msgid "Make advance payment" +msgstr "Effettua il pagamento anticipato" + +#. module: purchase_advance_payment +#: model:ir.model.fields.selection,name:purchase_advance_payment.selection__purchase_order__advance_payment_status__not_paid +msgid "Not Paid" +msgstr "Non pagato" + +#. module: purchase_advance_payment +#: model_terms:ir.ui.view,arch_db:purchase_advance_payment.view_account_payment_search +msgid "Not Reconciled" +msgstr "Non riconciliata" + +#. module: purchase_advance_payment +#: model:ir.model.fields,field_description:purchase_advance_payment.field_account_voucher_wizard_purchase__order_id +msgid "Order" +msgstr "Ordine" + +#. module: purchase_advance_payment +#: model_terms:ir.ui.view,arch_db:purchase_advance_payment.view_account_voucher_wizard +msgid "Order Due Amount" +msgstr "Importo dovuto dell'ordine" + +#. module: purchase_advance_payment +#: model:ir.model.fields.selection,name:purchase_advance_payment.selection__purchase_order__advance_payment_status__paid +msgid "Paid" +msgstr "Pagato" + +#. module: purchase_advance_payment +#: model:ir.model.fields.selection,name:purchase_advance_payment.selection__purchase_order__advance_payment_status__partial +msgid "Partially Paid" +msgstr "Pagato parzialmente" + +#. module: purchase_advance_payment +#: model:ir.model.fields,field_description:purchase_advance_payment.field_purchase_order__account_payment_ids +#: model_terms:ir.ui.view,arch_db:purchase_advance_payment.purchase_order_form +msgid "Pay purchase advanced" +msgstr "Paga anticipo acquisto" + +#. module: purchase_advance_payment +#: model:ir.model.fields,field_description:purchase_advance_payment.field_account_voucher_wizard_purchase__payment_method_line_id +msgid "Payment Method" +msgstr "Metodo di pagamento" + +#. module: purchase_advance_payment +#: model_terms:ir.ui.view,arch_db:purchase_advance_payment.view_account_supplier_payment_tree +msgid "Payment Sent?" +msgstr "Pagamento inviato?" + +#. module: purchase_advance_payment +#: model_terms:ir.ui.view,arch_db:purchase_advance_payment.purchase_order_form +msgid "Payment advances" +msgstr "Pagamenti anticipati" + +#. module: purchase_advance_payment +#: model:ir.model.fields,field_description:purchase_advance_payment.field_purchase_order__payment_line_ids +msgid "Payment move lines" +msgstr "Righe movimento pagamento" + +#. module: purchase_advance_payment +#: model:ir.model,name:purchase_advance_payment.model_account_payment +msgid "Payments" +msgstr "Pagamenti" + +#. module: purchase_advance_payment +#: model:ir.model,name:purchase_advance_payment.model_res_config_settings +msgid "Procurement purchase grouping settings" +msgstr "Impostazioni raggruppamento approvvigionamento acquisti" + +#. module: purchase_advance_payment +#: model:ir.model.fields,field_description:purchase_advance_payment.field_account_payment__purchase_id +msgid "Purchase" +msgstr "Acquisto" + +#. module: purchase_advance_payment +#: model:ir.model,name:purchase_advance_payment.model_purchase_order +#: model_terms:ir.ui.view,arch_db:purchase_advance_payment.view_account_payment_form +msgid "Purchase Order" +msgstr "Ordine di acquisto" + +#. module: purchase_advance_payment +#: model:ir.model.fields,field_description:purchase_advance_payment.field_account_voucher_wizard_purchase__payment_ref +msgid "Ref." +msgstr "Rif." + +#. module: purchase_advance_payment +#: model:ir.model.fields,field_description:purchase_advance_payment.field_purchase_order__amount_residual +msgid "Residual amount" +msgstr "Importo residuo" + +#. module: purchase_advance_payment +#: model_terms:ir.ui.view,arch_db:purchase_advance_payment.view_account_payment_search +msgid "Vendor Advance Payments" +msgstr "Anticipo pagamenti fornitore" + +#~ msgid "Not Sent" +#~ msgstr "Non inviato" + +#~ msgid "Operation" +#~ msgstr "Operazione" + +#~ msgid "Order Currency" +#~ msgstr "Valuta ordine" diff --git a/purchase_advance_payment/i18n/pt.po b/purchase_advance_payment/i18n/pt.po new file mode 100644 index 00000000000..1c3f6dc3179 --- /dev/null +++ b/purchase_advance_payment/i18n/pt.po @@ -0,0 +1,272 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * purchase_advance_payment +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 18.0\n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: 2025-08-22 12:25+0000\n" +"Last-Translator: Daniel Reis \n" +"Language-Team: none\n" +"Language: pt\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 5.10.4\n" + +#. module: purchase_advance_payment +#: model:ir.model,name:purchase_advance_payment.model_account_voucher_wizard_purchase +msgid "Account Voucher Wizard Purchase" +msgstr "Assistente de vales de compra" + +#. module: purchase_advance_payment +#: model:ir.actions.act_window,name:purchase_advance_payment.action_view_account_voucher_wizard +#: model_terms:ir.ui.view,arch_db:purchase_advance_payment.view_account_voucher_wizard +msgid "Advance Payment" +msgstr "Pagamento Adiantado" + +#. module: purchase_advance_payment +#: model:ir.model.fields,field_description:purchase_advance_payment.field_purchase_order__advance_payment_status +msgid "Advance Payment Status" +msgstr "Estado do Pagamento Adiantado" + +#. module: purchase_advance_payment +#: model:ir.actions.act_window,name:purchase_advance_payment.action_account_advance_payments_payable +#: model:ir.ui.menu,name:purchase_advance_payment.menu_account_advance_payments_payable +msgid "Advance Payments" +msgstr "Pagamentos Adiantados" + +#. module: purchase_advance_payment +#: model_terms:ir.ui.view,arch_db:purchase_advance_payment.purchase_order_kpis_tree +#: model_terms:ir.ui.view,arch_db:purchase_advance_payment.purchase_order_view_tree +msgid "Amount Residual" +msgstr "Montante Residual" + +#. module: purchase_advance_payment +#: model:ir.model.fields,field_description:purchase_advance_payment.field_account_voucher_wizard_purchase__amount_total +msgid "Amount Total" +msgstr "Montante Total" + +#. module: purchase_advance_payment +#: model:ir.model.fields,field_description:purchase_advance_payment.field_account_voucher_wizard_purchase__amount_advance +msgid "Amount advanced" +msgstr "Montante Adiantado" + +#. module: purchase_advance_payment +#: model_terms:ir.ui.view,arch_db:purchase_advance_payment.view_account_voucher_wizard +msgid "Amount in Order Currency" +msgstr "Montante na Moeda da Compra" + +#. module: purchase_advance_payment +#. odoo-python +#: code:addons/purchase_advance_payment/wizard/purchase_advance_payment_wizard.py:0 +msgid "Amount of advance is greater than residual amount on purchase" +msgstr "O valor do adiantamento é maior que o valor residual na compra" + +#. module: purchase_advance_payment +#. odoo-python +#: code:addons/purchase_advance_payment/wizard/purchase_advance_payment_wizard.py:0 +msgid "Amount of advance must be positive." +msgstr "O valor do adiantamento deve ser positivo." + +#. module: purchase_advance_payment +#: model:ir.model.fields,field_description:purchase_advance_payment.field_res_config_settings__auto_post_purchase_advance_payments +msgid "Auto Post Purchase Advance Payments" +msgstr "Validação automática dos pagamentos adiantados" + +#. module: purchase_advance_payment +#: model:ir.model.fields,field_description:purchase_advance_payment.field_res_config_settings__auto_reconcile_purchase_advance_payments +msgid "Auto Reconcile Purchase Advance Payments" +msgstr "Auto reconciliação dos pagamentos adiantados" + +#. module: purchase_advance_payment +#: model_terms:ir.ui.view,arch_db:purchase_advance_payment.res_config_settings_view_form_purchase +msgid "Automatically reconcile payments upon bill confirmation." +msgstr "Reconciliação automática dos pagamentos com a validação da fatura." + +#. module: purchase_advance_payment +#: model:ir.model.fields,field_description:purchase_advance_payment.field_account_voucher_wizard_purchase__available_payment_method_line_ids +msgid "Available Payment Method Line" +msgstr "Métodos de Pagamento Disponíveis" + +#. module: purchase_advance_payment +#: model_terms:ir.ui.view,arch_db:purchase_advance_payment.view_account_voucher_wizard +msgid "Cancel" +msgstr "Cancelar" + +#. module: purchase_advance_payment +#: model:ir.model.fields,field_description:purchase_advance_payment.field_account_voucher_wizard_purchase__create_uid +msgid "Created by" +msgstr "Criado por" + +#. module: purchase_advance_payment +#: model:ir.model.fields,field_description:purchase_advance_payment.field_account_voucher_wizard_purchase__create_date +msgid "Created on" +msgstr "Criado em" + +#. module: purchase_advance_payment +#: model:ir.model.fields,field_description:purchase_advance_payment.field_account_voucher_wizard_purchase__currency_amount +msgid "Curr. amount" +msgstr "Valor na moeda" + +#. module: purchase_advance_payment +#: model:ir.model.fields,field_description:purchase_advance_payment.field_account_voucher_wizard_purchase__currency_id +#: model_terms:ir.ui.view,arch_db:purchase_advance_payment.view_account_voucher_wizard +msgid "Currency" +msgstr "Moeda" + +#. module: purchase_advance_payment +#: model:ir.model.fields,field_description:purchase_advance_payment.field_account_voucher_wizard_purchase__date +msgid "Date" +msgstr "Data" + +#. module: purchase_advance_payment +#: model:ir.model.fields,field_description:purchase_advance_payment.field_account_voucher_wizard_purchase__display_name +msgid "Display Name" +msgstr "Nome Apresentado" + +#. module: purchase_advance_payment +#: model:ir.model.fields,field_description:purchase_advance_payment.field_account_voucher_wizard_purchase__id +msgid "ID" +msgstr "ID" + +#. module: purchase_advance_payment +#: model_terms:ir.ui.view,arch_db:purchase_advance_payment.res_config_settings_view_form_purchase +msgid "" +"If enabled, advance payments created from POs are automatically posted." +msgstr "" +"Se ativado, os pagamentos criados a partir da ordem de compra são " +"automaticamente contabilizados." + +#. module: purchase_advance_payment +#: model_terms:ir.ui.view,arch_db:purchase_advance_payment.res_config_settings_view_form_purchase +msgid "" +"If enabled, advance payments created from POs are automatically reconciled " +"upon bill confirmation." +msgstr "" +"Se ativado, os pagamentos adiantados criados a partir de uma ordem de compra " +"são automaticamente reconciliados com a validação da fatura." + +#. module: purchase_advance_payment +#: model:ir.model.fields,field_description:purchase_advance_payment.field_account_voucher_wizard_purchase__journal_id +msgid "Journal" +msgstr "Diário" + +#. module: purchase_advance_payment +#: model:ir.model.fields,field_description:purchase_advance_payment.field_account_voucher_wizard_purchase__journal_currency_id +msgid "Journal Currency" +msgstr "Moeda do Diário" + +#. module: purchase_advance_payment +#: model:ir.model,name:purchase_advance_payment.model_account_move +msgid "Journal Entry" +msgstr "Lançamento Contabilístico" + +#. module: purchase_advance_payment +#: model:ir.model.fields,field_description:purchase_advance_payment.field_account_voucher_wizard_purchase__write_uid +msgid "Last Updated by" +msgstr "Última Atualização por" + +#. module: purchase_advance_payment +#: model:ir.model.fields,field_description:purchase_advance_payment.field_account_voucher_wizard_purchase__write_date +msgid "Last Updated on" +msgstr "Última Atualização em" + +#. module: purchase_advance_payment +#: model_terms:ir.ui.view,arch_db:purchase_advance_payment.view_account_voucher_wizard +msgid "Make advance payment" +msgstr "Fazer Pagamento Adiantado" + +#. module: purchase_advance_payment +#: model:ir.model.fields.selection,name:purchase_advance_payment.selection__purchase_order__advance_payment_status__not_paid +msgid "Not Paid" +msgstr "Não Pago" + +#. module: purchase_advance_payment +#: model_terms:ir.ui.view,arch_db:purchase_advance_payment.view_account_payment_search +msgid "Not Reconciled" +msgstr "Não Reconciliado" + +#. module: purchase_advance_payment +#: model:ir.model.fields,field_description:purchase_advance_payment.field_account_voucher_wizard_purchase__order_id +msgid "Order" +msgstr "Pedido" + +#. module: purchase_advance_payment +#: model_terms:ir.ui.view,arch_db:purchase_advance_payment.view_account_voucher_wizard +msgid "Order Due Amount" +msgstr "Valor em Dívida do Pedido" + +#. module: purchase_advance_payment +#: model:ir.model.fields.selection,name:purchase_advance_payment.selection__purchase_order__advance_payment_status__paid +msgid "Paid" +msgstr "Pago" + +#. module: purchase_advance_payment +#: model:ir.model.fields.selection,name:purchase_advance_payment.selection__purchase_order__advance_payment_status__partial +msgid "Partially Paid" +msgstr "Parcialmente Pago" + +#. module: purchase_advance_payment +#: model:ir.model.fields,field_description:purchase_advance_payment.field_purchase_order__account_payment_ids +#: model_terms:ir.ui.view,arch_db:purchase_advance_payment.purchase_order_form +msgid "Pay purchase advanced" +msgstr "Pagar Compra Adiantadamente" + +#. module: purchase_advance_payment +#: model:ir.model.fields,field_description:purchase_advance_payment.field_account_voucher_wizard_purchase__payment_method_line_id +msgid "Payment Method" +msgstr "Método de Pagamento" + +#. module: purchase_advance_payment +#: model_terms:ir.ui.view,arch_db:purchase_advance_payment.view_account_supplier_payment_tree +msgid "Payment Sent?" +msgstr "Pagamento Enviado?" + +#. module: purchase_advance_payment +#: model_terms:ir.ui.view,arch_db:purchase_advance_payment.purchase_order_form +msgid "Payment advances" +msgstr "Pagamentos Adiantados" + +#. module: purchase_advance_payment +#: model:ir.model.fields,field_description:purchase_advance_payment.field_purchase_order__payment_line_ids +msgid "Payment move lines" +msgstr "Linhas de Pagamento" + +#. module: purchase_advance_payment +#: model:ir.model,name:purchase_advance_payment.model_account_payment +msgid "Payments" +msgstr "Pagamentos" + +#. module: purchase_advance_payment +#: model:ir.model,name:purchase_advance_payment.model_res_config_settings +msgid "Procurement purchase grouping settings" +msgstr "Definições de agrupamento de compras" + +#. module: purchase_advance_payment +#: model:ir.model.fields,field_description:purchase_advance_payment.field_account_payment__purchase_id +msgid "Purchase" +msgstr "Compra" + +#. module: purchase_advance_payment +#: model:ir.model,name:purchase_advance_payment.model_purchase_order +#: model_terms:ir.ui.view,arch_db:purchase_advance_payment.view_account_payment_form +msgid "Purchase Order" +msgstr "Ordem de Compra" + +#. module: purchase_advance_payment +#: model:ir.model.fields,field_description:purchase_advance_payment.field_account_voucher_wizard_purchase__payment_ref +msgid "Ref." +msgstr "Ref." + +#. module: purchase_advance_payment +#: model:ir.model.fields,field_description:purchase_advance_payment.field_purchase_order__amount_residual +msgid "Residual amount" +msgstr "Valor Residual" + +#. module: purchase_advance_payment +#: model_terms:ir.ui.view,arch_db:purchase_advance_payment.view_account_payment_search +msgid "Vendor Advance Payments" +msgstr "Pagamentos Adiantados a Fornecedores" diff --git a/purchase_advance_payment/i18n/pt_BR.po b/purchase_advance_payment/i18n/pt_BR.po new file mode 100644 index 00000000000..ed53dc0a8cb --- /dev/null +++ b/purchase_advance_payment/i18n/pt_BR.po @@ -0,0 +1,276 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * purchase_advance_payment +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 16.0\n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: 2023-11-01 11:38+0000\n" +"Last-Translator: Adriano Prado \n" +"Language-Team: none\n" +"Language: pt_BR\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: nplurals=2; plural=n > 1;\n" +"X-Generator: Weblate 4.17\n" + +#. module: purchase_advance_payment +#: model:ir.model,name:purchase_advance_payment.model_account_voucher_wizard_purchase +msgid "Account Voucher Wizard Purchase" +msgstr "" + +#. module: purchase_advance_payment +#: model:ir.actions.act_window,name:purchase_advance_payment.action_view_account_voucher_wizard +#: model_terms:ir.ui.view,arch_db:purchase_advance_payment.view_account_voucher_wizard +msgid "Advance Payment" +msgstr "Pagamento Antecipado" + +#. module: purchase_advance_payment +#: model:ir.model.fields,field_description:purchase_advance_payment.field_purchase_order__advance_payment_status +msgid "Advance Payment Status" +msgstr "Situação Pagamento Antecipado" + +#. module: purchase_advance_payment +#: model:ir.actions.act_window,name:purchase_advance_payment.action_account_advance_payments_payable +#: model:ir.ui.menu,name:purchase_advance_payment.menu_account_advance_payments_payable +msgid "Advance Payments" +msgstr "" + +#. module: purchase_advance_payment +#: model_terms:ir.ui.view,arch_db:purchase_advance_payment.purchase_order_kpis_tree +#: model_terms:ir.ui.view,arch_db:purchase_advance_payment.purchase_order_view_tree +msgid "Amount Residual" +msgstr "Valor Residual" + +#. module: purchase_advance_payment +#: model:ir.model.fields,field_description:purchase_advance_payment.field_account_voucher_wizard_purchase__amount_total +msgid "Amount Total" +msgstr "Valor Total" + +#. module: purchase_advance_payment +#: model:ir.model.fields,field_description:purchase_advance_payment.field_account_voucher_wizard_purchase__amount_advance +msgid "Amount advanced" +msgstr "Valor Antecipado" + +#. module: purchase_advance_payment +#: model_terms:ir.ui.view,arch_db:purchase_advance_payment.view_account_voucher_wizard +msgid "Amount in Order Currency" +msgstr "Valor na Moeda do Pedido" + +#. module: purchase_advance_payment +#. odoo-python +#: code:addons/purchase_advance_payment/wizard/purchase_advance_payment_wizard.py:0 +msgid "Amount of advance is greater than residual amount on purchase" +msgstr "O valor do adiantamento é maior que o valor residual na compra" + +#. module: purchase_advance_payment +#. odoo-python +#: code:addons/purchase_advance_payment/wizard/purchase_advance_payment_wizard.py:0 +msgid "Amount of advance must be positive." +msgstr "O valor do adiantamento deve ser positivo." + +#. module: purchase_advance_payment +#: model:ir.model.fields,field_description:purchase_advance_payment.field_res_config_settings__auto_post_purchase_advance_payments +msgid "Auto Post Purchase Advance Payments" +msgstr "" + +#. module: purchase_advance_payment +#: model:ir.model.fields,field_description:purchase_advance_payment.field_res_config_settings__auto_reconcile_purchase_advance_payments +msgid "Auto Reconcile Purchase Advance Payments" +msgstr "" + +#. module: purchase_advance_payment +#: model_terms:ir.ui.view,arch_db:purchase_advance_payment.res_config_settings_view_form_purchase +msgid "Automatically reconcile payments upon bill confirmation." +msgstr "" + +#. module: purchase_advance_payment +#: model:ir.model.fields,field_description:purchase_advance_payment.field_account_voucher_wizard_purchase__available_payment_method_line_ids +msgid "Available Payment Method Line" +msgstr "" + +#. module: purchase_advance_payment +#: model_terms:ir.ui.view,arch_db:purchase_advance_payment.view_account_voucher_wizard +msgid "Cancel" +msgstr "Cancelar" + +#. module: purchase_advance_payment +#: model:ir.model.fields,field_description:purchase_advance_payment.field_account_voucher_wizard_purchase__create_uid +msgid "Created by" +msgstr "Criado por" + +#. module: purchase_advance_payment +#: model:ir.model.fields,field_description:purchase_advance_payment.field_account_voucher_wizard_purchase__create_date +msgid "Created on" +msgstr "Criado em" + +#. module: purchase_advance_payment +#: model:ir.model.fields,field_description:purchase_advance_payment.field_account_voucher_wizard_purchase__currency_amount +msgid "Curr. amount" +msgstr "Valor moeda" + +#. module: purchase_advance_payment +#: model:ir.model.fields,field_description:purchase_advance_payment.field_account_voucher_wizard_purchase__currency_id +#: model_terms:ir.ui.view,arch_db:purchase_advance_payment.view_account_voucher_wizard +msgid "Currency" +msgstr "Moeda" + +#. module: purchase_advance_payment +#: model:ir.model.fields,field_description:purchase_advance_payment.field_account_voucher_wizard_purchase__date +msgid "Date" +msgstr "Data" + +#. module: purchase_advance_payment +#: model:ir.model.fields,field_description:purchase_advance_payment.field_account_voucher_wizard_purchase__display_name +msgid "Display Name" +msgstr "Nome Exibição" + +#. module: purchase_advance_payment +#: model:ir.model.fields,field_description:purchase_advance_payment.field_account_voucher_wizard_purchase__id +msgid "ID" +msgstr "ID" + +#. module: purchase_advance_payment +#: model_terms:ir.ui.view,arch_db:purchase_advance_payment.res_config_settings_view_form_purchase +msgid "If enabled, advance payments created from POs are automatically posted." +msgstr "" + +#. module: purchase_advance_payment +#: model_terms:ir.ui.view,arch_db:purchase_advance_payment.res_config_settings_view_form_purchase +msgid "" +"If enabled, advance payments created from POs are automatically reconciled " +"upon bill confirmation." +msgstr "" + +#. module: purchase_advance_payment +#: model:ir.model.fields,field_description:purchase_advance_payment.field_account_voucher_wizard_purchase__journal_id +msgid "Journal" +msgstr "Diário" + +#. module: purchase_advance_payment +#: model:ir.model.fields,field_description:purchase_advance_payment.field_account_voucher_wizard_purchase__journal_currency_id +msgid "Journal Currency" +msgstr "Moeda Díario" + +#. module: purchase_advance_payment +#: model:ir.model,name:purchase_advance_payment.model_account_move +msgid "Journal Entry" +msgstr "" + +#. module: purchase_advance_payment +#: model:ir.model.fields,field_description:purchase_advance_payment.field_account_voucher_wizard_purchase__write_uid +msgid "Last Updated by" +msgstr "Última Atualização por" + +#. module: purchase_advance_payment +#: model:ir.model.fields,field_description:purchase_advance_payment.field_account_voucher_wizard_purchase__write_date +msgid "Last Updated on" +msgstr "Última Atualização em" + +#. module: purchase_advance_payment +#: model_terms:ir.ui.view,arch_db:purchase_advance_payment.view_account_voucher_wizard +msgid "Make advance payment" +msgstr "Faça o Pagamento Antecipado" + +#. module: purchase_advance_payment +#: model:ir.model.fields.selection,name:purchase_advance_payment.selection__purchase_order__advance_payment_status__not_paid +msgid "Not Paid" +msgstr "Não Pago" + +#. module: purchase_advance_payment +#: model_terms:ir.ui.view,arch_db:purchase_advance_payment.view_account_payment_search +msgid "Not Reconciled" +msgstr "" + +#. module: purchase_advance_payment +#: model:ir.model.fields,field_description:purchase_advance_payment.field_account_voucher_wizard_purchase__order_id +msgid "Order" +msgstr "Pedido" + +#. module: purchase_advance_payment +#: model_terms:ir.ui.view,arch_db:purchase_advance_payment.view_account_voucher_wizard +msgid "Order Due Amount" +msgstr "Valor Devido do Pedido" + +#. module: purchase_advance_payment +#: model:ir.model.fields.selection,name:purchase_advance_payment.selection__purchase_order__advance_payment_status__paid +msgid "Paid" +msgstr "Pago" + +#. module: purchase_advance_payment +#: model:ir.model.fields.selection,name:purchase_advance_payment.selection__purchase_order__advance_payment_status__partial +msgid "Partially Paid" +msgstr "Parcialmente Pago" + +#. module: purchase_advance_payment +#: model:ir.model.fields,field_description:purchase_advance_payment.field_purchase_order__account_payment_ids +#: model_terms:ir.ui.view,arch_db:purchase_advance_payment.purchase_order_form +msgid "Pay purchase advanced" +msgstr "Pagar Compra Antecipadamente" + +#. module: purchase_advance_payment +#: model:ir.model.fields,field_description:purchase_advance_payment.field_account_voucher_wizard_purchase__payment_method_line_id +msgid "Payment Method" +msgstr "Forma de Pagamento" + +#. module: purchase_advance_payment +#: model_terms:ir.ui.view,arch_db:purchase_advance_payment.view_account_supplier_payment_tree +msgid "Payment Sent?" +msgstr "" + +#. module: purchase_advance_payment +#: model_terms:ir.ui.view,arch_db:purchase_advance_payment.purchase_order_form +msgid "Payment advances" +msgstr "Adiantamentos de pagamento" + +#. module: purchase_advance_payment +#: model:ir.model.fields,field_description:purchase_advance_payment.field_purchase_order__payment_line_ids +msgid "Payment move lines" +msgstr "Linhas Movimento Pagamento" + +#. module: purchase_advance_payment +#: model:ir.model,name:purchase_advance_payment.model_account_payment +msgid "Payments" +msgstr "Pagamentos" + +#. module: purchase_advance_payment +#: model:ir.model,name:purchase_advance_payment.model_res_config_settings +msgid "Procurement purchase grouping settings" +msgstr "" + +#. module: purchase_advance_payment +#: model:ir.model.fields,field_description:purchase_advance_payment.field_account_payment__purchase_id +msgid "Purchase" +msgstr "Compra" + +#. module: purchase_advance_payment +#: model:ir.model,name:purchase_advance_payment.model_purchase_order +#: model_terms:ir.ui.view,arch_db:purchase_advance_payment.view_account_payment_form +msgid "Purchase Order" +msgstr "Pedido Compra" + +#. module: purchase_advance_payment +#: model:ir.model.fields,field_description:purchase_advance_payment.field_account_voucher_wizard_purchase__payment_ref +msgid "Ref." +msgstr "Ref." + +#. module: purchase_advance_payment +#: model:ir.model.fields,field_description:purchase_advance_payment.field_purchase_order__amount_residual +msgid "Residual amount" +msgstr "Valor Residual" + +#. module: purchase_advance_payment +#: model_terms:ir.ui.view,arch_db:purchase_advance_payment.view_account_payment_search +msgid "Vendor Advance Payments" +msgstr "" + +#~ msgid "Operation" +#~ msgstr "Operação" + +#~ msgid "Order Currency" +#~ msgstr "Moeda Pedido" + +#~ msgid "Last Modified on" +#~ msgstr "Última Modificação em" diff --git a/purchase_advance_payment/i18n/purchase_advance_payment.pot b/purchase_advance_payment/i18n/purchase_advance_payment.pot new file mode 100644 index 00000000000..beb4e58c5c7 --- /dev/null +++ b/purchase_advance_payment/i18n/purchase_advance_payment.pot @@ -0,0 +1,273 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * purchase_advance_payment +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 19.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: purchase_advance_payment +#: model:ir.model,name:purchase_advance_payment.model_account_voucher_wizard_purchase +msgid "Account Voucher Wizard Purchase" +msgstr "" + +#. module: purchase_advance_payment +#: model:ir.actions.act_window,name:purchase_advance_payment.action_view_account_voucher_wizard +#: model_terms:ir.ui.view,arch_db:purchase_advance_payment.view_account_voucher_wizard +msgid "Advance Payment" +msgstr "" + +#. module: purchase_advance_payment +#: model:ir.model.fields,field_description:purchase_advance_payment.field_purchase_order__advance_payment_status +msgid "Advance Payment Status" +msgstr "" + +#. module: purchase_advance_payment +#: model:ir.actions.act_window,name:purchase_advance_payment.action_account_advance_payments_payable +#: model:ir.ui.menu,name:purchase_advance_payment.menu_account_advance_payments_payable +msgid "Advance Payments" +msgstr "" + +#. module: purchase_advance_payment +#: model_terms:ir.ui.view,arch_db:purchase_advance_payment.purchase_order_kpis_tree +#: model_terms:ir.ui.view,arch_db:purchase_advance_payment.purchase_order_view_tree +msgid "Amount Residual" +msgstr "" + +#. module: purchase_advance_payment +#: model:ir.model.fields,field_description:purchase_advance_payment.field_account_voucher_wizard_purchase__amount_total +msgid "Amount Total" +msgstr "" + +#. module: purchase_advance_payment +#: model:ir.model.fields,field_description:purchase_advance_payment.field_account_voucher_wizard_purchase__amount_advance +msgid "Amount advanced" +msgstr "" + +#. module: purchase_advance_payment +#: model_terms:ir.ui.view,arch_db:purchase_advance_payment.view_account_voucher_wizard +msgid "Amount in Order Currency" +msgstr "" + +#. module: purchase_advance_payment +#. odoo-python +#: code:addons/purchase_advance_payment/wizard/purchase_advance_payment_wizard.py:0 +msgid "Amount of advance is greater than residual amount on purchase" +msgstr "" + +#. module: purchase_advance_payment +#. odoo-python +#: code:addons/purchase_advance_payment/wizard/purchase_advance_payment_wizard.py:0 +msgid "Amount of advance must be positive." +msgstr "" + +#. module: purchase_advance_payment +#: model:ir.model.fields,field_description:purchase_advance_payment.field_res_config_settings__auto_post_purchase_advance_payments +msgid "Auto Post Purchase Advance Payments" +msgstr "" + +#. module: purchase_advance_payment +#: model:ir.model.fields,field_description:purchase_advance_payment.field_res_config_settings__auto_reconcile_purchase_advance_payments +msgid "Auto Reconcile Purchase Advance Payments" +msgstr "" + +#. module: purchase_advance_payment +#: model_terms:ir.ui.view,arch_db:purchase_advance_payment.res_config_settings_view_form_purchase +msgid "Automatically reconcile payments upon bill confirmation." +msgstr "" + +#. module: purchase_advance_payment +#: model:ir.model.fields,field_description:purchase_advance_payment.field_account_voucher_wizard_purchase__available_payment_method_line_ids +msgid "Available Payment Method Line" +msgstr "" + +#. module: purchase_advance_payment +#: model_terms:ir.ui.view,arch_db:purchase_advance_payment.view_account_voucher_wizard +msgid "Cancel" +msgstr "" + +#. module: purchase_advance_payment +#: model:ir.model.fields,field_description:purchase_advance_payment.field_account_voucher_wizard_purchase__create_uid +msgid "Created by" +msgstr "" + +#. module: purchase_advance_payment +#: model:ir.model.fields,field_description:purchase_advance_payment.field_account_voucher_wizard_purchase__create_date +msgid "Created on" +msgstr "" + +#. module: purchase_advance_payment +#: model:ir.model.fields,field_description:purchase_advance_payment.field_account_voucher_wizard_purchase__currency_amount +msgid "Curr. amount" +msgstr "" + +#. module: purchase_advance_payment +#: model:ir.model.fields,field_description:purchase_advance_payment.field_account_voucher_wizard_purchase__currency_id +#: model_terms:ir.ui.view,arch_db:purchase_advance_payment.view_account_voucher_wizard +msgid "Currency" +msgstr "" + +#. module: purchase_advance_payment +#: model:ir.model.fields,field_description:purchase_advance_payment.field_account_voucher_wizard_purchase__date +msgid "Date" +msgstr "" + +#. module: purchase_advance_payment +#: model:ir.model.fields,field_description:purchase_advance_payment.field_account_move__display_name +#: model:ir.model.fields,field_description:purchase_advance_payment.field_account_payment__display_name +#: model:ir.model.fields,field_description:purchase_advance_payment.field_account_voucher_wizard_purchase__display_name +#: model:ir.model.fields,field_description:purchase_advance_payment.field_purchase_order__display_name +#: model:ir.model.fields,field_description:purchase_advance_payment.field_res_config_settings__display_name +msgid "Display Name" +msgstr "" + +#. module: purchase_advance_payment +#: model:ir.model.fields,field_description:purchase_advance_payment.field_account_move__id +#: model:ir.model.fields,field_description:purchase_advance_payment.field_account_payment__id +#: model:ir.model.fields,field_description:purchase_advance_payment.field_account_voucher_wizard_purchase__id +#: model:ir.model.fields,field_description:purchase_advance_payment.field_purchase_order__id +#: model:ir.model.fields,field_description:purchase_advance_payment.field_res_config_settings__id +msgid "ID" +msgstr "" + +#. module: purchase_advance_payment +#: model_terms:ir.ui.view,arch_db:purchase_advance_payment.res_config_settings_view_form_purchase +msgid "" +"If enabled, advance payments created from POs are automatically posted." +msgstr "" + +#. module: purchase_advance_payment +#: model_terms:ir.ui.view,arch_db:purchase_advance_payment.res_config_settings_view_form_purchase +msgid "" +"If enabled, advance payments created from POs are automatically reconciled " +"upon bill confirmation." +msgstr "" + +#. module: purchase_advance_payment +#: model:ir.model.fields,field_description:purchase_advance_payment.field_account_voucher_wizard_purchase__journal_id +msgid "Journal" +msgstr "" + +#. module: purchase_advance_payment +#: model:ir.model.fields,field_description:purchase_advance_payment.field_account_voucher_wizard_purchase__journal_currency_id +msgid "Journal Currency" +msgstr "" + +#. module: purchase_advance_payment +#: model:ir.model,name:purchase_advance_payment.model_account_move +msgid "Journal Entry" +msgstr "" + +#. module: purchase_advance_payment +#: model:ir.model.fields,field_description:purchase_advance_payment.field_account_voucher_wizard_purchase__write_uid +msgid "Last Updated by" +msgstr "" + +#. module: purchase_advance_payment +#: model:ir.model.fields,field_description:purchase_advance_payment.field_account_voucher_wizard_purchase__write_date +msgid "Last Updated on" +msgstr "" + +#. module: purchase_advance_payment +#: model_terms:ir.ui.view,arch_db:purchase_advance_payment.view_account_voucher_wizard +msgid "Make advance payment" +msgstr "" + +#. module: purchase_advance_payment +#: model:ir.model.fields.selection,name:purchase_advance_payment.selection__purchase_order__advance_payment_status__not_paid +msgid "Not Paid" +msgstr "" + +#. module: purchase_advance_payment +#: model_terms:ir.ui.view,arch_db:purchase_advance_payment.view_account_payment_search +msgid "Not Reconciled" +msgstr "" + +#. module: purchase_advance_payment +#: model:ir.model.fields,field_description:purchase_advance_payment.field_account_voucher_wizard_purchase__order_id +msgid "Order" +msgstr "" + +#. module: purchase_advance_payment +#: model_terms:ir.ui.view,arch_db:purchase_advance_payment.view_account_voucher_wizard +msgid "Order Due Amount" +msgstr "" + +#. module: purchase_advance_payment +#: model:ir.model.fields.selection,name:purchase_advance_payment.selection__purchase_order__advance_payment_status__paid +msgid "Paid" +msgstr "" + +#. module: purchase_advance_payment +#: model:ir.model.fields.selection,name:purchase_advance_payment.selection__purchase_order__advance_payment_status__partial +msgid "Partially Paid" +msgstr "" + +#. module: purchase_advance_payment +#: model:ir.model.fields,field_description:purchase_advance_payment.field_purchase_order__account_payment_ids +#: model_terms:ir.ui.view,arch_db:purchase_advance_payment.purchase_order_form +msgid "Pay purchase advanced" +msgstr "" + +#. module: purchase_advance_payment +#: model:ir.model.fields,field_description:purchase_advance_payment.field_account_voucher_wizard_purchase__payment_method_line_id +msgid "Payment Method" +msgstr "" + +#. module: purchase_advance_payment +#: model_terms:ir.ui.view,arch_db:purchase_advance_payment.view_account_supplier_payment_tree +msgid "Payment Sent?" +msgstr "" + +#. module: purchase_advance_payment +#: model_terms:ir.ui.view,arch_db:purchase_advance_payment.purchase_order_form +msgid "Payment advances" +msgstr "" + +#. module: purchase_advance_payment +#: model:ir.model.fields,field_description:purchase_advance_payment.field_purchase_order__payment_line_ids +msgid "Payment move lines" +msgstr "" + +#. module: purchase_advance_payment +#: model:ir.model,name:purchase_advance_payment.model_account_payment +msgid "Payments" +msgstr "" + +#. module: purchase_advance_payment +#: model:ir.model,name:purchase_advance_payment.model_res_config_settings +msgid "Procurement purchase grouping settings" +msgstr "" + +#. module: purchase_advance_payment +#: model:ir.model.fields,field_description:purchase_advance_payment.field_account_payment__purchase_id +msgid "Purchase" +msgstr "" + +#. module: purchase_advance_payment +#: model:ir.model,name:purchase_advance_payment.model_purchase_order +#: model_terms:ir.ui.view,arch_db:purchase_advance_payment.view_account_payment_form +msgid "Purchase Order" +msgstr "" + +#. module: purchase_advance_payment +#: model:ir.model.fields,field_description:purchase_advance_payment.field_account_voucher_wizard_purchase__payment_ref +msgid "Ref." +msgstr "" + +#. module: purchase_advance_payment +#: model:ir.model.fields,field_description:purchase_advance_payment.field_purchase_order__amount_residual +msgid "Residual amount" +msgstr "" + +#. module: purchase_advance_payment +#: model_terms:ir.ui.view,arch_db:purchase_advance_payment.view_account_payment_search +msgid "Vendor Advance Payments" +msgstr "" diff --git a/purchase_advance_payment/models/__init__.py b/purchase_advance_payment/models/__init__.py new file mode 100644 index 00000000000..372ea8d20b1 --- /dev/null +++ b/purchase_advance_payment/models/__init__.py @@ -0,0 +1,4 @@ +from . import account_move +from . import payment +from . import purchase_order +from . import res_config_settings diff --git a/purchase_advance_payment/models/account_move.py b/purchase_advance_payment/models/account_move.py new file mode 100644 index 00000000000..15a2d0433f2 --- /dev/null +++ b/purchase_advance_payment/models/account_move.py @@ -0,0 +1,43 @@ +# Copyright 2024 Camptocamp SA +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import models + + +class AccountMove(models.Model): + _inherit = "account.move" + + def action_post(self): + # Automatic reconciliation of payment when bill confirmed. + res = super().action_post() + for move in self: + # Link advance payments without move_id to the new bill. + purchase_order = move.line_ids.purchase_order_id + move.matched_payment_ids = purchase_order.account_payment_ids.filtered( + lambda p: not p.outstanding_account_id + and not p.move_id + and not p.invoice_ids + ) + if bool( + self.env["ir.config_parameter"] + .sudo() + .get_param("purchase_advance_payment.auto_reconcile_advance_payments") + ): + for move in self: + purchase_order = move.line_ids.purchase_order_id + if ( + purchase_order + and move.invoice_outstanding_credits_debits_widget is not False + ): + json_invoice_outstanding_data = ( + move.invoice_outstanding_credits_debits_widget.get( + "content", [] + ) + ) + for data in json_invoice_outstanding_data: + if ( + data.get("move_id") + in purchase_order.account_payment_ids.move_id.ids + ): + move.js_assign_outstanding_line(line_id=data.get("id")) + return res diff --git a/purchase_advance_payment/models/payment.py b/purchase_advance_payment/models/payment.py new file mode 100644 index 00000000000..6fb9eb50a95 --- /dev/null +++ b/purchase_advance_payment/models/payment.py @@ -0,0 +1,14 @@ +# Copyright (C) 2021 ForgeFlow S.L. +# License AGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html) + +from odoo import fields, models + + +class AccountPayment(models.Model): + _inherit = "account.payment" + + purchase_id = fields.Many2one( + "purchase.order", + "Purchase", + readonly=True, + ) diff --git a/purchase_advance_payment/models/purchase_order.py b/purchase_advance_payment/models/purchase_order.py new file mode 100644 index 00000000000..d9f5588f570 --- /dev/null +++ b/purchase_advance_payment/models/purchase_order.py @@ -0,0 +1,123 @@ +# Copyright (C) 2021 ForgeFlow S.L. +# License AGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html) + +from odoo import api, fields, models +from odoo.tools import float_compare + + +class PurchaseOrder(models.Model): + _inherit = "purchase.order" + + account_payment_ids = fields.One2many( + "account.payment", "purchase_id", string="Pay purchase advanced", readonly=True + ) + amount_residual = fields.Float( + "Residual amount", + readonly=True, + compute="_compute_purchase_advance_payment", + store=True, + ) + payment_line_ids = fields.Many2many( + "account.move.line", + string="Payment move lines", + compute="_compute_purchase_advance_payment", + store=True, + ) + advance_payment_status = fields.Selection( + selection=[ + ("not_paid", "Not Paid"), + ("paid", "Paid"), + ("partial", "Partially Paid"), + ], + store=True, + readonly=True, + copy=False, + tracking=True, + compute="_compute_purchase_advance_payment", + ) + + @api.depends( + "currency_id", + "company_id", + "amount_total", + "account_payment_ids", + "account_payment_ids.state", + "account_payment_ids.move_id", + "account_payment_ids.move_id.line_ids", + "account_payment_ids.move_id.line_ids.date", + "account_payment_ids.move_id.line_ids.debit", + "account_payment_ids.move_id.line_ids.credit", + "account_payment_ids.move_id.line_ids.currency_id", + "account_payment_ids.move_id.line_ids.amount_currency", + "order_line.invoice_lines.move_id", + "order_line.invoice_lines.move_id.amount_total", + "order_line.invoice_lines.move_id.amount_residual", + ) + def _compute_purchase_advance_payment(self): + for order in self: + mls = order.account_payment_ids.mapped("move_id.line_ids").filtered( + lambda x: x.account_id.account_type == "liability_payable" + and x.parent_state == "posted" + ) + advance_amount = 0.0 + for line in mls: + line_currency = line.currency_id or line.company_id.currency_id + # Exclude reconciled pre-payments amount because once reconciled + # the pre-payment will reduce bill residual amount like any + # other payment. + line_amount = ( + line.amount_residual_currency + if line.currency_id + else line.amount_residual + ) + if line_currency != order.currency_id: + advance_amount += line.currency_id._convert( + line_amount, + order.currency_id, + order.company_id, + line.date or fields.Date.today(), + ) + else: + advance_amount += line_amount + # Compute amount by payments without an account.move related. + adv_pays = order.account_payment_ids.filtered( + lambda x: x.state in ["in_process", "paid"] + and not x.outstanding_account_id + and not x.move_id + ) + for ap in adv_pays: + if ap.invoice_ids: + # This is not perfect but it is the best we can do. + # Once the payment is linked to the invoice, it is better + # to not consider it anymore because is not going to be + # reconciled (it has no move_id), otherwise the risk to + # double-count payments is high. + continue + + ap_currency = ap.currency_id or ap.company_currency_id + if ap_currency != order.currency_id: + advance_amount += ap_currency._convert( + ap.amount, + order.currency_id, + order.company_id, + ap.date or fields.Date.today(), + ) + else: + advance_amount += ap.amount + # Consider payments in related invoices. + invoice_paid_amount = 0.0 + for inv in order.invoice_ids: + invoice_paid_amount += inv.amount_total - inv.amount_residual + amount_residual = order.amount_total - advance_amount - invoice_paid_amount + payment_state = "not_paid" + if mls or not order.currency_id.is_zero(invoice_paid_amount): + has_due_amount = float_compare( + amount_residual, 0.0, precision_rounding=order.currency_id.rounding + ) + if has_due_amount <= 0: + payment_state = "paid" + elif has_due_amount > 0: + payment_state = "partial" + order.payment_line_ids = mls + order.amount_residual = amount_residual + order.advance_payment_status = payment_state diff --git a/purchase_advance_payment/models/res_config_settings.py b/purchase_advance_payment/models/res_config_settings.py new file mode 100644 index 00000000000..17c14dab541 --- /dev/null +++ b/purchase_advance_payment/models/res_config_settings.py @@ -0,0 +1,15 @@ +# Copyright 2024 ForgeFlow S.L. +# License AGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html) + +from odoo import fields, models + + +class ResConfigSettings(models.TransientModel): + _inherit = "res.config.settings" + + auto_post_purchase_advance_payments = fields.Boolean( + config_parameter="purchase_advance_payment.auto_post_advance_payments", + ) + auto_reconcile_purchase_advance_payments = fields.Boolean( + config_parameter="purchase_advance_payment.auto_reconcile_advance_payments", + ) diff --git a/purchase_advance_payment/pyproject.toml b/purchase_advance_payment/pyproject.toml new file mode 100644 index 00000000000..4231d0cccb3 --- /dev/null +++ b/purchase_advance_payment/pyproject.toml @@ -0,0 +1,3 @@ +[build-system] +requires = ["whool"] +build-backend = "whool.buildapi" diff --git a/purchase_advance_payment/readme/CONTRIBUTORS.md b/purchase_advance_payment/readme/CONTRIBUTORS.md new file mode 100644 index 00000000000..7353904c451 --- /dev/null +++ b/purchase_advance_payment/readme/CONTRIBUTORS.md @@ -0,0 +1,10 @@ +- Mateu Griful \<\> + +- Lois Rilo \<\> + +- [Trobz](https://trobz.com): + + > - Son Ho \<\> + +- [Heliconia Solutions Pvt. Ltd.](https://www.heliconia.io) + - Bhavesh Heliconia diff --git a/purchase_advance_payment/readme/CREDITS.md b/purchase_advance_payment/readme/CREDITS.md new file mode 100644 index 00000000000..291e14c81e2 --- /dev/null +++ b/purchase_advance_payment/readme/CREDITS.md @@ -0,0 +1,2 @@ +The migration of this module from 15.0 to 16.0 was financially supported +by Camptocamp diff --git a/purchase_advance_payment/readme/DESCRIPTION.md b/purchase_advance_payment/readme/DESCRIPTION.md new file mode 100644 index 00000000000..2509a323453 --- /dev/null +++ b/purchase_advance_payment/readme/DESCRIPTION.md @@ -0,0 +1,3 @@ +The module allows to add advance payments on purchase orders and Request +for Quotation. The advance payments are allowed even before confirmation +and before starting the billing process. diff --git a/purchase_advance_payment/readme/USAGE.md b/purchase_advance_payment/readme/USAGE.md new file mode 100644 index 00000000000..f9f265c90e7 --- /dev/null +++ b/purchase_advance_payment/readme/USAGE.md @@ -0,0 +1,9 @@ +To use this module, you need to: + +- Go to a purchase order. +- Click on "Pay Purchase Advance". +- Select the Journal and specify the amount of the advanced payment. +- "Make Advance Payment". + +When generating the invoice, the system displays the advanced payments, +select those you want to add to the invoice. diff --git a/purchase_advance_payment/security/ir.model.access.csv b/purchase_advance_payment/security/ir.model.access.csv new file mode 100644 index 00000000000..a432eb67dbf --- /dev/null +++ b/purchase_advance_payment/security/ir.model.access.csv @@ -0,0 +1,4 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +access_account_payment_purchase_advance,account.payment purchase_advance,account.model_account_payment,purchase.group_purchase_user,1,1,1,0 +access_account_payment_method_purchase_advance,account.payment.method purchase_advance,account.model_account_payment_method,purchase.group_purchase_user,1,0,0,0 +access_account_voucher_wizard_purchase_advance,access_account_voucher_wizard_purchase_advance,model_account_voucher_wizard_purchase,purchase.group_purchase_user,1,1,1,0 diff --git a/purchase_advance_payment/static/description/icon.png b/purchase_advance_payment/static/description/icon.png new file mode 100644 index 00000000000..3a0328b516c Binary files /dev/null and b/purchase_advance_payment/static/description/icon.png differ diff --git a/purchase_advance_payment/static/description/index.html b/purchase_advance_payment/static/description/index.html new file mode 100644 index 00000000000..8b8c0866869 --- /dev/null +++ b/purchase_advance_payment/static/description/index.html @@ -0,0 +1,467 @@ + + + + + +README.rst + + + +
+ + + +Odoo Community Association + +
+

Purchase Advance Payment

+ +

Beta License: AGPL-3 OCA/purchase-workflow Translate me on Weblate Try me on Runboat

+

The module allows to add advance payments on purchase orders and Request +for Quotation. The advance payments are allowed even before confirmation +and before starting the billing process.

+

Table of contents

+ +
+

Usage

+

To use this module, you need to:

+
    +
  • Go to a purchase order.
  • +
  • Click on “Pay Purchase Advance”.
  • +
  • Select the Journal and specify the amount of the advanced payment.
  • +
  • “Make Advance Payment”.
  • +
+

When generating the invoice, the system displays the advanced payments, +select those you want to add to the invoice.

+
+
+

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

+
    +
  • Forgeflow
  • +
+
+
+

Contributors

+ +
+
+

Other credits

+

The migration of this module from 15.0 to 16.0 was financially supported +by Camptocamp

+
+
+

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 maintainer:

+

LoisRForgeFlow

+

This module is part of the OCA/purchase-workflow project on GitHub.

+

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

+
+
+
+
+ + diff --git a/purchase_advance_payment/tests/__init__.py b/purchase_advance_payment/tests/__init__.py new file mode 100644 index 00000000000..beeea01aff5 --- /dev/null +++ b/purchase_advance_payment/tests/__init__.py @@ -0,0 +1 @@ +from . import test_purchase_advance_payment diff --git a/purchase_advance_payment/tests/test_purchase_advance_payment.py b/purchase_advance_payment/tests/test_purchase_advance_payment.py new file mode 100644 index 00000000000..ba6e2eecf77 --- /dev/null +++ b/purchase_advance_payment/tests/test_purchase_advance_payment.py @@ -0,0 +1,859 @@ +# Copyright (C) 2021 ForgeFlow S.L. +# License AGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html) + + +from odoo import fields +from odoo.exceptions import ValidationError + +from odoo.addons.base.tests.common import BaseCommon + + +class TestPurchaseAdvancePayment(BaseCommon): + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.env = cls.env(context=dict(cls.env.context, tracking_disable=True)) + + # Partners + cls.res_partner_1 = cls.env["res.partner"].create({"name": "Wood Corner"}) + cls.res_partner_address_1 = cls.env["res.partner"].create( + {"name": "Willie Burke", "parent_id": cls.res_partner_1.id} + ) + cls.res_partner_2 = cls.env["res.partner"].create({"name": "Partner 12"}) + + # Products + cls.product_1 = cls.env["product.product"].create( + {"name": "Desk Combination", "type": "consu", "purchase_method": "purchase"} + ) + cls.product_2 = cls.env["product.product"].create( + {"name": "Conference Chair", "type": "consu", "purchase_method": "purchase"} + ) + cls.product_3 = cls.env["product.product"].create( + { + "name": "Repair Services", + "type": "service", + "purchase_method": "purchase", + } + ) + + cls.tax = cls.env["account.tax"].create( + { + "name": "Tax 20", + "type_tax_use": "purchase", + "amount": 20, + } + ) + + # purchase Order + cls.purchase_order_1 = cls.env["purchase.order"].create( + {"partner_id": cls.res_partner_1.id} + ) + cls.purchase_order_2 = cls.env["purchase.order"].create( + {"partner_id": cls.res_partner_2.id} + ) + + cls.order_line_2_1 = cls.env["purchase.order.line"].create( + { + "order_id": cls.purchase_order_2.id, + "product_id": cls.product_1.id, + "product_uom_id": cls.product_1.uom_id.id, + "product_qty": 10.0, + "price_unit": 100.0, + "tax_ids": cls.tax, + } + ) + + cls.order_line_1 = cls.env["purchase.order.line"].create( + { + "order_id": cls.purchase_order_1.id, + "product_id": cls.product_1.id, + "product_uom_id": cls.product_1.uom_id.id, + "product_qty": 10.0, + "price_unit": 100.0, + "tax_ids": cls.tax, + } + ) + cls.order_line_2 = cls.env["purchase.order.line"].create( + { + "order_id": cls.purchase_order_1.id, + "product_id": cls.product_2.id, + "product_uom_id": cls.product_2.uom_id.id, + "product_qty": 25.0, + "price_unit": 40.0, + "tax_ids": cls.tax, + } + ) + cls.order_line_3 = cls.env["purchase.order.line"].create( + { + "order_id": cls.purchase_order_1.id, + "product_id": cls.product_3.id, + "product_uom_id": cls.product_3.uom_id.id, + "product_qty": 20.0, + "price_unit": 50.0, + "tax_ids": cls.tax, + } + ) + + cls.active_euro = False + cls.currency_euro = ( + cls.env["res.currency"] + .with_context(active_test=False) + .search([("name", "=", "EUR")]) + ) + # active euro currency if inactive for test + if not cls.currency_euro.active: + cls.currency_euro.active = True + cls.active_euro = True + cls.currency_usd = cls.env["res.currency"].search([("name", "=", "USD")]) + cls.currency_rate = cls.env["res.currency.rate"].create( + { + "inverse_company_rate": 1.20, + "currency_id": cls.currency_euro.id, + } + ) + + cls.journal_eur_bank = cls.env["account.journal"].create( + { + "name": "Journal Euro Bank", + "type": "bank", + "code": "111", + "currency_id": cls.currency_euro.id, + } + ) + + cls.journal_usd_bank = cls.env["account.journal"].create( + { + "name": "Journal USD Bank", + "type": "bank", + "code": "222", + "currency_id": cls.currency_usd.id, + } + ) + cls.journal_eur_cash = cls.env["account.journal"].create( + { + "name": "Journal Euro Cash", + "type": "cash", + "code": "333", + "currency_id": cls.currency_euro.id, + } + ) + + cls.journal_usd_cash = cls.env["account.journal"].create( + { + "name": "Journal USD Cash", + "type": "cash", + "code": "444", + "currency_id": cls.currency_usd.id, + } + ) + cls.env["ir.config_parameter"].sudo().set_param( + "purchase_advance_payment.auto_reconcile_advance_payments", False + ) + + def test_00_with_context_payment(self): + context_payment_2 = { + "active_ids": [self.purchase_order_2.id], + "active_id": self.purchase_order_2.id, + } + advance_payment_with_context = ( + self.env["account.voucher.wizard.purchase"] + .with_context(**context_payment_2) + .create( + { + "journal_id": self.journal_eur_bank.id, + "amount_advance": 10, + } + ) + ) + self.assertEqual(advance_payment_with_context.order_id, self.purchase_order_2) + + advance_payment_without_context = self.env[ + "account.voucher.wizard.purchase" + ].create( + { + "journal_id": self.journal_eur_bank.id, + "amount_advance": 20, + "order_id": self.purchase_order_1.id, + } + ) + self.assertEqual( + advance_payment_without_context.order_id, self.purchase_order_1 + ) + + def test_01_purchase_advance_payment(self): + self.assertFalse( + bool( + self.env["ir.config_parameter"] + .sudo() + .get_param("purchase_advance_payment.auto_reconcile_advance_payments") + ) + ) + self.assertEqual( + self.purchase_order_1.amount_residual, + 3600, + ) + + context_payment = { + "active_ids": [self.purchase_order_1.id], + "active_id": self.purchase_order_1.id, + } + + # Create Advance Payment 1 - EUR - bank + advance_payment_1 = ( + self.env["account.voucher.wizard.purchase"] + .with_context(**context_payment) + .create( + { + "journal_id": self.journal_eur_bank.id, + "amount_advance": 100, + "order_id": self.purchase_order_1.id, + } + ) + ) + advance_payment_1.make_advance_payment() + + self.assertEqual(self.purchase_order_1.amount_residual, 3480) + + # Create Advance Payment 2 - USD - cash + advance_payment_2 = ( + self.env["account.voucher.wizard.purchase"] + .with_context(**context_payment) + .create( + { + "journal_id": self.journal_usd_cash.id, + "amount_advance": 200, + "order_id": self.purchase_order_1.id, + } + ) + ) + advance_payment_2.make_advance_payment() + + self.assertEqual(self.purchase_order_1.amount_residual, 3280) + + # Confirm Purchase Order + self.purchase_order_1.button_confirm() + + # Create Advance Payment 3 - EUR - cash + advance_payment_3 = ( + self.env["account.voucher.wizard.purchase"] + .with_context(**context_payment) + .create( + { + "journal_id": self.journal_eur_cash.id, + "amount_advance": 250, + "order_id": self.purchase_order_1.id, + } + ) + ) + advance_payment_3.make_advance_payment() + self.assertEqual(self.purchase_order_1.amount_residual, 2980) + + # Create Advance Payment 4 - USD - bank + advance_payment_4 = ( + self.env["account.voucher.wizard.purchase"] + .with_context(**context_payment) + .create( + { + "journal_id": self.journal_usd_bank.id, + "amount_advance": 400, + "order_id": self.purchase_order_1.id, + } + ) + ) + advance_payment_4.make_advance_payment() + self.assertEqual(self.purchase_order_1.amount_residual, 2580) + + def test_02_residual_amount_with_bill(self): + self.assertFalse( + bool( + self.env["ir.config_parameter"] + .sudo() + .get_param("purchase_advance_payment.auto_reconcile_advance_payments") + ) + ) + self.assertEqual( + self.purchase_order_1.amount_residual, + 3600, + ) + self.assertEqual( + self.purchase_order_1.amount_residual, + self.purchase_order_1.amount_total, + ) + # Create Advance Payment 1 - EUR - bank + context_payment = { + "active_ids": [self.purchase_order_1.id], + "active_id": self.purchase_order_1.id, + } + # Create Advance Payment 2 - USD - cash + advance_payment_2 = ( + self.env["account.voucher.wizard.purchase"] + .with_context(**context_payment) + .create( + { + "journal_id": self.journal_usd_cash.id, + "amount_advance": 200, + "order_id": self.purchase_order_1.id, + } + ) + ) + advance_payment_2.make_advance_payment() + pre_payment = self.purchase_order_1.account_payment_ids + self.assertEqual(len(pre_payment), 1) + self.assertEqual(self.purchase_order_1.amount_residual, 3400) + # generate bill, pay bill, check amount residual. + self.purchase_order_1.button_confirm() + self.assertEqual(self.purchase_order_1.invoice_status, "to invoice") + self.purchase_order_1.action_create_invoice() + self.assertEqual(self.purchase_order_1.invoice_status, "invoiced") + self.assertEqual(self.purchase_order_1.amount_residual, 3400) + invoice = self.purchase_order_1.invoice_ids + invoice.invoice_date = fields.Date.today() + invoice.action_post() + active_ids = invoice.ids + self.env["account.payment.register"].with_context( + active_model="account.move", active_ids=active_ids + ).create( + { + "amount": 1200.0, + "group_payment": True, + "payment_difference_handling": "open", + } + )._create_payments() + self.assertEqual(self.purchase_order_1.amount_residual, 2200) + + # Reconciling the pre-payment should not affect amount_residual in PO. + ( + liquidity_lines, + counterpart_lines, + writeoff_lines, + ) = pre_payment._seek_for_lines() + ( + counterpart_lines + + invoice.line_ids.filtered( + lambda line: line.account_type == "liability_payable" + ) + ).reconcile() + self.purchase_order_1.env.invalidate_all() + self.assertEqual(self.purchase_order_1.amount_residual, 2200) + + def test_03_residual_amount_big_pre_payment(self): + self.assertFalse( + bool( + self.env["ir.config_parameter"] + .sudo() + .get_param("purchase_advance_payment.auto_reconcile_advance_payments") + ) + ) + self.assertEqual( + self.purchase_order_1.amount_residual, + 3600, + ) + self.assertEqual( + self.purchase_order_1.amount_residual, + self.purchase_order_1.amount_total, + ) + # Create Advance Payment 1 - EUR - bank + context_payment = { + "active_ids": [self.purchase_order_1.id], + "active_id": self.purchase_order_1.id, + } + # Create Advance Payment 2 - USD - cash + advance_payment_2 = ( + self.env["account.voucher.wizard.purchase"] + .with_context(**context_payment) + .create( + { + "journal_id": self.journal_usd_cash.id, + "amount_advance": 2000, + "order_id": self.purchase_order_1.id, + } + ) + ) + advance_payment_2.make_advance_payment() + pre_payment = self.purchase_order_1.account_payment_ids + self.assertEqual(len(pre_payment), 1) + self.assertEqual(self.purchase_order_1.amount_residual, 1600) + # generate a partial bill, reconcile with pre payment, check amount residual. + self.purchase_order_1.button_confirm() + self.assertEqual(self.purchase_order_1.invoice_status, "to invoice") + # Adjust billing method to then do a partial bill with a total amount + # smaller than the pre-payment. + self.product_1.purchase_method = "receive" + self.order_line_1.qty_received = 10.0 + self.assertEqual(self.order_line_1.qty_to_invoice, 10.0) + self.product_2.purchase_method = "receive" + self.order_line_2.qty_received = 0.0 + self.assertEqual(self.order_line_2.qty_to_invoice, 0.0) + self.product_3.purchase_method = "receive" + self.order_line_3.qty_received = 0.0 + self.assertEqual(self.order_line_3.qty_to_invoice, 0.0) + self.purchase_order_1.action_create_invoice() + self.assertEqual(self.purchase_order_1.invoice_status, "invoiced") + self.assertEqual(self.purchase_order_1.amount_residual, 1600) + invoice = self.purchase_order_1.invoice_ids + invoice.invoice_date = fields.Date.today() + invoice.action_post() + self.assertEqual(invoice.amount_residual, 1200) + active_ids = invoice.ids + self.env["account.payment.register"].with_context( + active_model="account.move", active_ids=active_ids + ).create( + { + "amount": 300.0, + "group_payment": True, + "payment_difference_handling": "open", + } + )._create_payments() + self.assertEqual(invoice.amount_residual, 900) + self.assertEqual(self.purchase_order_1.amount_residual, 1300) + + # Partially reconciling the pre-payment should not affect amount_residual in PO. + ( + liquidity_lines, + counterpart_lines, + writeoff_lines, + ) = pre_payment._seek_for_lines() + ( + counterpart_lines + + invoice.line_ids.filtered( + lambda line: line.account_type == "liability_payable" + ) + ).reconcile() + self.purchase_order_1.env.invalidate_all() + self.assertEqual(self.purchase_order_1.amount_residual, 1300) + + def test_04_residual_amount_with_no_amount_left(self): + self.assertFalse( + bool( + self.env["ir.config_parameter"] + .sudo() + .get_param("purchase_advance_payment.auto_reconcile_advance_payments") + ) + ) + self.assertEqual( + self.purchase_order_1.amount_residual, + 3600, + ) + context_payment = { + "active_ids": [self.purchase_order_1.id], + "active_id": self.purchase_order_1.id, + } + # Create Advance Payment with the same residual amount + advance_payment = ( + self.env["account.voucher.wizard.purchase"] + .with_context(**context_payment) + .create( + { + "journal_id": self.journal_usd_cash.id, + "amount_advance": 3600, + "order_id": self.purchase_order_1.id, + } + ) + ) + advance_payment.make_advance_payment() + self.assertEqual(self.purchase_order_1.amount_residual, 0) + self.assertEqual(self.purchase_order_1.advance_payment_status, "paid") + + def test_05_check_residual_amount_warning(self): + self.assertFalse( + bool( + self.env["ir.config_parameter"] + .sudo() + .get_param("purchase_advance_payment.auto_reconcile_advance_payments") + ) + ) + self.assertEqual( + self.purchase_order_1.amount_residual, + 3600, + ) + self.assertEqual( + self.purchase_order_1.amount_residual, + self.purchase_order_1.amount_total, + "Amounts should match", + ) + + context_payment = { + "active_ids": [self.purchase_order_1.id], + "active_id": self.purchase_order_1.id, + } + + # Check residual > advance payment and the comparison takes + # into account the currency. 3001*1.2 > 3600 + mes = "Amount of advance is greater than residual amount on purchase" + with self.assertRaisesRegex(ValidationError, mes): + advance_payment_0 = ( + self.env["account.voucher.wizard.purchase"] + .with_context(**context_payment) + .create( + { + "journal_id": self.journal_eur_bank.id, + "amount_advance": 3001, + "order_id": self.purchase_order_1.id, + } + ) + ) + advance_payment_0.make_advance_payment() + # Check positive advance payment + mes2 = "Amount of advance must be positive." + with self.assertRaisesRegex(ValidationError, mes2): + self.env["account.voucher.wizard.purchase"].create( + { + "journal_id": self.journal_eur_bank.id, + "amount_advance": -300, + "order_id": self.purchase_order_2.id, + } + ) + + def test_06_skip_payment_post(self): + self.assertFalse( + bool( + self.env["ir.config_parameter"] + .sudo() + .get_param("purchase_advance_payment.auto_reconcile_advance_payments") + ) + ) + self.assertEqual( + self.purchase_order_1.amount_residual, + 3600, + ) + context_payment = { + "active_ids": [self.purchase_order_1.id], + "active_id": self.purchase_order_1.id, + } + # Create Advance Payment 1 + advance_payment_1 = ( + self.env["account.voucher.wizard.purchase"] + .with_context(**context_payment) + .create( + { + "journal_id": self.journal_eur_bank.id, + "amount_advance": 100, + "order_id": self.purchase_order_1.id, + } + ) + ) + advance_payment_1.make_advance_payment() + payment_1 = self.purchase_order_1.account_payment_ids + self.assertTrue(payment_1) + self.assertIn(payment_1.state, ["draft", "in_process"]) + + # Change setting and create a second payment: + self.env["ir.config_parameter"].sudo().set_param( + "purchase_advance_payment.auto_post_advance_payments", False + ) + advance_payment_2 = ( + self.env["account.voucher.wizard.purchase"] + .with_context(**context_payment) + .create( + { + "journal_id": self.journal_usd_cash.id, + "amount_advance": 200, + "order_id": self.purchase_order_1.id, + } + ) + ) + advance_payment_2.make_advance_payment() + payment_2 = self.purchase_order_1.account_payment_ids - payment_1 + self.assertEqual(len(payment_2), 1) + self.assertIn(payment_2.state, ["draft", "in_process"]) + + def test_07_auto_reconcile_advance_payment_enabled(self): + # Set the config parameter to True + self.assertFalse( + bool( + self.env["ir.config_parameter"] + .sudo() + .get_param("purchase_advance_payment.auto_reconcile_advance_payments") + ) + ) + # Set the config parameter to True + self.env["ir.config_parameter"].sudo().set_param( + "purchase_advance_payment.auto_reconcile_advance_payments", True + ) + self.assertTrue( + bool( + self.env["ir.config_parameter"] + .sudo() + .get_param("purchase_advance_payment.auto_reconcile_advance_payments") + ) + ) + self.assertEqual( + self.purchase_order_1.amount_residual, + 3600, + ) + self.assertEqual( + self.purchase_order_1.amount_residual, + self.purchase_order_1.amount_total, + ) + context_payment = { + "active_ids": [self.purchase_order_1.id], + "active_id": self.purchase_order_1.id, + } + # Create Advance Payment - USD - cash + advance_payment_usd = ( + self.env["account.voucher.wizard.purchase"] + .with_context(**context_payment) + .create( + { + "journal_id": self.journal_usd_cash.id, + "amount_advance": 200, + "order_id": self.purchase_order_1.id, + } + ) + ) + advance_payment_usd.make_advance_payment() + pre_payment = self.purchase_order_1.account_payment_ids + self.assertEqual(len(pre_payment), 1) + self.assertEqual(self.purchase_order_1.amount_residual, 3400) + # generate bill, pay bill, check amount residual. + self.purchase_order_1.button_confirm() + self.assertEqual(self.purchase_order_1.invoice_status, "to invoice") + self.purchase_order_1.action_create_invoice() + self.assertEqual(self.purchase_order_1.invoice_status, "invoiced") + self.assertEqual(self.purchase_order_1.amount_residual, 3400) + invoice = self.purchase_order_1.invoice_ids + invoice.invoice_date = fields.Date.today() + self.assertEqual(invoice.amount_residual, 3600) + invoice.action_post() + self.assertEqual(invoice.amount_residual, 3400) + self.assertNotEqual( + invoice.amount_residual, + invoice.amount_total, + ) + self.assertTrue( + invoice.payment_state in ["partial"], + "Advance payment should be reconciled automatically.", + ) + + def test_08_auto_reconcile_advance_payment_disabled(self): + self.assertFalse( + bool( + self.env["ir.config_parameter"] + .sudo() + .get_param("purchase_advance_payment.auto_reconcile_advance_payments") + ) + ) + + self.assertEqual( + self.purchase_order_1.amount_residual, + 3600, + ) + self.assertEqual( + self.purchase_order_1.amount_residual, + self.purchase_order_1.amount_total, + ) + context_payment = { + "active_ids": [self.purchase_order_1.id], + "active_id": self.purchase_order_1.id, + } + # Create Advance Payment - USD - cash + advance_payment_usd = ( + self.env["account.voucher.wizard.purchase"] + .with_context(**context_payment) + .create( + { + "journal_id": self.journal_usd_cash.id, + "amount_advance": 200, + "order_id": self.purchase_order_1.id, + } + ) + ) + advance_payment_usd.make_advance_payment() + pre_payment = self.purchase_order_1.account_payment_ids + self.assertEqual(len(pre_payment), 1) + self.assertEqual(self.purchase_order_1.amount_residual, 3400) + # generate bill, pay bill, check amount residual. + self.purchase_order_1.button_confirm() + self.assertEqual(self.purchase_order_1.invoice_status, "to invoice") + self.purchase_order_1.action_create_invoice() + self.assertEqual(self.purchase_order_1.invoice_status, "invoiced") + self.assertEqual(self.purchase_order_1.amount_residual, 3400) + invoice = self.purchase_order_1.invoice_ids + invoice.invoice_date = fields.Date.today() + self.assertEqual(invoice.amount_residual, 3600) + invoice.action_post() + self.assertEqual(invoice.amount_residual, 3600) + self.assertEqual( + invoice.amount_residual, + invoice.amount_total, + ) + self.assertEqual( + invoice.payment_state, + "not_paid", + "Advance payment should not be automatically reconciled " + "when setting is disabled.", + ) + + def test_09_no_reconcile_when_no_matching_payment(self): + # Set the config parameter to True + self.env["ir.config_parameter"].sudo().set_param( + "purchase_advance_payment.auto_reconcile_advance_payments", True + ) + self.assertEqual( + self.purchase_order_1.amount_residual, + 3600, + ) + self.assertEqual( + self.purchase_order_1.amount_residual, + self.purchase_order_1.amount_total, + ) + + # Confirm Purchase Order + self.purchase_order_1.button_confirm() + self.assertEqual(self.purchase_order_1.invoice_status, "to invoice") + + # Create and post the invoice without making an advance payment + self.purchase_order_1.action_create_invoice() + self.assertEqual(self.purchase_order_1.invoice_status, "invoiced") + self.assertEqual(self.purchase_order_1.amount_residual, 3600) + invoice = self.purchase_order_1.invoice_ids + invoice.write({"invoice_date": fields.Date.today()}) + invoice.action_post() + self.assertEqual(invoice.amount_residual, 3600) + self.assertEqual( + invoice.amount_residual, + invoice.amount_total, + ) + # Ensure no reconciliation happens + self.assertEqual( + invoice.payment_state, + "not_paid", + "No reconciliation should happen without advance payments.", + ) + + def test_10_advance_payment_status_with_bill(self): + self.assertEqual( + self.purchase_order_1.amount_residual, + 3600, + ) + self.assertEqual( + self.purchase_order_1.amount_residual, + self.purchase_order_1.amount_total, + ) + self.assertEqual(self.purchase_order_1.advance_payment_status, "not_paid") + + # Create Bill + self.purchase_order_1.button_confirm() + self.assertEqual(self.purchase_order_1.invoice_status, "to invoice") + self.purchase_order_1.action_create_invoice() + self.assertEqual(self.purchase_order_1.invoice_status, "invoiced") + self.assertEqual(self.purchase_order_1.amount_residual, 3600) + invoice = self.purchase_order_1.invoice_ids + invoice.invoice_date = fields.Date.today() + invoice.action_post() + self.assertEqual(self.purchase_order_1.advance_payment_status, "not_paid") + + # Create Payment 1 + active_ids = invoice.ids + self.env["account.payment.register"].with_context( + active_model="account.move", active_ids=active_ids + ).create( + { + "amount": 200.0, + "group_payment": True, + "payment_difference_handling": "open", + } + )._create_payments() + self.assertEqual(self.purchase_order_1.amount_residual, 3400) + self.assertEqual(self.purchase_order_1.advance_payment_status, "partial") + + # Create Payment 2 + self.env["account.payment.register"].with_context( + active_model="account.move", active_ids=active_ids + ).create( + { + "amount": 3400.0, + "group_payment": True, + "payment_difference_handling": "open", + } + )._create_payments() + self.assertEqual(self.purchase_order_1.amount_residual, 0) + self.assertEqual(self.purchase_order_1.advance_payment_status, "paid") + + def test_11_advance_payments_without_account_moves(self): + """Test full flow with advance payments without outstanding + account.""" + self.env["ir.config_parameter"].sudo().set_param( + "purchase_advance_payment.auto_post_advance_payments", False + ) + self.purchase_order_2.button_confirm() + self.assertEqual( + self.purchase_order_2.amount_residual, + 1200, + ) + + context_payment = { + "active_ids": [self.purchase_order_2.id], + "active_id": self.purchase_order_2.id, + } + advance_payment_1 = ( + self.env["account.voucher.wizard.purchase"] + .with_context(**context_payment) + .create( + { + "amount_advance": 300, + "order_id": self.purchase_order_2.id, + } + ) + ) + advance_payment_1.make_advance_payment() + + payment_1 = self.purchase_order_2.account_payment_ids + self.assertFalse(payment_1.move_id) + self.assertEqual(payment_1.state, "draft") + if payment_1.outstanding_account_id: + # This is forced by Odoo in EE. See + # https://github.com/odoo/odoo/commit/0c2810df991b5ac48483f03d4cd0f5d281ece4b8 + # for more details. We remove it to simulate, + # the case of a payment without journal entry. + payment_1.outstanding_account_id = False + payment_1.action_post() + self.assertFalse(payment_1.move_id) + self.assertEqual(payment_1.state, "in_process") + + self.assertEqual(self.purchase_order_2.amount_residual, 900) + + advance_payment_2 = ( + self.env["account.voucher.wizard.purchase"] + .with_context(**context_payment) + .create( + { + "amount_advance": 200, + "order_id": self.purchase_order_2.id, + } + ) + ) + advance_payment_2.make_advance_payment() + + # In the second payment, we confirm without removing the + # outstanding account. In CE, this will create the + # journal entry anyway, thefore we test the combination of + # payments with and without entry. + self.assertEqual(self.purchase_order_2.amount_residual, 900) + payment_2 = self.purchase_order_2.account_payment_ids - payment_1 + self.assertEqual(len(payment_2), 1) + payment_2.action_post() + self.assertEqual(payment_2.state, "in_process") + self.assertTrue(payment_2.move_id) + + self.assertEqual(self.purchase_order_2.amount_residual, 700) + + self.purchase_order_2.action_create_invoice() + self.assertEqual(self.purchase_order_2.invoice_status, "invoiced") + aml = self.env["account.move.line"].search( + [("purchase_line_id", "=", self.purchase_order_2.order_line.id)] + ) + invoice = aml.mapped("move_id") + self.assertEqual(len(invoice), 1) + self.assertFalse(invoice.matched_payment_ids) + invoice.invoice_date = fields.Date.today() + invoice.action_post() + # We expect payment_1 linked to the invoice because + # it has no move_id. + self.assertEqual(invoice.matched_payment_ids, payment_1) + self.assertEqual(invoice.amount_residual, 1200) + self.assertEqual( + self.purchase_order_2.amount_residual, + 1000, + "Only payment 2 should be considered at this point.", + ) diff --git a/purchase_advance_payment/views/account_payment_views.xml b/purchase_advance_payment/views/account_payment_views.xml new file mode 100644 index 00000000000..8730210f77e --- /dev/null +++ b/purchase_advance_payment/views/account_payment_views.xml @@ -0,0 +1,84 @@ + + + + account.payment.form - purchase_advance_payment + account.payment + + + + + + + + + + account.payment.search + account.payment + + + + + + + + + + + + + + + account.payment.tree + account.payment + + + + + + + + + + + Advance Payments + account.payment + list,kanban,form,graph + { + 'default_payment_type': 'outbound', + 'default_partner_type': 'supplier', + 'search_default_vendor_adv_payment_filter': 1, + 'default_move_journal_types': ('bank', 'cash'), + 'show_payment_sent': 1, + } + + + + + diff --git a/purchase_advance_payment/views/purchase_view.xml b/purchase_advance_payment/views/purchase_view.xml new file mode 100644 index 00000000000..0e562b339b8 --- /dev/null +++ b/purchase_advance_payment/views/purchase_view.xml @@ -0,0 +1,98 @@ + + + + purchase.order.form + purchase.order + + + + + + + + + + 2 + + + + + + + + + + purchase.order.view.tree + purchase.order + + + + + + + + + + + + + purchase.order.inherit.purchase.order.tree + purchase.order + + + + + + + + + + + diff --git a/purchase_advance_payment/views/res_config_settings_views.xml b/purchase_advance_payment/views/res_config_settings_views.xml new file mode 100644 index 00000000000..84e210b11f3 --- /dev/null +++ b/purchase_advance_payment/views/res_config_settings_views.xml @@ -0,0 +1,34 @@ + + + + res.config.settings.view.form.inherit.purchase - purchase_advance_payment + res.config.settings + + + + + + + + + + + + + diff --git a/purchase_advance_payment/wizard/__init__.py b/purchase_advance_payment/wizard/__init__.py new file mode 100644 index 00000000000..875157a0fda --- /dev/null +++ b/purchase_advance_payment/wizard/__init__.py @@ -0,0 +1 @@ +from . import purchase_advance_payment_wizard diff --git a/purchase_advance_payment/wizard/purchase_advance_payment_wizard.py b/purchase_advance_payment/wizard/purchase_advance_payment_wizard.py new file mode 100644 index 00000000000..5eb896e442d --- /dev/null +++ b/purchase_advance_payment/wizard/purchase_advance_payment_wizard.py @@ -0,0 +1,192 @@ +# Copyright (C) 2021 ForgeFlow S.L. +# License AGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html) + +from odoo import api, exceptions, fields, models + + +class AccountVoucherWizardPurchase(models.TransientModel): + _name = "account.voucher.wizard.purchase" + _description = "Account Voucher Wizard Purchase" + + order_id = fields.Many2one("purchase.order", required=True) + journal_id = fields.Many2one( + "account.journal", + "Journal", + required=True, + domain=[("type", "in", ("bank", "cash"))], + ) + journal_currency_id = fields.Many2one( + "res.currency", + "Journal Currency", + store=True, + readonly=False, + compute="_compute_get_journal_currency", + ) + currency_id = fields.Many2one("res.currency", "Currency", readonly=True) + amount_total = fields.Monetary(readonly=True) + amount_advance = fields.Monetary( + "Amount advanced", required=True, currency_field="journal_currency_id" + ) + date = fields.Date(required=True, default=fields.Date.context_today) + currency_amount = fields.Monetary( + "Curr. amount", + readonly=True, + currency_field="currency_id", + compute="_compute_currency_amount", + store=True, + ) + payment_ref = fields.Char("Ref.") + payment_method_line_id = fields.Many2one( + comodel_name="account.payment.method.line", + string="Payment Method", + readonly=False, + store=True, + compute="_compute_payment_method_line_id", + domain="[('id', 'in', available_payment_method_line_ids)]", + ) + available_payment_method_line_ids = fields.Many2many( + comodel_name="account.payment.method.line", + compute="_compute_available_payment_method_line_ids", + ) + + @api.depends("journal_id") + def _compute_get_journal_currency(self): + for wzd in self: + wzd.journal_currency_id = ( + wzd.journal_id.currency_id.id or self.env.user.company_id.currency_id.id + ) + + @api.depends("journal_id") + def _compute_payment_method_line_id(self): + for wizard in self: + if wizard.journal_id: + available_payment_method_lines = ( + wizard.journal_id._get_available_payment_method_lines("outbound") + ) + else: + available_payment_method_lines = False + # Select the first available one by default. + if available_payment_method_lines: + wizard.payment_method_line_id = available_payment_method_lines[ + 0 + ]._origin + else: + wizard.payment_method_line_id = False + + @api.depends("journal_id") + def _compute_available_payment_method_line_ids(self): + for wizard in self: + if wizard.journal_id: + wizard.available_payment_method_line_ids = ( + wizard.journal_id._get_available_payment_method_lines("outbound") + ) + else: + wizard.available_payment_method_line_ids = False + + @api.constrains("amount_advance") + def check_amount(self): + if self.journal_currency_id.compare_amounts(self.amount_advance, 0.0) <= 0: + raise exceptions.ValidationError( + self.env._("Amount of advance must be positive.") + ) + if self.env.context.get("active_id", False): + if ( + self.currency_id.compare_amounts( + self.currency_amount, self.order_id.amount_residual + ) + > 0 + ): + raise exceptions.ValidationError( + self.env._( + "Amount of advance is greater than residual amount on purchase" + ) + ) + + @api.model + def default_get(self, fields_list): + res = super().default_get(fields_list) + purchase_ids = self.env.context.get("active_ids", []) + if not purchase_ids: + return res + purchase_id = purchase_ids[0] + purchase = self.env["purchase.order"].browse(purchase_id) + if "amount_total" in fields_list: + res.update( + { + "order_id": purchase.id, + "amount_total": purchase.amount_residual, + "currency_id": purchase.currency_id.id, + } + ) + no_currency_journal_domain = [ + ("type", "in", ("bank", "cash")), + ("company_id", "=", purchase.company_id.id), + ("outbound_payment_method_line_ids", "!=", False), + ] + journal_domain = no_currency_journal_domain + if purchase.company_id.currency_id != purchase.currency_id: + journal_domain.append( + ("currency_id", "=", purchase.currency_id.id), + ) + journal = self.env["account.journal"].search( + journal_domain, + limit=1, + ) + if not journal: + journal = self.env["account.journal"].search( + no_currency_journal_domain, + limit=1, + ) + res["journal_id"] = journal.id + return res + + @api.depends("journal_id", "date", "amount_advance", "journal_currency_id") + def _compute_currency_amount(self): + if self.journal_currency_id != self.currency_id: + amount_advance = self.journal_currency_id._convert( + self.amount_advance, + self.currency_id, + self.order_id.company_id, + self.date or fields.Date.today(), + ) + else: + amount_advance = self.amount_advance + self.currency_amount = amount_advance + + def _prepare_payment_vals(self, purchase): + partner_id = purchase.partner_id.commercial_partner_id.id + return { + "purchase_id": purchase.id, + "date": self.date, + "amount": self.amount_advance, + "payment_type": "outbound", + "partner_type": "supplier", + "memo": self.payment_ref or purchase.name, + "journal_id": self.journal_id.id, + "currency_id": self.journal_currency_id.id, + "partner_id": partner_id, + "payment_method_line_id": self.payment_method_line_id.id, + } + + def make_advance_payment(self): + """Create customer paylines and validates the payment""" + self.ensure_one() + payment_obj = self.env["account.payment"] + purchase_obj = self.env["purchase.order"] + + purchase_ids = self.env.context.get("active_ids", []) + if purchase_ids: + purchase_id = purchase_ids[0] + purchase = purchase_obj.browse(purchase_id) + payment_vals = self._prepare_payment_vals(purchase) + payment = payment_obj.create(payment_vals) + if bool( + self.env["ir.config_parameter"] + .sudo() + .get_param("purchase_advance_payment.auto_post_advance_payments") + ): + payment.action_post() + + return { + "type": "ir.actions.act_window_close", + } diff --git a/purchase_advance_payment/wizard/purchase_advance_payment_wizard_view.xml b/purchase_advance_payment/wizard/purchase_advance_payment_wizard_view.xml new file mode 100644 index 00000000000..44f9417569c --- /dev/null +++ b/purchase_advance_payment/wizard/purchase_advance_payment_wizard_view.xml @@ -0,0 +1,64 @@ + + + + Advance Payment + account.voucher.wizard.purchase + form + +
+ + + + + + + + + + +
+
+
+
+
+ + Advance Payment + ir.actions.act_window + account.voucher.wizard.purchase + form + new + +
diff --git a/purchase_delivery_split_date/README.rst b/purchase_delivery_split_date/README.rst new file mode 100644 index 00000000000..95a156669f5 --- /dev/null +++ b/purchase_delivery_split_date/README.rst @@ -0,0 +1,144 @@ +.. image:: https://odoo-community.org/readme-banner-image + :target: https://odoo-community.org/get-involved?utm_source=readme + :alt: Odoo Community Association + +============================ +Purchase Delivery Split Date +============================ + +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:e14ccb527049e300440d09d5a59f338c8485b399aa99ad1c4b83db1c3f44e8c8 + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |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/license-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%2Fpurchase--workflow-lightgray.png?logo=github + :target: https://github.com/OCA/purchase-workflow/tree/19.0/purchase_delivery_split_date + :alt: OCA/purchase-workflow +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/purchase-workflow-19-0/purchase-workflow-19-0-purchase_delivery_split_date + :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/purchase-workflow&target_branch=19.0 + :alt: Try me on Runboat + +|badge1| |badge2| |badge3| |badge4| |badge5| + +When this module is installed, each Purchase Order you confirm will +generate one Incoming Shipment for each schedule date indicated in the +Purchase Order Lines. + +Once the Purchase Order has been confirmed, subsequent changes made to +the scheduled dates in the PO lines will produce a reorganization of the +corresponding stock moves in the Incoming Shipments, creating/deleting +new Incoming Shipments when needed, to ensure that each Incoming +Shipment contains moves to be received in the same date. + +This module is also designed for extensibility, so that you can define +in other modules new criteria to split deliveries. + +**Table of contents** + +.. contents:: + :local: + +Usage +===== + +When a Purchase Order is confirmed, shipments will be grouped by same +scheduled date. + +Changelog +========= + +12.0.2.1.0 (2020-04-30) +----------------------- + +- [FIX] when adding a new line on a confirmed PO, split the delivery + (this was done only if a date was changed on an existing line) +- [IMP] when the quantity on a line is changed, the onchange would reset + the planned date -> change this to prevent setting a date earlier than + the one on the line, since if we are using this module the user + probably has manually set the date first + +12.0.2.0.0 (2020-04-10) +----------------------- + +- Improve the module: when changing the date on a purchase line, this + will cause a split or a merge of the pickings, to keep 1 picking per + date. + +11.0.1.0.0 (2018-09-16) +----------------------- + +- Migration to 11.0. + (`#461 `__) +- When the scheduled date is changed in the PO after confirmation the + pickings are reorganized so as to force that every picking will have + only moves to be delivered on the same date. + +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 +------- + +* Numerigraphe +* ForgeFlow +* Camptocamp +* BCIM + +Contributors +------------ + +- Philippe Rossi (initial patch against v6.0) +- Lionel Sausin (modularization for v7+) +- Jordi Ballester Alomar (modularization + v8, v9) +- Lois Rilo (migration to v10) +- Alexandre Fayolle +- Pimolnat Suntian +- Mallory Marcot +- Denis Roussel +- Jacques-Etienne Baudoux (BCIM) + +Other credits +------------- + +The migration of this module from 18.0 to 19.0 was financially supported +by SprintIT Ltd. + +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/purchase-workflow `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/purchase_delivery_split_date/__init__.py b/purchase_delivery_split_date/__init__.py new file mode 100644 index 00000000000..4b76c7b2d5c --- /dev/null +++ b/purchase_delivery_split_date/__init__.py @@ -0,0 +1,3 @@ +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). + +from . import models diff --git a/purchase_delivery_split_date/__manifest__.py b/purchase_delivery_split_date/__manifest__.py new file mode 100644 index 00000000000..f08b2cb84fb --- /dev/null +++ b/purchase_delivery_split_date/__manifest__.py @@ -0,0 +1,20 @@ +# Copyright 2014-2016 Numérigraphe SARL +# Copyright 2017 ForgeFlow, S.L. +# Copyright 2021 Jacques-Etienne Baudoux (BCIM) +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). + +{ + "name": "Purchase Delivery Split Date", + "version": "19.0.1.0.0", + "summary": "Allows Purchase Order you confirm to generate one Incoming " + "Shipment for each expected date indicated in the Purchase Order Lines", + "author": ( + "Numerigraphe, ForgeFlow, Camptocamp, BCIM, Odoo Community Association (OCA)" + ), + "website": "https://github.com/OCA/purchase-workflow", + "category": "Purchase Management", + "license": "AGPL-3", + "depends": ["purchase_stock"], + "installable": True, + "application": False, +} diff --git a/purchase_delivery_split_date/i18n/ca.po b/purchase_delivery_split_date/i18n/ca.po new file mode 100644 index 00000000000..5396e52f7fb --- /dev/null +++ b/purchase_delivery_split_date/i18n/ca.po @@ -0,0 +1,34 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * purchase_delivery_split_date +# +# Translators: +# OCA Transbot , 2017 +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 9.0c\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2017-02-24 13:09+0000\n" +"PO-Revision-Date: 2017-02-24 13:09+0000\n" +"Last-Translator: OCA Transbot , 2017\n" +"Language-Team: Catalan (https://www.transifex.com/oca/teams/23907/ca/)\n" +"Language: ca\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" + +#. module: purchase_delivery_split_date +#: model:ir.model,name:purchase_delivery_split_date.model_purchase_order_line +msgid "Purchase Order Line" +msgstr "" + +#. module: purchase_delivery_split_date +#: model:ir.model,name:purchase_delivery_split_date.model_stock_move +msgid "Stock Move" +msgstr "" + +#. module: purchase_delivery_split_date +#: model:ir.model,name:purchase_delivery_split_date.model_stock_picking +msgid "Transfer" +msgstr "Transferència" diff --git a/purchase_delivery_split_date/i18n/de.po b/purchase_delivery_split_date/i18n/de.po new file mode 100644 index 00000000000..3c257cd198a --- /dev/null +++ b/purchase_delivery_split_date/i18n/de.po @@ -0,0 +1,39 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * purchase_delivery_split_date +# +# Translators: +# OCA Transbot , 2017 +# Rudolf Schnapka , 2017 +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 9.0c\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2017-02-24 13:09+0000\n" +"PO-Revision-Date: 2020-07-22 12:19+0000\n" +"Last-Translator: c2cdidier \n" +"Language-Team: German (https://www.transifex.com/oca/teams/23907/de/)\n" +"Language: de\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 3.10\n" + +#. module: purchase_delivery_split_date +#: model:ir.model,name:purchase_delivery_split_date.model_purchase_order_line +msgid "Purchase Order Line" +msgstr "Bestellposition" + +#. module: purchase_delivery_split_date +#: model:ir.model,name:purchase_delivery_split_date.model_stock_move +msgid "Stock Move" +msgstr "" + +#. module: purchase_delivery_split_date +#: model:ir.model,name:purchase_delivery_split_date.model_stock_picking +msgid "Transfer" +msgstr "Transfer" + +#~ msgid "Purchase Order" +#~ msgstr "Bestellung" diff --git a/purchase_delivery_split_date/i18n/es.po b/purchase_delivery_split_date/i18n/es.po new file mode 100644 index 00000000000..70ab5c9c2c4 --- /dev/null +++ b/purchase_delivery_split_date/i18n/es.po @@ -0,0 +1,39 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * purchase_delivery_split_date +# +# Translators: +# OCA Transbot , 2017 +# Pedro M. Baeza , 2017 +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 9.0c\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2017-02-24 13:09+0000\n" +"PO-Revision-Date: 2023-07-20 10:15+0000\n" +"Last-Translator: Ivorra78 \n" +"Language-Team: Spanish (https://www.transifex.com/oca/teams/23907/es/)\n" +"Language: es\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: purchase_delivery_split_date +#: model:ir.model,name:purchase_delivery_split_date.model_purchase_order_line +msgid "Purchase Order Line" +msgstr "Línea orden de compra" + +#. module: purchase_delivery_split_date +#: model:ir.model,name:purchase_delivery_split_date.model_stock_move +msgid "Stock Move" +msgstr "" + +#. module: purchase_delivery_split_date +#: model:ir.model,name:purchase_delivery_split_date.model_stock_picking +msgid "Transfer" +msgstr "Transferir" + +#~ msgid "Purchase Order" +#~ msgstr "orden de compra" diff --git a/purchase_delivery_split_date/i18n/es_MX.po b/purchase_delivery_split_date/i18n/es_MX.po new file mode 100644 index 00000000000..8812a64a002 --- /dev/null +++ b/purchase_delivery_split_date/i18n/es_MX.po @@ -0,0 +1,39 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * purchase_delivery_split_date +# +# Translators: +# Juan González , 2017 +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 9.0c\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2017-02-24 13:09+0000\n" +"PO-Revision-Date: 2017-02-24 13:09+0000\n" +"Last-Translator: Juan González , 2017\n" +"Language-Team: Spanish (Mexico) (https://www.transifex.com/oca/teams/23907/" +"es_MX/)\n" +"Language: es_MX\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" + +#. module: purchase_delivery_split_date +#: model:ir.model,name:purchase_delivery_split_date.model_purchase_order_line +msgid "Purchase Order Line" +msgstr "Línea de orden de compra" + +#. module: purchase_delivery_split_date +#: model:ir.model,name:purchase_delivery_split_date.model_stock_move +msgid "Stock Move" +msgstr "" + +#. module: purchase_delivery_split_date +#: model:ir.model,name:purchase_delivery_split_date.model_stock_picking +msgid "Transfer" +msgstr "" + +#, fuzzy +#~ msgid "Purchase Order" +#~ msgstr "Línea de orden de compra" diff --git a/purchase_delivery_split_date/i18n/es_PE.po b/purchase_delivery_split_date/i18n/es_PE.po new file mode 100644 index 00000000000..7f53b260e1c --- /dev/null +++ b/purchase_delivery_split_date/i18n/es_PE.po @@ -0,0 +1,39 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * purchase_delivery_split_date +# +# Translators: +# Henry Garcia , 2017 +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 10.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2017-07-15 06:42+0000\n" +"PO-Revision-Date: 2017-07-15 06:42+0000\n" +"Last-Translator: Henry Garcia , 2017\n" +"Language-Team: Spanish (Peru) (https://www.transifex.com/oca/teams/23907/" +"es_PE/)\n" +"Language: es_PE\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" + +#. module: purchase_delivery_split_date +#: model:ir.model,name:purchase_delivery_split_date.model_purchase_order_line +msgid "Purchase Order Line" +msgstr "Linea de orden de compra" + +#. module: purchase_delivery_split_date +#: model:ir.model,name:purchase_delivery_split_date.model_stock_move +msgid "Stock Move" +msgstr "" + +#. module: purchase_delivery_split_date +#: model:ir.model,name:purchase_delivery_split_date.model_stock_picking +msgid "Transfer" +msgstr "Transferir" + +#, fuzzy +#~ msgid "Purchase Order" +#~ msgstr "Linea de orden de compra" diff --git a/purchase_delivery_split_date/i18n/fi.po b/purchase_delivery_split_date/i18n/fi.po new file mode 100644 index 00000000000..e9d489104a4 --- /dev/null +++ b/purchase_delivery_split_date/i18n/fi.po @@ -0,0 +1,38 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * purchase_delivery_split_date +# +# Translators: +# Timo Talvitie , 2017 +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 9.0c\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2017-03-02 12:10+0000\n" +"PO-Revision-Date: 2017-03-02 12:10+0000\n" +"Last-Translator: Timo Talvitie , 2017\n" +"Language-Team: Finnish (https://www.transifex.com/oca/teams/23907/fi/)\n" +"Language: fi\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" + +#. module: purchase_delivery_split_date +#: model:ir.model,name:purchase_delivery_split_date.model_purchase_order_line +msgid "Purchase Order Line" +msgstr "Ostotilausrivi" + +#. module: purchase_delivery_split_date +#: model:ir.model,name:purchase_delivery_split_date.model_stock_move +msgid "Stock Move" +msgstr "" + +#. module: purchase_delivery_split_date +#: model:ir.model,name:purchase_delivery_split_date.model_stock_picking +msgid "Transfer" +msgstr "" + +#, fuzzy +#~ msgid "Purchase Order" +#~ msgstr "Ostotilausrivi" diff --git a/purchase_delivery_split_date/i18n/fr.po b/purchase_delivery_split_date/i18n/fr.po new file mode 100644 index 00000000000..541d76edfa3 --- /dev/null +++ b/purchase_delivery_split_date/i18n/fr.po @@ -0,0 +1,38 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * purchase_delivery_split_date +# +# Translators: +# OCA Transbot , 2017 +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 9.0c\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2017-02-24 13:09+0000\n" +"PO-Revision-Date: 2020-07-22 12:19+0000\n" +"Last-Translator: c2cdidier \n" +"Language-Team: French (https://www.transifex.com/oca/teams/23907/fr/)\n" +"Language: fr\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 3.10\n" + +#. module: purchase_delivery_split_date +#: model:ir.model,name:purchase_delivery_split_date.model_purchase_order_line +msgid "Purchase Order Line" +msgstr "Ligne de commande d'achat" + +#. module: purchase_delivery_split_date +#: model:ir.model,name:purchase_delivery_split_date.model_stock_move +msgid "Stock Move" +msgstr "" + +#. module: purchase_delivery_split_date +#: model:ir.model,name:purchase_delivery_split_date.model_stock_picking +msgid "Transfer" +msgstr "Transférer" + +#~ msgid "Purchase Order" +#~ msgstr "Commande d'achat" diff --git a/purchase_delivery_split_date/i18n/gl.po b/purchase_delivery_split_date/i18n/gl.po new file mode 100644 index 00000000000..d5baa0fb0a4 --- /dev/null +++ b/purchase_delivery_split_date/i18n/gl.po @@ -0,0 +1,34 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * purchase_delivery_split_date +# +# Translators: +# OCA Transbot , 2017 +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 9.0c\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2017-02-24 13:09+0000\n" +"PO-Revision-Date: 2017-02-24 13:09+0000\n" +"Last-Translator: OCA Transbot , 2017\n" +"Language-Team: Galician (https://www.transifex.com/oca/teams/23907/gl/)\n" +"Language: gl\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" + +#. module: purchase_delivery_split_date +#: model:ir.model,name:purchase_delivery_split_date.model_purchase_order_line +msgid "Purchase Order Line" +msgstr "" + +#. module: purchase_delivery_split_date +#: model:ir.model,name:purchase_delivery_split_date.model_stock_move +msgid "Stock Move" +msgstr "" + +#. module: purchase_delivery_split_date +#: model:ir.model,name:purchase_delivery_split_date.model_stock_picking +msgid "Transfer" +msgstr "Transferencia" diff --git a/purchase_delivery_split_date/i18n/hr.po b/purchase_delivery_split_date/i18n/hr.po new file mode 100644 index 00000000000..e96f339a43b --- /dev/null +++ b/purchase_delivery_split_date/i18n/hr.po @@ -0,0 +1,39 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * purchase_delivery_split_date +# +# Translators: +# Bole , 2017 +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 10.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2017-07-11 12:52+0000\n" +"PO-Revision-Date: 2017-07-11 12:52+0000\n" +"Last-Translator: Bole , 2017\n" +"Language-Team: Croatian (https://www.transifex.com/oca/teams/23907/hr/)\n" +"Language: hr\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && " +"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n" + +#. module: purchase_delivery_split_date +#: model:ir.model,name:purchase_delivery_split_date.model_purchase_order_line +msgid "Purchase Order Line" +msgstr "Stavka naloga za nabavu" + +#. module: purchase_delivery_split_date +#: model:ir.model,name:purchase_delivery_split_date.model_stock_move +msgid "Stock Move" +msgstr "" + +#. module: purchase_delivery_split_date +#: model:ir.model,name:purchase_delivery_split_date.model_stock_picking +msgid "Transfer" +msgstr "Transfer" + +#, fuzzy +#~ msgid "Purchase Order" +#~ msgstr "Stavka naloga za nabavu" diff --git a/purchase_delivery_split_date/i18n/it.po b/purchase_delivery_split_date/i18n/it.po new file mode 100644 index 00000000000..e57d043877f --- /dev/null +++ b/purchase_delivery_split_date/i18n/it.po @@ -0,0 +1,39 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * purchase_delivery_split_date +# +# Translators: +# OCA Transbot , 2017 +# Paolo Valier , 2017 +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 9.0c\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2017-02-24 13:09+0000\n" +"PO-Revision-Date: 2025-06-09 09:04+0000\n" +"Last-Translator: mymage \n" +"Language-Team: Italian (https://www.transifex.com/oca/teams/23907/it/)\n" +"Language: it\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 5.10.4\n" + +#. module: purchase_delivery_split_date +#: model:ir.model,name:purchase_delivery_split_date.model_purchase_order_line +msgid "Purchase Order Line" +msgstr "Riga ordine di acquisto" + +#. module: purchase_delivery_split_date +#: model:ir.model,name:purchase_delivery_split_date.model_stock_move +msgid "Stock Move" +msgstr "Movimento di magazzino" + +#. module: purchase_delivery_split_date +#: model:ir.model,name:purchase_delivery_split_date.model_stock_picking +msgid "Transfer" +msgstr "Trasferimento" + +#~ msgid "Purchase Order" +#~ msgstr "Ordine di acquisto" diff --git a/purchase_delivery_split_date/i18n/nl_NL.po b/purchase_delivery_split_date/i18n/nl_NL.po new file mode 100644 index 00000000000..c0204d84807 --- /dev/null +++ b/purchase_delivery_split_date/i18n/nl_NL.po @@ -0,0 +1,39 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * purchase_delivery_split_date +# +# Translators: +# Peter Hageman , 2017 +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 10.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2017-06-22 02:44+0000\n" +"PO-Revision-Date: 2017-06-22 02:44+0000\n" +"Last-Translator: Peter Hageman , 2017\n" +"Language-Team: Dutch (Netherlands) (https://www.transifex.com/oca/" +"teams/23907/nl_NL/)\n" +"Language: nl_NL\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" + +#. module: purchase_delivery_split_date +#: model:ir.model,name:purchase_delivery_split_date.model_purchase_order_line +msgid "Purchase Order Line" +msgstr "Inkooporderregel" + +#. module: purchase_delivery_split_date +#: model:ir.model,name:purchase_delivery_split_date.model_stock_move +msgid "Stock Move" +msgstr "" + +#. module: purchase_delivery_split_date +#: model:ir.model,name:purchase_delivery_split_date.model_stock_picking +msgid "Transfer" +msgstr "Verplaatsen" + +#, fuzzy +#~ msgid "Purchase Order" +#~ msgstr "Inkooporderregel" diff --git a/purchase_delivery_split_date/i18n/pt_BR.po b/purchase_delivery_split_date/i18n/pt_BR.po new file mode 100644 index 00000000000..aa106399992 --- /dev/null +++ b/purchase_delivery_split_date/i18n/pt_BR.po @@ -0,0 +1,39 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * purchase_delivery_split_date +# +# Translators: +# OCA Transbot , 2017 +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 9.0c\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2017-02-24 13:09+0000\n" +"PO-Revision-Date: 2022-10-03 15:53+0000\n" +"Last-Translator: Douglas Custódio \n" +"Language-Team: Portuguese (Brazil) (https://www.transifex.com/oca/" +"teams/23907/pt_BR/)\n" +"Language: pt_BR\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: nplurals=2; plural=n > 1;\n" +"X-Generator: Weblate 4.3.2\n" + +#. module: purchase_delivery_split_date +#: model:ir.model,name:purchase_delivery_split_date.model_purchase_order_line +msgid "Purchase Order Line" +msgstr "Linha do Pedido de Compra" + +#. module: purchase_delivery_split_date +#: model:ir.model,name:purchase_delivery_split_date.model_stock_move +msgid "Stock Move" +msgstr "" + +#. module: purchase_delivery_split_date +#: model:ir.model,name:purchase_delivery_split_date.model_stock_picking +msgid "Transfer" +msgstr "Transferir" + +#~ msgid "Purchase Order" +#~ msgstr "Pedido de Compra" diff --git a/purchase_delivery_split_date/i18n/pt_PT.po b/purchase_delivery_split_date/i18n/pt_PT.po new file mode 100644 index 00000000000..b2aa13a9bae --- /dev/null +++ b/purchase_delivery_split_date/i18n/pt_PT.po @@ -0,0 +1,39 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * purchase_delivery_split_date +# +# Translators: +# Pedro Castro Silva , 2017 +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 9.0c\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2017-02-24 13:09+0000\n" +"PO-Revision-Date: 2017-02-24 13:09+0000\n" +"Last-Translator: Pedro Castro Silva , 2017\n" +"Language-Team: Portuguese (Portugal) (https://www.transifex.com/oca/" +"teams/23907/pt_PT/)\n" +"Language: pt_PT\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" + +#. module: purchase_delivery_split_date +#: model:ir.model,name:purchase_delivery_split_date.model_purchase_order_line +msgid "Purchase Order Line" +msgstr "Linha de Encomenda de Compra" + +#. module: purchase_delivery_split_date +#: model:ir.model,name:purchase_delivery_split_date.model_stock_move +msgid "Stock Move" +msgstr "" + +#. module: purchase_delivery_split_date +#: model:ir.model,name:purchase_delivery_split_date.model_stock_picking +msgid "Transfer" +msgstr "" + +#, fuzzy +#~ msgid "Purchase Order" +#~ msgstr "Linha de Encomenda de Compra" diff --git a/purchase_delivery_split_date/i18n/purchase_delivery_split_date.pot b/purchase_delivery_split_date/i18n/purchase_delivery_split_date.pot new file mode 100644 index 00000000000..824ea9f9663 --- /dev/null +++ b/purchase_delivery_split_date/i18n/purchase_delivery_split_date.pot @@ -0,0 +1,43 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * purchase_delivery_split_date +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 19.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: purchase_delivery_split_date +#: model:ir.model.fields,field_description:purchase_delivery_split_date.field_purchase_order_line__display_name +#: model:ir.model.fields,field_description:purchase_delivery_split_date.field_stock_move__display_name +#: model:ir.model.fields,field_description:purchase_delivery_split_date.field_stock_picking__display_name +msgid "Display Name" +msgstr "" + +#. module: purchase_delivery_split_date +#: model:ir.model.fields,field_description:purchase_delivery_split_date.field_purchase_order_line__id +#: model:ir.model.fields,field_description:purchase_delivery_split_date.field_stock_move__id +#: model:ir.model.fields,field_description:purchase_delivery_split_date.field_stock_picking__id +msgid "ID" +msgstr "" + +#. module: purchase_delivery_split_date +#: model:ir.model,name:purchase_delivery_split_date.model_purchase_order_line +msgid "Purchase Order Line" +msgstr "" + +#. module: purchase_delivery_split_date +#: model:ir.model,name:purchase_delivery_split_date.model_stock_move +msgid "Stock Move" +msgstr "" + +#. module: purchase_delivery_split_date +#: model:ir.model,name:purchase_delivery_split_date.model_stock_picking +msgid "Transfer" +msgstr "" diff --git a/purchase_delivery_split_date/i18n/ro.po b/purchase_delivery_split_date/i18n/ro.po new file mode 100644 index 00000000000..1b44d2a6c70 --- /dev/null +++ b/purchase_delivery_split_date/i18n/ro.po @@ -0,0 +1,39 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * purchase_delivery_split_date +# +# Translators: +# Dorin Hongu , 2017 +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 9.0c\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2017-02-24 13:09+0000\n" +"PO-Revision-Date: 2017-02-24 13:09+0000\n" +"Last-Translator: Dorin Hongu , 2017\n" +"Language-Team: Romanian (https://www.transifex.com/oca/teams/23907/ro/)\n" +"Language: ro\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: nplurals=3; plural=(n==1?0:(((n%100>19)||((n%100==0)&&(n!=0)))?" +"2:1));\n" + +#. module: purchase_delivery_split_date +#: model:ir.model,name:purchase_delivery_split_date.model_purchase_order_line +msgid "Purchase Order Line" +msgstr "Linie comandă achiziție" + +#. module: purchase_delivery_split_date +#: model:ir.model,name:purchase_delivery_split_date.model_stock_move +msgid "Stock Move" +msgstr "" + +#. module: purchase_delivery_split_date +#: model:ir.model,name:purchase_delivery_split_date.model_stock_picking +msgid "Transfer" +msgstr "" + +#, fuzzy +#~ msgid "Purchase Order" +#~ msgstr "Linie comandă achiziție" diff --git a/purchase_delivery_split_date/i18n/sl.po b/purchase_delivery_split_date/i18n/sl.po new file mode 100644 index 00000000000..74445b5a4a8 --- /dev/null +++ b/purchase_delivery_split_date/i18n/sl.po @@ -0,0 +1,39 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * purchase_delivery_split_date +# +# Translators: +# OCA Transbot , 2017 +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 9.0c\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2017-02-24 13:09+0000\n" +"PO-Revision-Date: 2017-02-24 13:09+0000\n" +"Last-Translator: OCA Transbot , 2017\n" +"Language-Team: Slovenian (https://www.transifex.com/oca/teams/23907/sl/)\n" +"Language: sl\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: nplurals=4; plural=(n%100==1 ? 0 : n%100==2 ? 1 : n%100==3 || " +"n%100==4 ? 2 : 3);\n" + +#. module: purchase_delivery_split_date +#: model:ir.model,name:purchase_delivery_split_date.model_purchase_order_line +msgid "Purchase Order Line" +msgstr "Postavka nabavnega naloga" + +#. module: purchase_delivery_split_date +#: model:ir.model,name:purchase_delivery_split_date.model_stock_move +msgid "Stock Move" +msgstr "" + +#. module: purchase_delivery_split_date +#: model:ir.model,name:purchase_delivery_split_date.model_stock_picking +msgid "Transfer" +msgstr "Premestitev" + +#, fuzzy +#~ msgid "Purchase Order" +#~ msgstr "Postavka nabavnega naloga" diff --git a/purchase_delivery_split_date/i18n/zh_CN.po b/purchase_delivery_split_date/i18n/zh_CN.po new file mode 100644 index 00000000000..3788586696b --- /dev/null +++ b/purchase_delivery_split_date/i18n/zh_CN.po @@ -0,0 +1,36 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * purchase_delivery_split_date +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 12.0\n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: 2019-09-01 16:00+0000\n" +"Last-Translator: 黎伟杰 <674416404@qq.com>\n" +"Language-Team: none\n" +"Language: zh_CN\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: nplurals=1; plural=0;\n" +"X-Generator: Weblate 3.8\n" + +#. module: purchase_delivery_split_date +#: model:ir.model,name:purchase_delivery_split_date.model_purchase_order_line +msgid "Purchase Order Line" +msgstr "采购订单行" + +#. module: purchase_delivery_split_date +#: model:ir.model,name:purchase_delivery_split_date.model_stock_move +msgid "Stock Move" +msgstr "" + +#. module: purchase_delivery_split_date +#: model:ir.model,name:purchase_delivery_split_date.model_stock_picking +msgid "Transfer" +msgstr "调拨" + +#, fuzzy +#~ msgid "Purchase Order" +#~ msgstr "采购订单行" diff --git a/purchase_delivery_split_date/models/__init__.py b/purchase_delivery_split_date/models/__init__.py new file mode 100644 index 00000000000..a40e6896bd5 --- /dev/null +++ b/purchase_delivery_split_date/models/__init__.py @@ -0,0 +1,4 @@ +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). + + +from . import purchase_order_line, stock_move, stock_picking diff --git a/purchase_delivery_split_date/models/purchase_order_line.py b/purchase_delivery_split_date/models/purchase_order_line.py new file mode 100644 index 00000000000..07e338415e6 --- /dev/null +++ b/purchase_delivery_split_date/models/purchase_order_line.py @@ -0,0 +1,89 @@ +# Copyright 2014-2016 Numérigraphe SARL +# Copyright 2017 ForgeFlow, S.L. +# Copyright 2021 Jacques-Etienne Baudoux (BCIM) +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). + +import pytz + +from odoo import models + + +class PurchaseOrderLine(models.Model): + _inherit = "purchase.order.line" + + def _purchase_split_date_get_group_keys(self, picking): + """Define the key that will be used to group. + + The key should be defined as a tuple of dictionaries, with each element + containing a dictionary element with the field that you want to group + by. This method is designed for extensibility, so that other modules + can add additional keys or replace them by others. + """ + tz = self.order_id.picking_type_id.warehouse_id.partner_id.tz + wh_tz = pytz.timezone(tz) if tz else self.env.tz + date_planned_tz = self.date_planned.astimezone(pytz.utc).astimezone(wh_tz) + date = date_planned_tz.date() + # Split date value to obtain only the attributes year, month and day + key = (("date_planned", date),) + return key + + def _purchase_split_date_get_sorted_keys(self): + """Return a tuple of keys to use in order to sort the order lines. + + This method is designed for extensibility, so that other modules can + add additional keys or replace them by others. + """ + return (self.date_planned,) + + def _create_stock_moves(self, picking): + """Group the receptions in one picking per assignation domain""" + if not picking: + # A picking should be provided + return super()._create_stock_moves(picking) + moves = self.env["stock.move"] + tz = picking.picking_type_id.warehouse_id.partner_id.tz + # Group the order lines by group key + order_lines = self.filtered( + lambda line: not line.display_type and line.product_id.type == "consu" + ) + date_groups = order_lines.grouped( + lambda line: line._purchase_split_date_get_group_keys(picking), + ) + for key, po_lines in date_groups.items(): + if ( + not picking.move_ids + or picking.move_ids == po_lines + or picking.filtered_domain( + picking._purchase_split_date_assign_domain(key, tz) + ) + ): + # when the picking is empty or contains all the lines or is + # valid for the key + moves += super(PurchaseOrderLine, po_lines)._create_stock_moves(picking) + else: + moves_to_assign = super( + PurchaseOrderLine, po_lines + )._create_stock_moves(picking.browse()) + moves_to_assign.with_context( + purchase_delivery_split_date=True + )._assign_picking() + moves_to_assign._action_confirm() + moves += moves_to_assign + return moves + + def _compute_price_unit_and_date_planned_and_name(self): + """ + If the line product quantity is changed and a seller is found, + the date_planned is updated from the supplier (in _get_date_planned()) + """ + date_planned_by_record = dict() + for line in self: + date_planned_by_record[line.id] = line.date_planned + res = super()._compute_price_unit_and_date_planned_and_name() + for line in self: + if ( + date_planned_by_record[line.id] + and line.date_planned <= date_planned_by_record[line.id] + ): + line.date_planned = date_planned_by_record[line.id] + return res diff --git a/purchase_delivery_split_date/models/stock_move.py b/purchase_delivery_split_date/models/stock_move.py new file mode 100644 index 00000000000..f4bdd2013a1 --- /dev/null +++ b/purchase_delivery_split_date/models/stock_move.py @@ -0,0 +1,82 @@ +# Copyright 2021 Jacques-Etienne Baudoux (BCIM) +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). + +import pytz + +from odoo import models +from odoo.fields import Domain + + +class StockMove(models.Model): + _inherit = "stock.move" + + def _purchase_split_date_get_group_keys(self): + tz = self.picking_type_id.warehouse_id.partner_id.tz + wh_tz = pytz.timezone(tz) if tz else self.env.tz + date_planned_tz = self.date_deadline.astimezone(pytz.utc).astimezone(wh_tz) + date = date_planned_tz.date() + # Split date value to obtain only the attributes year, month and day + key = (("date_planned", date),) + return key + + def _search_picking_for_assignation_domain(self): + domain = super()._search_picking_for_assignation_domain() + if self.env.context.get("purchase_delivery_split_date"): + key = self._purchase_split_date_get_group_keys() + tz = self.picking_type_id.warehouse_id.partner_id.tz + domain = Domain.AND( + [domain, self.picking_id._purchase_split_date_assign_domain(key, tz)] + ) + return domain + + def write(self, vals): + res = super().write(vals) + if "date_deadline" in vals: + self._purchase_split_by_date(vals["date_deadline"]) + return res + + def _purchase_split_by_date(self, new_deadline): + po_moves = self.filtered( + lambda m: m.purchase_line_id and m.state not in ("done", "cancel") + ) + if not po_moves: + return + po_moves.date = new_deadline + for picking, moves in po_moves.grouped("picking_id").items(): + if picking.printed: + # Do not split by date anymore + continue + # the picking is not valid anymore + reserved_moves = moves.filtered( + lambda m: m.state in ("partially_available", "assigned") + ) + reserved_moves._do_unreserve() + moves.picking_id = False + if picking.move_ids: + # recompute the picking dates as some moves have been + # removed + picking._compute_scheduled_date() + picking._compute_date_deadline() + else: + picking.state = "cancel" + moves.with_context(purchase_delivery_split_date=True)._assign_picking() + reserved_moves._action_assign() + + def _get_new_picking_values(self): + vals = super()._get_new_picking_values() + if self.env.context.get("purchase_delivery_split_date"): + is_dropship = all([move._is_dropshipped() for move in self]) + if not vals.get("partner_id") or is_dropship: + partners = self.purchase_line_id.partner_id + vals["partner_id"] = next(iter(partners), partners).id + return vals + + def _assign_picking_values(self, picking): + vals = super()._assign_picking_values(picking) + # The core function will remove the partner from the picking if it is + # different than the one on the moves (Destination Address). + # For dropshipping the partner on the pick is the contact ! + if self.env.context.get("purchase_delivery_split_date"): + if "partner_id" in vals.keys(): + vals.pop("partner_id") + return vals diff --git a/purchase_delivery_split_date/models/stock_picking.py b/purchase_delivery_split_date/models/stock_picking.py new file mode 100644 index 00000000000..113a63bd0fc --- /dev/null +++ b/purchase_delivery_split_date/models/stock_picking.py @@ -0,0 +1,25 @@ +# Copyright 2014-2016 Numérigraphe SARL +# Copyright 2017 ForgeFlow, S.L. +# Copyright 2021 Jacques-Etienne Baudoux (BCIM) +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). + +import pytz + +from odoo import api, fields, models + + +class StockPicking(models.Model): + _inherit = "stock.picking" + + @api.model + def _purchase_split_date_assign_domain(self, key, tz): + date = key[0][1] + wh_tz = pytz.timezone(tz) if tz else self.env.tz + # The date is in local time + dt_start_tz = wh_tz.localize(fields.Datetime.to_datetime(date)) + dt_start = dt_start_tz.astimezone(pytz.utc).replace(tzinfo=None) + dt_end = fields.Datetime.add(dt_start, days=1) + return [ + ("scheduled_date", ">=", dt_start), + ("scheduled_date", "<", dt_end), + ] diff --git a/purchase_delivery_split_date/pyproject.toml b/purchase_delivery_split_date/pyproject.toml new file mode 100644 index 00000000000..4231d0cccb3 --- /dev/null +++ b/purchase_delivery_split_date/pyproject.toml @@ -0,0 +1,3 @@ +[build-system] +requires = ["whool"] +build-backend = "whool.buildapi" diff --git a/purchase_delivery_split_date/readme/CONTRIBUTORS.md b/purchase_delivery_split_date/readme/CONTRIBUTORS.md new file mode 100644 index 00000000000..59eb340ada7 --- /dev/null +++ b/purchase_delivery_split_date/readme/CONTRIBUTORS.md @@ -0,0 +1,10 @@ +- Philippe Rossi \<\> (initial patch against v6.0) +- Lionel Sausin \<\> (modularization for v7+) +- Jordi Ballester Alomar \<\> + (modularization v8, v9) +- Lois Rilo \<\> (migration to v10) +- Alexandre Fayolle \<\> +- Pimolnat Suntian \<\> +- Mallory Marcot \<\> +- Denis Roussel \<\> +- Jacques-Etienne Baudoux (BCIM) \<\> diff --git a/purchase_delivery_split_date/readme/CREDITS.md b/purchase_delivery_split_date/readme/CREDITS.md new file mode 100644 index 00000000000..47e2d463523 --- /dev/null +++ b/purchase_delivery_split_date/readme/CREDITS.md @@ -0,0 +1,2 @@ +The migration of this module from 18.0 to 19.0 was financially supported +by SprintIT Ltd. diff --git a/purchase_delivery_split_date/readme/DESCRIPTION.md b/purchase_delivery_split_date/readme/DESCRIPTION.md new file mode 100644 index 00000000000..7861778c07c --- /dev/null +++ b/purchase_delivery_split_date/readme/DESCRIPTION.md @@ -0,0 +1,12 @@ +When this module is installed, each Purchase Order you confirm will +generate one Incoming Shipment for each schedule date indicated in the +Purchase Order Lines. + +Once the Purchase Order has been confirmed, subsequent changes made to +the scheduled dates in the PO lines will produce a reorganization of the +corresponding stock moves in the Incoming Shipments, creating/deleting +new Incoming Shipments when needed, to ensure that each Incoming +Shipment contains moves to be received in the same date. + +This module is also designed for extensibility, so that you can define +in other modules new criteria to split deliveries. diff --git a/purchase_delivery_split_date/readme/HISTORY.md b/purchase_delivery_split_date/readme/HISTORY.md new file mode 100644 index 00000000000..afe0f9f410c --- /dev/null +++ b/purchase_delivery_split_date/readme/HISTORY.md @@ -0,0 +1,22 @@ +## 12.0.2.1.0 (2020-04-30) + +- \[FIX\] when adding a new line on a confirmed PO, split the delivery + (this was done only if a date was changed on an existing line) +- \[IMP\] when the quantity on a line is changed, the onchange would + reset the planned date -\> change this to prevent setting a date + earlier than the one on the line, since if we are using this module + the user probably has manually set the date first + +## 12.0.2.0.0 (2020-04-10) + +- Improve the module: when changing the date on a purchase line, this + will cause a split or a merge of the pickings, to keep 1 picking per + date. + +## 11.0.1.0.0 (2018-09-16) + +- Migration to 11.0. + ([\#461](https://github.com/OCA/purchase-workflow/issues/461)) +- When the scheduled date is changed in the PO after confirmation the + pickings are reorganized so as to force that every picking will have + only moves to be delivered on the same date. diff --git a/purchase_delivery_split_date/readme/USAGE.md b/purchase_delivery_split_date/readme/USAGE.md new file mode 100644 index 00000000000..cc4af969d6f --- /dev/null +++ b/purchase_delivery_split_date/readme/USAGE.md @@ -0,0 +1,2 @@ +When a Purchase Order is confirmed, shipments will be grouped by same +scheduled date. diff --git a/purchase_delivery_split_date/static/description/icon.png b/purchase_delivery_split_date/static/description/icon.png new file mode 100644 index 00000000000..3a0328b516c Binary files /dev/null and b/purchase_delivery_split_date/static/description/icon.png differ diff --git a/purchase_delivery_split_date/static/description/index.html b/purchase_delivery_split_date/static/description/index.html new file mode 100644 index 00000000000..9982163fee4 --- /dev/null +++ b/purchase_delivery_split_date/static/description/index.html @@ -0,0 +1,500 @@ + + + + + +README.rst + + + +
+ + + +Odoo Community Association + +
+

Purchase Delivery Split Date

+ +

Beta License: AGPL-3 OCA/purchase-workflow Translate me on Weblate Try me on Runboat

+

When this module is installed, each Purchase Order you confirm will +generate one Incoming Shipment for each schedule date indicated in the +Purchase Order Lines.

+

Once the Purchase Order has been confirmed, subsequent changes made to +the scheduled dates in the PO lines will produce a reorganization of the +corresponding stock moves in the Incoming Shipments, creating/deleting +new Incoming Shipments when needed, to ensure that each Incoming +Shipment contains moves to be received in the same date.

+

This module is also designed for extensibility, so that you can define +in other modules new criteria to split deliveries.

+

Table of contents

+ +
+

Usage

+

When a Purchase Order is confirmed, shipments will be grouped by same +scheduled date.

+
+
+

Changelog

+
+

12.0.2.1.0 (2020-04-30)

+
    +
  • [FIX] when adding a new line on a confirmed PO, split the delivery +(this was done only if a date was changed on an existing line)
  • +
  • [IMP] when the quantity on a line is changed, the onchange would reset +the planned date -> change this to prevent setting a date earlier than +the one on the line, since if we are using this module the user +probably has manually set the date first
  • +
+
+
+

12.0.2.0.0 (2020-04-10)

+
    +
  • Improve the module: when changing the date on a purchase line, this +will cause a split or a merge of the pickings, to keep 1 picking per +date.
  • +
+
+
+

11.0.1.0.0 (2018-09-16)

+
    +
  • Migration to 11.0. +(#461)
  • +
  • When the scheduled date is changed in the PO after confirmation the +pickings are reorganized so as to force that every picking will have +only moves to be delivered on the same date.
  • +
+
+
+
+

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

+
    +
  • Numerigraphe
  • +
  • ForgeFlow
  • +
  • Camptocamp
  • +
  • BCIM
  • +
+
+
+

Contributors

+ +
+
+

Other credits

+

The migration of this module from 18.0 to 19.0 was financially supported +by SprintIT Ltd.

+
+
+

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.

+

This module is part of the OCA/purchase-workflow project on GitHub.

+

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

+
+
+
+
+ + diff --git a/purchase_delivery_split_date/static/description/split_delivery.png b/purchase_delivery_split_date/static/description/split_delivery.png new file mode 100644 index 00000000000..2b992ade8c0 Binary files /dev/null and b/purchase_delivery_split_date/static/description/split_delivery.png differ diff --git a/purchase_delivery_split_date/tests/__init__.py b/purchase_delivery_split_date/tests/__init__.py new file mode 100644 index 00000000000..2e7686eda3a --- /dev/null +++ b/purchase_delivery_split_date/tests/__init__.py @@ -0,0 +1,3 @@ +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). + +from . import test_purchase_delivery diff --git a/purchase_delivery_split_date/tests/test_purchase_delivery.py b/purchase_delivery_split_date/tests/test_purchase_delivery.py new file mode 100644 index 00000000000..465ad48aae4 --- /dev/null +++ b/purchase_delivery_split_date/tests/test_purchase_delivery.py @@ -0,0 +1,505 @@ +# Copyright 2014-2016 Numérigraphe SARL +# Copyright 2017 ForgeFlow, S.L. +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). + +from freezegun import freeze_time + +from odoo import Command +from odoo.fields import Datetime +from odoo.tests import Form + +from odoo.addons.purchase_stock.tests.common import PurchaseTestCommon + + +class TestDeliverySingle(PurchaseTestCommon): + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.product_model = cls.env["product.product"] + + # Create products: + cls.p1 = cls.product1 = cls.product_model.create( + { + "name": "Test Product 1", + "type": "consu", + "default_code": "PROD1", + "standard_price": 10, + } + ) + cls.p2 = cls.product2 = cls.product_model.create( + { + "name": "Test Product 2", + "type": "consu", + "default_code": "PROD2", + "standard_price": 10, + } + ) + cls.p3 = cls.product2 = cls.product_model.create( + { + "name": "Test Product 3", + "type": "consu", + "default_code": "PROD3", + "standard_price": 10, + } + ) + cls.service_1 = cls.product_model.create( + { + "name": "Test Service 1", + "type": "service", + "default_code": "SERV1", + "standard_price": 20, + } + ) + + # Two dates which we can use to test the features: + cls.date_sooner = "2015-01-01" + cls.date_later = "2015-12-13" + cls.date_3rd = "2015-12-31" + + cls.po = cls.env["purchase.order"].create( + { + "partner_id": cls.vendor.id, + "order_line": [ + Command.create( + { + "product_id": cls.p1.id, + "product_uom_id": cls.p1.uom_id.id, + "name": cls.p1.name, + "price_unit": cls.p1.standard_price, + "date_planned": cls.date_sooner, + "product_qty": 42.0, + }, + ), + Command.create( + { + "product_id": cls.p2.id, + "product_uom_id": cls.p2.uom_id.id, + "name": cls.p2.name, + "price_unit": cls.p2.standard_price, + "date_planned": cls.date_sooner, + "product_qty": 12.0, + }, + ), + Command.create( + { + "product_id": cls.p1.id, + "product_uom_id": cls.p1.uom_id.id, + "name": cls.p1.name, + "price_unit": cls.p1.standard_price, + "date_planned": cls.date_sooner, + "product_qty": 1.0, + }, + ), + ], + } + ) + + def test_01_check_single_date(self): + """Tests with single date.""" + self.assertEqual( + len(self.po.picking_ids), + 0, + "There must not be pickings for the PO when draft", + ) + self.po.button_confirm() + self.assertEqual( + len(self.po.picking_ids), + 1, + "There must be 1 picking for the PO when confirmed", + ) + self.assertEqual( + str(self.po.picking_ids[0].scheduled_date)[:10], + self.date_sooner, + "The picking must be planned at the expected date", + ) + + def test_02_adding_line(self): + # A modification on line product quantity will recompute the + # date_planned field with the seller (supplierinfo) lead time + # Check if the original date planned is kept if new date_planned is before + + # We first add a seller to the product + self.env["product.supplierinfo"].create( + { + "partner_id": self.vendor.id, + "product_tmpl_id": self.p1.product_tmpl_id.id, + } + ) + # Set today earlier as planned date + today = "2015-12-01" + self.po.date_order = today + + self.po.order_line[0].date_planned = self.date_later + + # Then change the line quantity + with freeze_time(today): + self.po.order_line[0].product_qty = 43.0 + + # We check the later planned date is kept + self.assertEqual( + Datetime.to_datetime("2015-12-13"), self.po.order_line[0].date_planned + ) + + def test_03_check_multiple_dates(self): + """Tests changing the date of the first line.""" + self.po.order_line[0].date_planned = self.date_later + self.assertEqual( + len(self.po.picking_ids), + 0, + "There must not be pickings for the PO when draft", + ) + self.po.button_confirm() + self.assertEqual( + len(self.po.picking_ids), + 2, + f"There must be 2 pickings for the PO when confirmed. " + f"{len(self.po.picking_ids)} found", + ) + + sorted_pickings = sorted(self.po.picking_ids, key=lambda x: x.scheduled_date) + self.assertEqual( + str(sorted_pickings[0].scheduled_date)[:10], + self.date_sooner, + "The first picking must be planned at the soonest date", + ) + self.assertEqual( + str(sorted_pickings[1].scheduled_date)[:10], + self.date_later, + "The second picking must be planned at the latest date", + ) + + def test_04_purchase_line_date_change(self): + self.po.order_line[0].date_planned = self.date_later + self.po.button_confirm() + moves = self.env["stock.move"].search( + [("purchase_line_id", "=", self.po.order_line[0].id)] + ) + line = self.po.order_line[0] + line.write({"date_planned": self.date_3rd}) + self.assertEqual(moves.date_deadline.strftime("%Y-%m-%d"), self.date_3rd) + + def test_05_group_multiple_picking_same_date(self): + """Check multiple picking with same planned date are also merged + + This can happen if another module changes the picking planned date + before the _check_split_pickings is being called from the write method. + """ + self.po.order_line[0].date_planned = self.date_later + self.po.button_confirm() + moves = self.env["stock.move"].search( + [("purchase_line_id", "in", self.po.order_line.ids)] + ) + pickings = moves.mapped("picking_id") + self.assertEqual(len(pickings), 2) + pickings[1].scheduled_date = pickings[0].scheduled_date + self.po.order_line[0].date_planned = self.date_sooner + self.assertEqual(len(moves.mapped("picking_id")), 1) + self.assertEqual(len(pickings.filtered(lambda r: r.state == "cancel")), 1) + + def test_purchase_line_time_change_nosplit_picking(self): + self.po.button_confirm() + line1 = self.po.order_line[0] + line1.write({"date_planned": Datetime.add(line1.date_planned, minutes=1)}) + self.assertEqual( + len(self.po.picking_ids), + 1, + "There must be 1 picking when I change the time", + ) + + def test_06_purchase_line_date_change_split_picking(self): + self.po.button_confirm() + line1 = self.po.order_line[0] + line2 = self.po.order_line[1] + move1 = self.env["stock.move"].search([("purchase_line_id", "=", line1.id)]) + move2 = self.env["stock.move"].search([("purchase_line_id", "=", line2.id)]) + + # Check when date is put later + line1.write({"date_planned": self.date_later}) + self.assertEqual( + len(self.po.picking_ids), + 2, + "There must be 2 pickings when I change the date", + ) + self.assertEqual(move1.date_deadline.strftime("%Y-%m-%d"), self.date_later) + self.assertEqual(move2.date_deadline.strftime("%Y-%m-%d"), self.date_sooner) + self.assertNotEqual(move1.picking_id, move2.picking_id) + line2.write({"date_planned": self.date_later}) + self.assertEqual( + move1.picking_id, + move2.picking_id, + "If I change the other line to the same date as the first, " + "both moves must be in the same picking", + ) + self.assertEqual( + len(self.po.picking_ids), + 2, + "There must be 2 picking as line3 is still sooner", + ) + # Check move is well assigned + self.assertEqual("assigned", move2.picking_id.state) + self.assertTrue(move2.move_line_ids) + # Now check when date is put sooner + line1.write({"date_planned": self.date_sooner}) + self.assertEqual( + len(self.po.picking_ids), + 2, + "There must be 2 pickings when I change the date", + ) + self.assertEqual(move1.date_deadline.strftime("%Y-%m-%d"), self.date_sooner) + self.assertEqual(move2.date_deadline.strftime("%Y-%m-%d"), self.date_later) + self.assertNotEqual(move1.picking_id, move2.picking_id) + line2.write({"date_planned": self.date_sooner}) + self.assertEqual( + move1.picking_id, + move2.picking_id, + "If I change the other line to the same date as the first, " + "both moves must be in the same picking", + ) + self.assertEqual( + len(self.po.picking_ids), + 1, + "There must be 1 picking", + ) + + def test_07_purchase_line_created_after_confirm(self): + """Check new line created when order is confirmed. + + When a new line is added on an already `purchased` order + If it is planned for a non yet existing date in the purchase, a + new picking should be created. + + """ + self.po.button_confirm() + self.assertEqual(self.po.state, "purchase") + new_date = "2016-01-30" + moves_before = self.env["stock.move"].search( + [("purchase_line_id", "in", self.po.order_line.ids)] + ) + self.assertEqual(len(moves_before.mapped("picking_id")), 1) + self.po.order_line = [ + Command.create( + { + "product_id": self.p3.id, + "product_uom_id": self.p3.uom_id.id, + "name": self.p3.name, + "price_unit": self.p3.standard_price, + "date_planned": new_date, + "product_qty": 2.0, + }, + ), + ] + moves_after = self.env["stock.move"].search( + [("purchase_line_id", "in", self.po.order_line.ids)] + ) + self.assertEqual(len(moves_after.mapped("picking_id")), 2) + + def test_08_purchase_line_date_change_tz_aware(self): + """Check that the grouping is time zone aware. + + Datetime are always stored in utc in the database. + + """ + self.po.order_line[2].unlink() + self.po.button_confirm() + line1 = self.po.order_line[0] + line2 = self.po.order_line[1] + brussels_context = { + "tz": "Europe/Brussels", + } + self.assertEqual(len(self.po.picking_ids), 1) + line1.with_context(**brussels_context).write( + {"date_planned": "2021-05-05 03:00:00"} + ) + self.assertEqual(len(self.po.picking_ids), 2) + # Time difference of at least +1 so should be same day (1 picking) + line2.with_context(**brussels_context).write( + {"date_planned": "2021-05-04 23:00:00"} + ) + self.assertEqual(len(self.po.picking_ids), 1) + + utc_context = { + "tz": "Etc/UTC", + } + # No time difference so will be another day (2 pickings) + line1.with_context(**utc_context).write({"date_planned": "2021-05-05 03:00:00"}) + self.assertEqual(len(self.po.picking_ids), 2) + line2.with_context(**utc_context).write({"date_planned": "2021-05-04 23:00:00"}) + self.assertEqual(len(self.po.picking_ids), 2) + + def test_09_create_from_form(self): + partner_purchase = self.env["res.partner"].create( + {"name": "Partner 1 of purchase on create from form"} + ) + has_order_type = "order_type" in self.env["purchase.order"]._fields + if has_order_type: + order_type = self.env.ref("purchase_order_type.po_type_regular") + if order_type.company_id and order_type.company_id != self.company: + order_type.company_id = self.company + partner_purchase["purchase_type"] = order_type + + with Form(self.env["purchase.order"]) as purchase_form: + purchase_form.partner_id = partner_purchase + self.assertEqual(purchase_form.partner_id, partner_purchase) + + def test_10_po_with_services(self): + """Test that no empty pickings are created because of service lines.""" + prev_count = self.env["stock.picking"].search_count([]) + self.env["purchase.order.line"].create( + { + "order_id": self.po.id, + "product_id": self.service_1.id, + "product_uom_id": self.service_1.uom_id.id, + "name": self.service_1.name, + "price_unit": self.service_1.standard_price, + "date_planned": self.date_later, + "product_qty": 15.0, + } + ) + self.po.button_confirm() + post_count = self.env["stock.picking"].search_count([]) + new_pickings = post_count - prev_count + self.assertEqual(new_pickings, 1) + + def test_11_picking_partner_matches_po_partner(self): + """Ensure all pickings' partner_id matches the PO partner_id.""" + self.po.order_line[0].date_planned = self.date_later + self.po.button_confirm() + self.assertEqual( + len(self.po.picking_ids), + 2, + f"There must be 2 pickings for the PO when confirmed. " + f"{len(self.po.picking_ids)} found", + ) + for picking in self.po.picking_ids: + self.assertEqual( + picking.partner_id, + self.po.partner_id, + f"Picking {picking.name} partner_id must match the PO partner_id", + ) + + def test_12_picking_partner_matches_po_dest_address(self): + """Ensure all pickings' partner_id matches the PO dest_address_id (if set)""" + # purchase.order.line::_prepare_stock_move_vals + # assigns partner_id using the value from order_id.dest_address_id + self.po.dest_address_id = self.po.partner_id + self.po.order_line[0].date_planned = self.date_later + self.po.button_confirm() + self.assertEqual( + len(self.po.picking_ids), + 2, + f"There must be 2 pickings for the PO when confirmed. " + f"{len(self.po.picking_ids)} found", + ) + for picking in self.po.picking_ids: + self.assertEqual( + picking.partner_id, + self.po.dest_address_id, + f"Picking {picking.name} partner_id must match the PO dest_address_id", + ) + + def test_13_picking_partner_dropship(self): + """Ensure pickings for dropship PO have the correct partner_id.""" + location_customer = self.env.ref("stock.stock_location_customers") + dropship_customer = self.env["res.partner"].create( + {"name": "Dropship Customer"} + ) + dropship_partner = self.env["res.partner"].create({"name": "Dropship Partner"}) + self.po.dest_address_id = dropship_customer + self.po.partner_id = dropship_partner + self.po.button_confirm() + # Because the module `stock_dropshipping is not a dependency + # We need to fake a dropshipping PO by changing the destination location + # On the operation type and the picking and moves. + self.po.picking_ids.picking_type_id.default_location_dest_id = location_customer + self.po.picking_ids.location_dest_id = location_customer + # Change the date to trigger a new picking + self.po.order_line[0].date_planned = self.date_later + for picking in self.po.picking_ids: + self.assertEqual( + picking.partner_id, + dropship_partner, + f"Picking {picking.name} partner_id must match the dropship address", + ) + + def test_14_purchase_line_created_after_confirm_merge_pickings(self): + """ + If a line is modified and two different moves in different pickings + are now compatible, the pickings should be merged. + + """ + self.po.button_confirm() + self.assertEqual(self.po.state, "purchase") + new_date = "2016-01-30" + moves_before = self.env["stock.move"].search( + [("purchase_line_id", "in", self.po.order_line.ids)] + ) + self.assertEqual(len(moves_before.mapped("picking_id")), 1) + self.po.order_line = [ + ( + 0, + 0, + { + "product_id": self.p3.id, + "product_uom_id": self.p3.uom_id.id, + "name": self.p3.name, + "price_unit": self.p3.standard_price, + "date_planned": new_date, + "product_qty": 2.0, + }, + ), + ] + moves_after = self.env["stock.move"].search( + [("purchase_line_id", "in", self.po.order_line.ids)] + ) + self.assertEqual(len(moves_after.mapped("picking_id")), 2) + + po_line = self.po.order_line.filtered(lambda line: line.product_id == self.p3) + po_line.write({"date_planned": self.date_sooner}) + + moves_after = self.env["stock.move"].search( + [("purchase_line_id", "in", self.po.order_line.ids)] + ) + self.assertEqual(len(moves_after.mapped("picking_id")), 1) + for move in moves_after: + self.assertEqual(move.date, Datetime.to_datetime(self.date_sooner)) + + def test_15_purchase_line_created_after_confirm_check_dates(self): + """ + + When a new line is added on an already `purchased` order + If it is planned for a non yet existing date in the purchase, a + new picking should be created and the previous dates should not be + modified. + + """ + self.po.button_confirm() + self.assertEqual(self.po.state, "purchase") + new_date = "2016-01-30" + moves_before = self.env["stock.move"].search( + [("purchase_line_id", "in", self.po.order_line.ids)] + ) + self.assertEqual(len(moves_before.mapped("picking_id")), 1) + self.po.order_line = [ + ( + 0, + 0, + { + "product_id": self.p3.id, + "product_uom_id": self.p3.uom_id.id, + "name": self.p3.name, + "price_unit": self.p3.standard_price, + "date_planned": new_date, + "product_qty": 2.0, + }, + ), + ] + moves_after = self.env["stock.move"].search( + [("purchase_line_id", "in", self.po.order_line.ids)] + ) + self.assertEqual(len(moves_after.mapped("picking_id")), 2) + + new_move = moves_after.filtered(lambda line: line.product_id == self.p3) + self.assertEqual(new_move.date, Datetime.to_datetime(new_date)) + + for move in moves_after - new_move: + self.assertEqual(move.date, Datetime.to_datetime(self.date_sooner)) diff --git a/purchase_invoice_status_line/README.rst b/purchase_invoice_status_line/README.rst new file mode 100644 index 00000000000..e50d06ce581 --- /dev/null +++ b/purchase_invoice_status_line/README.rst @@ -0,0 +1,99 @@ +.. image:: https://odoo-community.org/readme-banner-image + :target: https://odoo-community.org/get-involved?utm_source=readme + :alt: Odoo Community Association + +============================ +Purchase Invoice Status Line +============================ + +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:28354a4b69311651d46cacb9a4e09e9c7e661178d169f701801369a29bd90a1c + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |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/license-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%2Fpurchase--workflow-lightgray.png?logo=github + :target: https://github.com/OCA/purchase-workflow/tree/19.0/purchase_invoice_status_line + :alt: OCA/purchase-workflow +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/purchase-workflow-19-0/purchase-workflow-19-0-purchase_invoice_status_line + :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/purchase-workflow&target_branch=19.0 + :alt: Try me on Runboat + +|badge1| |badge2| |badge3| |badge4| |badge5| + +This module adds the invoice status on the purchase order lines. + +**Table of contents** + +.. contents:: + :local: + +Usage +===== + +To use this module, you need to: + +In the lines, you will see the 'Invoice Status' field and you can force +invoice. + +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 +------- + +* ForgeFlow + +Contributors +------------ + +- Joan Sisquella (JoanSForgeFlow) +- `Heliconia Solutions Pvt. Ltd. `__ + + - Bhavesh Heliconia + +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-JoanSForgeFlow| image:: https://github.com/JoanSForgeFlow.png?size=40px + :target: https://github.com/JoanSForgeFlow + :alt: JoanSForgeFlow + +Current `maintainer `__: + +|maintainer-JoanSForgeFlow| + +This module is part of the `OCA/purchase-workflow `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/purchase_invoice_status_line/__init__.py b/purchase_invoice_status_line/__init__.py new file mode 100644 index 00000000000..cc6b6354ad8 --- /dev/null +++ b/purchase_invoice_status_line/__init__.py @@ -0,0 +1,2 @@ +from . import models +from .hooks import post_init_hook diff --git a/purchase_invoice_status_line/__manifest__.py b/purchase_invoice_status_line/__manifest__.py new file mode 100644 index 00000000000..06960a93d9a --- /dev/null +++ b/purchase_invoice_status_line/__manifest__.py @@ -0,0 +1,22 @@ +# Copyright 2025 ForgeFlow (http://www.akretion.com/) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +{ + "name": "Purchase Invoice Status Line", + "version": "19.0.1.0.0", + "category": "Purchases", + "license": "AGPL-3", + "summary": "Add invoice status on purchase order lines", + "author": "ForgeFlow, Odoo Community Association (OCA)", + "maintainers": ["JoanSForgeFlow"], + "website": "https://github.com/OCA/purchase-workflow", + "depends": [ + "purchase_force_invoiced", + "purchase_order_line_menu", + ], + "post_init_hook": "post_init_hook", + "data": [ + "views/purchase_order_line_views.xml", + ], + "installable": True, +} diff --git a/purchase_invoice_status_line/hooks.py b/purchase_invoice_status_line/hooks.py new file mode 100644 index 00000000000..f2a1ceeb665 --- /dev/null +++ b/purchase_invoice_status_line/hooks.py @@ -0,0 +1,12 @@ +def post_init_hook(env): + lines_to_update = env["purchase.order.line"].search( + [ + ("force_invoiced", "=", False), + ("order_id.force_invoiced", "=", True), + ] + ) + if lines_to_update: + lines_to_update.write({"force_invoiced": True}) + all_lines = env["purchase.order.line"].search([]) + for line in all_lines: + line._compute_invoice_status() diff --git a/purchase_invoice_status_line/i18n/it.po b/purchase_invoice_status_line/i18n/it.po new file mode 100644 index 00000000000..fb380926fe8 --- /dev/null +++ b/purchase_invoice_status_line/i18n/it.po @@ -0,0 +1,72 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * purchase_invoice_status_line +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 18.0\n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: 2025-09-17 13:37+0000\n" +"Last-Translator: mymage \n" +"Language-Team: none\n" +"Language: it\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 5.10.4\n" + +#. module: purchase_invoice_status_line +#: model:ir.model.fields,field_description:purchase_invoice_status_line.field_purchase_order_line__invoice_status +msgid "Billing Status" +msgstr "Stato fatturazione" + +#. module: purchase_invoice_status_line +#: model:ir.model.fields,field_description:purchase_invoice_status_line.field_purchase_order__force_invoiced +#: model:ir.model.fields,field_description:purchase_invoice_status_line.field_purchase_order_line__force_invoiced +msgid "Force Invoiced" +msgstr "Forza a fatturato" + +#. module: purchase_invoice_status_line +#: model:ir.model.fields.selection,name:purchase_invoice_status_line.selection__purchase_order_line__invoice_status__invoiced +msgid "Fully Billed" +msgstr "Completamente pagato" + +#. module: purchase_invoice_status_line +#: model:ir.model.fields,help:purchase_invoice_status_line.field_purchase_order_line__force_invoiced +msgid "" +"If true, the invoice status will be forced to Fully Invoiced, even if some " +"quantities are not fully invoiced. " +msgstr "" +"Se è vero, lo stato della fattura verrà forzato su completamente fatturato, " +"anche se alcune quantità non sono completamente fatturate. " + +#. module: purchase_invoice_status_line +#: model:ir.model.fields,help:purchase_invoice_status_line.field_purchase_order__force_invoiced +msgid "" +"If true, the order is marked forced only when all lines are fully invoiced " +"and at least one line was manually forced." +msgstr "" +"Se è vero, l'ordine viene contrassegnato come forzato solo quando tutte le " +"righe sono state completamente fatturate e almeno una riga è stata forzata " +"manualmente." + +#. module: purchase_invoice_status_line +#: model:ir.model.fields.selection,name:purchase_invoice_status_line.selection__purchase_order_line__invoice_status__no +msgid "Nothing to Bill" +msgstr "Niente da fatturare" + +#. module: purchase_invoice_status_line +#: model:ir.model,name:purchase_invoice_status_line.model_purchase_order +msgid "Purchase Order" +msgstr "Ordine di acquisto" + +#. module: purchase_invoice_status_line +#: model:ir.model,name:purchase_invoice_status_line.model_purchase_order_line +msgid "Purchase Order Line" +msgstr "Riga ordine di acquisto" + +#. module: purchase_invoice_status_line +#: model:ir.model.fields.selection,name:purchase_invoice_status_line.selection__purchase_order_line__invoice_status__to_invoice +msgid "Waiting Bills" +msgstr "Fatture in attesa" diff --git a/purchase_invoice_status_line/i18n/purchase_invoice_status_line.pot b/purchase_invoice_status_line/i18n/purchase_invoice_status_line.pot new file mode 100644 index 00000000000..45573fa0807 --- /dev/null +++ b/purchase_invoice_status_line/i18n/purchase_invoice_status_line.pot @@ -0,0 +1,64 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * purchase_invoice_status_line +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 18.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: purchase_invoice_status_line +#: model:ir.model.fields,field_description:purchase_invoice_status_line.field_purchase_order_line__invoice_status +msgid "Billing Status" +msgstr "" + +#. module: purchase_invoice_status_line +#: model:ir.model.fields,field_description:purchase_invoice_status_line.field_purchase_order__force_invoiced +#: model:ir.model.fields,field_description:purchase_invoice_status_line.field_purchase_order_line__force_invoiced +msgid "Force Invoiced" +msgstr "" + +#. module: purchase_invoice_status_line +#: model:ir.model.fields.selection,name:purchase_invoice_status_line.selection__purchase_order_line__invoice_status__invoiced +msgid "Fully Billed" +msgstr "" + +#. module: purchase_invoice_status_line +#: model:ir.model.fields,help:purchase_invoice_status_line.field_purchase_order_line__force_invoiced +msgid "" +"If true, the invoice status will be forced to Fully Invoiced, even if some " +"quantities are not fully invoiced. " +msgstr "" + +#. module: purchase_invoice_status_line +#: model:ir.model.fields,help:purchase_invoice_status_line.field_purchase_order__force_invoiced +msgid "" +"If true, the order is marked forced only when all lines are fully invoiced " +"and at least one line was manually forced." +msgstr "" + +#. module: purchase_invoice_status_line +#: model:ir.model.fields.selection,name:purchase_invoice_status_line.selection__purchase_order_line__invoice_status__no +msgid "Nothing to Bill" +msgstr "" + +#. module: purchase_invoice_status_line +#: model:ir.model,name:purchase_invoice_status_line.model_purchase_order +msgid "Purchase Order" +msgstr "" + +#. module: purchase_invoice_status_line +#: model:ir.model,name:purchase_invoice_status_line.model_purchase_order_line +msgid "Purchase Order Line" +msgstr "" + +#. module: purchase_invoice_status_line +#: model:ir.model.fields.selection,name:purchase_invoice_status_line.selection__purchase_order_line__invoice_status__to_invoice +msgid "Waiting Bills" +msgstr "" diff --git a/purchase_invoice_status_line/models/__init__.py b/purchase_invoice_status_line/models/__init__.py new file mode 100644 index 00000000000..677dbb3b247 --- /dev/null +++ b/purchase_invoice_status_line/models/__init__.py @@ -0,0 +1,2 @@ +from . import purchase_order +from . import purchase_order_line diff --git a/purchase_invoice_status_line/models/purchase_order.py b/purchase_invoice_status_line/models/purchase_order.py new file mode 100644 index 00000000000..c5884487b97 --- /dev/null +++ b/purchase_invoice_status_line/models/purchase_order.py @@ -0,0 +1,66 @@ +# Copyright 2025 ForgeFlow (http://www.forgeflow.com/) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import api, fields, models + + +class PurchaseOrder(models.Model): + _inherit = "purchase.order" + + force_invoiced = fields.Boolean( + compute="_compute_force_invoiced", + inverse="_inverse_force_invoiced", + store=True, + tracking=True, + help="If true, the order is marked forced only " + "when all lines are fully invoiced" + " and at least one line was manually forced.", + ) + + @api.depends("order_line.invoice_status", "force_invoiced") + def _get_invoiced(self): + super()._get_invoiced() + for order in self: + if not order.order_line: + order.invoice_status = "no" + continue + if order.force_invoiced: + order.invoice_status = "invoiced" + continue + if any(line.invoice_status == "to invoice" for line in order.order_line): + order.invoice_status = "to invoice" + elif all( + line.invoice_status == "invoiced" + for line in order.order_line.filtered( + lambda line: not line.display_type + ) + ): + order.invoice_status = "invoiced" + else: + order.invoice_status = "no" + return True + + @api.depends("order_line.invoice_status", "order_line.force_invoiced") + def _compute_force_invoiced(self): + for po in self: + po.order_line._compute_invoice_status() + non_display_lines = po.order_line.filtered( + lambda line: not line.display_type + ) + all_invoiced = all( + line.invoice_status == "invoiced" for line in non_display_lines + ) + any_forced = any(line.force_invoiced for line in non_display_lines) + po.force_invoiced = all_invoiced and any_forced + + def _inverse_force_invoiced(self): + for po in self: + if po.force_invoiced: + to_force = po.order_line.filtered( + lambda line: line.invoice_status != "invoiced" + ) + to_force.write({"force_invoiced": True}) + else: + forced_lines = po.order_line.filtered(lambda line: line.force_invoiced) + forced_lines.write({"force_invoiced": False}) + forced_lines._compute_invoice_status() diff --git a/purchase_invoice_status_line/models/purchase_order_line.py b/purchase_invoice_status_line/models/purchase_order_line.py new file mode 100644 index 00000000000..e294ec01c7a --- /dev/null +++ b/purchase_invoice_status_line/models/purchase_order_line.py @@ -0,0 +1,51 @@ +# Copyright 2025 ForgeFlow (http://www.forgeflow.com/) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import api, fields, models +from odoo.tools import float_is_zero + + +class PurchaseOrderLine(models.Model): + _inherit = "purchase.order.line" + + invoice_status = fields.Selection( + selection=[ + ("no", "Nothing to Bill"), + ("to invoice", "Waiting Bills"), + ("invoiced", "Fully Billed"), + ], + compute="_compute_invoice_status", + store=True, + related=False, + string="Billing Status", + ) + + force_invoiced = fields.Boolean( + store=True, + copy=False, + help="If true, the invoice status will be forced to Fully Invoiced, " + "even if some quantities are not fully invoiced. ", + ) + + @api.depends("qty_invoiced", "product_qty", "force_invoiced", "qty_to_invoice") + def _compute_invoice_status(self): + precision = self.env["decimal.precision"].precision_get( + "Product Unit of Measure" + ) + for line in self: + if line.order_id.state not in ["purchase", "done"]: + line.invoice_status = "no" + continue + if line.display_type: + line.invoice_status = False + continue + if line.force_invoiced: + line.invoice_status = "invoiced" + continue + if float_is_zero(line.qty_to_invoice, precision_digits=precision): + if line.qty_invoiced >= line.product_qty: + line.invoice_status = "invoiced" + else: + line.invoice_status = "no" + else: + line.invoice_status = "to invoice" diff --git a/purchase_invoice_status_line/pyproject.toml b/purchase_invoice_status_line/pyproject.toml new file mode 100644 index 00000000000..4231d0cccb3 --- /dev/null +++ b/purchase_invoice_status_line/pyproject.toml @@ -0,0 +1,3 @@ +[build-system] +requires = ["whool"] +build-backend = "whool.buildapi" diff --git a/purchase_invoice_status_line/readme/CONTRIBUTORS.md b/purchase_invoice_status_line/readme/CONTRIBUTORS.md new file mode 100644 index 00000000000..8cd45d52132 --- /dev/null +++ b/purchase_invoice_status_line/readme/CONTRIBUTORS.md @@ -0,0 +1,3 @@ +- Joan Sisquella \ (JoanSForgeFlow) +- [Heliconia Solutions Pvt. Ltd.](https://www.heliconia.io) + - Bhavesh Heliconia diff --git a/purchase_invoice_status_line/readme/DESCRIPTION.md b/purchase_invoice_status_line/readme/DESCRIPTION.md new file mode 100644 index 00000000000..36dc15095da --- /dev/null +++ b/purchase_invoice_status_line/readme/DESCRIPTION.md @@ -0,0 +1 @@ +This module adds the invoice status on the purchase order lines. diff --git a/purchase_invoice_status_line/readme/USAGE.md b/purchase_invoice_status_line/readme/USAGE.md new file mode 100644 index 00000000000..c41b031431e --- /dev/null +++ b/purchase_invoice_status_line/readme/USAGE.md @@ -0,0 +1,4 @@ +To use this module, you need to: + +In the lines, you will see the 'Invoice Status' field and you can force +invoice. diff --git a/purchase_invoice_status_line/static/description/icon.png b/purchase_invoice_status_line/static/description/icon.png new file mode 100644 index 00000000000..1dcc49c24f3 Binary files /dev/null and b/purchase_invoice_status_line/static/description/icon.png differ diff --git a/purchase_invoice_status_line/static/description/index.html b/purchase_invoice_status_line/static/description/index.html new file mode 100644 index 00000000000..f80da1391c9 --- /dev/null +++ b/purchase_invoice_status_line/static/description/index.html @@ -0,0 +1,442 @@ + + + + + +README.rst + + + +
+ + + +Odoo Community Association + +
+

Purchase Invoice Status Line

+ +

Beta License: AGPL-3 OCA/purchase-workflow Translate me on Weblate Try me on Runboat

+

This module adds the invoice status on the purchase order lines.

+

Table of contents

+ +
+

Usage

+

To use this module, you need to:

+

In the lines, you will see the ‘Invoice Status’ field and you can force +invoice.

+
+
+

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

+
    +
  • ForgeFlow
  • +
+
+
+

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 maintainer:

+

JoanSForgeFlow

+

This module is part of the OCA/purchase-workflow project on GitHub.

+

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

+
+
+
+
+ + diff --git a/purchase_invoice_status_line/tests/__init__.py b/purchase_invoice_status_line/tests/__init__.py new file mode 100644 index 00000000000..4044c3b6290 --- /dev/null +++ b/purchase_invoice_status_line/tests/__init__.py @@ -0,0 +1 @@ +from . import test_purchase_invoice_status_line diff --git a/purchase_invoice_status_line/tests/test_purchase_invoice_status_line.py b/purchase_invoice_status_line/tests/test_purchase_invoice_status_line.py new file mode 100644 index 00000000000..1b28f4d9596 --- /dev/null +++ b/purchase_invoice_status_line/tests/test_purchase_invoice_status_line.py @@ -0,0 +1,75 @@ +# Copyright 2025 ForgeFlow (http://www.forgeflow.com/) +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo import Command +from odoo.tests import tagged + +from odoo.addons.account.tests.common import AccountTestInvoicingCommon + + +@tagged("post_install", "-at_install") +class TestPurchaseInvoiceStatusLine(AccountTestInvoicingCommon): + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.partner = cls.partner_a + cls.product_order = cls.product_a + cls.product_received = cls.product_b + cls.product_order.write({"purchase_method": "purchase"}) + cls.product_received.write({"purchase_method": "receive"}) + + def test_force_invoice_logic(self): + po = self.env["purchase.order"].create( + { + "partner_id": self.partner.id, + "order_line": [ + Command.create( + { + "product_id": self.product_order.id, + "product_qty": 10, + "price_unit": 10.0, + }, + ), + Command.create( + { + "product_id": self.product_order.id, + "product_qty": 5, + "price_unit": 20.0, + }, + ), + ], + } + ) + po.button_confirm() + line1 = po.order_line[0] + line2 = po.order_line[1] + self.assertEqual(line1.invoice_status, "to invoice") + self.assertEqual(line2.invoice_status, "to invoice") + self.assertFalse(po.force_invoiced, "PO should not be forced initially") + line1.force_invoiced = True + self.assertEqual( + line1.invoice_status, "invoiced", "L1 status should be invoiced when forced" + ) + self.assertFalse(po.force_invoiced, "PO not forced if only one line is done") + line2.force_invoiced = True + self.assertEqual( + line2.invoice_status, "invoiced", "L2 status should be invoiced when forced" + ) + self.assertTrue( + po.force_invoiced, "PO should be forced when all lines are invoiced" + ) + line1.force_invoiced = False + self.assertEqual(line1.invoice_status, "to invoice", "L1 status should revert") + self.assertFalse( + po.force_invoiced, "PO should be un-forced if one line reverts" + ) + po.force_invoiced = True + self.assertTrue(line1.force_invoiced, "L1 should be forced by PO") + self.assertTrue(line2.force_invoiced, "L2 should be forced by PO") + self.assertEqual(line1.invoice_status, "invoiced") + self.assertEqual(line2.invoice_status, "invoiced") + po.force_invoiced = False + self.assertFalse(line1.force_invoiced, "L1 should be un-forced by PO") + self.assertFalse(line2.force_invoiced, "L2 should be un-forced by PO") + self.assertEqual(line1.invoice_status, "to invoice") + self.assertEqual(line2.invoice_status, "to invoice") diff --git a/purchase_invoice_status_line/views/purchase_order_line_views.xml b/purchase_invoice_status_line/views/purchase_order_line_views.xml new file mode 100644 index 00000000000..f5e570ade2c --- /dev/null +++ b/purchase_invoice_status_line/views/purchase_order_line_views.xml @@ -0,0 +1,37 @@ + + + + purchase.order.line.tree.invoice.status + purchase.order.line + + + + + + + + + + + purchase.order.line.tree.invoice.status + purchase.order.line + + + + 1 + + + + diff --git a/purchase_order_weight_volume/i18n/it.po b/purchase_order_weight_volume/i18n/it.po index 6e4d4cfa390..9122333dd6d 100644 --- a/purchase_order_weight_volume/i18n/it.po +++ b/purchase_order_weight_volume/i18n/it.po @@ -6,7 +6,7 @@ msgid "" msgstr "" "Project-Id-Version: Odoo Server 14.0\n" "Report-Msgid-Bugs-To: \n" -"PO-Revision-Date: 2023-12-20 15:38+0000\n" +"PO-Revision-Date: 2026-04-21 10:49+0000\n" "Last-Translator: mymage \n" "Language-Team: none\n" "Language: it\n" @@ -14,7 +14,7 @@ msgstr "" "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" +"X-Generator: Weblate 5.15.2\n" #. module: purchase_order_weight_volume #: model_terms:ir.ui.view,arch_db:purchase_order_weight_volume.report_purchaseorder_document @@ -51,7 +51,7 @@ msgstr "Aziende" #: model:ir.model.fields,field_description:purchase_order_weight_volume.field_res_company__display_name #: model:ir.model.fields,field_description:purchase_order_weight_volume.field_res_config_settings__display_name msgid "Display Name" -msgstr "Nome da visualizzare" +msgstr "Nome visualizzato" #. module: purchase_order_weight_volume #: model:ir.model.fields,field_description:purchase_order_weight_volume.field_purchase_order__display_order_volume_in_po diff --git a/purchase_reception_status/README.rst b/purchase_reception_status/README.rst new file mode 100644 index 00000000000..b1dcec1741f --- /dev/null +++ b/purchase_reception_status/README.rst @@ -0,0 +1,103 @@ +.. image:: https://odoo-community.org/readme-banner-image + :target: https://odoo-community.org/get-involved?utm_source=readme + :alt: Odoo Community Association + +========================= +Purchase Reception Status +========================= + +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:eb110dde3c5c3aae79a419759824c8b09cc66edfd91880ec9c1ec93bed2089f5 + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |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/license-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%2Fpurchase--workflow-lightgray.png?logo=github + :target: https://github.com/OCA/purchase-workflow/tree/19.0/purchase_reception_status + :alt: OCA/purchase-workflow +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/purchase-workflow-19-0/purchase-workflow-19-0-purchase_reception_status + :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/purchase-workflow&target_branch=19.0 + :alt: Try me on Runboat + +|badge1| |badge2| |badge3| |badge4| |badge5| + +This module adds a field *Receiption Status* on purchase orders. On a +confirmed purchase order, it can have 3 different values: + +- Nothing Received +- Partially Received +- Fully Received + +**Table of contents** + +.. contents:: + :local: + +Usage +===== + +If you are part of the *Purchase Manager* group, you can force a +confirmed purchase order to **Full Received** status: you should first +*lock* the order (to enable locking, ensure the 'Locked' setting is +checked in the Purchase configuration), then check the field **Force +Received** located in the *Other Information* tab. + +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 +------------ + +- Alexis de Lattre +- Urvisha Desai + +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-alexis-via| image:: https://github.com/alexis-via.png?size=40px + :target: https://github.com/alexis-via + :alt: alexis-via + +Current `maintainer `__: + +|maintainer-alexis-via| + +This module is part of the `OCA/purchase-workflow `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/purchase_reception_status/__init__.py b/purchase_reception_status/__init__.py new file mode 100644 index 00000000000..0650744f6bc --- /dev/null +++ b/purchase_reception_status/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/purchase_reception_status/__manifest__.py b/purchase_reception_status/__manifest__.py new file mode 100644 index 00000000000..cf30ff8fdb4 --- /dev/null +++ b/purchase_reception_status/__manifest__.py @@ -0,0 +1,17 @@ +# Copyright 2020 Akretion France (http://www.akretion.com/) +# @author: Alexis de Lattre +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +{ + "name": "Purchase Reception Status", + "version": "19.0.1.0.0", + "category": "Purchases", + "license": "AGPL-3", + "summary": "Add reception status on purchase orders (OCA logic)", + "author": "Akretion,Odoo Community Association (OCA)", + "maintainers": ["alexis-via"], + "website": "https://github.com/OCA/purchase-workflow", + "depends": ["purchase_stock"], + "data": ["views/purchase_order.xml"], + "installable": True, +} diff --git a/purchase_reception_status/i18n/es.po b/purchase_reception_status/i18n/es.po new file mode 100644 index 00000000000..395b58a7ad5 --- /dev/null +++ b/purchase_reception_status/i18n/es.po @@ -0,0 +1,65 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * purchase_reception_status +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 16.0\n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: 2023-12-29 10:35+0000\n" +"Last-Translator: Ivorra78 \n" +"Language-Team: none\n" +"Language: es\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: purchase_reception_status +#: model:ir.model.fields,field_description:purchase_reception_status.field_purchase_order__force_received +msgid "Force Received" +msgstr "Forzar Recibido" + +#. module: purchase_reception_status +#: model:ir.model.fields.selection,name:purchase_reception_status.selection__purchase_order__reception_status__received +#: model_terms:ir.ui.view,arch_db:purchase_reception_status.view_purchase_order_filter +msgid "Fully Received" +msgstr "Totalmente Recibido" + +#. module: purchase_reception_status +#: model:ir.model.fields,help:purchase_reception_status.field_purchase_order__force_received +msgid "" +"If true, the reception status will be forced to Fully Received, even if some" +" lines are not fully received. To be able to modify this field, you must " +"first lock the order." +msgstr "" +"Si es verdadero, el estado de recepción será forzado a Recibido " +"Completamente, incluso si algunas líneas no se reciben en su totalidad. Para " +"poder modificar este campo, primero debes bloquear el pedido." + +#. module: purchase_reception_status +#: model_terms:ir.ui.view,arch_db:purchase_reception_status.view_purchase_order_filter +msgid "Not Fully Received" +msgstr "No se ha Recibido Totalmente" + +#. module: purchase_reception_status +#: model:ir.model.fields.selection,name:purchase_reception_status.selection__purchase_order__reception_status__no +msgid "Nothing Received" +msgstr "Nada Recibido" + +#. module: purchase_reception_status +#: model:ir.model.fields.selection,name:purchase_reception_status.selection__purchase_order__reception_status__partial +msgid "Partially Received" +msgstr "Parcialmente Recibido" + +#. module: purchase_reception_status +#: model:ir.model,name:purchase_reception_status.model_purchase_order +msgid "Purchase Order" +msgstr "Orden de Compra" + +#. module: purchase_reception_status +#: model:ir.model.fields,field_description:purchase_reception_status.field_purchase_order__reception_status +#: model_terms:ir.ui.view,arch_db:purchase_reception_status.view_purchase_order_filter +msgid "Reception Status" +msgstr "Estado de Recepción" diff --git a/purchase_reception_status/i18n/fr.po b/purchase_reception_status/i18n/fr.po new file mode 100644 index 00000000000..adc9ca99523 --- /dev/null +++ b/purchase_reception_status/i18n/fr.po @@ -0,0 +1,62 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * purchase_reception_status +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 14.0\n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: 2025-05-27 09:26+0000\n" +"Last-Translator: MDgrap \n" +"Language-Team: none\n" +"Language: fr\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 5.10.4\n" + +#. module: purchase_reception_status +#: model:ir.model.fields,field_description:purchase_reception_status.field_purchase_order__force_received +msgid "Force Received" +msgstr "Forcer réception" + +#. module: purchase_reception_status +#: model:ir.model.fields.selection,name:purchase_reception_status.selection__purchase_order__reception_status__received +#: model_terms:ir.ui.view,arch_db:purchase_reception_status.view_purchase_order_filter +msgid "Fully Received" +msgstr "Totalement réceptionné" + +#. module: purchase_reception_status +#: model:ir.model.fields,help:purchase_reception_status.field_purchase_order__force_received +msgid "" +"If true, the reception status will be forced to Fully Received, even if some " +"lines are not fully received. To be able to modify this field, you must " +"first lock the order." +msgstr "" + +#. module: purchase_reception_status +#: model_terms:ir.ui.view,arch_db:purchase_reception_status.view_purchase_order_filter +msgid "Not Fully Received" +msgstr "" + +#. module: purchase_reception_status +#: model:ir.model.fields.selection,name:purchase_reception_status.selection__purchase_order__reception_status__no +msgid "Nothing Received" +msgstr "" + +#. module: purchase_reception_status +#: model:ir.model.fields.selection,name:purchase_reception_status.selection__purchase_order__reception_status__partial +msgid "Partially Received" +msgstr "" + +#. module: purchase_reception_status +#: model:ir.model,name:purchase_reception_status.model_purchase_order +msgid "Purchase Order" +msgstr "" + +#. module: purchase_reception_status +#: model:ir.model.fields,field_description:purchase_reception_status.field_purchase_order__reception_status +#: model_terms:ir.ui.view,arch_db:purchase_reception_status.view_purchase_order_filter +msgid "Reception Status" +msgstr "" diff --git a/purchase_reception_status/i18n/it.po b/purchase_reception_status/i18n/it.po new file mode 100644 index 00000000000..bc0a69f80e6 --- /dev/null +++ b/purchase_reception_status/i18n/it.po @@ -0,0 +1,65 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * purchase_reception_status +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 13.0\n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: 2025-09-17 10:36+0000\n" +"Last-Translator: mymage \n" +"Language-Team: none\n" +"Language: it\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 5.10.4\n" + +#. module: purchase_reception_status +#: model:ir.model.fields,field_description:purchase_reception_status.field_purchase_order__force_received +msgid "Force Received" +msgstr "Forza a ricevuto" + +#. module: purchase_reception_status +#: model:ir.model.fields.selection,name:purchase_reception_status.selection__purchase_order__reception_status__received +#: model_terms:ir.ui.view,arch_db:purchase_reception_status.view_purchase_order_filter +msgid "Fully Received" +msgstr "Ricevuto completamente" + +#. module: purchase_reception_status +#: model:ir.model.fields,help:purchase_reception_status.field_purchase_order__force_received +msgid "" +"If true, the reception status will be forced to Fully Received, even if some " +"lines are not fully received. To be able to modify this field, you must " +"first lock the order." +msgstr "" +"Se vero, lo stato di ricezione sarà forzato a Completamente Ricevuto, anche " +"se alcune righe non sono state completamente ricevute. Per essere in grado " +"di modificare questo campo, devi prima aver bloccato l'ordine." + +#. module: purchase_reception_status +#: model_terms:ir.ui.view,arch_db:purchase_reception_status.view_purchase_order_filter +msgid "Not Fully Received" +msgstr "Ricevuto Parzialmente" + +#. module: purchase_reception_status +#: model:ir.model.fields.selection,name:purchase_reception_status.selection__purchase_order__reception_status__no +msgid "Nothing Received" +msgstr "Nulla ricevuto" + +#. module: purchase_reception_status +#: model:ir.model.fields.selection,name:purchase_reception_status.selection__purchase_order__reception_status__partial +msgid "Partially Received" +msgstr "Ricevuto parzialmente" + +#. module: purchase_reception_status +#: model:ir.model,name:purchase_reception_status.model_purchase_order +msgid "Purchase Order" +msgstr "Ordine di acquisto" + +#. module: purchase_reception_status +#: model:ir.model.fields,field_description:purchase_reception_status.field_purchase_order__reception_status +#: model_terms:ir.ui.view,arch_db:purchase_reception_status.view_purchase_order_filter +msgid "Reception Status" +msgstr "Stato ricezione" diff --git a/purchase_reception_status/i18n/ja.po b/purchase_reception_status/i18n/ja.po new file mode 100644 index 00000000000..f5f5614a922 --- /dev/null +++ b/purchase_reception_status/i18n/ja.po @@ -0,0 +1,65 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * purchase_reception_status +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 15.0\n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: 2023-01-10 02:02+0000\n" +"Last-Translator: Wang-TKurata \n" +"Language-Team: none\n" +"Language: ja\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: nplurals=1; plural=0;\n" +"X-Generator: Weblate 4.14.1\n" + +#. module: purchase_reception_status +#: model:ir.model.fields,field_description:purchase_reception_status.field_purchase_order__force_received +msgid "Force Received" +msgstr "強制受領" + +#. module: purchase_reception_status +#: model:ir.model.fields.selection,name:purchase_reception_status.selection__purchase_order__reception_status__received +#: model_terms:ir.ui.view,arch_db:purchase_reception_status.view_purchase_order_filter +msgid "Fully Received" +msgstr "完納" + +#. module: purchase_reception_status +#: model:ir.model.fields,help:purchase_reception_status.field_purchase_order__force_received +msgid "" +"If true, the reception status will be forced to Fully Received, even if some " +"lines are not fully received. To be able to modify this field, you must " +"first lock the order." +msgstr "" +"チェックを付けた場合、未受領の明細が有ったとしても受領ステータスは強制的に完" +"全受領済みとなります。この項目を編集するためにはオーダーのロックを解除する必" +"要があります。" + +#. module: purchase_reception_status +#: model_terms:ir.ui.view,arch_db:purchase_reception_status.view_purchase_order_filter +msgid "Not Fully Received" +msgstr "未完納" + +#. module: purchase_reception_status +#: model:ir.model.fields.selection,name:purchase_reception_status.selection__purchase_order__reception_status__no +msgid "Nothing Received" +msgstr "未受領" + +#. module: purchase_reception_status +#: model:ir.model.fields.selection,name:purchase_reception_status.selection__purchase_order__reception_status__partial +msgid "Partially Received" +msgstr "分納" + +#. module: purchase_reception_status +#: model:ir.model,name:purchase_reception_status.model_purchase_order +msgid "Purchase Order" +msgstr "購買オーダ" + +#. module: purchase_reception_status +#: model:ir.model.fields,field_description:purchase_reception_status.field_purchase_order__reception_status +#: model_terms:ir.ui.view,arch_db:purchase_reception_status.view_purchase_order_filter +msgid "Reception Status" +msgstr "納入状況" diff --git a/purchase_reception_status/i18n/pt.po b/purchase_reception_status/i18n/pt.po new file mode 100644 index 00000000000..eac7a8351b5 --- /dev/null +++ b/purchase_reception_status/i18n/pt.po @@ -0,0 +1,65 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * purchase_reception_status +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 16.0\n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: 2025-02-25 20:06+0000\n" +"Last-Translator: Peter Romão \n" +"Language-Team: none\n" +"Language: pt\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 5.6.2\n" + +#. module: purchase_reception_status +#: model:ir.model.fields,field_description:purchase_reception_status.field_purchase_order__force_received +msgid "Force Received" +msgstr "Forçar Recebido" + +#. module: purchase_reception_status +#: model:ir.model.fields.selection,name:purchase_reception_status.selection__purchase_order__reception_status__received +#: model_terms:ir.ui.view,arch_db:purchase_reception_status.view_purchase_order_filter +msgid "Fully Received" +msgstr "Completamente Recebido" + +#. module: purchase_reception_status +#: model:ir.model.fields,help:purchase_reception_status.field_purchase_order__force_received +msgid "" +"If true, the reception status will be forced to Fully Received, even if some" +" lines are not fully received. To be able to modify this field, you must " +"first lock the order." +msgstr "" +"Se verdadeiro o estado da recepção será forçado a Completamente Recebido, " +"mesmo que algumas linhas não tenham sido completamente recebidas. Para poder " +"modificar este campo, primeiro, deve-se bloquear a encomenda." + +#. module: purchase_reception_status +#: model_terms:ir.ui.view,arch_db:purchase_reception_status.view_purchase_order_filter +msgid "Not Fully Received" +msgstr "Não Recebido Completamente" + +#. module: purchase_reception_status +#: model:ir.model.fields.selection,name:purchase_reception_status.selection__purchase_order__reception_status__no +msgid "Nothing Received" +msgstr "Nada Recebido" + +#. module: purchase_reception_status +#: model:ir.model.fields.selection,name:purchase_reception_status.selection__purchase_order__reception_status__partial +msgid "Partially Received" +msgstr "Parcialmente Recebido" + +#. module: purchase_reception_status +#: model:ir.model,name:purchase_reception_status.model_purchase_order +msgid "Purchase Order" +msgstr "Ordem de Compra" + +#. module: purchase_reception_status +#: model:ir.model.fields,field_description:purchase_reception_status.field_purchase_order__reception_status +#: model_terms:ir.ui.view,arch_db:purchase_reception_status.view_purchase_order_filter +msgid "Reception Status" +msgstr "Estado da Receção" diff --git a/purchase_reception_status/i18n/purchase_reception_status.pot b/purchase_reception_status/i18n/purchase_reception_status.pot new file mode 100644 index 00000000000..bc4cec9a596 --- /dev/null +++ b/purchase_reception_status/i18n/purchase_reception_status.pot @@ -0,0 +1,77 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * purchase_reception_status +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 19.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: purchase_reception_status +#: model:ir.model.fields,field_description:purchase_reception_status.field_purchase_order__display_name +msgid "Display Name" +msgstr "" + +#. module: purchase_reception_status +#: model:ir.model.fields,field_description:purchase_reception_status.field_purchase_order__force_received +msgid "Force Received" +msgstr "" + +#. module: purchase_reception_status +#: model:ir.model.fields.selection,name:purchase_reception_status.selection__purchase_order__receipt_status__full +#: model_terms:ir.ui.view,arch_db:purchase_reception_status.view_purchase_order_filter +msgid "Fully Received" +msgstr "" + +#. module: purchase_reception_status +#: model:ir.model.fields,field_description:purchase_reception_status.field_purchase_order__id +msgid "ID" +msgstr "" + +#. module: purchase_reception_status +#: model:ir.model.fields,help:purchase_reception_status.field_purchase_order__force_received +msgid "" +"If true, the reception status will be forced to Fully Received, even if some" +" lines are not fully received. To be able to modify this field, you must " +"first lock the order." +msgstr "" + +#. module: purchase_reception_status +#: model_terms:ir.ui.view,arch_db:purchase_reception_status.view_purchase_order_filter +msgid "Not Fully Received" +msgstr "" + +#. module: purchase_reception_status +#: model:ir.model.fields.selection,name:purchase_reception_status.selection__purchase_order__receipt_status__pending +msgid "Nothing Received" +msgstr "" + +#. module: purchase_reception_status +#: model:ir.model.fields.selection,name:purchase_reception_status.selection__purchase_order__receipt_status__partial +msgid "Partially Received" +msgstr "" + +#. module: purchase_reception_status +#: model:ir.model,name:purchase_reception_status.model_purchase_order +msgid "Purchase Order" +msgstr "" + +#. module: purchase_reception_status +#: model:ir.model.fields,field_description:purchase_reception_status.field_purchase_order__receipt_status +#: model_terms:ir.ui.view,arch_db:purchase_reception_status.view_purchase_order_filter +msgid "Receipt Status" +msgstr "" + +#. module: purchase_reception_status +#: model:ir.model.fields,help:purchase_reception_status.field_purchase_order__receipt_status +msgid "" +"Red: Late\n" +" Orange: To process today\n" +" Green: On time" +msgstr "" diff --git a/purchase_reception_status/models/__init__.py b/purchase_reception_status/models/__init__.py new file mode 100644 index 00000000000..9f03530643d --- /dev/null +++ b/purchase_reception_status/models/__init__.py @@ -0,0 +1 @@ +from . import purchase_order diff --git a/purchase_reception_status/models/purchase_order.py b/purchase_reception_status/models/purchase_order.py new file mode 100644 index 00000000000..94173f257dc --- /dev/null +++ b/purchase_reception_status/models/purchase_order.py @@ -0,0 +1,60 @@ +# Copyright 2020 Akretion France (http://www.akretion.com/) +# @author: Alexis de Lattre +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import api, fields, models +from odoo.tools import float_compare + + +class PurchaseOrder(models.Model): + _inherit = "purchase.order" + + receipt_status = fields.Selection( + [ + ("pending", "Nothing Received"), + ("partial", "Partially Received"), + ("full", "Fully Received"), + ], + compute="_compute_oca_receipt_status", + store=True, + ) + force_received = fields.Boolean( + readonly=True, + copy=False, + help="If true, the reception status will be forced to Fully Received, " + "even if some lines are not fully received. " + "To be able to modify this field, you must first lock the order.", + ) + + @api.depends( + "state", + "force_received", + "locked", + "order_line.qty_received", + "order_line.product_qty", + ) + def _compute_oca_receipt_status(self): + prec = self.env["decimal.precision"].precision_get("Product Unit") + for order in self: + status = "pending" + if order.state == "purchase" or order.locked: + if order.force_received: + status = "full" + elif all( + [ + float_compare( + line.qty_received, line.product_qty, precision_digits=prec + ) + >= 0 + for line in order.order_line + ] + ): + status = "full" + elif any( + [ + float_compare(line.qty_received, 0, precision_digits=prec) > 0 + for line in order.order_line + ] + ): + status = "partial" + order.receipt_status = status diff --git a/purchase_reception_status/pyproject.toml b/purchase_reception_status/pyproject.toml new file mode 100644 index 00000000000..4231d0cccb3 --- /dev/null +++ b/purchase_reception_status/pyproject.toml @@ -0,0 +1,3 @@ +[build-system] +requires = ["whool"] +build-backend = "whool.buildapi" diff --git a/purchase_reception_status/readme/CONTRIBUTORS.md b/purchase_reception_status/readme/CONTRIBUTORS.md new file mode 100644 index 00000000000..4a6a2bbeba9 --- /dev/null +++ b/purchase_reception_status/readme/CONTRIBUTORS.md @@ -0,0 +1,2 @@ +- Alexis de Lattre \ +- Urvisha Desai \ diff --git a/purchase_reception_status/readme/DESCRIPTION.md b/purchase_reception_status/readme/DESCRIPTION.md new file mode 100644 index 00000000000..d57922f543d --- /dev/null +++ b/purchase_reception_status/readme/DESCRIPTION.md @@ -0,0 +1,6 @@ +This module adds a field *Receiption Status* on purchase orders. On a +confirmed purchase order, it can have 3 different values: + +- Nothing Received +- Partially Received +- Fully Received diff --git a/purchase_reception_status/readme/USAGE.md b/purchase_reception_status/readme/USAGE.md new file mode 100644 index 00000000000..c676e7b9532 --- /dev/null +++ b/purchase_reception_status/readme/USAGE.md @@ -0,0 +1,5 @@ +If you are part of the *Purchase Manager* group, you can force a +confirmed purchase order to **Full Received** status: you should first +*lock* the order (to enable locking, ensure the 'Locked' setting is checked +in the Purchase configuration), then check the field **Force Received** located in the +*Other Information* tab. diff --git a/purchase_reception_status/static/description/icon.png b/purchase_reception_status/static/description/icon.png new file mode 100644 index 00000000000..3a0328b516c Binary files /dev/null and b/purchase_reception_status/static/description/icon.png differ diff --git a/purchase_reception_status/static/description/index.html b/purchase_reception_status/static/description/index.html new file mode 100644 index 00000000000..9d5b59b24c9 --- /dev/null +++ b/purchase_reception_status/static/description/index.html @@ -0,0 +1,447 @@ + + + + + +README.rst + + + +
+ + + +Odoo Community Association + +
+

Purchase Reception Status

+ +

Beta License: AGPL-3 OCA/purchase-workflow Translate me on Weblate Try me on Runboat

+

This module adds a field Receiption Status on purchase orders. On a +confirmed purchase order, it can have 3 different values:

+
    +
  • Nothing Received
  • +
  • Partially Received
  • +
  • Fully Received
  • +
+

Table of contents

+ +
+

Usage

+

If you are part of the Purchase Manager group, you can force a +confirmed purchase order to Full Received status: you should first +lock the order (to enable locking, ensure the ‘Locked’ setting is +checked in the Purchase configuration), then check the field Force +Received located in the Other Information tab.

+
+
+

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

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 maintainer:

+

alexis-via

+

This module is part of the OCA/purchase-workflow project on GitHub.

+

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

+
+
+
+
+ + diff --git a/purchase_reception_status/tests/__init__.py b/purchase_reception_status/tests/__init__.py new file mode 100644 index 00000000000..59dbd4c385a --- /dev/null +++ b/purchase_reception_status/tests/__init__.py @@ -0,0 +1 @@ +from . import test_purchase_reception_status diff --git a/purchase_reception_status/tests/test_purchase_reception_status.py b/purchase_reception_status/tests/test_purchase_reception_status.py new file mode 100644 index 00000000000..bf6f3cb8327 --- /dev/null +++ b/purchase_reception_status/tests/test_purchase_reception_status.py @@ -0,0 +1,92 @@ +# Copyright 2025 ForgeFlow S.L. +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import fields +from odoo.tests.common import TransactionCase + + +class TestPurchaseReceptionStatus(TransactionCase): + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.supplier = cls.env["res.partner"].create( + { + "name": "Test Supplier", + "is_company": True, + "supplier_rank": 1, + } + ) + cls.product_1 = cls.env["product.product"].create( + { + "name": "Test Product 1", + "type": "consu", + "purchase_method": "receive", + "list_price": 100.0, + "is_storable": True, + } + ) + + def test_01_receipt_status_functionality(self): + po = self.env["purchase.order"].create( + { + "partner_id": self.supplier.id, + } + ) + pol = self.env["purchase.order.line"].create( + { + "order_id": po.id, + "product_id": self.product_1.id, + "name": self.product_1.name, + "product_qty": 10.0, + "product_uom_id": self.product_1.uom_id.id, + "price_unit": 100.0, + "date_planned": fields.Date.today(), + } + ) + po.button_confirm() + self.assertEqual(po.receipt_status, "pending") + pol.qty_received = 5.0 + self.assertEqual(po.receipt_status, "partial") + po.button_lock() + po.force_received = True + self.assertEqual(po.receipt_status, "full") + + def test_02_receipt_status_draft_state(self): + po = self.env["purchase.order"].create( + { + "partner_id": self.supplier.id, + } + ) + self.env["purchase.order.line"].create( + { + "order_id": po.id, + "product_id": self.product_1.id, + "name": self.product_1.name, + "product_qty": 10.0, + "product_uom_id": self.product_1.uom_id.id, + "price_unit": 100.0, + "date_planned": fields.Date.today(), + } + ) + self.assertEqual(po.receipt_status, "pending") + + def test_03_receipt_status_over_received(self): + po = self.env["purchase.order"].create( + { + "partner_id": self.supplier.id, + } + ) + pol = self.env["purchase.order.line"].create( + { + "order_id": po.id, + "product_id": self.product_1.id, + "name": self.product_1.name, + "product_qty": 10.0, + "product_uom_id": self.product_1.uom_id.id, + "price_unit": 100.0, + "date_planned": fields.Date.today(), + } + ) + po.button_confirm() + pol.qty_received = 15.0 + self.assertEqual(po.receipt_status, "full") diff --git a/purchase_reception_status/views/purchase_order.xml b/purchase_reception_status/views/purchase_order.xml new file mode 100644 index 00000000000..a0396312b50 --- /dev/null +++ b/purchase_reception_status/views/purchase_order.xml @@ -0,0 +1,67 @@ + + + + + received_status.purchase.order.form + purchase.order + + + + + + + + + received_status.purchase.order.tree + purchase.order + + + + + + + + + received_status.purchase.order.search + purchase.order + + + + + + + + + + + + + diff --git a/purchase_tag/README.rst b/purchase_tag/README.rst new file mode 100644 index 00000000000..dfbe0500355 --- /dev/null +++ b/purchase_tag/README.rst @@ -0,0 +1,85 @@ +.. image:: https://odoo-community.org/readme-banner-image + :target: https://odoo-community.org/get-involved?utm_source=readme + :alt: Odoo Community Association + +============= +Purchase Tags +============= + +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:7e1a17aab6e4f4d094c6e036630efa0d60d026bf901bf2279dec9e0e58c0041c + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |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/license-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%2Fpurchase--workflow-lightgray.png?logo=github + :target: https://github.com/OCA/purchase-workflow/tree/19.0/purchase_tag + :alt: OCA/purchase-workflow +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/purchase-workflow-19-0/purchase-workflow-19-0-purchase_tag + :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/purchase-workflow&target_branch=19.0 + :alt: Try me on Runboat + +|badge1| |badge2| |badge3| |badge4| |badge5| + +This module allows to add multiple tags to purchase orders + +**Table of contents** + +.. contents:: + :local: + +Usage +===== + +To use this module, you need to go to Purchase Orders and select tags. + +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 +------- + +* ForgeFlow + +Contributors +------------ + +- Jasmin Solanki + +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/purchase-workflow `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/purchase_tag/__init__.py b/purchase_tag/__init__.py new file mode 100644 index 00000000000..4b76c7b2d5c --- /dev/null +++ b/purchase_tag/__init__.py @@ -0,0 +1,3 @@ +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). + +from . import models diff --git a/purchase_tag/__manifest__.py b/purchase_tag/__manifest__.py new file mode 100644 index 00000000000..2db1a9b61f4 --- /dev/null +++ b/purchase_tag/__manifest__.py @@ -0,0 +1,21 @@ +# Copyright 2022 ForgeFlow S.L. +# (http://www.forgeflow.com) +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). + +{ + "name": "Purchase Tags", + "summary": "Allows to add multiple tags to purchase orders", + "version": "19.0.1.0.0", + "author": "ForgeFlow, Odoo Community Association (OCA)", + "website": "https://github.com/OCA/purchase-workflow", + "category": "Purchases", + "depends": ["purchase"], + "data": [ + "security/ir.model.access.csv", + "views/purchase_view.xml", + "views/purchase_tag_view.xml", + ], + "license": "AGPL-3", + "installable": True, + "application": False, +} diff --git a/purchase_tag/i18n/es.po b/purchase_tag/i18n/es.po new file mode 100644 index 00000000000..acd5da790d1 --- /dev/null +++ b/purchase_tag/i18n/es.po @@ -0,0 +1,110 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * purchase_tag +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 16.0\n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: 2024-02-11 13:34+0000\n" +"Last-Translator: Ivorra78 \n" +"Language-Team: none\n" +"Language: es\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: purchase_tag +#: model:ir.model.fields,field_description:purchase_tag.field_purchase_tag__child_ids +msgid "Child" +msgstr "Hijo(a)" + +#. module: purchase_tag +#: model:ir.model.fields,field_description:purchase_tag.field_purchase_tag__color +msgid "Color" +msgstr "Color" + +#. module: purchase_tag +#: model_terms:ir.actions.act_window,help:purchase_tag.purchase_tag_action +msgid "Create new tags for your purchase orders" +msgstr "Crea nuevas etiquetas para tus órdenes de compra" + +#. module: purchase_tag +#: model:ir.model.fields,field_description:purchase_tag.field_purchase_tag__create_uid +msgid "Created by" +msgstr "Creado por" + +#. module: purchase_tag +#: model:ir.model.fields,field_description:purchase_tag.field_purchase_tag__create_date +msgid "Created on" +msgstr "Creado el" + +#. module: purchase_tag +#: model:ir.model.fields,field_description:purchase_tag.field_purchase_tag__display_name +msgid "Display Name" +msgstr "Mostrar Nombre" + +#. module: purchase_tag +#: model:ir.model.fields,field_description:purchase_tag.field_purchase_tag__id +msgid "ID" +msgstr "ID (identificación)" + +#. module: purchase_tag +#: model:ir.model.fields,field_description:purchase_tag.field_purchase_tag__write_uid +msgid "Last Updated by" +msgstr "Actualizado por última vez por" + +#. module: purchase_tag +#: model:ir.model.fields,field_description:purchase_tag.field_purchase_tag__write_date +msgid "Last Updated on" +msgstr "Última Actualización el" + +#. module: purchase_tag +#: model:ir.model.fields,field_description:purchase_tag.field_purchase_tag__parent_id +msgid "Parent" +msgstr "Parental" + +#. module: purchase_tag +#: model:ir.model.fields,field_description:purchase_tag.field_purchase_tag__parent_path +msgid "Parent Path" +msgstr "Ruta Parental" + +#. module: purchase_tag +#: model:ir.model,name:purchase_tag.model_purchase_order +msgid "Purchase Order" +msgstr "Orden de compra" + +#. module: purchase_tag +#: model:ir.model,name:purchase_tag.model_purchase_tag +msgid "Purchase Tag" +msgstr "Etiqueta de compra" + +#. module: purchase_tag +#: model:ir.model.fields,field_description:purchase_tag.field_purchase_tag__name +msgid "Tag Name" +msgstr "Nombre de etiqueta" + +#. module: purchase_tag +#: model:ir.model.constraint,message:purchase_tag.constraint_purchase_tag_tag_name_uniq +msgid "Tag name already exists !" +msgstr "¡El nombre de etiqueta ya existe!" + +#. module: purchase_tag +#: model:ir.actions.act_window,name:purchase_tag.purchase_tag_action +#: model:ir.model.fields,field_description:purchase_tag.field_purchase_order__tag_ids +#: model:ir.ui.menu,name:purchase_tag.menu_purchase_tag_config +#: model_terms:ir.ui.view,arch_db:purchase_tag.purchase_tag_view_form +msgid "Tags" +msgstr "Etiquetas" + +#. module: purchase_tag +#. odoo-python +#: code:addons/purchase_tag/models/purchase_tag.py:0 +#, python-format +msgid "Tags cannot be recursive." +msgstr "Las etiquetas no pueden ser recursivas." + +#~ msgid "Last Modified on" +#~ msgstr "Última Modificación el" diff --git a/purchase_tag/i18n/it.po b/purchase_tag/i18n/it.po new file mode 100644 index 00000000000..d29ca75b3c2 --- /dev/null +++ b/purchase_tag/i18n/it.po @@ -0,0 +1,110 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * purchase_tag +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 16.0\n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: 2024-02-04 15:37+0000\n" +"Last-Translator: mymage \n" +"Language-Team: none\n" +"Language: it\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: purchase_tag +#: model:ir.model.fields,field_description:purchase_tag.field_purchase_tag__child_ids +msgid "Child" +msgstr "Figlio" + +#. module: purchase_tag +#: model:ir.model.fields,field_description:purchase_tag.field_purchase_tag__color +msgid "Color" +msgstr "Colore" + +#. module: purchase_tag +#: model_terms:ir.actions.act_window,help:purchase_tag.purchase_tag_action +msgid "Create new tags for your purchase orders" +msgstr "Crea nuove etichette per gli ordini di acquisto" + +#. module: purchase_tag +#: model:ir.model.fields,field_description:purchase_tag.field_purchase_tag__create_uid +msgid "Created by" +msgstr "Creato da" + +#. module: purchase_tag +#: model:ir.model.fields,field_description:purchase_tag.field_purchase_tag__create_date +msgid "Created on" +msgstr "Creato il" + +#. module: purchase_tag +#: model:ir.model.fields,field_description:purchase_tag.field_purchase_tag__display_name +msgid "Display Name" +msgstr "Nome visualizzato" + +#. module: purchase_tag +#: model:ir.model.fields,field_description:purchase_tag.field_purchase_tag__id +msgid "ID" +msgstr "ID" + +#. module: purchase_tag +#: model:ir.model.fields,field_description:purchase_tag.field_purchase_tag__write_uid +msgid "Last Updated by" +msgstr "Ultimo aggiornamento di" + +#. module: purchase_tag +#: model:ir.model.fields,field_description:purchase_tag.field_purchase_tag__write_date +msgid "Last Updated on" +msgstr "Ultimo aggiornamento il" + +#. module: purchase_tag +#: model:ir.model.fields,field_description:purchase_tag.field_purchase_tag__parent_id +msgid "Parent" +msgstr "Padre" + +#. module: purchase_tag +#: model:ir.model.fields,field_description:purchase_tag.field_purchase_tag__parent_path +msgid "Parent Path" +msgstr "Percorso padre" + +#. module: purchase_tag +#: model:ir.model,name:purchase_tag.model_purchase_order +msgid "Purchase Order" +msgstr "Ordine di acquisto" + +#. module: purchase_tag +#: model:ir.model,name:purchase_tag.model_purchase_tag +msgid "Purchase Tag" +msgstr "Etichetta acquisti" + +#. module: purchase_tag +#: model:ir.model.fields,field_description:purchase_tag.field_purchase_tag__name +msgid "Tag Name" +msgstr "Nome etichetta" + +#. module: purchase_tag +#: model:ir.model.constraint,message:purchase_tag.constraint_purchase_tag_tag_name_uniq +msgid "Tag name already exists !" +msgstr "Il nome etichetta esiste già!" + +#. module: purchase_tag +#: model:ir.actions.act_window,name:purchase_tag.purchase_tag_action +#: model:ir.model.fields,field_description:purchase_tag.field_purchase_order__tag_ids +#: model:ir.ui.menu,name:purchase_tag.menu_purchase_tag_config +#: model_terms:ir.ui.view,arch_db:purchase_tag.purchase_tag_view_form +msgid "Tags" +msgstr "Etichette" + +#. module: purchase_tag +#. odoo-python +#: code:addons/purchase_tag/models/purchase_tag.py:0 +#, python-format +msgid "Tags cannot be recursive." +msgstr "Le etichette non possono essere ricorsive." + +#~ msgid "Last Modified on" +#~ msgstr "Ultima modifica il" diff --git a/purchase_tag/i18n/pt_BR.po b/purchase_tag/i18n/pt_BR.po new file mode 100644 index 00000000000..3a71ec6f0f6 --- /dev/null +++ b/purchase_tag/i18n/pt_BR.po @@ -0,0 +1,110 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * purchase_tag +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 16.0\n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: 2023-10-29 08:31+0000\n" +"Last-Translator: Adriano Prado \n" +"Language-Team: none\n" +"Language: pt_BR\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: nplurals=2; plural=n > 1;\n" +"X-Generator: Weblate 4.17\n" + +#. module: purchase_tag +#: model:ir.model.fields,field_description:purchase_tag.field_purchase_tag__child_ids +msgid "Child" +msgstr "" + +#. module: purchase_tag +#: model:ir.model.fields,field_description:purchase_tag.field_purchase_tag__color +msgid "Color" +msgstr "Cor" + +#. module: purchase_tag +#: model_terms:ir.actions.act_window,help:purchase_tag.purchase_tag_action +msgid "Create new tags for your purchase orders" +msgstr "Crie novos marcadores para seus pedidos de compra" + +#. module: purchase_tag +#: model:ir.model.fields,field_description:purchase_tag.field_purchase_tag__create_uid +msgid "Created by" +msgstr "Criado por" + +#. module: purchase_tag +#: model:ir.model.fields,field_description:purchase_tag.field_purchase_tag__create_date +msgid "Created on" +msgstr "Criado em" + +#. module: purchase_tag +#: model:ir.model.fields,field_description:purchase_tag.field_purchase_tag__display_name +msgid "Display Name" +msgstr "Nome Exibição" + +#. module: purchase_tag +#: model:ir.model.fields,field_description:purchase_tag.field_purchase_tag__id +msgid "ID" +msgstr "ID" + +#. module: purchase_tag +#: model:ir.model.fields,field_description:purchase_tag.field_purchase_tag__write_uid +msgid "Last Updated by" +msgstr "Última Atualização por" + +#. module: purchase_tag +#: model:ir.model.fields,field_description:purchase_tag.field_purchase_tag__write_date +msgid "Last Updated on" +msgstr "Última Atualização em" + +#. module: purchase_tag +#: model:ir.model.fields,field_description:purchase_tag.field_purchase_tag__parent_id +msgid "Parent" +msgstr "" + +#. module: purchase_tag +#: model:ir.model.fields,field_description:purchase_tag.field_purchase_tag__parent_path +msgid "Parent Path" +msgstr "" + +#. module: purchase_tag +#: model:ir.model,name:purchase_tag.model_purchase_order +msgid "Purchase Order" +msgstr "Pedido de Compra" + +#. module: purchase_tag +#: model:ir.model,name:purchase_tag.model_purchase_tag +msgid "Purchase Tag" +msgstr "Marcador Compra" + +#. module: purchase_tag +#: model:ir.model.fields,field_description:purchase_tag.field_purchase_tag__name +msgid "Tag Name" +msgstr "Nome do Marcador" + +#. module: purchase_tag +#: model:ir.model.constraint,message:purchase_tag.constraint_purchase_tag_tag_name_uniq +msgid "Tag name already exists !" +msgstr "Marcador já Existe!" + +#. module: purchase_tag +#: model:ir.actions.act_window,name:purchase_tag.purchase_tag_action +#: model:ir.model.fields,field_description:purchase_tag.field_purchase_order__tag_ids +#: model:ir.ui.menu,name:purchase_tag.menu_purchase_tag_config +#: model_terms:ir.ui.view,arch_db:purchase_tag.purchase_tag_view_form +msgid "Tags" +msgstr "Marcadores" + +#. module: purchase_tag +#. odoo-python +#: code:addons/purchase_tag/models/purchase_tag.py:0 +#, python-format +msgid "Tags cannot be recursive." +msgstr "" + +#~ msgid "Last Modified on" +#~ msgstr "Última Modificação em" diff --git a/purchase_tag/i18n/purchase_tag.pot b/purchase_tag/i18n/purchase_tag.pot new file mode 100644 index 00000000000..dcc25eeeb4d --- /dev/null +++ b/purchase_tag/i18n/purchase_tag.pot @@ -0,0 +1,99 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * purchase_tag +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 19.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: purchase_tag +#: model:ir.model.fields,field_description:purchase_tag.field_purchase_tag__child_ids +msgid "Child" +msgstr "" + +#. module: purchase_tag +#: model:ir.model.fields,field_description:purchase_tag.field_purchase_tag__color +msgid "Color" +msgstr "" + +#. module: purchase_tag +#: model_terms:ir.actions.act_window,help:purchase_tag.purchase_tag_action +msgid "Create new tags for your purchase orders" +msgstr "" + +#. module: purchase_tag +#: model:ir.model.fields,field_description:purchase_tag.field_purchase_tag__create_uid +msgid "Created by" +msgstr "" + +#. module: purchase_tag +#: model:ir.model.fields,field_description:purchase_tag.field_purchase_tag__create_date +msgid "Created on" +msgstr "" + +#. module: purchase_tag +#: model:ir.model.fields,field_description:purchase_tag.field_purchase_order__display_name +#: model:ir.model.fields,field_description:purchase_tag.field_purchase_tag__display_name +msgid "Display Name" +msgstr "" + +#. module: purchase_tag +#: model:ir.model.fields,field_description:purchase_tag.field_purchase_order__id +#: model:ir.model.fields,field_description:purchase_tag.field_purchase_tag__id +msgid "ID" +msgstr "" + +#. module: purchase_tag +#: model:ir.model.fields,field_description:purchase_tag.field_purchase_tag__write_uid +msgid "Last Updated by" +msgstr "" + +#. module: purchase_tag +#: model:ir.model.fields,field_description:purchase_tag.field_purchase_tag__write_date +msgid "Last Updated on" +msgstr "" + +#. module: purchase_tag +#: model:ir.model.fields,field_description:purchase_tag.field_purchase_tag__parent_id +msgid "Parent" +msgstr "" + +#. module: purchase_tag +#: model:ir.model.fields,field_description:purchase_tag.field_purchase_tag__parent_path +msgid "Parent Path" +msgstr "" + +#. module: purchase_tag +#: model:ir.model,name:purchase_tag.model_purchase_order +msgid "Purchase Order" +msgstr "" + +#. module: purchase_tag +#: model:ir.model,name:purchase_tag.model_purchase_tag +msgid "Purchase Tag" +msgstr "" + +#. module: purchase_tag +#: model:ir.model.fields,field_description:purchase_tag.field_purchase_tag__name +msgid "Tag Name" +msgstr "" + +#. module: purchase_tag +#: model:ir.model.constraint,message:purchase_tag.constraint_purchase_tag_tag_name_uniq +msgid "Tag name already exists !" +msgstr "" + +#. module: purchase_tag +#: model:ir.actions.act_window,name:purchase_tag.purchase_tag_action +#: model:ir.model.fields,field_description:purchase_tag.field_purchase_order__tag_ids +#: model:ir.ui.menu,name:purchase_tag.menu_purchase_tag_config +#: model_terms:ir.ui.view,arch_db:purchase_tag.purchase_tag_view_form +msgid "Tags" +msgstr "" diff --git a/purchase_tag/models/__init__.py b/purchase_tag/models/__init__.py new file mode 100644 index 00000000000..d54d894c559 --- /dev/null +++ b/purchase_tag/models/__init__.py @@ -0,0 +1,2 @@ +from . import purchase_tag +from . import purchase_order diff --git a/purchase_tag/models/purchase_order.py b/purchase_tag/models/purchase_order.py new file mode 100644 index 00000000000..5267d137cc4 --- /dev/null +++ b/purchase_tag/models/purchase_order.py @@ -0,0 +1,17 @@ +# Copyright 2022 ForgeFlow S.L. +# (http://www.forgeflow.com) +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). + +from odoo import fields, models + + +class PurchaseOrder(models.Model): + _inherit = "purchase.order" + + tag_ids = fields.Many2many( + comodel_name="purchase.tag", + relation="purchase_order_tag_rel", + column1="purchase_order_id", + column2="tag_id", + string="Tags", + ) diff --git a/purchase_tag/models/purchase_tag.py b/purchase_tag/models/purchase_tag.py new file mode 100644 index 00000000000..4277a6965c3 --- /dev/null +++ b/purchase_tag/models/purchase_tag.py @@ -0,0 +1,43 @@ +# Copyright 2022 ForgeFlow S.L. +# (http://www.forgeflow.com) +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). + +from random import randint + +from odoo import api, fields, models + + +class PurchaseTag(models.Model): + _name = "purchase.tag" + _description = "Purchase Tag" + _parent_store = True + + def _get_default_color(self): + return randint(1, 11) + + name = fields.Char("Tag Name", required=True, translate=True) + color = fields.Integer(default=lambda self: self._get_default_color()) + parent_id = fields.Many2one("purchase.tag", index=True, ondelete="cascade") + child_ids = fields.One2many("purchase.tag", "parent_id") + parent_path = fields.Char(index=True) + + _tag_name_uniq = models.Constraint( + "unique (name)", + "Tag name already exists !", + ) + + def _compute_display_name(self): + for tag in self: + names = [] + current = tag + while current: + if current.name: + names.append(current.name) + current = current.parent_id + display_name = " / ".join(reversed(names)) + tag.display_name = display_name + + @api.model + def _search_display_name(self, operator, value): + domain = [("name", operator, value.split(" / ")[-1])] + return domain diff --git a/purchase_tag/pyproject.toml b/purchase_tag/pyproject.toml new file mode 100644 index 00000000000..4231d0cccb3 --- /dev/null +++ b/purchase_tag/pyproject.toml @@ -0,0 +1,3 @@ +[build-system] +requires = ["whool"] +build-backend = "whool.buildapi" diff --git a/purchase_tag/readme/CONTRIBUTORS.md b/purchase_tag/readme/CONTRIBUTORS.md new file mode 100644 index 00000000000..88b3ed521e7 --- /dev/null +++ b/purchase_tag/readme/CONTRIBUTORS.md @@ -0,0 +1 @@ +- Jasmin Solanki \<\> diff --git a/purchase_tag/readme/DESCRIPTION.md b/purchase_tag/readme/DESCRIPTION.md new file mode 100644 index 00000000000..feb507c6000 --- /dev/null +++ b/purchase_tag/readme/DESCRIPTION.md @@ -0,0 +1 @@ +This module allows to add multiple tags to purchase orders diff --git a/purchase_tag/readme/USAGE.md b/purchase_tag/readme/USAGE.md new file mode 100644 index 00000000000..7e54d63369c --- /dev/null +++ b/purchase_tag/readme/USAGE.md @@ -0,0 +1 @@ +To use this module, you need to go to Purchase Orders and select tags. diff --git a/purchase_tag/security/ir.model.access.csv b/purchase_tag/security/ir.model.access.csv new file mode 100644 index 00000000000..8ad7fcd8e8a --- /dev/null +++ b/purchase_tag/security/ir.model.access.csv @@ -0,0 +1,3 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +access_purchase_tag,purchase_tag,model_purchase_tag,base.group_user,1,0,0,0 +access_purchase_tag_manager,purchase_tag_manager,model_purchase_tag,purchase.group_purchase_manager,1,1,1,1 diff --git a/purchase_tag/static/description/icon.png b/purchase_tag/static/description/icon.png new file mode 100644 index 00000000000..3a0328b516c Binary files /dev/null and b/purchase_tag/static/description/icon.png differ diff --git a/purchase_tag/static/description/index.html b/purchase_tag/static/description/index.html new file mode 100644 index 00000000000..ca7fea47cf8 --- /dev/null +++ b/purchase_tag/static/description/index.html @@ -0,0 +1,434 @@ + + + + + +README.rst + + + +
+ + + +Odoo Community Association + +
+

Purchase Tags

+ +

Beta License: AGPL-3 OCA/purchase-workflow Translate me on Weblate Try me on Runboat

+

This module allows to add multiple tags to purchase orders

+

Table of contents

+ +
+

Usage

+

To use this module, you need to go to Purchase Orders and select tags.

+
+
+

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

+
    +
  • ForgeFlow
  • +
+
+
+

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.

+

This module is part of the OCA/purchase-workflow project on GitHub.

+

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

+
+
+
+
+ + diff --git a/purchase_tag/tests/__init__.py b/purchase_tag/tests/__init__.py new file mode 100644 index 00000000000..521324209dd --- /dev/null +++ b/purchase_tag/tests/__init__.py @@ -0,0 +1 @@ +from . import test_purchase_tag diff --git a/purchase_tag/tests/test_purchase_tag.py b/purchase_tag/tests/test_purchase_tag.py new file mode 100644 index 00000000000..5c82605c6c0 --- /dev/null +++ b/purchase_tag/tests/test_purchase_tag.py @@ -0,0 +1,39 @@ +from odoo.exceptions import UserError + +from odoo.addons.base.tests.common import BaseCommon + + +class TestPurchaseTag(BaseCommon): + @classmethod + def setUpClass(cls): + super().setUpClass() + PurchaseTag = cls.env["purchase.tag"] + cls.tag_parent = PurchaseTag.create({"name": "Parent"}) + cls.tag_child = PurchaseTag.create( + {"name": "Child", "parent_id": cls.tag_parent.id} + ) + cls.tag_grandchild = PurchaseTag.create( + {"name": "Grandchild", "parent_id": cls.tag_child.id} + ) + + def test_display_name_computation(self): + # Verify the hierarchical display_name. + self.assertEqual( + self.tag_grandchild.display_name, "Parent / Child / Grandchild" + ) + self.assertEqual(self.tag_child.display_name, "Parent / Child") + self.assertEqual(self.tag_parent.display_name, "Parent") + + def test_name_search(self): + # Perform a search using part of the display_name. + results = self.env["purchase.tag"].name_search( + name="Parent / Child / Grandchild" + ) + self.assertTrue(results) + self.assertEqual(results[0][0], self.tag_grandchild.id) + self.assertEqual(results[0][1], "Parent / Child / Grandchild") + + def test_recursion_error(self): + # Trigger recursion to raise a UserError from Odoo's core. + with self.assertRaisesRegex(UserError, "Recursion Detected."): + self.tag_parent.write({"parent_id": self.tag_grandchild.id}) diff --git a/purchase_tag/views/purchase_tag_view.xml b/purchase_tag/views/purchase_tag_view.xml new file mode 100644 index 00000000000..4605921391b --- /dev/null +++ b/purchase_tag/views/purchase_tag_view.xml @@ -0,0 +1,61 @@ + + + + + + + purchase.tag.view.form + purchase.tag + +
+ +
+
+
+

+ +

+
+ + + + + + +
+
+
+
+ + + purchase.tag.view.tree + purchase.tag + + + + + + + + + + + Tags + purchase.tag + + +

+ Create new tags for your purchase orders +

+
+
+ + +
diff --git a/purchase_tag/views/purchase_view.xml b/purchase_tag/views/purchase_view.xml new file mode 100644 index 00000000000..0f35491c3cb --- /dev/null +++ b/purchase_tag/views/purchase_view.xml @@ -0,0 +1,52 @@ + + + + + + + purchase.order.form + purchase.order + + + + + + + + + + purchase.order.tree + purchase.order + + + + + + + + + + purchase.order.tree + purchase.order + + + + + + + + diff --git a/setup/_metapackage/pyproject.toml b/setup/_metapackage/pyproject.toml index eea2049c981..cdbfa65d173 100644 --- a/setup/_metapackage/pyproject.toml +++ b/setup/_metapackage/pyproject.toml @@ -1,12 +1,14 @@ [project] name = "odoo-addons-oca-purchase-workflow" -version = "19.0.20260417.0" +version = "19.0.20260424.0" dependencies = [ "odoo-addon-partner_supplierinfo_smartbutton==19.0.*", "odoo-addon-procurement_purchase_no_grouping==19.0.*", "odoo-addon-product_supplier_code_purchase==19.0.*", + "odoo-addon-purchase_advance_payment==19.0.*", "odoo-addon-purchase_allowed_product==19.0.*", "odoo-addon-purchase_default_terms_conditions==19.0.*", + "odoo-addon-purchase_delivery_split_date==19.0.*", "odoo-addon-purchase_force_invoiced==19.0.*", "odoo-addon-purchase_line_reassign==19.0.*", "odoo-addon-purchase_no_rfq==19.0.*", @@ -16,8 +18,10 @@ dependencies = [ "odoo-addon-purchase_order_type==19.0.*", "odoo-addon-purchase_order_weight_volume==19.0.*", "odoo-addon-purchase_partner_incoterm==19.0.*", + "odoo-addon-purchase_reception_status==19.0.*", "odoo-addon-purchase_request==19.0.*", "odoo-addon-purchase_stock_price_unit_sync==19.0.*", + "odoo-addon-purchase_tag==19.0.*", ] classifiers=[ "Programming Language :: Python", diff --git a/test-requirements.txt b/test-requirements.txt new file mode 100644 index 00000000000..a7834895fb8 --- /dev/null +++ b/test-requirements.txt @@ -0,0 +1 @@ +odoo-addon-purchase_order_line_menu @ git+https://github.com/OCA/purchase-workflow.git@refs/pull/2846/head#subdirectory=purchase_order_line_menu