Skip to content

Commit

Permalink
[ADD] custom sequence mechanism
Browse files Browse the repository at this point in the history
  • Loading branch information
hbrunn committed May 1, 2023
1 parent bf9bbcd commit 5bce6fa
Show file tree
Hide file tree
Showing 10 changed files with 324 additions and 29 deletions.
24 changes: 24 additions & 0 deletions datev_export/models/res_company.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,27 @@ class ResCompany(models.Model):
string="DATEV account code length",
default=5,
)

datev_partner_numbering = fields.Selection(
selection="_selection_datev_partner_numbering",
string="DATEV Partner numbering",
default="none",
)

datev_customer_sequence_id = fields.Many2one(
"ir.sequence", "DATEV sequence for customers"
)

datev_supplier_sequence_id = fields.Many2one(
"ir.sequence", "DATEV sequence for suppliers"
)

def _selection_datev_partner_numbering(self):
reports_installed = (
"l10n_de_datev_reports" in self.env["ir.module.module"]._installed()
)
return (
[("none", "None")]
+ ([("ee", "Enterprises Edition")] if reports_installed else [])
+ [("sequence", "Sequence")]
)
11 changes: 11 additions & 0 deletions datev_export/models/res_config_settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,14 @@ class ResConfigSettings(models.TransientModel):
related="company_id.datev_account_code_length",
readonly=False,
)
datev_partner_numbering = fields.Selection(
related="company_id.datev_partner_numbering", readonly=False
)

datev_customer_sequence_id = fields.Many2one(
related="company_id.datev_customer_sequence_id", readonly=False
)

datev_supplier_sequence_id = fields.Many2one(
related="company_id.datev_supplier_sequence_id", readonly=False
)
82 changes: 82 additions & 0 deletions datev_export/views/res_config_settings_views.xml
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,88 @@
</div>
</div>
</div>

<div class="o_setting_left_pane" />
<div class="o_setting_right_pane">
<span class="o_form_label">Partner numbering</span>
<span
class="fa fa-lg fa-building-o"
title="Values set here are company-specific."
aria-label="Values set here are company-specific."
groups="base.group_multi_company"
/>
<div class="text-muted">
Select the way partners in DATEV export are numbered
</div>
<div class="content-group">
<div class="mt16">
<field
name="datev_partner_numbering"
class="o_light_label"
/>
</div>
</div>
</div>

<div
class="o_setting_left_pane"
attrs="{'invisible': [('datev_partner_numbering', '!=', 'sequence')]}"
/>
<div
class="o_setting_right_pane"
attrs="{'invisible': [('datev_partner_numbering', '!=', 'sequence')]}"
>
<span class="o_form_label">Customer sequence</span>
<span
class="fa fa-lg fa-building-o"
title="Values set here are company-specific."
aria-label="Values set here are company-specific."
groups="base.group_multi_company"
/>
<div class="text-muted">
The sequence used to number customers
</div>
<div class="content-group">
<div class="mt16">
<field
name="datev_customer_sequence_id"
class="o_light_label"
attrs="{'required': [('datev_partner_numbering', '=', 'sequence')]}"
domain="[('company_id', 'in', (company_id, False))]"
/>
</div>
</div>
</div>

<div
class="o_setting_left_pane"
attrs="{'invisible': [('datev_partner_numbering', '!=', 'sequence')]}"
/>
<div
class="o_setting_right_pane"
attrs="{'invisible': [('datev_partner_numbering', '!=', 'sequence')]}"
>
<span class="o_form_label">Supplier sequence</span>
<span
class="fa fa-lg fa-building-o"
title="Values set here are company-specific."
aria-label="Values set here are company-specific."
groups="base.group_multi_company"
/>
<div class="text-muted">
The sequence used to number suppliers
</div>
<div class="content-group">
<div class="mt16">
<field
name="datev_supplier_sequence_id"
class="o_light_label"
attrs="{'required': [('datev_partner_numbering', '=', 'sequence')]}"
domain="[('company_id', 'in', (company_id, False))]"
/>
</div>
</div>
</div>
</div>
</div>
</div>
Expand Down
1 change: 1 addition & 0 deletions datev_export_dtvf/__manifest__.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,6 @@
"security/ir.model.access.csv",
"views/account_account.xml",
"views/datev_export_dtvf.xml",
"views/res_partner.xml",
],
}
1 change: 1 addition & 0 deletions datev_export_dtvf/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@

from . import account_account
from . import datev_export_dtvf
from . import res_partner
99 changes: 82 additions & 17 deletions datev_export_dtvf/models/datev_export_dtvf.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,17 @@
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
import base64
import io
import string
import zipfile

from odoo import _, exceptions, fields, models
from odoo import _, api, exceptions, fields, models
from odoo.osv.expression import TRUE_LEAF

from ..datev import DatevAccountWriter, DatevPartnerWriter, DatevTransactionWriter


class DatevExportDtvfExport(models.Model):
_name = "datev_export_dtvf.export"
_rec_name = "fiscalyear_id"
_description = "DATEV export"
_order = "fiscalyear_id desc, create_date desc"

Expand All @@ -25,6 +25,10 @@ class DatevExportDtvfExport(models.Model):
states={"draft": [("required", True), ("readonly", False)]},
readonly=True,
)
name = fields.Char(
states={"draft": [("required", True), ("readonly", False)]},
readonly=True,
)
fiscalyear_start = fields.Date(related=["fiscalyear_id", "date_start"])
fiscalyear_end = fields.Date(related=["fiscalyear_id", "date_end"])
period_ids = fields.Many2many(
Expand All @@ -41,13 +45,28 @@ class DatevExportDtvfExport(models.Model):
)
date_generated = fields.Datetime("Generated at", readonly=True, copy=False)
file_data = fields.Binary("Data", readonly=True, copy=False)
file_name = fields.Char("Filename", readonly=True, copy=False)
file_name = fields.Char("Filename", readonly=True, compute="_compute_file_name")
company_id = fields.Many2one(
"res.company",
required=True,
default=lambda self: self.env.company,
)

@api.onchange("fiscalyear_id")
def _onchange_fiscalyear_id(self):
self.name = self.name or self.fiscalyear_id.display_name

@api.depends("name")
def _compute_file_name(self):
for this in self:
this.file_name = (
"".join(
c if c in string.ascii_letters + string.digits + "-_" else "_"
for c in (this.name or "datev_export")
)
+ ".zip"
)

def action_draft(self):
return self.filtered(lambda x: x.state != "draft").write({"state": "draft"})

Expand All @@ -72,6 +91,8 @@ def action_generate(self):
token[:1].upper() for token in self.env.user.name.split()
)[:2]

partners = self.env["res.partner"].browse([])

for date_range in this.period_ids:
moves = self.env["account.move"].search(
[
Expand All @@ -85,6 +106,9 @@ def action_generate(self):
],
order="date desc",
)
partners += moves.mapped("line_ids.partner_id") + moves.mapped(
"partner_id"
)
writer = DatevTransactionWriter(
this.company_id.datev_consultant_number,
this.company_id.datev_client_number,
Expand All @@ -105,13 +129,6 @@ def action_generate(self):
)
zip_file.writestr(filename, writer.buffer.getvalue())

partners = self.env["res.partner"].search(
[
"|",
("company_id", "=", False),
("company_id", "=", this.company_id.id),
]
)
writer = DatevPartnerWriter(
self.company_id.datev_consultant_number,
self.company_id.datev_client_number,
Expand Down Expand Up @@ -150,7 +167,6 @@ def action_generate(self):
zip_file.close()
this.write(
{
"file_name": "%s.zip" % self.display_name,
"file_data": base64.b64encode(zip_buffer.getvalue()),
"state": "done",
"date_generated": fields.Datetime.now(),
Expand Down Expand Up @@ -190,13 +206,35 @@ def _get_data_transaction(self, move):
move_line, move_line2 = move_line2, move_line
if move_line.account_id.datev_export_nonautomatic:
move_line, move_line2 = move_line2, move_line
account_number = move_line.account_id.code[-code_length:]
offset_account_number = move_line2.account_id.code[-code_length:]
if self.company_id.datev_partner_numbering in ("ee", "sequence"):
for ml in (move_line, move_line2):
number = (
account_number if ml == move_line else offset_account_number
)
number_type = (
"customer"
if ml.account_id.internal_type == "receivable"
else (
"supplier"
if ml.account_id.internal_type == "payable"
else None
)
)
if number_type and ml.partner_id:
number = self._get_partner_number(
ml.partner_id, number_type, True
)
if ml == move_line:
account_number = number
else:
offset_account_number = number
data = {
"Umsatz (ohne Soll/Haben-Kz)": ("%.2f" % abs(amount)).replace(".", ","),
"Soll/Haben-Kennzeichen": move_line.debit and "S" or "H",
"Konto": move_line.account_id.code[-code_length:],
"Gegenkonto (ohne BU-Schlüssel)": move_line2.account_id.code[
-code_length:
],
"Konto": account_number,
"Gegenkonto (ohne BU-Schlüssel)": offset_account_number,
"BU-Schlüssel": "40"
if move_line.account_id.datev_export_nonautomatic
or move_line2.account_id.datev_export_nonautomatic
Expand Down Expand Up @@ -238,8 +276,10 @@ def _get_data_transaction(self, move):
yield data

def _get_data_partner(self, partner):
yield {
"Konto": partner.id,
data = {
"Konto": self._get_partner_number(partner, "customer")
or self._get_partner_number(partner, "supplier")
or partner.id,
"Name (Adressattyp Unternehmen)": partner.name,
"Name (Adressattyp natürl. Person)": partner.name,
"Adressattyp": partner.is_company and "2" or "1",
Expand Down Expand Up @@ -271,6 +311,12 @@ def _get_data_partner(self, partner):
if partner.lang[:2] == "it"
else "5",
}
yield data
if self._get_partner_number(partner, "customer") and self._get_partner_number(
partner, "supplier"
):
data["Konto"] = self._get_partner_number(partner, "supplier")
yield data

Check warning on line 319 in datev_export_dtvf/models/datev_export_dtvf.py

View check run for this annotation

Codecov / codecov/patch

datev_export_dtvf/models/datev_export_dtvf.py#L318-L319

Added lines #L318 - L319 were not covered by tests

def _get_data_account(self, account):
yield {
Expand All @@ -279,3 +325,22 @@ def _get_data_account(self, account):
"SprachId": self.env.user.lang.replace("_", "-"),
"Kontenbeschriftung lang": account.name,
}

def _get_partner_number(self, partner, number_type, generate=False):
if self.company_id.datev_partner_numbering == "sequence":
field_name = "l10n_de_datev_export_identifier_%s" % number_type
if not partner[field_name] and generate:
getattr(
partner,
"action_l10n_de_datev_export_identifier_%s" % number_type,
)()
return partner[field_name]
elif self.company_id.datev_partner_numbering == "ee":
account_length = self.env["account.general.ledger"]._get_account_length()
return partner[

Check warning on line 340 in datev_export_dtvf/models/datev_export_dtvf.py

View check run for this annotation

Codecov / codecov/patch

datev_export_dtvf/models/datev_export_dtvf.py#L339-L340

Added lines #L339 - L340 were not covered by tests
"l10n_de_datev_identifier%s"
% ("_customer" if number_type == "customer" else "")
] or str(
(1 if number_type == "customer" else 7) * 10**account_length
+ partner.id
)
34 changes: 34 additions & 0 deletions datev_export_dtvf/models/res_partner.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# Copyright 2022 Hunki Enterprises BV
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
from odoo import fields, models


class ResPartner(models.Model):
_inherit = "res.partner"

l10n_de_datev_export_identifier_customer = fields.Char("DATEV number (customer)")
l10n_de_datev_export_identifier_supplier = fields.Char("DATEV number (supplier)")
l10n_de_datev_export_show = fields.Boolean(
compute="_compute_l10n_de_datev_export_show"
)

def _compute_l10n_de_datev_export_show(self):
"""Determine if we show the identifiers in the form"""
for this in self:
this.l10n_de_datev_export_show = (

Check warning on line 18 in datev_export_dtvf/models/res_partner.py

View check run for this annotation

Codecov / codecov/patch

datev_export_dtvf/models/res_partner.py#L17-L18

Added lines #L17 - L18 were not covered by tests
self.env.company.datev_partner_numbering == "sequence"
)

def action_l10n_de_datev_export_identifier_customer(self):
"""Generate number if not set"""
self.l10n_de_datev_export_identifier_customer = (
self.l10n_de_datev_export_identifier_customer
or self.env.company.datev_customer_sequence_id._next()
)

def action_l10n_de_datev_export_identifier_supplier(self):
"""Generate number if not set"""
self.l10n_de_datev_export_identifier_supplier = (

Check warning on line 31 in datev_export_dtvf/models/res_partner.py

View check run for this annotation

Codecov / codecov/patch

datev_export_dtvf/models/res_partner.py#L31

Added line #L31 was not covered by tests
self.l10n_de_datev_export_identifier_supplier
or self.env.company.datev_supplier_sequence_id._next()
)
Loading

0 comments on commit 5bce6fa

Please sign in to comment.