Skip to content

Commit a514b3d

Browse files
committed
[MIG] l10n_es_vat_prorate: Migration to 18.0
1 parent 2edd330 commit a514b3d

File tree

9 files changed

+271
-135
lines changed

9 files changed

+271
-135
lines changed

l10n_es_vat_prorate/README.rst

Lines changed: 28 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,3 @@
1-
.. image:: https://odoo-community.org/readme-banner-image
2-
:target: https://odoo-community.org/get-involved?utm_source=readme
3-
:alt: Odoo Community Association
4-
51
===============
62
Prorrata de IVA
73
===============
@@ -17,17 +13,17 @@ Prorrata de IVA
1713
.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png
1814
:target: https://odoo-community.org/page/development-status
1915
:alt: Beta
20-
.. |badge2| image:: https://img.shields.io/badge/license-AGPL--3-blue.png
16+
.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png
2117
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
2218
:alt: License: AGPL-3
2319
.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fl10n--spain-lightgray.png?logo=github
24-
:target: https://github.com/OCA/l10n-spain/tree/17.0/l10n_es_vat_prorate
20+
:target: https://github.com/OCA/l10n-spain/tree/18.0/l10n_es_vat_prorate
2521
:alt: OCA/l10n-spain
2622
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
27-
:target: https://translation.odoo-community.org/projects/l10n-spain-17-0/l10n-spain-17-0-l10n_es_vat_prorate
23+
:target: https://translation.odoo-community.org/projects/l10n-spain-18-0/l10n-spain-18-0-l10n_es_vat_prorate
2824
:alt: Translate me on Weblate
2925
.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png
30-
:target: https://runboat.odoo-community.org/builds?repo=OCA/l10n-spain&target_branch=17.0
26+
:target: https://runboat.odoo-community.org/builds?repo=OCA/l10n-spain&target_branch=18.0
3127
:alt: Try me on Runboat
3228

3329
|badge1| |badge2| |badge3| |badge4| |badge5|
@@ -70,7 +66,7 @@ Bug Tracker
7066
Bugs are tracked on `GitHub Issues <https://github.com/OCA/l10n-spain/issues>`_.
7167
In case of trouble, please check there if your issue has already been reported.
7268
If you spotted it first, help us to smash it by providing a detailed and welcomed
73-
`feedback <https://github.com/OCA/l10n-spain/issues/new?body=module:%20l10n_es_vat_prorate%0Aversion:%2017.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.
69+
`feedback <https://github.com/OCA/l10n-spain/issues/new?body=module:%20l10n_es_vat_prorate%0Aversion:%2018.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.
7470

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

@@ -97,6 +93,14 @@ Contributors
9793
- Harald Panten
9894
- Manuel Regidor
9995
- Alberto Martínez
96+
- Anxo82
97+
- ValentinVinagre
98+
99+
- `Moduon <https://www.moduon.team/>`__:
100+
101+
- Rafael Blasco
102+
- Andrii Kompaniiets
103+
- Emilio Pascual
100104

101105
Maintainers
102106
-----------
@@ -111,6 +115,20 @@ OCA, or the Odoo Community Association, is a nonprofit organization whose
111115
mission is to support the collaborative development of Odoo features and
112116
promote its widespread use.
113117

114-
This module is part of the `OCA/l10n-spain <https://github.com/OCA/l10n-spain/tree/17.0/l10n_es_vat_prorate>`_ project on GitHub.
118+
.. |maintainer-rafaelbn| image:: https://github.com/rafaelbn.png?size=40px
119+
:target: https://github.com/rafaelbn
120+
:alt: rafaelbn
121+
.. |maintainer-Andrii9090| image:: https://github.com/Andrii9090.png?size=40px
122+
:target: https://github.com/Andrii9090
123+
:alt: Andrii9090
124+
.. |maintainer-EmilioPascual| image:: https://github.com/EmilioPascual.png?size=40px
125+
:target: https://github.com/EmilioPascual
126+
:alt: EmilioPascual
127+
128+
Current `maintainers <https://odoo-community.org/page/maintainer-role>`__:
129+
130+
|maintainer-rafaelbn| |maintainer-Andrii9090| |maintainer-EmilioPascual|
131+
132+
This module is part of the `OCA/l10n-spain <https://github.com/OCA/l10n-spain/tree/18.0/l10n_es_vat_prorate>`_ project on GitHub.
115133

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

l10n_es_vat_prorate/__manifest__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,13 @@
55
{
66
"name": "Prorrata de IVA",
77
"summary": "Prorrata de IVA para la localización española",
8-
"version": "17.0.3.1.2",
8+
"version": "18.0.1.0.0",
99
"license": "AGPL-3",
1010
"author": "Creu Blanca, Tecnativa, Odoo Community Association (OCA)",
1111
"website": "https://github.com/OCA/l10n-spain",
1212
"pre_init_hook": "pre_init_hook",
1313
"depends": ["l10n_es_aeat"],
14+
"maintainers": ["rafaelbn", "Andrii9090", "EmilioPascual"],
1415
"data": [
1516
"security/ir.model.access.csv",
1617
"views/account_move_views.xml",

l10n_es_vat_prorate/models/account_move.py

Lines changed: 154 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -1,49 +1,193 @@
11
# Copyright 2021 Creu Blanca
22
# Copyright 2023 Tecnativa - Pedro M. Baeza
3-
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
3+
# License AGPL-3.0 or later[](http://www.gnu.org/licenses/agpl).
4+
45

56
from odoo import api, fields, models
6-
from odoo.tools import float_round, frozendict
7+
from odoo.tools import float_round
78

89

910
class AccountMove(models.Model):
1011
_inherit = "account.move"
1112

1213
prorate_id = fields.Many2one(
13-
string="Prorate",
14-
comodel_name="res.company.vat.prorate",
14+
"res.company.vat.prorate",
15+
compute="_compute_prorate_id",
16+
store=True,
17+
)
18+
with_special_vat_prorate = fields.Boolean(
1519
compute="_compute_prorate_id",
16-
ondelete="restrict",
1720
store=True,
1821
)
19-
with_special_vat_prorate = fields.Boolean(compute="_compute_prorate_id", store=True)
2022

2123
@api.depends("company_id", "date", "invoice_date")
2224
def _compute_prorate_id(self):
25+
self.prorate_id = False
26+
self.with_special_vat_prorate = False
2327
for rec in self:
2428
if rec.company_id.with_vat_prorate:
2529
prorate_date = rec.date or rec.invoice_date or fields.Date.today()
30+
if rec.move_type == "in_refund":
31+
prorate_date = (
32+
rec.reversed_entry_id.date or rec.reversed_entry_id.invoice_date
33+
if rec.reversed_entry_id
34+
else prorate_date
35+
)
2636
rec.prorate_id = rec.company_id.get_prorate(prorate_date)
2737
rec.with_special_vat_prorate = rec.prorate_id.type == "special"
38+
39+
def button_draft(self):
40+
res = super().button_draft()
41+
for move in self:
42+
move._apply_vat_prorate()
43+
return res
44+
45+
def _calculate_vat_prorate(self, invoice_lines):
46+
"""This method calculates tax_total and prorate_total for each line of
47+
the invoice that has taxes with with_vat_prorate set to True.
48+
"""
49+
prorate_vals = {}
50+
for invoice_line in invoice_lines:
51+
prorate = invoice_line.move_id.prorate_id.vat_prorate / 100.0
52+
prec = invoice_line.move_id.currency_id.rounding
53+
for tax in invoice_line.tax_ids.filtered(lambda t: t.with_vat_prorate):
54+
if tax.id in prorate_vals:
55+
tax_total = invoice_line.balance * tax.amount / 100
56+
prorate_vals[tax.id]["tax_total"] += float_round(
57+
tax_total, precision_rounding=prec
58+
)
59+
prorate_vals[tax.id]["prorate_total"] += float_round(
60+
tax_total * prorate, precision_rounding=prec
61+
)
62+
else:
63+
tax_total = invoice_line.balance * tax.amount / 100
64+
prorate_vals[tax.id] = {
65+
"tax_total": float_round(tax_total, precision_rounding=prec),
66+
"prorate_total": float_round(
67+
tax_total * prorate, precision_rounding=prec
68+
),
69+
"account_id": invoice_line.account_id,
70+
}
71+
return prorate_vals
72+
73+
def _get_lines_with_tax_prorate(self):
74+
return self.line_ids.filtered_domain(
75+
[
76+
("vat_prorate", "=", False),
77+
("tax_line_id.with_vat_prorate", "=", True),
78+
]
79+
)
80+
81+
def _get_invoice_line_with_prorate(self):
82+
self.ensure_one()
83+
return self.invoice_line_ids.filtered_domain([("with_vat_prorate", "=", True)])
84+
85+
def _apply_vat_prorate(self):
86+
"""Recalculate move.line_ids by applying the prorate.
87+
If a move line with with_tax_prorate=True already has a prorate_line,
88+
we need to recalculate the prorate and tax.
89+
If a move line with with_tax_prorate=True does not have a
90+
prorate_line, we need to use the copy method to create a new line
91+
with the prorate applied and recalculate prorate balance and tax balance
92+
"""
93+
invoice_lines_with_prorate = self._get_invoice_line_with_prorate()
94+
prorate_vals = self._calculate_vat_prorate(invoice_lines_with_prorate)
95+
line_to_update = {"line_ids": []}
96+
tax_lines = self._get_lines_with_tax_prorate()
97+
for line in tax_lines:
98+
base_balance = line.balance
99+
prorate_line = line.prorate_line
100+
prorate_val = prorate_vals.get(line.tax_line_id.id, None)
101+
if not prorate_line and prorate_val:
102+
prorate_line = line.copy()
28103
else:
29-
rec.prorate_id = rec.with_special_vat_prorate = False
104+
base_balance += prorate_line.balance
105+
if prorate_val:
106+
expense_account = prorate_val["account_id"]
107+
tax_total = prorate_val.get("tax_total", 0)
108+
prorate_total = prorate_val.get("prorate_total", 0)
109+
line_to_update["line_ids"].append(
110+
[
111+
1,
112+
prorate_line.id,
113+
{
114+
"balance": tax_total - prorate_total,
115+
"account_id": expense_account.id,
116+
"vat_prorate": True,
117+
},
118+
]
119+
)
120+
line_to_update["line_ids"].append(
121+
[
122+
1,
123+
line.id,
124+
{
125+
"prorate_line": prorate_line.id,
126+
"balance": (base_balance - tax_total + prorate_total),
127+
},
128+
]
129+
)
130+
else:
131+
line_to_update["line_ids"].append(
132+
[
133+
1,
134+
prorate_line.id,
135+
{
136+
"balance": 0,
137+
},
138+
]
139+
)
140+
line_to_update["line_ids"].append(
141+
[1, line.id, {"prorate_line": False, "balance": base_balance}]
142+
)
143+
self.with_context(skip_vat_prorate=True).write(line_to_update)
144+
145+
@api.model_create_multi
146+
def create(self, vals_list):
147+
moves = super().create(vals_list)
148+
for move in moves:
149+
if move.move_type in ["in_invoice", "in_refund"]:
150+
move._apply_vat_prorate()
151+
return moves
152+
153+
def write(self, vals):
154+
res = super().write(vals)
155+
for move in self:
156+
if (
157+
move.move_type in ["in_invoice", "in_refund"]
158+
and not self.env.context.get("skip_vat_prorate", False)
159+
and move.state == "draft"
160+
and (
161+
"line_ds" in vals
162+
and len(vals["line_ids"])
163+
or "invoice_line_ids" in vals
164+
)
165+
or "partner_id" in vals
166+
or "currency_id" in vals
167+
or "invoice_currency_rate" in vals
168+
or any(key in vals for key in ["prorate_id", "date", "invoice_date"])
169+
):
170+
move._apply_vat_prorate()
171+
return res
30172

31173

32174
class AccountMoveLine(models.Model):
33175
_inherit = "account.move.line"
34176

35177
vat_prorate = fields.Boolean(
36-
string="Is vat prorate", help="The line is a vat prorate"
178+
string="VAT Prorate",
179+
help="This line represents the non-deductible part of the prorated VAT.",
180+
copy=False,
37181
)
38182

39183
with_vat_prorate = fields.Boolean(
40-
string="With VAT Prorate",
41-
help="The line will create a vat prorate",
42184
compute="_compute_with_vat_prorate",
43185
store=True,
44186
readonly=False,
45187
)
46188

189+
prorate_line = fields.Many2one("account.move.line", default=False)
190+
47191
@api.depends("move_id.prorate_id", "company_id")
48192
def _compute_with_vat_prorate(self):
49193
for rec in self:
@@ -57,57 +201,3 @@ def _process_aeat_tax_fee_info(self, res, tax, sign):
57201
if self.vat_prorate:
58202
res[tax]["deductible_amount"] -= self.balance * sign
59203
return result
60-
61-
@api.depends("with_vat_prorate")
62-
def _compute_all_tax(self):
63-
"""After getting normal taxes dict that is dumped into this field, we loop
64-
into it to check if any of them applies VAT prorate, and if it's the case,
65-
we modify its amount and add the corresponding extra tax line.
66-
"""
67-
res = None
68-
for line in self:
69-
res = super(AccountMoveLine, line)._compute_all_tax()
70-
prorate_tax_list = {}
71-
for tax_key, tax_vals in line.compute_all_tax.items():
72-
tax_vals["vat_prorate"] = False
73-
tax = (
74-
self.env["account.tax.repartition.line"]
75-
.browse(tax_key.get("tax_repartition_line_id", False))
76-
.tax_id
77-
)
78-
if (
79-
line.with_vat_prorate
80-
and tax.with_vat_prorate
81-
and tax_key.get("account_id")
82-
and (
83-
not tax.prorate_account_ids
84-
or tax_key.get("account_id") in tax.prorate_account_ids.ids
85-
)
86-
):
87-
prec = line.move_id.currency_id.rounding
88-
prorate = line.move_id.prorate_id.vat_prorate
89-
new_vals = tax_vals.copy()
90-
for field in {"amount_currency", "balance"}:
91-
tax_vals[field] = float_round(
92-
tax_vals[field] * (prorate / 100),
93-
precision_rounding=prec,
94-
)
95-
new_vals[field] -= tax_vals[field]
96-
new_vals["vat_prorate"] = True
97-
new_key = dict(tax_key)
98-
new_key.update(
99-
{
100-
"vat_prorate": True,
101-
"account_id": line.account_id.id,
102-
"analytic_distribution": line.analytic_distribution,
103-
}
104-
)
105-
new_key = frozendict(new_key)
106-
if prorate_tax_list.get(new_key):
107-
for field in {"amount_currency", "balance"}:
108-
prorate_tax_list[new_key][field] += new_vals[field]
109-
else:
110-
prorate_tax_list[new_key] = new_vals
111-
if prorate_tax_list:
112-
line.compute_all_tax.update(prorate_tax_list)
113-
return res

0 commit comments

Comments
 (0)