Skip to content

Commit 0c752a2

Browse files
committed
[MIG] datev_export_dtvf: Migration to 18.0
1 parent c3d4c99 commit 0c752a2

File tree

15 files changed

+228
-74
lines changed

15 files changed

+228
-74
lines changed

datev_export_dtvf/README.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,8 @@ Configuration
4040

4141
To configure this module, you need to:
4242

43-
1. Go to your company
44-
2. Fill in the fields in the DATEV tab
43+
1. Go to Settings -> Invoicing
44+
2. Fill in the fields in the DATEV section
4545
3. For accounts where you want to suppress automatic calculations (for
4646
ie taxes), set the according flag
4747

datev_export_dtvf/__manifest__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
{
44
"name": "DATEV",
55
"summary": "Export Data for DATEV (dtvf)",
6-
"version": "15.0.2.0.0",
6+
"version": "18.0.1.0.0",
77
"development_status": "Beta",
88
"category": "Accounting",
99
"website": "https://github.com/OCA/l10n-germany",
@@ -20,6 +20,7 @@
2020
"security/ir.model.access.csv",
2121
"views/account_account.xml",
2222
"views/datev_export_dtvf.xml",
23+
"views/res_config_settings.xml",
2324
"views/res_partner.xml",
2425
],
2526
}

datev_export_dtvf/data/ir_cron.xml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
<field name="name">Monthly DATEV export via mail (dtvf)</field>
44
<field name="interval_number">1</field>
55
<field name="interval_type">months</field>
6-
<field name="numbercall">-1</field>
76
<field name="model_id" ref="model_datev_export_dtvf_export" />
87
<field name="active" eval="False" />
98
<field name="code">

datev_export_dtvf/datev.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ def _coerce_value(self, field, value):
128128
if field.length:
129129
value = str(value)[: field.length]
130130
if field.quote:
131-
value = '"%s"' % str(value).replace('"', '""')
131+
value = f'''"{str(value).replace('"', '""')}"'''
132132
return str(value)
133133

134134
def writerow(self, row):
@@ -165,7 +165,7 @@ def __init__(
165165
account_code_length,
166166
period_start,
167167
period_end,
168-
"Buchungsstapel %s" % period_start,
168+
f"Buchungsstapel {period_start}",
169169
user_initials,
170170
currency,
171171
[

datev_export_dtvf/models/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,6 @@
22

33
from . import account_account
44
from . import datev_export_dtvf
5+
from . import res_company
6+
from . import res_config_settings
57
from . import res_partner

datev_export_dtvf/models/account_account.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,5 +13,6 @@ class AccountAccount(models.Model):
1313
"in DATEV.",
1414
)
1515
datev_code = fields.Char(
16-
help="In case your COA codes don't work for DATEV, fill in an alternative code here"
16+
help="In case your COA codes don't work for DATEV, fill in an alternative code "
17+
"here"
1718
)

datev_export_dtvf/models/datev_export_dtvf.py

Lines changed: 36 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -22,26 +22,17 @@ class DatevExportDtvfExport(models.Model):
2222
fiscalyear_id = fields.Many2one(
2323
"date.range",
2424
string="Fiscal year",
25-
states={"draft": [("required", True), ("readonly", False)]},
26-
readonly=True,
27-
)
28-
name = fields.Char(
29-
states={"draft": [("required", True), ("readonly", False)]},
30-
readonly=True,
3125
)
26+
name = fields.Char()
3227
fiscalyear_start = fields.Date(related="fiscalyear_id.date_start")
3328
fiscalyear_end = fields.Date(related="fiscalyear_id.date_end")
3429
period_ids = fields.Many2many(
3530
"date.range",
3631
string="Periods",
37-
states={"draft": [("required", True), ("readonly", False)]},
38-
readonly=True,
3932
)
4033
journal_ids = fields.Many2many(
4134
"account.journal",
4235
string="Journals",
43-
states={"draft": [("readonly", False)]},
44-
readonly=True,
4536
)
4637
date_generated = fields.Datetime("Generated at", readonly=True, copy=False)
4738
file_data = fields.Binary("Data", readonly=True, copy=False)
@@ -80,7 +71,7 @@ def action_generate(self):
8071
)
8172
):
8273
raise exceptions.ValidationError(
83-
_("Please fill in the DATEV tab of your company")
74+
_("Please fill in the DATEV section in the invoicing configuration")
8475
)
8576

8677
zip_buffer = io.BytesIO()
@@ -123,9 +114,8 @@ def action_generate(self):
123114
for move in moves:
124115
writer.writerows(this._get_data_transaction(move))
125116

126-
filename = (
127-
"EXTF_Buchungsstapel_%s.csv"
128-
% date_range.date_start.strftime("%Y%m%d")
117+
filename = "EXTF_Buchungsstapel_{}.csv".format(
118+
date_range.date_start.strftime("%Y%m%d")
129119
)
130120
zip_file.writestr(filename, writer.buffer.getvalue())
131121

@@ -146,7 +136,7 @@ def action_generate(self):
146136

147137
accounts = self.env["account.account"].search(
148138
[
149-
("company_id", "=", this.company_id.id),
139+
("company_ids", "=", this.company_id.id),
150140
]
151141
)
152142
writer = DatevAccountWriter(
@@ -178,7 +168,8 @@ def _get_data_transaction(self, move):
178168
move_line2amount = {
179169
move_line: move_line.credit or move_line.debit
180170
for move_line in move.line_ids
181-
if not move_line.display_type and (move_line.debit or move_line.credit)
171+
if not move_line.display_type.startswith("line_")
172+
and (move_line.debit or move_line.credit)
182173
}
183174
currency = move.currency_id or move.company_id.currency_id
184175
code_length = move.company_id.datev_account_code_length
@@ -199,10 +190,13 @@ def _get_data_transaction(self, move):
199190
if currency.is_zero(move_line2amount[move_line2]):
200191
move_line2amount.pop(move_line2)
201192
break
202-
if move_line.account_id.internal_type not in (
203-
"receivable",
204-
"payable",
205-
) and move_line2.account_id.internal_type in ("receivable", "payable"):
193+
if move_line.account_id.account_type not in (
194+
"asset_receivable",
195+
"liability_payable",
196+
) and move_line2.account_id.account_type in (
197+
"asset_receivable",
198+
"liability_payable",
199+
):
206200
move_line, move_line2 = move_line2, move_line
207201
if move_line.account_id.datev_export_nonautomatic:
208202
move_line, move_line2 = move_line2, move_line
@@ -215,10 +209,10 @@ def _get_data_transaction(self, move):
215209
)
216210
number_type = (
217211
"customer"
218-
if ml.account_id.internal_type == "receivable"
212+
if ml.account_id.account_type == "asset_receivable"
219213
else (
220214
"supplier"
221-
if ml.account_id.internal_type == "payable"
215+
if ml.account_id.account_type == "liability_payable"
222216
else None
223217
)
224218
)
@@ -231,7 +225,7 @@ def _get_data_transaction(self, move):
231225
else:
232226
offset_account_number = number
233227
data = {
234-
"Umsatz (ohne Soll/Haben-Kz)": ("%.2f" % abs(amount)).replace(".", ","),
228+
"Umsatz (ohne Soll/Haben-Kz)": f"{abs(amount):.2f}".replace(".", ","),
235229
"Soll/Haben-Kennzeichen": move_line.debit and "S" or "H",
236230
"Konto": account_number,
237231
"Gegenkonto (ohne BU-Schlüssel)": offset_account_number,
@@ -259,33 +253,28 @@ def _get_data_transaction(self, move):
259253
)[:1].name
260254
or move.name,
261255
"Belegfeld 2": move_line2.name,
262-
"KOST1 - Kostenstelle": move_line.analytic_account_id.code
263-
or move_line.analytic_account_id.name
264-
or move_line2.analytic_account_id.code
265-
or move_line2.analytic_account_id.name,
256+
"KOST1 - Kostenstelle": move_line.analytic_line_ids[:1].account_id.code
257+
or move_line.analytic_line_ids[:1].account_id.name
258+
or move_line2.analytic_line_ids[:1].account_id.code
259+
or move_line2.analytic_line_ids[:1].account_id.name,
266260
"KOST-Datum": move.date.strftime("%d%m%Y"),
267261
}
268262
if move_line.amount_currency:
269263
factor = abs(amount / (move_line.debit or move_line.credit))
264+
rate = 1 / currency._get_conversion_rate(
265+
move_line.currency_id,
266+
currency,
267+
move.company_id,
268+
move.date,
269+
)
270270
data.update(
271271
{
272272
"Umsatz (ohne Soll/Haben-Kz)": (
273-
"%.2f" % abs(move_line.amount_currency * factor)
273+
f"{abs(move_line.amount_currency * factor):.2f}"
274274
).replace(".", ","),
275275
"WKZ Umsatz": move_line.currency_id.name,
276-
"Kurs": (
277-
"%.6f"
278-
% (
279-
1
280-
/ currency._get_conversion_rate(
281-
move_line.currency_id,
282-
currency,
283-
move.company_id,
284-
move.date,
285-
)
286-
)
287-
).replace(".", ","),
288-
"Basis-Umsatz": ("%.2f" % abs(amount)).replace(".", ","),
276+
"Kurs": f"{rate:.6f}".replace(".", ","),
277+
"Basis-Umsatz": f"{abs(amount):.2f}".replace(".", ","),
289278
"WKZ Basis-Umsatz": currency.name,
290279
}
291280
)
@@ -337,26 +326,27 @@ def _get_data_partner(self, partner):
337326
def _get_data_account(self, account):
338327
yield {
339328
"Konto": account.datev_code
340-
or account.code[-account.company_id.datev_account_code_length :],
329+
or account.code[-self.company_id.datev_account_code_length :],
341330
"Kontobeschriftung": account.name,
342331
"SprachId": self.env.user.lang.replace("_", "-"),
343332
"Kontenbeschriftung lang": account.name,
344333
}
345334

346335
def _get_partner_number(self, partner, number_type, generate=False):
347336
if self.company_id.datev_partner_numbering == "sequence":
348-
field_name = "l10n_de_datev_export_identifier_%s" % number_type
337+
field_name = f"l10n_de_datev_export_identifier_{number_type}"
349338
if not partner[field_name] and generate:
350339
getattr(
351340
partner,
352-
"action_l10n_de_datev_export_identifier_%s" % number_type,
341+
f"action_l10n_de_datev_export_identifier_{number_type}",
353342
)()
354343
return partner[field_name]
355344
elif self.company_id.datev_partner_numbering == "ee":
356345
account_length = self.env["account.general.ledger"]._get_account_length()
357346
return partner[
358-
"l10n_de_datev_identifier%s"
359-
% ("_customer" if number_type == "customer" else "")
347+
"l10n_de_datev_identifier{}".format(
348+
"_customer" if number_type == "customer" else ""
349+
)
360350
] or str(
361351
(1 if number_type == "customer" else 7) * 10**account_length
362352
+ partner.id
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# Copyright 2026 Hunki Enterprises BV
2+
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
3+
from odoo import fields, models
4+
5+
6+
class ResCompany(models.Model):
7+
_inherit = "res.company"
8+
9+
datev_account_code_length = fields.Integer(
10+
"DATEV account code length",
11+
default=5,
12+
)
13+
14+
datev_partner_numbering = fields.Selection(
15+
string="DATEV Partner numbering",
16+
selection="_selection_datev_partner_numbering",
17+
default="none",
18+
)
19+
20+
datev_customer_sequence_id = fields.Many2one(
21+
"ir.sequence", "DATEV sequence for customers"
22+
)
23+
24+
datev_supplier_sequence_id = fields.Many2one(
25+
"ir.sequence", "DATEV sequence for suppliers"
26+
)
27+
28+
def _selection_datev_partner_numbering(self):
29+
reports_installed = (
30+
"l10n_de_datev_reports" in self.env["ir.module.module"]._installed()
31+
)
32+
return (
33+
[("none", "None")]
34+
+ ([("ee", "Enterprises Edition")] if reports_installed else [])
35+
+ [("sequence", "Sequence")]
36+
)
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# Copyright 2026 Hunki Enterprises BV
2+
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
3+
from odoo import fields, models
4+
5+
6+
class ResConfigSettings(models.TransientModel):
7+
_inherit = "res.config.settings"
8+
9+
datev_account_code_length = fields.Integer(
10+
related="company_id.datev_account_code_length",
11+
readonly=False,
12+
)
13+
14+
datev_partner_numbering = fields.Selection(
15+
related="company_id.datev_partner_numbering",
16+
readonly=False,
17+
)
18+
19+
datev_customer_sequence_id = fields.Many2one(
20+
related="company_id.datev_customer_sequence_id",
21+
readonly=False,
22+
)
23+
24+
datev_supplier_sequence_id = fields.Many2one(
25+
related="company_id.datev_supplier_sequence_id",
26+
readonly=False,
27+
)

datev_export_dtvf/readme/CONFIGURE.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
To configure this module, you need to:
22

3-
1. Go to your company
4-
2. Fill in the fields in the DATEV tab
3+
1. Go to Settings -> Invoicing
4+
2. Fill in the fields in the DATEV section
55
3. For accounts where you want to suppress automatic calculations (for
66
ie taxes), set the according flag
77

0 commit comments

Comments
 (0)