diff --git a/purchase_deposit/README.rst b/purchase_deposit/README.rst index d38e2ee336f..57ce9d2c4bc 100644 --- a/purchase_deposit/README.rst +++ b/purchase_deposit/README.rst @@ -1,7 +1,3 @@ -.. image:: https://odoo-community.org/readme-banner-image - :target: https://odoo-community.org/get-involved?utm_source=readme - :alt: Odoo Community Association - ================ Purchase Deposit ================ @@ -17,7 +13,7 @@ Purchase Deposit .. |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 +.. |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 @@ -87,6 +83,8 @@ Contributors - Aung Ko Ko Lin +- Saran Lim. + Maintainers ----------- diff --git a/purchase_deposit/__manifest__.py b/purchase_deposit/__manifest__.py index e6c276a577b..0840de54338 100644 --- a/purchase_deposit/__manifest__.py +++ b/purchase_deposit/__manifest__.py @@ -14,6 +14,7 @@ "data": [ "security/ir.model.access.csv", "wizard/purchase_make_invoice_advance_views.xml", + "wizard/purchase_advance_deduct_option_views.xml", "views/res_config_settings_views.xml", "views/purchase_view.xml", ], diff --git a/purchase_deposit/models/account_move.py b/purchase_deposit/models/account_move.py index 6a39ac39b41..fd099e2f3f1 100644 --- a/purchase_deposit/models/account_move.py +++ b/purchase_deposit/models/account_move.py @@ -1,7 +1,7 @@ # Copyright 2023 Quartile Limited (https://www.quartile.co) # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). -from odoo import models +from odoo import api, models class AccountMove(models.Model): @@ -15,3 +15,20 @@ def action_post(self): line.purchase_line_id.taxes_id = line.tax_ids line.purchase_line_id.price_unit = line.price_unit return res + + @api.model_create_multi + def create(self, vals_list): + moves = super().create(vals_list) + if self.env.context.get("advance_deduct_option") == "proportional": + for move in moves: + inv_lines = move.invoice_line_ids.filtered(lambda x: x.quantity > 0) + adv_lines = move.invoice_line_ids.filtered(lambda x: x.quantity < 0) + inv_untaxed = sum(inv_lines.mapped("price_subtotal")) + purchases = inv_lines.mapped("purchase_line_id.order_id") + if purchases: + prop = inv_untaxed / purchases.ensure_one().amount_untaxed + for line in adv_lines: + line.with_context(check_move_validity=False).write( + {"quantity": max(-prop, line.quantity)} + ) + return moves diff --git a/purchase_deposit/models/purchase.py b/purchase_deposit/models/purchase.py index 85976b1d9f1..53b5b13947f 100644 --- a/purchase_deposit/models/purchase.py +++ b/purchase_deposit/models/purchase.py @@ -18,6 +18,21 @@ def copy_data(self, default=None): ] return super().copy_data(default) + def action_create_invoice(self): + has_deposit = len(self.filtered("order_line.is_deposit")) > 0 + if not has_deposit or self.env.context.get("advance_deduct_option"): + return super().action_create_invoice() + wizard = self.env.ref("purchase_deposit.view_purchase_advance_deduct_option") + return { + "name": self.env._("Advance/Deposit Deduction Option"), + "type": "ir.actions.act_window", + "view_mode": "form", + "res_model": "purchase.advance.deduct.option", + "views": [(wizard.id, "form")], + "view_id": wizard.id, + "target": "new", + } + class PurchaseOrderLine(models.Model): _inherit = "purchase.order.line" diff --git a/purchase_deposit/readme/CONTRIBUTORS.md b/purchase_deposit/readme/CONTRIBUTORS.md index 281706e7123..bdfd7b83765 100644 --- a/purchase_deposit/readme/CONTRIBUTORS.md +++ b/purchase_deposit/readme/CONTRIBUTORS.md @@ -4,3 +4,4 @@ - Joan Mateu \<\> - [Quartile](https://www.quartile.co): - Aung Ko Ko Lin +- Saran Lim. \<\> diff --git a/purchase_deposit/security/ir.model.access.csv b/purchase_deposit/security/ir.model.access.csv index 1bba833496a..dce18df8b61 100644 --- a/purchase_deposit/security/ir.model.access.csv +++ b/purchase_deposit/security/ir.model.access.csv @@ -1,2 +1,3 @@ id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink access_purchase_advance_payment_inv,access_purchase_advance_payment_inv,model_purchase_advance_payment_inv,base.group_user,1,1,1,1 +access_purchase_advance_deduct_option,access_purchase_advance_deduct_option,model_purchase_advance_deduct_option,base.group_user,1,1,1,1 diff --git a/purchase_deposit/static/description/index.html b/purchase_deposit/static/description/index.html index 33a34035165..092bdb7dfeb 100644 --- a/purchase_deposit/static/description/index.html +++ b/purchase_deposit/static/description/index.html @@ -3,7 +3,7 @@ -README.rst +Purchase Deposit -
+
+

Purchase Deposit

- - -Odoo Community Association - -
-

Purchase Deposit

-

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

+

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

This module allow purchase order to register deposit similar to that in sales order

Table of contents

@@ -391,7 +386,7 @@

Purchase Deposit

-

Usage

+

Usage

When purchase order is confirmed, a new button “Register Deposit” will appear. Normally, deposit will be used to create the 1st bill (as deposit).

@@ -407,7 +402,7 @@

Usage

deducted.

-

Bug Tracker

+

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 @@ -415,16 +410,16 @@

Bug Tracker

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

-

Credits

+

Credits

-

Authors

+

Authors

  • Elico Corp
  • Ecosoft
-

Contributors

+

Contributors

+
  • Saran Lim. <saranl@ecosoft.co.th>
  • -

    Maintainers

    +

    Maintainers

    This module is maintained by the OCA.

    Odoo Community Association @@ -450,6 +446,5 @@

    Maintainers

    -
    diff --git a/purchase_deposit/tests/test_purchase_deposit.py b/purchase_deposit/tests/test_purchase_deposit.py index 9c57e086313..3b8da0a735e 100644 --- a/purchase_deposit/tests/test_purchase_deposit.py +++ b/purchase_deposit/tests/test_purchase_deposit.py @@ -2,7 +2,7 @@ # Copyright 2019 Ecosoft Co., Ltd., Kitti U. # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). -from odoo import fields +from odoo import Command, fields from odoo.exceptions import UserError from odoo.tests import Form, TransactionCase @@ -14,6 +14,7 @@ def setUpClass(cls): cls.product_model = cls.env["product.product"] cls.account_model = cls.env["account.account"] cls.invoice_model = cls.env["account.move"] + cls.advance_deduct_wizard = cls.env["purchase.advance.deduct.option"] # Create Deposit Account cls.account_deposit = cls.account_model.create( @@ -37,9 +38,7 @@ def setUpClass(cls): { "partner_id": cls.env.ref("base.res_partner_3").id, "order_line": [ - ( - 0, - 0, + Command.create( { "product_id": p1.id, "product_uom": p1.uom_id.id, @@ -52,6 +51,33 @@ def setUpClass(cls): ], } ) + # Create product with control policy = on received quantities + p2 = cls.product2 = cls.product_model.create( + { + "name": "Test Product 2", + "type": "service", + "default_code": "PROD2", + "purchase_method": "receive", # For testing partial return + } + ) + cls.po2 = cls.env["purchase.order"].create( + { + "partner_id": cls.env.ref("base.res_partner_3").id, + "order_line": [ + Command.create( + { + "product_id": p2.id, + "product_uom": p2.uom_id.id, + "name": p2.name, + "price_unit": 100.0, + "date_planned": fields.Datetime.now(), + "product_qty": 10, + "qty_received": 1, # Partial received + }, + ) + ], + } + ) cls.tax = cls.env["account.tax"].create( { @@ -109,8 +135,10 @@ def test_create_deposit_invoice(self): {"product_id": deposit.id, "price_unit": 420.0, "is_deposit": True}, ], ) - # On Purchase Order, create normal billing - res = self.po.with_context(create_bill=True).action_create_invoice() + # On Purchase Order, create normal billing with advance return option = Full + res = self.po.with_context( + create_bill=True, advance_deduct_option="full" + ).action_create_invoice() invoice = self.invoice_model.browse(res["res_id"]) self.assertRecordValues( invoice.invoice_line_ids, @@ -199,3 +227,65 @@ def test_deposit_invoice_update_price_and_taxes(self): deposit_line = self.po.order_line.filtered(lambda p: p.is_deposit) self.assertEqual(deposit_line.price_unit, 500.0) self.assertEqual(deposit_line.taxes_id.id, self.tax.id) + + def test_create_deposit_invoice_partial_deduct(self): + self.assertEqual(len(self.po2.order_line), 1) + # We create invoice from purchase + ctx = { + "active_id": self.po2.id, + "active_ids": [self.po2.id], + "active_model": "purchase.order", + "create_bills": True, + } + CreateDeposit = self.env["purchase.advance.payment.inv"] + self.po2.button_confirm() + with Form(CreateDeposit.with_context(**ctx)) as f: + f.advance_payment_method = "percentage" + f.deposit_account_id = self.account_deposit + wizard = f.save() + wizard.amount = 10.0 # 10% + wizard.create_invoices() + # New Purchase Deposit is created automatically + deposit = self.env.company.purchase_deposit_product_id + self.assertEqual(deposit.name, "Purchase Deposit") + # 1 Deposit Invoice is created + self.assertRecordValues( + self.po2.invoice_ids.invoice_line_ids, + [ + { + "product_id": deposit.id, + "price_unit": 100.0, + "name": "Deposit Payment", + } + ], + ) + # On Purchase Order, there will be new deposit line create + self.assertRecordValues( + self.po2.order_line, + [ + { + "product_id": self.product2.id, + "price_unit": 100.0, + "is_deposit": False, + }, + {"product_id": deposit.id, "price_unit": 100.0, "is_deposit": True}, + ], + ) + # On Purchase Order, + # create normal billing with advance return option = proportional + res = self.po2.action_create_invoice() + self.assertEqual(res["res_model"], "purchase.advance.deduct.option") + advance_deduct = self.advance_deduct_wizard.create( + {"advance_deduct_option": "proportional"} + ) + res = advance_deduct.with_context( + active_id=self.po2.id, create_bill=True + ).create_invoice() + invoice = self.invoice_model.browse(res["res_id"]) + self.assertRecordValues( + invoice.invoice_line_ids, + [ + {"product_id": self.product2.id, "price_unit": 100.0, "quantity": 1}, + {"product_id": deposit.id, "price_unit": 100.0, "quantity": -0.1}, + ], + ) diff --git a/purchase_deposit/wizard/__init__.py b/purchase_deposit/wizard/__init__.py index e49b2a25f1f..6b7922e30f9 100644 --- a/purchase_deposit/wizard/__init__.py +++ b/purchase_deposit/wizard/__init__.py @@ -3,3 +3,4 @@ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). from . import purchase_make_invoice_advance +from . import purchase_advance_deduct_option diff --git a/purchase_deposit/wizard/purchase_advance_deduct_option.py b/purchase_deposit/wizard/purchase_advance_deduct_option.py new file mode 100644 index 00000000000..d076223831e --- /dev/null +++ b/purchase_deposit/wizard/purchase_advance_deduct_option.py @@ -0,0 +1,32 @@ +# Copyright 2019 Elico Corp, Dominique K. +# Copyright 2019 Ecosoft Co., Ltd., Kitti U. +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from odoo import fields, models + + +class PurchaseAdvanceReturnOption(models.TransientModel): + _name = "purchase.advance.deduct.option" + _description = "Select how to return advance in vendor bill" + + advance_deduct_option = fields.Selection( + [ + ("proportional", "Deduct Deposit Proportionally"), + ("full", "Deduct Full Deposit (Standard)"), + ], + string="Advance/Deposit Deduction Option", + default="proportional", + required=True, + ) + + def create_invoice(self): + self.ensure_one() + order = ( + self.env["purchase.order"] + .browse(self.env.context.get("active_id")) + .with_context( + create_bill=self.env.context.get("create_bill"), + advance_deduct_option=self.advance_deduct_option, + ) + ) + return order.sudo().action_create_invoice() diff --git a/purchase_deposit/wizard/purchase_advance_deduct_option_views.xml b/purchase_deposit/wizard/purchase_advance_deduct_option_views.xml new file mode 100644 index 00000000000..b06edb27939 --- /dev/null +++ b/purchase_deposit/wizard/purchase_advance_deduct_option_views.xml @@ -0,0 +1,37 @@ + + + + Advance/Deposit Deduction Option + purchase.advance.deduct.option + +
    + + + +
    +
    +
    +
    +
    + + Advance/Deposit Deduction Option + ir.actions.act_window + purchase.advance.deduct.option + form + new + +