Maintainers
+Maintainers
This module is maintained by the OCA.
@@ -443,6 +438,5 @@ diff --git a/purchase_manual_currency/README.rst b/purchase_manual_currency/README.rst index d975270ef47..98c2936c205 100644 --- a/purchase_manual_currency/README.rst +++ b/purchase_manual_currency/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 - Manual Currency ========================== @@ -17,7 +13,7 @@ Purchase - Manual Currency .. |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 diff --git a/purchase_manual_currency/__manifest__.py b/purchase_manual_currency/__manifest__.py index 0553bc09dd8..f989a529ebe 100644 --- a/purchase_manual_currency/__manifest__.py +++ b/purchase_manual_currency/__manifest__.py @@ -3,7 +3,7 @@ { "name": "Purchase - Manual Currency", - "version": "18.0.1.0.0", + "version": "18.0.2.0.0", "category": "Purchase Management", "summary": "Allows to manual currency of Purchase", "author": "Ecosoft, Odoo Community Association (OCA)", diff --git a/purchase_manual_currency/migrations/18.0.2.0.0/post-migration.py b/purchase_manual_currency/migrations/18.0.2.0.0/post-migration.py new file mode 100644 index 00000000000..707a0b9e45a --- /dev/null +++ b/purchase_manual_currency/migrations/18.0.2.0.0/post-migration.py @@ -0,0 +1,11 @@ +# Copyright 2026 Ecosoft Co., Ltd (https://ecosoft.co.th/) +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). + +from openupgradelib import openupgrade + + +@openupgrade.migrate() +def migrate(env, version): + # Recompute + orders = env["purchase.order"].search([("manual_currency", "=", True)]) + orders._amount_all() diff --git a/purchase_manual_currency/migrations/18.0.2.0.0/pre-migration.py b/purchase_manual_currency/migrations/18.0.2.0.0/pre-migration.py new file mode 100644 index 00000000000..d2c6d1078df --- /dev/null +++ b/purchase_manual_currency/migrations/18.0.2.0.0/pre-migration.py @@ -0,0 +1,16 @@ +# Copyright 2026 Ecosoft Co., Ltd (https://ecosoft.co.th/) +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). + +from openupgradelib import openupgrade + + +@openupgrade.migrate() +def migrate(env, version): + openupgrade.logged_query( + env.cr, + """ + UPDATE purchase_order + SET currency_rate = manual_currency_rate + WHERE manual_currency = TRUE + """, + ) diff --git a/purchase_manual_currency/models/purchase.py b/purchase_manual_currency/models/purchase.py index 8e179fe6be7..513d55284f5 100644 --- a/purchase_manual_currency/models/purchase.py +++ b/purchase_manual_currency/models/purchase.py @@ -1,9 +1,7 @@ # Copyright 2020 Ecosoft Co., Ltd. (http://ecosoft.co.th) # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). -from lxml import etree - -from odoo import _, api, fields, models +from odoo import api, fields, models from odoo.exceptions import UserError, ValidationError @@ -11,64 +9,21 @@ class PurchaseOrder(models.Model): _inherit = "purchase.order" manual_currency = fields.Boolean() - is_manual = fields.Boolean(compute="_compute_currency") type_currency = fields.Selection( selection=lambda self: self._get_label_currency_name(), default=lambda self: self._get_label_currency_name()[0][0], ) - manual_currency_rate = fields.Float( - digits="Manual Currency", - tracking=True, - help="Set new currency rate to apply on the invoice\n." - "This rate will be taken in order to convert amounts between the " - "currency on the purchase order and last currency", - ) company_currency_id = fields.Many2one( comodel_name="res.currency", related="company_id.currency_id", string="Company Currency", ) - total_company_currency = fields.Monetary( - compute="_compute_total_company_currency", - store=True, - currency_field="company_currency_id", - ) - currency_diff = fields.Boolean( - compute="_compute_currency_diff", - store=True, - ) - - @api.depends("currency_id") - def _compute_currency_diff(self): - for rec in self: - rec.currency_diff = rec.company_currency_id != rec.currency_id - - @api.depends("order_line.subtotal_company_currency") - def _compute_total_company_currency(self): - """Convert total currency to company currency""" - for rec in self: - # check manual currency - if rec.manual_currency: - rate = ( - rec.manual_currency_rate - if rec.type_currency == "inverse_company_rate" - else (1.0 / rec.manual_currency_rate) - ) - rec.total_company_currency = rec.amount_total * rate - # default rate currency - else: - rec.total_company_currency = rec.currency_id._convert( - rec.amount_total, - rec.company_currency_id, - rec.company_id, - fields.Date.today(), - ) def _get_label_currency_name(self): """Get label related currency""" names = { "company_currency_name": ( - self.env["res.company"].browse(self._context.get("company_id")) + self.env["res.company"].browse(self.env.context.get("company_id")) or self.env.company ).currency_id.name, "rate_currency_name": "Currency", @@ -76,60 +31,42 @@ def _get_label_currency_name(self): return [ [ "company_rate", - _("%(rate_currency_name)s per 1 %(company_currency_name)s", **names), + self.env._( + "%(rate_currency_name)s per 1 %(company_currency_name)s", **names + ), ], [ "inverse_company_rate", - _("%(company_currency_name)s per 1 %(rate_currency_name)s", **names), + self.env._( + "%(company_currency_name)s per 1 %(rate_currency_name)s", **names + ), ], ] - @api.onchange("manual_currency", "type_currency", "currency_id", "date_order") - def _onchange_currency_change_rate(self): - today = fields.Date.today() - company_currency = self.env.company.currency_id - amount_currency = company_currency._get_conversion_rate( - company_currency, - self.currency_id, - self.company_id, - self.date_order or today, - ) - if self.type_currency == "inverse_company_rate": - amount_currency = 1.0 / amount_currency - self.manual_currency_rate = amount_currency + @api.depends( + "order_line.price_subtotal", "company_id", "currency_id", "currency_rate" + ) + def _amount_all(self): + """Add currency_rate dependency to trigger recompute""" + return super()._amount_all() - @api.depends("currency_id") - def _compute_currency(self): - for rec in self: - rec.is_manual = rec.currency_id != rec.company_id.currency_id + @api.depends( + "currency_id", "date_order", "company_id", "manual_currency", "type_currency" + ) + def _compute_currency_rate(self): + res = super()._compute_currency_rate() + for order in self: + if order.manual_currency and order.type_currency == "inverse_company_rate": + order.currency_rate = 1.0 / order.currency_rate + return res def action_refresh_currency(self): self.ensure_one() if self.state != "draft": - raise ValidationError(_("Rate currency can refresh state draft only.")) - self._onchange_currency_change_rate() - return True - - @api.model - def get_view(self, view_id=None, view_type="form", **options): - """Change string name to company currency""" - result = super().get_view(view_id=view_id, view_type=view_type, **options) - if view_type == "form": - company_currency_name = ( - self.env["res.company"].browse(self._context.get("company_id")) - or self.env.company - ).currency_id.name - doc = etree.XML(result["arch"]) - # Total company currency - node = doc.xpath("//field[@name='total_company_currency']") - if node: - node[0].set("string", f"Total ({company_currency_name})") - # Subtotal company currency - node = doc.xpath("//field[@name='subtotal_company_currency']") - if node: - node[0].set("string", f"Subtotal ({company_currency_name})") - result["arch"] = etree.tostring(doc, encoding="unicode") - return result + raise ValidationError( + self.env._("Rate currency can refresh state draft only.") + ) + return self._compute_currency_rate() def action_view_invoice(self, invoices=False): result = super().action_view_invoice(invoices) @@ -141,11 +78,13 @@ def action_view_invoice(self, invoices=False): purchases = inv.invoice_line_ids.mapped("purchase_order_id") if len(set(purchases.mapped("manual_currency"))) != 1: raise UserError( - _("In invoice cannot have a mixture of different manual currency.") + self.env._( + "In invoice cannot have a mixture of different manual currency." + ) ) - elif len(set(purchases.mapped("manual_currency_rate"))) != 1: + elif len(set(purchases.mapped("currency_rate"))) != 1: raise UserError( - _( + self.env._( "In invoice cannot have a mixture of different " "manual currency rates in purchases." ) @@ -159,7 +98,7 @@ def action_view_invoice(self, invoices=False): { "manual_currency": purchases[0].manual_currency, "type_currency": purchases[0].type_currency, - "manual_currency_rate": purchases[0].manual_currency_rate, + "manual_currency_rate": purchases[0].currency_rate, } ) return result diff --git a/purchase_manual_currency/models/purchase_order_line.py b/purchase_manual_currency/models/purchase_order_line.py index 256b476d6e5..5bac4556039 100644 --- a/purchase_manual_currency/models/purchase_order_line.py +++ b/purchase_manual_currency/models/purchase_order_line.py @@ -10,6 +10,7 @@ class PurchaseOrderLine(models.Model): company_currency_id = fields.Many2one( comodel_name="res.currency", related="order_id.company_currency_id", + store=True, string="Company Currency", ) subtotal_company_currency = fields.Monetary( @@ -21,28 +22,37 @@ class PurchaseOrderLine(models.Model): @api.depends( "price_subtotal", - "order_id.manual_currency_rate", + "order_id.currency_rate", "order_id.type_currency", "order_id.manual_currency", ) def _compute_amount_company_currency(self): - for rec in self: - rec.subtotal_company_currency = rec.price_subtotal - # check multi-currency - if rec.company_currency_id != rec.currency_id: - # check manual currency - if rec.order_id.manual_currency: - rate = ( - rec.order_id.manual_currency_rate - if rec.order_id.type_currency == "inverse_company_rate" - else (1.0 / rec.order_id.manual_currency_rate) - ) - rec.subtotal_company_currency = rec.price_subtotal * rate - # default rate currency - else: - rec.subtotal_company_currency = rec.currency_id._convert( - rec.price_subtotal, - rec.company_currency_id, - rec.company_id, - fields.Date.today(), - ) + for line in self: + line.subtotal_company_currency = line.price_subtotal + if line.company_currency_id != line.currency_id: + order = line.order_id + rate = ( + order.currency_rate + if order.type_currency == "inverse_company_rate" + else (1.0 / order.currency_rate) + ) + line.subtotal_company_currency = line.price_subtotal * rate + + def _prepare_base_line_for_taxes_computation(self): + self.ensure_one() + order = self.order_id + if order.manual_currency and order.type_currency == "inverse_company_rate": + # Convert Rate back to company_rate + rate = 1 + if order.currency_rate: + rate = 1 / order.currency_rate + return self.env["account.tax"]._prepare_base_line_for_taxes_computation( + self, + tax_ids=self.taxes_id, + quantity=self.product_qty, + partner_id=self.order_id.partner_id, + currency_id=self.order_id.currency_id + or self.order_id.company_id.currency_id, + rate=rate, + ) + return super()._prepare_base_line_for_taxes_computation() diff --git a/purchase_manual_currency/static/description/index.html b/purchase_manual_currency/static/description/index.html index 66dcdb1f219..b131da98e4d 100644 --- a/purchase_manual_currency/static/description/index.html +++ b/purchase_manual_currency/static/description/index.html @@ -3,7 +3,7 @@
-This module allows users to update the currency manual of Purchase Order in draft state.
Example, The company will make a deal with vendor before buy product @@ -395,7 +390,7 @@
To use this module, the company have to access rights Multi Currencies.
Step to used manual currency: #. Go to Invoicing > Configuration > Settings > Currencies > Multi-Currencies #. Go to Purchase > Create new @@ -403,7 +398,7 @@
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 @@ -411,15 +406,15 @@
Do not contact contributors directly about support or help with technical issues.