Skip to content

Commit 1962803

Browse files
committed
[ADD] account_invoice_move_currency: re-add module
X-original-commit: ef35f5a
1 parent 6fc288b commit 1962803

10 files changed

Lines changed: 262 additions & 0 deletions

File tree

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
# Account Invoice Move Currency
2+
3+
This module extends Odoo's accounting functionality to enable dual-currency tracking on invoices. It allows storing invoice values in a secondary currency alongside the primary invoice currency, providing better visibility for businesses operating in multi-currency environments.
4+
5+
## Features
6+
7+
- **Secondary Currency Tracking**: Add a secondary currency to invoices to track values in an additional currency
8+
- **Custom Exchange Rates**: Define specific exchange rates for the secondary currency independently from the primary currency
9+
- **Real-time Rate Calculation**: Automatically calculate exchange rates based on company currency and selected secondary currency
10+
- **Currency Change Integration**: Optionally save the original currency as secondary when changing invoice currency
11+
- **Multi-currency Validation**: Prevent conflicts between primary and secondary currencies with built-in validation rules
12+
- **Reporting Support**: Display secondary currency values on invoice reports
13+
14+
## Configuration
15+
16+
### Prerequisites
17+
18+
- Ensure currency rates are configured under Accounting > Configuration > Currencies
19+
20+
### Installation
21+
22+
1. Install the module from the Apps menu
23+
2. The module will automatically extend invoice forms with secondary currency fields
24+
25+
## Usage
26+
27+
### Adding a Secondary Currency to an Invoice
28+
29+
1. Navigate to Accounting > Customers > Invoices (or Vendors > Bills)
30+
2. Open a draft invoice
31+
3. In the invoice form, locate the **Secondary Currency** field (visible only with multi-currency enabled)
32+
4. Select a secondary currency from the dropdown
33+
5. The **Account Move Secondary Currency Rate** will be automatically calculated
34+
6. You can manually adjust the rate if needed
35+
7. Save the invoice
36+
37+
### Important Constraints
38+
39+
- **Same Currency Restriction**: The secondary currency cannot be the same as the invoice currency
40+
- **Company Currency Requirement**: Secondary currency can only be used when the invoice currency matches the company currency
41+
- If these conditions are not met, the system will display a validation error
42+
43+
### Changing Invoice Currency
44+
45+
When using the "Change Currency" wizard:
46+
47+
1. Open a draft invoice
48+
2. Use the currency change wizard (available from `account_ux` module)
49+
3. Select the new currency
50+
4. Check the **Save in secondary currency?** option if you want to preserve the original currency as the secondary currency
51+
5. When changing to the company currency, this option allows tracking the original currency values
52+
6. Confirm the change
53+
54+
### Example Use Case
55+
56+
**Scenario**: A company operates in USD but wants to track invoice values in EUR for reporting purposes.
57+
58+
1. Create an invoice with currency = USD (company currency)
59+
2. Set Secondary Currency = EUR
60+
3. The system calculates the EUR rate automatically (or enter manually)
61+
4. The invoice values are now tracked in both USD (primary) and EUR (secondary)
62+
5. The secondary currency information is available for reporting and analysis
63+
64+
## Technical Details
65+
66+
### Dependencies
67+
68+
- `account_ux`: Provides extended accounting functionality and the currency change wizard
69+
70+
### Models Extended
71+
72+
#### account.move
73+
74+
**New Fields:**
75+
- `move_currency_id`: Many2one field to store the secondary currency
76+
- `move_inverse_currency_rate`: Float field (16,4 digits) to store the exchange rate
77+
78+
**Methods:**
79+
- `change_move_currency()`: Onchange method that calculates the exchange rate when secondary currency is selected
80+
- `check_move_currency()`: Constraint method that validates currency selection rules
81+
82+
#### account.change.currency (TransientModel)
83+
84+
**New Fields:**
85+
- `save_secondary_currency`: Boolean field to enable saving original currency as secondary
86+
- `same_currency`: Computed field to determine if target currency matches company currency
87+
- `currency_company_id`: Related field for company currency
88+
89+
**Methods:**
90+
- `_compute_same_currency()`: Computes whether the target currency is the company currency
91+
- `change_currency()`: Extended to handle secondary currency preservation logic
92+
93+
### Views
94+
95+
- **Invoice Form View**: Adds secondary currency fields to the invoice header (visible only for invoices/bills in multi-currency setups)
96+
- **Change Currency Wizard**: Adds checkbox to save original currency as secondary
97+
- **Invoice Report**: Shows secondary currency information on printed invoices
98+
99+
### Security
100+
101+
No additional security groups or access rights are required beyond standard Odoo accounting permissions.
102+
103+
## Known Issues / Limitations
104+
105+
- Secondary currency is only available when the invoice currency is the same as the company currency
106+
- The secondary currency and invoice currency cannot be the same
107+
- Exchange rate modifications are only allowed in draft state
108+
109+
## Bug Tracker
110+
111+
Bugs are tracked on [GitHub Issues](https://github.com/ingadhoc/account-invoicing/issues). In case of trouble, please check there if your issue has already been reported.
112+
113+
## Credits
114+
115+
### Authors
116+
117+
* ADHOC SA
118+
119+
### Contributors
120+
121+
* ADHOC SA <info@adhoc.com.ar>
122+
123+
### Maintainers
124+
125+
This module is maintained by ADHOC SA.
126+
127+
To learn more about ADHOC SA, visit [www.adhoc.com.ar](http://www.adhoc.com.ar).
128+
129+
## License
130+
131+
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
132+
133+
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
134+
135+
You should have received a copy of the GNU Affero General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
from . import models
2+
from . import wizards
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
{
2+
"name": "Account Invoice Move Currency",
3+
"version": "18.0.1.0.0",
4+
"author": "ADHOC SA",
5+
"category": "Accounting & Finance",
6+
"depends": ["account_ux"],
7+
"data": [
8+
"views/account_move_views.xml",
9+
"views/report_invoice.xml",
10+
"wizards/account_change_currency_views.xml",
11+
],
12+
"website": "www.adhoc.com.ar",
13+
"license": "AGPL-3",
14+
"installable": True,
15+
"auto_install": False,
16+
"application": False,
17+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
from . import account_move
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
from odoo import _, api, fields, models
2+
from odoo.exceptions import UserError
3+
4+
5+
class AccountMove(models.Model):
6+
_inherit = "account.move"
7+
8+
move_currency_id = fields.Many2one(
9+
"res.currency",
10+
"Secondary Currency",
11+
readonly=True,
12+
help="If you set a currency here, then this invoice values will be also stored in the related Account Move Secondary Currency",
13+
)
14+
15+
move_inverse_currency_rate = fields.Float(
16+
digits=(16, 4), string="Account Move Secondary Currency Rate", readonly=True
17+
)
18+
19+
@api.onchange("move_currency_id")
20+
def change_move_currency(self):
21+
if not self.move_currency_id:
22+
self.move_inverse_currency_rate = False
23+
else:
24+
self.move_inverse_currency_rate = self.move_currency_id._convert(
25+
1.0, self.company_id.currency_id, self.company_id, self.invoice_date or fields.Date.context_today(self)
26+
)
27+
28+
@api.constrains("move_currency_id", "currency_id")
29+
def check_move_currency(self):
30+
for rec in self.filtered("move_currency_id"):
31+
if rec.move_currency_id == rec.currency_id:
32+
raise UserError(_("Secondary currency can not be the same as Invoice Currency"))
33+
if rec.currency_id != rec.company_id.currency_id:
34+
raise UserError(
35+
_("Can not use Secondary currency if invoice is in a Currency different from Company Currency")
36+
)
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<odoo>
3+
<record model="ir.ui.view" id="view_move_form">
4+
<field name="name">account.move.view</field>
5+
<field name="inherit_id" ref="account_ux.view_move_form"/>
6+
<field name="model">account.move</field>
7+
<field name="priority" eval="50"/>
8+
<field name="arch" type="xml">
9+
<div name="journal_div" position="after">
10+
<label for="move_currency_id" name="Move Currency" groups="base.group_multi_currency" invisible="move_type not in ('out_invoice', 'out_refund', 'in_invoice', 'in_refund')"/>
11+
<div groups="base.group_multi_currency" class="d-flex" invisible="move_type not in ('out_invoice', 'out_refund', 'in_invoice', 'in_refund')">
12+
<field name="move_currency_id" options="{'no_create': True, 'no_open': True}" class="oe_inline" readonly="state != 'draft'"/>
13+
<field name="move_inverse_currency_rate" class="oe_inline" placeholder="Rate..." invisible="not move_currency_id" readonly="state != 'draft'" required="move_currency_id"/>
14+
</div>
15+
</div>
16+
</field>
17+
</record>
18+
</odoo>
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<odoo>
2+
<template id="report_invoice_document" inherit_id="account.report_invoice_document">
3+
<div name="comment" position="before">
4+
<p t-if="o._fields.get('move_currency_id') and o.move_currency_id and o.move_inverse_currency_rate &gt; 0.0">
5+
El total de este comprobante equivale a un total de <span t-out="o.amount_total / o.move_inverse_currency_rate" t-options="{'widget': 'monetary', 'display_currency': o.move_currency_id}"/> a un tipo de cambio consignado de <span t-field="o.move_inverse_currency_rate"/>
6+
</p>
7+
</div>
8+
</template>
9+
</odoo>
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
from . import account_change_currency
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
from odoo import api, fields, models
2+
3+
4+
class AccountChangeCurrency(models.TransientModel):
5+
_inherit = "account.change.currency"
6+
7+
save_secondary_currency = fields.Boolean("Save in secondary currency?")
8+
same_currency = fields.Boolean(compute="_compute_same_currency")
9+
currency_company_id = fields.Many2one("res.currency", related="move_id.company_id.currency_id", store=True)
10+
11+
@api.depends("currency_company_id", "currency_to_id")
12+
def _compute_same_currency(self):
13+
for rec in self:
14+
if rec.currency_company_id == rec.currency_to_id:
15+
rec.same_currency = True
16+
else:
17+
rec.same_currency = False
18+
19+
def change_currency(self):
20+
# We set it false because if you change the currency to
21+
# the same as the secondary currency they can not be the same
22+
if self.move_id.move_currency_id == self.currency_to_id:
23+
self.move_id.move_currency_id = False
24+
self.move_id.move_inverse_currency_rate = False
25+
currency_from_id = self.currency_from_id
26+
res = super().change_currency()
27+
if self.save_secondary_currency and self.same_currency:
28+
self.move_id.move_currency_id = currency_from_id.id
29+
self.move_id.move_inverse_currency_rate = self.conversion_rate
30+
return res
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<odoo>
3+
<record id="view_account_change_currency" model="ir.ui.view">
4+
<field name="name">Change Currency inherit</field>
5+
<field name="model">account.change.currency</field>
6+
<field name="inherit_id" ref="account_ux.view_account_change_currency"/>
7+
<field name="arch" type="xml">
8+
<field name="conversion_rate" position="after">
9+
<field name="save_secondary_currency" invisible="not same_currency"/>
10+
</field>
11+
</field>
12+
</record>
13+
</odoo>

0 commit comments

Comments
 (0)