Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[15.0][WIP][REF] l10n_th_bank_payment_export: refactor structure text file #476

Open
wants to merge 9 commits into
base: 15.0
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions l10n_th_bank_payment_export/__manifest__.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
"data/report_action.xml",
"data/server_action.xml",
"templates/report_template.xml",
"views/bank_export_format_view.xml",
"views/bank_payment_template_view.xml",
"views/account_payment_view.xml",
"views/bank_payment_export_view.xml",
Expand Down
4 changes: 2 additions & 2 deletions l10n_th_bank_payment_export/data/report_action.xml
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo>
<!-- TEXT COMMON FILE -->
<record id="action_payment_demo_txt" model="ir.actions.report">
<field name="name">Export Text Payment Demo</field>
<record id="action_payment_txt" model="ir.actions.report">
<field name="name">Export Text Payment</field>
<field name="model">bank.payment.export</field>
<field name="type">ir.actions.report</field>
<field
Expand Down
1 change: 1 addition & 0 deletions l10n_th_bank_payment_export/models/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Copyright 2021 Ecosoft Co., Ltd. (http://ecosoft.co.th)
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).

from . import bank_export_format
from . import bank_payment_template
from . import account_payment
from . import bank_payment_export_line
Expand Down
119 changes: 119 additions & 0 deletions l10n_th_bank_payment_export/models/bank_export_format.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
# Copyright 2024 Ecosoft Co., Ltd. (http://ecosoft.co.th)
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).

from odoo import _, api, fields, models
from odoo.exceptions import UserError
from odoo.tools.safe_eval import safe_eval


class BankExportFormat(models.Model):
_name = "bank.export.format"
_description = "Bank Export Format"

name = fields.Char(
required=True,
)
bank = fields.Selection(
selection=[],
)
export_format_ids = fields.One2many(
comodel_name="bank.export.format.line",
inverse_name="bank_id",
)


class BankExportFormatLine(models.Model):
_name = "bank.export.format.line"
_description = "Bank Export Format Line"
_order = "sequence, id"

bank_id = fields.Many2one(
comodel_name="bank.export.format",
required=True,
ondelete="cascade",
index=True,
)
sequence = fields.Integer(
default=10,
required=True,
)
display_type = fields.Selection(
[
("line_section", "Section"),
("line_note", "Note"),
],
default=False,
help="Technical field for UX purpose.",
)
lenght = fields.Integer(
required=True,
)
lenght_from = fields.Integer(compute="_compute_lenght", store=True, string="From")
lenght_to = fields.Integer(compute="_compute_lenght", store=True, string="To")
name = fields.Char(string="Description")
condition_line = fields.Text()
match_group = fields.Char()
end_line = fields.Boolean()
need_loop = fields.Boolean()
sub_loop = fields.Boolean()
sub_value_loop = fields.Text()
value_type = fields.Selection(
selection=[
("fixed", "Fixed"),
("dynamic", "Dynamic"),
],
required=True,
default="fixed",
)
value_alignment = fields.Selection(
selection=[
("ljust", "Left Justify"),
("rjust", "Right Justify"),
],
string="Alignment",
required=True,
default="ljust",
)
value_blank_space = fields.Char(
string="Blank Space",
help="Blank space for fill in value",
)
value = fields.Text()

@api.depends("lenght", "sequence")
def _compute_lenght(self):
for rec in self:
# Retrieve all records for the given bank_id ordered by sequence
export_format_lines = self.search(
[("bank_id", "=", rec.bank_id.id)], order="sequence"
)
# Initialize the starting point for lenght_from
previous_lenght_to = 0

# Iterate over the records and calculate lenght_from and lenght_to
for line in export_format_lines:
line.lenght_from = previous_lenght_to + 1
line.lenght_to = line.lenght_from + line.lenght - 1
# Reset the starting point for lenght_from if end_line is True
if line.end_line:
previous_lenght_to = 0
else:
previous_lenght_to = line.lenght_to

def _get_value(self, globals_dict):
value = self.value or ""
if self.value_type == "dynamic" and value:
value = safe_eval(value, globals_dict=globals_dict)
text_line = getattr(value, self.value_alignment)(
self.lenght, self.value_blank_space or " "
)
return text_line

@api.constrains("lenght", "value")
def _check_lenght(self):
for rec in self:
if rec.value_type == "fixed" and rec.value and len(rec.value) > rec.lenght:
raise UserError(

Check warning on line 116 in l10n_th_bank_payment_export/models/bank_export_format.py

View check run for this annotation

Codecov / codecov/patch

l10n_th_bank_payment_export/models/bank_export_format.py#L116

Added line #L116 was not covered by tests
_("Value %(value)s is longer than lenght %(lenght)s")
% {"value": rec.value, "lenght": rec.lenght}
)
186 changes: 176 additions & 10 deletions l10n_th_bank_payment_export/models/bank_payment_export.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
# Copyright 2021 Ecosoft Co., Ltd. (http://ecosoft.co.th)
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).

from datetime import datetime

from odoo import _, api, fields, models
from odoo.exceptions import UserError
from odoo.tools.safe_eval import safe_eval


class BankPaymentExport(models.Model):
Expand Down Expand Up @@ -32,6 +35,14 @@
tracking=True,
check_company=True,
)
bank_export_format_id = fields.Many2one(
comodel_name="bank.export.format",
string="Bank Export Format",
readonly=True,
states={"draft": [("readonly", False)]},
domain="[('bank', '=', bank)]",
tracking=True,
)
effective_date = fields.Date(
copy=False,
readonly=True,
Expand Down Expand Up @@ -154,7 +165,7 @@
return "{}".format(self.name)

def _get_view_report_text(self):
return "l10n_th_bank_payment_export.action_payment_demo_txt"
return "l10n_th_bank_payment_export.action_payment_txt"

def _get_view_report_xlsx(self):
return "l10n_th_bank_payment_export.action_export_payment_xlsx"
Expand All @@ -168,18 +179,159 @@
view_report = self._get_view_report_xlsx()
return self.env.ref(view_report).sudo().report_action(self, config=False)

def _set_global_dict(self):
"""Set global dict for eval"""
today = fields.Date.context_today(self)
today_datetime = fields.Datetime.context_timestamp(
self.env.user, datetime.now()
)
globals_dict = {
"rec": self,
"line": self.export_line_ids,
"today": today,
"today_datetime": today_datetime,
}
return globals_dict

def _update_global_dict(self, globals_dict, **kwargs):
"""Update global dict with kwargs"""
globals_dict.update(kwargs)
return globals_dict

def _generate_bank_payment_text(self):
self.ensure_one()
return
globals_dict = self._set_global_dict()
text_parts = []
processed_match = set()

# Get format from bank
if not self.bank_export_format_id:
raise UserError(_("Bank format not found."))

Check warning on line 209 in l10n_th_bank_payment_export/models/bank_payment_export.py

View check run for this annotation

Codecov / codecov/patch

l10n_th_bank_payment_export/models/bank_payment_export.py#L209

Added line #L209 was not covered by tests

exp_format_lines = self.bank_export_format_id.export_format_ids

for idx, exp_format in enumerate(exp_format_lines):
if exp_format.display_type:
continue

# Skip if value has already been processed, and need_loop is True
if exp_format.need_loop and exp_format.match_group in processed_match:
continue

# Add idx to globals_dict
globals_dict = self._update_global_dict(globals_dict, idx=idx)

# Skip this line if condition is not met
if not exp_format.need_loop and exp_format.condition_line:
condition = safe_eval(

Check warning on line 226 in l10n_th_bank_payment_export/models/bank_payment_export.py

View check run for this annotation

Codecov / codecov/patch

l10n_th_bank_payment_export/models/bank_payment_export.py#L226

Added line #L226 was not covered by tests
exp_format.condition_line, globals_dict=globals_dict
)
if not condition:
continue

Check warning on line 230 in l10n_th_bank_payment_export/models/bank_payment_export.py

View check run for this annotation

Codecov / codecov/patch

l10n_th_bank_payment_export/models/bank_payment_export.py#L230

Added line #L230 was not covered by tests

# Add value to the set of processed values
if exp_format.match_group:
processed_match.add(exp_format.match_group)

if exp_format.need_loop:
self._process_loop(
exp_format, exp_format_lines, globals_dict, text_parts
)
continue

# Get value from instruction
text_line = exp_format._get_value(globals_dict)
text_parts.append(text_line)

if exp_format.end_line:
# TODO: Change this to configurable
text_parts.append("\r\n")

text = "".join(text_parts)
return text

def _process_loop(self, exp_format, exp_format_lines, globals_dict, text_parts):
# Get all lines that match the current group
for idx_line, line in enumerate(self.export_line_ids):
# Change the value of the line in the globals_dict
globals_dict_line = self._update_global_dict(
globals_dict, line=line, idx_line=idx_line
)

# search only lines that match the current group and condition
# filter in loop because we need to check condition_line
exp_format_line_group = exp_format_lines.filtered(
lambda l: l.match_group == exp_format.match_group
and (
not l.condition_line
or safe_eval(l.condition_line, globals_dict=globals_dict_line)
)
)

processed_subloop = set()

for exp_format_line in exp_format_line_group:
# Sub-loop logic
if exp_format_line.sub_loop:
self._process_sub_loop(

Check warning on line 276 in l10n_th_bank_payment_export/models/bank_payment_export.py

View check run for this annotation

Codecov / codecov/patch

l10n_th_bank_payment_export/models/bank_payment_export.py#L276

Added line #L276 was not covered by tests
exp_format_line,
exp_format_line_group,
globals_dict_line,
text_parts,
processed_subloop,
)
continue

Check warning on line 283 in l10n_th_bank_payment_export/models/bank_payment_export.py

View check run for this annotation

Codecov / codecov/patch

l10n_th_bank_payment_export/models/bank_payment_export.py#L283

Added line #L283 was not covered by tests

# Get value from instruction
text_line = exp_format_line._get_value(globals_dict_line)
text_parts.append(text_line)

if exp_format_line.end_line:
# TODO: Change this to configurable
text_parts.append("\r\n")
return text_parts

def _process_sub_loop(
self,
exp_format_line,
exp_format_line_group,
globals_dict_line,
text_parts,
processed_subloop,
):
if exp_format_line.sub_value_loop not in processed_subloop:
processed_subloop.add(exp_format_line.sub_value_loop)

Check warning on line 303 in l10n_th_bank_payment_export/models/bank_payment_export.py

View check run for this annotation

Codecov / codecov/patch

l10n_th_bank_payment_export/models/bank_payment_export.py#L303

Added line #L303 was not covered by tests

exp_format_sub_line_group = exp_format_line_group.filtered(
lambda l: l.sub_value_loop == exp_format_line.sub_value_loop
)
sub_lines = safe_eval(

Check warning on line 308 in l10n_th_bank_payment_export/models/bank_payment_export.py

View check run for this annotation

Codecov / codecov/patch

l10n_th_bank_payment_export/models/bank_payment_export.py#L308

Added line #L308 was not covered by tests
exp_format_line.sub_value_loop, globals_dict=globals_dict_line
)

for idx_sub_line, sub_line in enumerate(sub_lines):
for exp_format_sub_line in exp_format_sub_line_group:
# Update globals_dict for sub-loop
globals_dict_sub_line = self._update_global_dict(

Check warning on line 315 in l10n_th_bank_payment_export/models/bank_payment_export.py

View check run for this annotation

Codecov / codecov/patch

l10n_th_bank_payment_export/models/bank_payment_export.py#L315

Added line #L315 was not covered by tests
globals_dict_line, sub_line=sub_line, idx_sub_line=idx_sub_line
)

# Get value from sub-instruction
sub_text_line = exp_format_sub_line._get_value(

Check warning on line 320 in l10n_th_bank_payment_export/models/bank_payment_export.py

View check run for this annotation

Codecov / codecov/patch

l10n_th_bank_payment_export/models/bank_payment_export.py#L320

Added line #L320 was not covered by tests
globals_dict_sub_line
)
text_parts.append(sub_text_line)

Check warning on line 323 in l10n_th_bank_payment_export/models/bank_payment_export.py

View check run for this annotation

Codecov / codecov/patch

l10n_th_bank_payment_export/models/bank_payment_export.py#L323

Added line #L323 was not covered by tests

if exp_format_sub_line.end_line:
# TODO: Change this to configurable
text_parts.append("\r\n")
return text_parts

Check warning on line 328 in l10n_th_bank_payment_export/models/bank_payment_export.py

View check run for this annotation

Codecov / codecov/patch

l10n_th_bank_payment_export/models/bank_payment_export.py#L327-L328

Added lines #L327 - L328 were not covered by tests

def _export_bank_payment_text_file(self):
self.ensure_one()
if self.bank:
return self._generate_bank_payment_text()
return (
"Demo Text File. You can inherit function "
"_generate_bank_payment_text() for customize your format."
)
return "Demo Text File. You must config `Bank Export Format` First."

def _check_constraint_line(self):
# Add condition with line on this function
Expand Down Expand Up @@ -239,10 +391,6 @@
)
return ctx

def _get_amount_no_decimal(self, amount, digits=False):
"""Implementation is available"""
return amount

@api.constrains("effective_date")
def check_effective_date(self):
today = fields.Date.context_today(self)
Expand Down Expand Up @@ -303,3 +451,21 @@
"view_id": view.id,
"context": ctx,
}

# ====================== Function Common Text File ======================

def _get_receiver_address(self, object_address):
receiver_address = " ".join(

Check warning on line 458 in l10n_th_bank_payment_export/models/bank_payment_export.py

View check run for this annotation

Codecov / codecov/patch

l10n_th_bank_payment_export/models/bank_payment_export.py#L458

Added line #L458 was not covered by tests
[
object_address.street or "",
object_address.street2 or "",
object_address.city or "",
object_address.zip or "",
]
)
return receiver_address

Check warning on line 466 in l10n_th_bank_payment_export/models/bank_payment_export.py

View check run for this annotation

Codecov / codecov/patch

l10n_th_bank_payment_export/models/bank_payment_export.py#L466

Added line #L466 was not covered by tests

def _get_address(self, object_address, max_length):
receiver_address = self._get_receiver_address(object_address)
address = receiver_address[:max_length]
return address

Check warning on line 471 in l10n_th_bank_payment_export/models/bank_payment_export.py

View check run for this annotation

Codecov / codecov/patch

l10n_th_bank_payment_export/models/bank_payment_export.py#L469-L471

Added lines #L469 - L471 were not covered by tests
Loading
Loading