diff --git a/purchase_invoice_plan_deposit/README.rst b/purchase_invoice_plan_deposit/README.rst new file mode 100644 index 00000000000..446969313b6 --- /dev/null +++ b/purchase_invoice_plan_deposit/README.rst @@ -0,0 +1,108 @@ +============================================== +Purchase Invoice Plan - Deposit on 1st invoice +============================================== + +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:6069c0bd14421f07bd6c6e9b2af926438a50ddef40a969b4c4143c240fb567e5 + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |badge1| image:: https://img.shields.io/badge/maturity-Alpha-red.png + :target: https://odoo-community.org/page/development-status + :alt: Alpha +.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png + :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 +.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fpurchase--workflow-lightgray.png?logo=github + :target: https://github.com/OCA/purchase-workflow/tree/18.0/purchase_invoice_plan_deposit + :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-18-0/purchase-workflow-18-0-purchase_invoice_plan_deposit + :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=18.0 + :alt: Try me on Runboat + +|badge1| |badge2| |badge3| |badge4| |badge5| + +This module bridge features from purchase_invoice_plan and +purchase_deposit. Now, it posssible create invoice plan with 1st invoice +as a deposit invoice. + +.. IMPORTANT:: + This is an alpha version, the data model and design can change at any time without warning. + Only for development or testing purpose, do not use in production. + `More details on development status `_ + +**Table of contents** + +.. contents:: + :local: + +Usage +===== + +- Create new purchase quotation as per normal process +- Select option "Use Invoice Plan", a new Invoice Plan tab will appear +- Click on "=> Create Invoice Plan" link to open invoice planning wizard +- Do plan for number of installment and/or **Deposit on 1st invoice**, + start date and interval +- Double check that each installment has correct plan percentage + (Deposit's plan percentage must > 0) +- After confirm purchases order, we have new option to "Create Bill by + Plan" +- User can create only next bill or create all bills at the same time + +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 +------- + +* Ecosoft + +Contributors +------------ + +- Kitti Upariphutthiphong + +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-kittiu| image:: https://github.com/kittiu.png?size=40px + :target: https://github.com/kittiu + :alt: kittiu +.. |maintainer-Saran440| image:: https://github.com/Saran440.png?size=40px + :target: https://github.com/Saran440 + :alt: Saran440 + +Current `maintainers `__: + +|maintainer-kittiu| |maintainer-Saran440| + +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_plan_deposit/__init__.py b/purchase_invoice_plan_deposit/__init__.py new file mode 100644 index 00000000000..dff17efd48b --- /dev/null +++ b/purchase_invoice_plan_deposit/__init__.py @@ -0,0 +1,5 @@ +# Copyright 2021 Ecosoft Co., Ltd (http://ecosoft.co.th/) +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html) + +from . import models +from . import wizard diff --git a/purchase_invoice_plan_deposit/__manifest__.py b/purchase_invoice_plan_deposit/__manifest__.py new file mode 100644 index 00000000000..c8e14f183da --- /dev/null +++ b/purchase_invoice_plan_deposit/__manifest__.py @@ -0,0 +1,21 @@ +# Copyright 2021 Ecosoft Co., Ltd (http://ecosoft.co.th/) +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html) + +{ + "name": "Purchase Invoice Plan - Deposit on 1st invoice", + "summary": "Add to purchase invoice plan, the deposit invoice", + "version": "18.0.1.0.0", + "author": "Ecosoft,Odoo Community Association (OCA)", + "license": "AGPL-3", + "website": "https://github.com/OCA/purchase-workflow", + "category": "Purchase", + "depends": ["purchase_invoice_plan", "purchase_deposit"], + "data": [ + "wizard/purchase_create_invoice_plan_view.xml", + "views/purchase_view.xml", + ], + "installable": True, + "auto_install": True, + "maintainers": ["kittiu", "Saran440"], + "development_status": "Alpha", +} diff --git a/purchase_invoice_plan_deposit/i18n/es.po b/purchase_invoice_plan_deposit/i18n/es.po new file mode 100644 index 00000000000..69263b3d6e0 --- /dev/null +++ b/purchase_invoice_plan_deposit/i18n/es.po @@ -0,0 +1,125 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * purchase_invoice_plan_deposit +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 14.0\n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: 2024-03-30 22:33+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_invoice_plan_deposit +#: model:ir.model.fields.selection,name:purchase_invoice_plan_deposit.selection__purchase_invoice_plan__invoice_type__advance +msgid "Deposit" +msgstr "Depósito" + +#. module: purchase_invoice_plan_deposit +#: model:ir.model.fields,field_description:purchase_invoice_plan_deposit.field_purchase_create_invoice_plan__advance +msgid "Deposit on 1st Invoice" +msgstr "Depósito en la 1ª Factura" + +#. module: purchase_invoice_plan_deposit +#: model:ir.model.fields,field_description:purchase_invoice_plan_deposit.field_purchase_advance_payment_inv__display_name +#: model:ir.model.fields,field_description:purchase_invoice_plan_deposit.field_purchase_create_invoice_plan__display_name +#: model:ir.model.fields,field_description:purchase_invoice_plan_deposit.field_purchase_invoice_plan__display_name +#: model:ir.model.fields,field_description:purchase_invoice_plan_deposit.field_purchase_make_planned_invoice__display_name +#: model:ir.model.fields,field_description:purchase_invoice_plan_deposit.field_purchase_order__display_name +msgid "Display Name" +msgstr "Mostrar Nombre" + +#. module: purchase_invoice_plan_deposit +#: model:ir.model,name:purchase_invoice_plan_deposit.model_purchase_create_invoice_plan +msgid "Fillig invoice planning criteria" +msgstr "Cumplimiento de criterios de planificación de facturas" + +#. module: purchase_invoice_plan_deposit +#: model:ir.model.fields,field_description:purchase_invoice_plan_deposit.field_purchase_advance_payment_inv__id +#: model:ir.model.fields,field_description:purchase_invoice_plan_deposit.field_purchase_create_invoice_plan__id +#: model:ir.model.fields,field_description:purchase_invoice_plan_deposit.field_purchase_invoice_plan__id +#: model:ir.model.fields,field_description:purchase_invoice_plan_deposit.field_purchase_make_planned_invoice__id +#: model:ir.model.fields,field_description:purchase_invoice_plan_deposit.field_purchase_order__id +msgid "ID" +msgstr "ID" + +#. module: purchase_invoice_plan_deposit +#: model:ir.actions.act_window,name:purchase_invoice_plan_deposit.action_view_purchase_advance_payment_inv_highlight +msgid "Invoice Order" +msgstr "Orden de Factura" + +#. module: purchase_invoice_plan_deposit +#: model:ir.model,name:purchase_invoice_plan_deposit.model_purchase_invoice_plan +msgid "Invoice Planning Detail" +msgstr "Detalle de Planificación de Facturas" + +#. module: purchase_invoice_plan_deposit +#: code:addons/purchase_invoice_plan_deposit/models/purchase.py:0 +#, python-format +msgid "Invoice plan requires deposit, please register deposit first." +msgstr "" +"El plan de facturación requiere depósito, por favor registre el depósito " +"primero." + +#. module: purchase_invoice_plan_deposit +#: model:ir.model.fields,field_description:purchase_invoice_plan_deposit.field_purchase_advance_payment_inv____last_update +#: model:ir.model.fields,field_description:purchase_invoice_plan_deposit.field_purchase_create_invoice_plan____last_update +#: model:ir.model.fields,field_description:purchase_invoice_plan_deposit.field_purchase_invoice_plan____last_update +#: model:ir.model.fields,field_description:purchase_invoice_plan_deposit.field_purchase_make_planned_invoice____last_update +#: model:ir.model.fields,field_description:purchase_invoice_plan_deposit.field_purchase_order____last_update +msgid "Last Modified on" +msgstr "Última Modificación el" + +#. module: purchase_invoice_plan_deposit +#: model:ir.model.fields,field_description:purchase_invoice_plan_deposit.field_purchase_order__need_advance +msgid "Need Advance" +msgstr "Necesita Anticipo" + +#. module: purchase_invoice_plan_deposit +#: code:addons/purchase_invoice_plan_deposit/models/purchase.py:0 +#, python-format +msgid "Only installment 0 can be of type 'Deposit'" +msgstr "Sólo el plazo 0 puede ser de tipo \"Depósito\"" + +#. module: purchase_invoice_plan_deposit +#: model:ir.model.fields,field_description:purchase_invoice_plan_deposit.field_purchase_create_invoice_plan__advance_percent +msgid "Percent Deposit" +msgstr "Porcentaje Depósito" + +#. module: purchase_invoice_plan_deposit +#: model:ir.model,name:purchase_invoice_plan_deposit.model_purchase_advance_payment_inv +msgid "Purchase Advance Payment Invoice" +msgstr "Factura de Pago Anticipado de Compra" + +#. module: purchase_invoice_plan_deposit +#: model:ir.model,name:purchase_invoice_plan_deposit.model_purchase_order +msgid "Purchase Order" +msgstr "Orden de Compra" + +#. module: purchase_invoice_plan_deposit +#: model_terms:ir.ui.view,arch_db:purchase_invoice_plan_deposit.view_purchase_order_form_inherit +msgid "Register Deposit" +msgstr "Registrar Depósito" + +#. module: purchase_invoice_plan_deposit +#: model:ir.model.fields,help:purchase_invoice_plan_deposit.field_purchase_order__need_advance +msgid "True if use 1st deposit invoice plan, but no deposit yet" +msgstr "" +"Verdadero si utiliza el plan de factura de primer depósito, pero aún no ha " +"realizado ningún depósito" + +#. module: purchase_invoice_plan_deposit +#: model:ir.model.fields,field_description:purchase_invoice_plan_deposit.field_purchase_invoice_plan__invoice_type +msgid "Type" +msgstr "Tipo" + +#. module: purchase_invoice_plan_deposit +#: model:ir.model,name:purchase_invoice_plan_deposit.model_purchase_make_planned_invoice +msgid "Wizard when create invoice by plan" +msgstr "Asistente al crear factura por plan" diff --git a/purchase_invoice_plan_deposit/i18n/it.po b/purchase_invoice_plan_deposit/i18n/it.po new file mode 100644 index 00000000000..5c8a1946e31 --- /dev/null +++ b/purchase_invoice_plan_deposit/i18n/it.po @@ -0,0 +1,121 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * purchase_invoice_plan_deposit +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 14.0\n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: 2025-05-21 08:28+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_plan_deposit +#: model:ir.model.fields.selection,name:purchase_invoice_plan_deposit.selection__purchase_invoice_plan__invoice_type__advance +msgid "Deposit" +msgstr "" + +#. module: purchase_invoice_plan_deposit +#: model:ir.model.fields,field_description:purchase_invoice_plan_deposit.field_purchase_create_invoice_plan__advance +msgid "Deposit on 1st Invoice" +msgstr "" + +#. module: purchase_invoice_plan_deposit +#: model:ir.model.fields,field_description:purchase_invoice_plan_deposit.field_purchase_advance_payment_inv__display_name +#: model:ir.model.fields,field_description:purchase_invoice_plan_deposit.field_purchase_create_invoice_plan__display_name +#: model:ir.model.fields,field_description:purchase_invoice_plan_deposit.field_purchase_invoice_plan__display_name +#: model:ir.model.fields,field_description:purchase_invoice_plan_deposit.field_purchase_make_planned_invoice__display_name +#: model:ir.model.fields,field_description:purchase_invoice_plan_deposit.field_purchase_order__display_name +msgid "Display Name" +msgstr "" + +#. module: purchase_invoice_plan_deposit +#: model:ir.model,name:purchase_invoice_plan_deposit.model_purchase_create_invoice_plan +msgid "Fillig invoice planning criteria" +msgstr "" + +#. module: purchase_invoice_plan_deposit +#: model:ir.model.fields,field_description:purchase_invoice_plan_deposit.field_purchase_advance_payment_inv__id +#: model:ir.model.fields,field_description:purchase_invoice_plan_deposit.field_purchase_create_invoice_plan__id +#: model:ir.model.fields,field_description:purchase_invoice_plan_deposit.field_purchase_invoice_plan__id +#: model:ir.model.fields,field_description:purchase_invoice_plan_deposit.field_purchase_make_planned_invoice__id +#: model:ir.model.fields,field_description:purchase_invoice_plan_deposit.field_purchase_order__id +msgid "ID" +msgstr "ID" + +#. module: purchase_invoice_plan_deposit +#: model:ir.actions.act_window,name:purchase_invoice_plan_deposit.action_view_purchase_advance_payment_inv_highlight +msgid "Invoice Order" +msgstr "" + +#. module: purchase_invoice_plan_deposit +#: model:ir.model,name:purchase_invoice_plan_deposit.model_purchase_invoice_plan +msgid "Invoice Planning Detail" +msgstr "" + +#. module: purchase_invoice_plan_deposit +#: code:addons/purchase_invoice_plan_deposit/models/purchase.py:0 +#, python-format +msgid "Invoice plan requires deposit, please register deposit first." +msgstr "" + +#. module: purchase_invoice_plan_deposit +#: model:ir.model.fields,field_description:purchase_invoice_plan_deposit.field_purchase_advance_payment_inv____last_update +#: model:ir.model.fields,field_description:purchase_invoice_plan_deposit.field_purchase_create_invoice_plan____last_update +#: model:ir.model.fields,field_description:purchase_invoice_plan_deposit.field_purchase_invoice_plan____last_update +#: model:ir.model.fields,field_description:purchase_invoice_plan_deposit.field_purchase_make_planned_invoice____last_update +#: model:ir.model.fields,field_description:purchase_invoice_plan_deposit.field_purchase_order____last_update +msgid "Last Modified on" +msgstr "" + +#. module: purchase_invoice_plan_deposit +#: model:ir.model.fields,field_description:purchase_invoice_plan_deposit.field_purchase_order__need_advance +msgid "Need Advance" +msgstr "" + +#. module: purchase_invoice_plan_deposit +#: code:addons/purchase_invoice_plan_deposit/models/purchase.py:0 +#, python-format +msgid "Only installment 0 can be of type 'Deposit'" +msgstr "" + +#. module: purchase_invoice_plan_deposit +#: model:ir.model.fields,field_description:purchase_invoice_plan_deposit.field_purchase_create_invoice_plan__advance_percent +msgid "Percent Deposit" +msgstr "" + +#. module: purchase_invoice_plan_deposit +#: model:ir.model,name:purchase_invoice_plan_deposit.model_purchase_advance_payment_inv +msgid "Purchase Advance Payment Invoice" +msgstr "" + +#. module: purchase_invoice_plan_deposit +#: model:ir.model,name:purchase_invoice_plan_deposit.model_purchase_order +msgid "Purchase Order" +msgstr "" + +#. module: purchase_invoice_plan_deposit +#: model_terms:ir.ui.view,arch_db:purchase_invoice_plan_deposit.view_purchase_order_form_inherit +msgid "Register Deposit" +msgstr "" + +#. module: purchase_invoice_plan_deposit +#: model:ir.model.fields,help:purchase_invoice_plan_deposit.field_purchase_order__need_advance +msgid "True if use 1st deposit invoice plan, but no deposit yet" +msgstr "" + +#. module: purchase_invoice_plan_deposit +#: model:ir.model.fields,field_description:purchase_invoice_plan_deposit.field_purchase_invoice_plan__invoice_type +msgid "Type" +msgstr "" + +#. module: purchase_invoice_plan_deposit +#: model:ir.model,name:purchase_invoice_plan_deposit.model_purchase_make_planned_invoice +msgid "Wizard when create invoice by plan" +msgstr "" diff --git a/purchase_invoice_plan_deposit/i18n/purchase_invoice_plan_deposit.pot b/purchase_invoice_plan_deposit/i18n/purchase_invoice_plan_deposit.pot new file mode 100644 index 00000000000..daa043d2768 --- /dev/null +++ b/purchase_invoice_plan_deposit/i18n/purchase_invoice_plan_deposit.pot @@ -0,0 +1,118 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * purchase_invoice_plan_deposit +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 14.0\n" +"Report-Msgid-Bugs-To: \n" +"Last-Translator: \n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: \n" + +#. module: purchase_invoice_plan_deposit +#: model:ir.model.fields.selection,name:purchase_invoice_plan_deposit.selection__purchase_invoice_plan__invoice_type__advance +msgid "Deposit" +msgstr "" + +#. module: purchase_invoice_plan_deposit +#: model:ir.model.fields,field_description:purchase_invoice_plan_deposit.field_purchase_create_invoice_plan__advance +msgid "Deposit on 1st Invoice" +msgstr "" + +#. module: purchase_invoice_plan_deposit +#: model:ir.model.fields,field_description:purchase_invoice_plan_deposit.field_purchase_advance_payment_inv__display_name +#: model:ir.model.fields,field_description:purchase_invoice_plan_deposit.field_purchase_create_invoice_plan__display_name +#: model:ir.model.fields,field_description:purchase_invoice_plan_deposit.field_purchase_invoice_plan__display_name +#: model:ir.model.fields,field_description:purchase_invoice_plan_deposit.field_purchase_make_planned_invoice__display_name +#: model:ir.model.fields,field_description:purchase_invoice_plan_deposit.field_purchase_order__display_name +msgid "Display Name" +msgstr "" + +#. module: purchase_invoice_plan_deposit +#: model:ir.model,name:purchase_invoice_plan_deposit.model_purchase_create_invoice_plan +msgid "Fillig invoice planning criteria" +msgstr "" + +#. module: purchase_invoice_plan_deposit +#: model:ir.model.fields,field_description:purchase_invoice_plan_deposit.field_purchase_advance_payment_inv__id +#: model:ir.model.fields,field_description:purchase_invoice_plan_deposit.field_purchase_create_invoice_plan__id +#: model:ir.model.fields,field_description:purchase_invoice_plan_deposit.field_purchase_invoice_plan__id +#: model:ir.model.fields,field_description:purchase_invoice_plan_deposit.field_purchase_make_planned_invoice__id +#: model:ir.model.fields,field_description:purchase_invoice_plan_deposit.field_purchase_order__id +msgid "ID" +msgstr "" + +#. module: purchase_invoice_plan_deposit +#: model:ir.actions.act_window,name:purchase_invoice_plan_deposit.action_view_purchase_advance_payment_inv_highlight +msgid "Invoice Order" +msgstr "" + +#. module: purchase_invoice_plan_deposit +#: model:ir.model,name:purchase_invoice_plan_deposit.model_purchase_invoice_plan +msgid "Invoice Planning Detail" +msgstr "" + +#. module: purchase_invoice_plan_deposit +#: code:addons/purchase_invoice_plan_deposit/models/purchase.py:0 +#, python-format +msgid "Invoice plan requires deposit, please register deposit first." +msgstr "" + +#. module: purchase_invoice_plan_deposit +#: model:ir.model.fields,field_description:purchase_invoice_plan_deposit.field_purchase_advance_payment_inv____last_update +#: model:ir.model.fields,field_description:purchase_invoice_plan_deposit.field_purchase_create_invoice_plan____last_update +#: model:ir.model.fields,field_description:purchase_invoice_plan_deposit.field_purchase_invoice_plan____last_update +#: model:ir.model.fields,field_description:purchase_invoice_plan_deposit.field_purchase_make_planned_invoice____last_update +#: model:ir.model.fields,field_description:purchase_invoice_plan_deposit.field_purchase_order____last_update +msgid "Last Modified on" +msgstr "" + +#. module: purchase_invoice_plan_deposit +#: model:ir.model.fields,field_description:purchase_invoice_plan_deposit.field_purchase_order__need_advance +msgid "Need Advance" +msgstr "" + +#. module: purchase_invoice_plan_deposit +#: code:addons/purchase_invoice_plan_deposit/models/purchase.py:0 +#, python-format +msgid "Only installment 0 can be of type 'Deposit'" +msgstr "" + +#. module: purchase_invoice_plan_deposit +#: model:ir.model.fields,field_description:purchase_invoice_plan_deposit.field_purchase_create_invoice_plan__advance_percent +msgid "Percent Deposit" +msgstr "" + +#. module: purchase_invoice_plan_deposit +#: model:ir.model,name:purchase_invoice_plan_deposit.model_purchase_advance_payment_inv +msgid "Purchase Advance Payment Invoice" +msgstr "" + +#. module: purchase_invoice_plan_deposit +#: model:ir.model,name:purchase_invoice_plan_deposit.model_purchase_order +msgid "Purchase Order" +msgstr "" + +#. module: purchase_invoice_plan_deposit +#: model_terms:ir.ui.view,arch_db:purchase_invoice_plan_deposit.view_purchase_order_form_inherit +msgid "Register Deposit" +msgstr "" + +#. module: purchase_invoice_plan_deposit +#: model:ir.model.fields,help:purchase_invoice_plan_deposit.field_purchase_order__need_advance +msgid "True if use 1st deposit invoice plan, but no deposit yet" +msgstr "" + +#. module: purchase_invoice_plan_deposit +#: model:ir.model.fields,field_description:purchase_invoice_plan_deposit.field_purchase_invoice_plan__invoice_type +msgid "Type" +msgstr "" + +#. module: purchase_invoice_plan_deposit +#: model:ir.model,name:purchase_invoice_plan_deposit.model_purchase_make_planned_invoice +msgid "Wizard when create invoice by plan" +msgstr "" diff --git a/purchase_invoice_plan_deposit/models/__init__.py b/purchase_invoice_plan_deposit/models/__init__.py new file mode 100644 index 00000000000..a2d31548aee --- /dev/null +++ b/purchase_invoice_plan_deposit/models/__init__.py @@ -0,0 +1,4 @@ +# Copyright 2021 Ecosoft Co., Ltd (http://ecosoft.co.th/) +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html) + +from . import purchase diff --git a/purchase_invoice_plan_deposit/models/purchase.py b/purchase_invoice_plan_deposit/models/purchase.py new file mode 100644 index 00000000000..183d1f22b41 --- /dev/null +++ b/purchase_invoice_plan_deposit/models/purchase.py @@ -0,0 +1,137 @@ +# Copyright 2021 Ecosoft Co., Ltd (http://ecosoft.co.th/) +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html) + +from odoo import Command, api, fields, models +from odoo.exceptions import UserError + + +class PurchaseOrder(models.Model): + _inherit = "purchase.order" + + need_advance = fields.Boolean( + compute="_compute_need_advance", + help="True if use 1st deposit invoice plan, but no deposit yet", + ) + + @api.constrains("invoice_plan_ids") + def _check_invoice_plan_ids(self): + for rec in self: + for plan in rec.invoice_plan_ids: + if plan.invoice_type == "advance" and plan.installment != 0: + raise UserError( + self.env._("Only installment 0 can be of type 'Deposit'") + ) + + def _compute_ip_invoice_plan(self): + """With case advance in place, do overwrite""" + for rec in self: + has_invoice_plan = rec.use_invoice_plan and rec.invoice_plan_ids + to_invoice = rec.invoice_plan_ids.filtered(lambda pln: not pln.invoiced) + if rec.state in ["purchase", "done"] and has_invoice_plan and to_invoice: + if rec.invoice_status == "to invoice" or ( + rec.invoice_status == "no" + and "advance" in to_invoice.mapped("invoice_type") + ): + rec.ip_invoice_plan = True + continue + rec.ip_invoice_plan = False + + def create_invoice_plan( + self, num_installment, installment_date, interval, interval_type + ): + advance = self.env.context.get("advance") + advance_percent = self.env.context.get("advance_percent") + advance_number = self.env.context.get("num_advance", 1) + advance_date = installment_date + if advance: # installment_date will be after advance_date + installment_date = self._next_date(advance_date, interval, interval_type) + # Call super to create non-advance installments + res = super().create_invoice_plan( + num_installment, installment_date, interval, interval_type + ) + # Create advance following advance number + if advance: + plan_deposit = [ + Command.create( + { + "installment": 0, + "plan_date": advance_date, + "invoice_type": "advance", + "percent": advance_percent, + } + ) + for _num in range(advance_number) + ] + self.write({"invoice_plan_ids": plan_deposit}) + return res + + @api.depends("invoice_plan_ids.invoice_type", "invoice_plan_ids.advance_created") + def _compute_need_advance(self): + for order in self: + advance = order.invoice_plan_ids.filtered_domain( + [ + ("invoice_type", "=", "advance"), + ("advance_created", "=", False), + ] + ) + order.need_advance = bool(advance) + + def action_create_invoice(self): + """If there is deposit installment, and no invoice is_deposit yet. + Give user a warning. + """ + if self.filtered("need_advance"): + raise UserError( + self.env._( + "Invoice plan requires deposit, please register deposit first." + ) + ) + return super().action_create_invoice() + + +class PurchaseInvoicePlan(models.Model): + _inherit = "purchase.invoice.plan" + _order = "installment, plan_date, id" + + invoice_type = fields.Selection( + selection_add=[("advance", "Deposit")], + ondelete={"advance": "cascade"}, + ) + advance_created = fields.Boolean() + + def _compute_to_invoice(self): + for rec in self: + rec.to_invoice = False + + advance_sorted = sorted( + self.filtered(lambda pln: pln.invoice_type == "advance"), + key=lambda pln: ( + pln.plan_date or "", + pln.id if isinstance(pln.id, int) else 0, + ), + ) + regular_sorted = list( + self.filtered(lambda pln: pln.invoice_type != "advance").sorted( + "installment" + ) + ) + for rec in advance_sorted + regular_sorted: + if rec.purchase_id.state != "purchase": + continue + if not rec.invoiced: + rec.to_invoice = True + break + + def _update_new_quantity(self, line, percent): + if line.purchase_line_id.is_deposit: # based on 1 unit + line.write({"quantity": -percent / 100}) + return + super()._update_new_quantity(line, percent) + + def _get_amount_invoice(self, invoices): + if self.invoice_type == "advance": + return super()._get_amount_invoice(invoices) + lines = invoices.mapped("invoice_line_ids").filtered( + lambda line: not line.purchase_line_id.is_deposit + ) + return sum(lines.mapped("price_subtotal")) diff --git a/purchase_invoice_plan_deposit/pyproject.toml b/purchase_invoice_plan_deposit/pyproject.toml new file mode 100644 index 00000000000..4231d0cccb3 --- /dev/null +++ b/purchase_invoice_plan_deposit/pyproject.toml @@ -0,0 +1,3 @@ +[build-system] +requires = ["whool"] +build-backend = "whool.buildapi" diff --git a/purchase_invoice_plan_deposit/readme/CONTRIBUTORS.md b/purchase_invoice_plan_deposit/readme/CONTRIBUTORS.md new file mode 100644 index 00000000000..2aa3c531c3f --- /dev/null +++ b/purchase_invoice_plan_deposit/readme/CONTRIBUTORS.md @@ -0,0 +1 @@ +- Kitti Upariphutthiphong \ diff --git a/purchase_invoice_plan_deposit/readme/DESCRIPTION.md b/purchase_invoice_plan_deposit/readme/DESCRIPTION.md new file mode 100644 index 00000000000..2ae70d96bab --- /dev/null +++ b/purchase_invoice_plan_deposit/readme/DESCRIPTION.md @@ -0,0 +1,3 @@ +This module bridge features from purchase_invoice_plan and +purchase_deposit. Now, it posssible create invoice plan with 1st invoice +as a deposit invoice. diff --git a/purchase_invoice_plan_deposit/readme/USAGE.md b/purchase_invoice_plan_deposit/readme/USAGE.md new file mode 100644 index 00000000000..97736936282 --- /dev/null +++ b/purchase_invoice_plan_deposit/readme/USAGE.md @@ -0,0 +1,11 @@ +- Create new purchase quotation as per normal process +- Select option "Use Invoice Plan", a new Invoice Plan tab will appear +- Click on "=\> Create Invoice Plan" link to open invoice planning + wizard +- Do plan for number of installment and/or **Deposit on 1st invoice**, + start date and interval +- Double check that each installment has correct plan percentage + (Deposit's plan percentage must \> 0) +- After confirm purchases order, we have new option to "Create Bill by + Plan" +- User can create only next bill or create all bills at the same time diff --git a/purchase_invoice_plan_deposit/static/description/icon.png b/purchase_invoice_plan_deposit/static/description/icon.png new file mode 100644 index 00000000000..3a0328b516c Binary files /dev/null and b/purchase_invoice_plan_deposit/static/description/icon.png differ diff --git a/purchase_invoice_plan_deposit/static/description/index.html b/purchase_invoice_plan_deposit/static/description/index.html new file mode 100644 index 00000000000..d60c0a14476 --- /dev/null +++ b/purchase_invoice_plan_deposit/static/description/index.html @@ -0,0 +1,449 @@ + + + + + +Purchase Invoice Plan - Deposit on 1st invoice + + + +
+

Purchase Invoice Plan - Deposit on 1st invoice

+ + +

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

+

This module bridge features from purchase_invoice_plan and +purchase_deposit. Now, it posssible create invoice plan with 1st invoice +as a deposit invoice.

+
+

Important

+

This is an alpha version, the data model and design can change at any time without warning. +Only for development or testing purpose, do not use in production. +More details on development status

+
+

Table of contents

+ +
+

Usage

+
    +
  • Create new purchase quotation as per normal process
  • +
  • Select option “Use Invoice Plan”, a new Invoice Plan tab will appear
  • +
  • Click on “=> Create Invoice Plan” link to open invoice planning wizard
  • +
  • Do plan for number of installment and/or Deposit on 1st invoice, +start date and interval
  • +
  • Double check that each installment has correct plan percentage +(Deposit’s plan percentage must > 0)
  • +
  • After confirm purchases order, we have new option to “Create Bill by +Plan”
  • +
  • User can create only next bill or create all bills at the same time
  • +
+
+
+

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

+
    +
  • Ecosoft
  • +
+
+
+

Contributors

+ +
+
+

Maintainers

+

This module is maintained by the OCA.

+ +Odoo Community Association + +

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

+

Current maintainers:

+

kittiu Saran440

+

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_plan_deposit/tests/__init__.py b/purchase_invoice_plan_deposit/tests/__init__.py new file mode 100644 index 00000000000..7e33a52fba6 --- /dev/null +++ b/purchase_invoice_plan_deposit/tests/__init__.py @@ -0,0 +1,3 @@ +# Copyright 2021 Ecosoft Co., Ltd (http://ecosoft.co.th/) +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html) +from . import test_purchase_invoice_plan diff --git a/purchase_invoice_plan_deposit/tests/test_purchase_invoice_plan.py b/purchase_invoice_plan_deposit/tests/test_purchase_invoice_plan.py new file mode 100644 index 00000000000..c7a5e69ece8 --- /dev/null +++ b/purchase_invoice_plan_deposit/tests/test_purchase_invoice_plan.py @@ -0,0 +1,146 @@ +# Copyright 2021 Ecosoft Co., Ltd (http://ecosoft.co.th/) +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html) + +from odoo.exceptions import UserError +from odoo.tests import Form + +from odoo.addons.purchase_deposit.tests.test_purchase_deposit import TestPurchaseDeposit +from odoo.addons.purchase_invoice_plan.tests.test_purchase_invoice_plan import ( + TestPurchaseInvoicePlan, +) + + +class TestPurchaseInvoicePlanDeposit(TestPurchaseInvoicePlan, TestPurchaseDeposit): + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.deposit_model = cls.env["purchase.advance.payment.inv"] + + def test_invoice_plan_with_advance(self): + self.test_service.purchase_method = "purchase" # invoiced by order qty + ctx = { + "active_id": self.test_po_service.id, + "active_ids": [self.test_po_service.id], + "all_remain_invoices": True, + "create_bills": True, + } + # Create purchase plan with advance + num_installment = 5 + with Form(self.PurchaseInvoicePlan) as p: + p.num_installment = num_installment + p.advance = True + purchase_plan = p.save() + purchase_plan.with_context(**ctx).purchase_create_invoice_plan() + # Check invoice plan created + self.assertTrue(self.test_po_service.invoice_plan_ids) + with self.assertRaises(UserError): + self.test_po_service.invoice_plan_ids[1].invoice_type = "advance" + self.test_po_service._check_invoice_plan_ids() + # If advance percent is not filled, show error + advance_line = self.test_po_service.invoice_plan_ids.filtered( + lambda pln: pln.invoice_type == "advance" + ) + self.assertEqual(len(advance_line), 1, "No one advance line") + # Add 10% to advance + advance_line.percent = 10 + # Confirm PO and create invoices + self.test_po_service.button_confirm() + self.assertEqual(self.test_po_service.state, "purchase") + self.assertTrue(self.test_po_service.ip_invoice_plan) + # Check there is deposit installment must register deposit first + with self.assertRaises(UserError): + self.test_po_service.action_create_invoice() + purchase_create = self.env["purchase.make.planned.invoice"].create({}) + purchase_create.with_context(**ctx).create_invoices_by_plan() + # Valid number of invoices, including advance + invoices = self.test_po_service.invoice_ids + self.assertEqual( + len(invoices), num_installment + 1, "Wrong number of invoice created" + ) + # Validate advance amount, which is 10% of purhcase order + adv_invoice = ( + invoices.mapped("invoice_line_ids") + .filtered( + lambda line: line.purchase_line_id.is_deposit and line.quantity == 1 + ) + .mapped("move_id") + ) + self.assertEqual( + adv_invoice.amount_total, + self.test_po_service.amount_total * 0.1, + "Wrong advance amount", + ) + # Valid total quantity of invoices (exclude Advance line), must be equal to 1 + quantity = sum( + invoices.mapped("invoice_line_ids") + .filtered(lambda line: line.product_id == self.test_service) + .mapped("quantity") + ) + self.assertEqual(quantity, 1, "Wrong number of total invoice quantity") + + def test_invoice_plan_with_multiple_advances(self): + """Two advance lines with different plan_dates. + to_invoice and default_get must follow (plan_date, id) order, not id alone.""" + self.test_service.purchase_method = "purchase" + ctx = { + "active_id": self.test_po_service.id, + "active_ids": [self.test_po_service.id], + } + # Create plan: 3 installments + 2 advances via wizard + with Form(self.PurchaseInvoicePlan) as p: + p.num_installment = 3 + p.advance = True + p.num_advance = 2 + p.advance_percent = 10 + purchase_plan = p.save() + purchase_plan.with_context(**ctx).purchase_create_invoice_plan() + advance_lines = self.test_po_service.invoice_plan_ids.filtered( + lambda pln: pln.invoice_type == "advance" + ) + self.assertEqual(len(advance_lines), 2) + # Change percent + advance_lines.sorted("id")[0].write({"percent": 30, "plan_date": "2026-07-01"}) + advance_lines.sorted("id")[1].write({"percent": 20, "plan_date": "2026-06-01"}) + self.test_po_service.button_confirm() + self.assertEqual(self.test_po_service.state, "purchase") + # to_invoice must be on earlier plan_date advance (20%, 2026-06-01) + adv_to_invoice = self.test_po_service.invoice_plan_ids.filtered( + lambda pln: pln.invoice_type == "advance" and pln.to_invoice + ) + self.assertEqual(len(adv_to_invoice), 1) + self.assertEqual(adv_to_invoice.percent, 20) + # default_get defaults to first-to-process advance (20%) + defaults = self.deposit_model.with_context(**ctx).default_get(["amount"]) + self.assertAlmostEqual(defaults.get("amount", 0), 20) + # Register first deposit (20%) + deposit_wiz = self.deposit_model.with_context(**ctx).create( + {"advance_payment_method": "percentage", "amount": 20} + ) + deposit_wiz.with_context(**ctx).create_invoices() + self.test_po_service.invoice_plan_ids.invalidate_recordset() + # First invoice = 20% of PO amount_untaxed + po_amount = self.test_po_service.amount_untaxed + invoices = self.test_po_service.invoice_ids + self.assertEqual(len(invoices), 1) + self.assertAlmostEqual(invoices.amount_untaxed, po_amount * 0.20, places=2) + self.assertTrue(self.test_po_service.need_advance) + # to_invoice shifts to 30% advance + adv_to_invoice2 = self.test_po_service.invoice_plan_ids.filtered( + lambda pln: pln.invoice_type == "advance" and pln.to_invoice + ) + self.assertEqual(len(adv_to_invoice2), 1) + self.assertEqual(adv_to_invoice2.percent, 30) + # default_get now returns 30% + defaults2 = self.deposit_model.with_context(**ctx).default_get(["amount"]) + self.assertAlmostEqual(defaults2.get("amount", 0), 30) + # Register second deposit (30%) + deposit_wiz2 = self.deposit_model.with_context(**ctx).create( + {"advance_payment_method": "percentage", "amount": 30} + ) + deposit_wiz2.with_context(**ctx).create_invoices() + self.test_po_service.invoice_plan_ids.invalidate_recordset() + # Both deposits done: need_advance False, 2 invoices total + self.assertFalse(self.test_po_service.need_advance) + second_inv = self.test_po_service.invoice_ids - invoices + self.assertEqual(len(self.test_po_service.invoice_ids), 2) + self.assertAlmostEqual(second_inv.amount_untaxed, po_amount * 0.30, places=2) diff --git a/purchase_invoice_plan_deposit/views/purchase_view.xml b/purchase_invoice_plan_deposit/views/purchase_view.xml new file mode 100644 index 00000000000..34007d15459 --- /dev/null +++ b/purchase_invoice_plan_deposit/views/purchase_view.xml @@ -0,0 +1,40 @@ + + + Invoice Order + ir.actions.act_window + purchase.advance.payment.inv + form + new + + + view.purchase.order.inherit + purchase.order + + + + + + + + + diff --git a/purchase_invoice_plan_deposit/wizard/__init__.py b/purchase_invoice_plan_deposit/wizard/__init__.py new file mode 100644 index 00000000000..7bd8436bdbb --- /dev/null +++ b/purchase_invoice_plan_deposit/wizard/__init__.py @@ -0,0 +1,5 @@ +# Copyright 2019 Ecosoft Co., Ltd (http://ecosoft.co.th/) +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html) +from . import purchase_create_invoice_plan +from . import purchase_make_invoice_advance +from . import purchase_make_planned_invoice diff --git a/purchase_invoice_plan_deposit/wizard/purchase_create_invoice_plan.py b/purchase_invoice_plan_deposit/wizard/purchase_create_invoice_plan.py new file mode 100644 index 00000000000..fe9da3e4e9a --- /dev/null +++ b/purchase_invoice_plan_deposit/wizard/purchase_create_invoice_plan.py @@ -0,0 +1,31 @@ +# Copyright 2021 Ecosoft Co., Ltd (http://ecosoft.co.th/) +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html) + +from odoo import fields, models + + +class PurchaseCreateInvoicePlan(models.TransientModel): + _inherit = "purchase.create.invoice.plan" + + advance = fields.Boolean( + string="Deposit on 1st Invoice", + default=False, + ) + num_advance = fields.Integer( + string="Number of Advance", + default=1, + required=True, + ) + advance_percent = fields.Float( + string="Percent Deposit", + default=1, + required=True, + ) + + def purchase_create_invoice_plan(self): + self = self.with_context( + advance=self.advance, + advance_percent=self.advance_percent, + num_advance=self.num_advance, + ) + return super().purchase_create_invoice_plan() diff --git a/purchase_invoice_plan_deposit/wizard/purchase_create_invoice_plan_view.xml b/purchase_invoice_plan_deposit/wizard/purchase_create_invoice_plan_view.xml new file mode 100644 index 00000000000..ebc3107e9c0 --- /dev/null +++ b/purchase_invoice_plan_deposit/wizard/purchase_create_invoice_plan_view.xml @@ -0,0 +1,26 @@ + + + + Create Invoice Plan + purchase.create.invoice.plan + + + + + diff --git a/purchase_invoice_plan_deposit/wizard/purchase_make_invoice_advance.py b/purchase_invoice_plan_deposit/wizard/purchase_make_invoice_advance.py new file mode 100644 index 00000000000..d315d06343e --- /dev/null +++ b/purchase_invoice_plan_deposit/wizard/purchase_make_invoice_advance.py @@ -0,0 +1,57 @@ +# Copyright 2021 Ecosoft Co., Ltd (http://ecosoft.co.th/) +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html) + +from odoo import api, models + + +class PurchaseAdvancePaymentInv(models.TransientModel): + _inherit = "purchase.advance.payment.inv" + + def _create_invoice(self, order, po_line, amount): + invoice = super()._create_invoice(order, po_line, amount) + invoice_plan_id = self._context.get("invoice_plan_id") + if invoice_plan_id: + plan = self.env["purchase.invoice.plan"].browse(invoice_plan_id) + plan.invoice_ids += invoice + invoice.sudo().write( + { + "date": plan.plan_date, + "invoice_date": plan.plan_date, + } + ) + return invoice + + @api.model + def default_get(self, fields_list): + res = super().default_get(fields_list) + order = self.env["purchase.order"].browse(self.env.context.get("active_id")) + if order.use_invoice_plan: + advance_list = sorted( + order.invoice_plan_ids.filtered( + lambda pln: pln.installment == 0 and not pln.advance_created + ), + key=lambda pln: (pln.plan_date, pln.id), + ) + if advance_list: + res["amount"] = advance_list[0].percent + return res + + def create_invoices(self): + order = self.env["purchase.order"].browse(self.env.context.get("active_id")) + if order.use_invoice_plan: + # Filter and sort plan_advance records by Plan Date and ID + plan_advance = sorted( + order.invoice_plan_ids.filtered( + lambda pln: pln.installment == 0 and not pln.advance_created + ), + key=lambda pln: (pln.plan_date, pln.id), + ) + # Retrieve the first advance record, if any + advance = next((rec for rec in plan_advance), False) + if advance: + advance.write({"advance_created": True}) + return super( + PurchaseAdvancePaymentInv, + self.with_context(invoice_plan_id=advance.id), + ).create_invoices() + return super().create_invoices() diff --git a/purchase_invoice_plan_deposit/wizard/purchase_make_planned_invoice.py b/purchase_invoice_plan_deposit/wizard/purchase_make_planned_invoice.py new file mode 100644 index 00000000000..e1d4da8fd42 --- /dev/null +++ b/purchase_invoice_plan_deposit/wizard/purchase_make_planned_invoice.py @@ -0,0 +1,28 @@ +# Copyright 2021 Ecosoft Co., Ltd (http://ecosoft.co.th/) +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html) + +from odoo import models + + +class PurchaseAdvancePaymentInv(models.TransientModel): + _inherit = "purchase.make.planned.invoice" + + def create_invoices_by_plan(self): + # Create advance, if any + purchase = self.env["purchase.order"].browse(self._context.get("active_id")) + purchase.ensure_one() + plan_advance = purchase.invoice_plan_ids.filtered( + lambda pln: pln.to_invoice and pln.invoice_type == "advance" + ) + if plan_advance: # Create advance invoice using percentage + MakeInvoice = self.env["purchase.advance.payment.inv"] + makeinv_wizard = { + "advance_payment_method": "percentage", + "amount": plan_advance.percent, + } + makeinvoice = MakeInvoice.create(makeinv_wizard) + makeinvoice.with_context(invoice_plan_id=plan_advance.id).create_invoices() + # Create non-advance invoices + if self._context.get("all_remain_invoices") or not plan_advance: + return super().create_invoices_by_plan() + return {"type": "ir.actions.act_window_close"}