OCA, or the Odoo Community Association, is a nonprofit organization whose
mission is to support the collaborative development of Odoo features and
promote its widespread use.
diff --git a/account_factoring_receivable_balance_factofrance/data/subrogation_seq.xml b/account_factoring_receivable_balance_factofrance/data/subrogation_seq.xml
index 6bca09c9c8..b0bb0daa3f 100644
--- a/account_factoring_receivable_balance_factofrance/data/subrogation_seq.xml
+++ b/account_factoring_receivable_balance_factofrance/data/subrogation_seq.xml
@@ -2,7 +2,7 @@
Factoring Receivablefactoring.receivable
- 5
+ 3
-
\ No newline at end of file
+
diff --git a/account_factoring_receivable_balance_factofrance/models/subrogation_receipt.py b/account_factoring_receivable_balance_factofrance/models/subrogation_receipt.py
index d81bc9dc50..895e7e4178 100644
--- a/account_factoring_receivable_balance_factofrance/models/subrogation_receipt.py
+++ b/account_factoring_receivable_balance_factofrance/models/subrogation_receipt.py
@@ -3,10 +3,9 @@
import base64
import re
-from odoo import Command, _, api, fields, models, tools
+from odoo import Command, _, api, fields, models
from odoo.exceptions import UserError
-FORMAT_VERSION = "7.0"
RETURN = "\r\n"
@@ -55,14 +54,17 @@ def _prepare_factor_file_factofrance(self):
}
def action_confirm(self):
- if self.name == _("New"):
- self.name = self.env["ir.sequence"].next_by_code("factoring.receivable")
- super().action_confirm()
+ for rec in self:
+ if rec.name == _("New"):
+ rec.name = self.env["ir.sequence"].next_by_code("factoring.receivable")
+ res = super().action_confirm()
+ self.write({"state": "posted"})
+ return res
def action_draft(self):
- self.state = "draft"
+ self.write({"state": "draft"})
self.env["ir.attachment"].search(
- [("res_id", "=", self.id), ("res_model", "=", self._name)]
+ [("res_id", "in", self.ids), ("res_model", "=", self._name)]
).unlink()
def _prepare_factor_file_data_factofrance(self):
@@ -80,22 +82,13 @@ def _prepare_factor_file_data_factofrance(self):
header = self._get_factofrance_header()
# check_column_size(header)
- body, max_row, balance = self._get_factofrance_body()
- ender = self._get_factofrance_ender(max_row, balance)
+ rows, totals = self._prepare_factofrance_body()
+ body = self._get_factofrance_body(rows)
+ ender = self._get_factofrance_ender(totals)
+ self.balance = totals["balance"]
# check_column_size(ender)
- raw_data = f"{header}{RETURN}{body}{RETURN}{ender}{RETURN}".replace(
- "False", " "
- )
- # data = clean_string(raw_data)
- data = raw_data
- dev_mode = tools.config.options.get("dev_mode")
- if dev_mode and dev_mode[0][-3:] == "pdb" or False:
- # make debugging easier saving file on filesystem to check
- debug(raw_data, "_raw")
- debug(data)
- # pylint: disable=C8107
- raise UserError("See files /odoo/subrog*.txt")
- self.write({"balance": balance})
+ raw_data = RETURN.join([header, body, ender])
+ data = clean_string(raw_data)
# non ascii chars are replaced
data = bytes(data, "ascii", "replace").replace(b"?", b" ")
return base64.b64encode(data)
@@ -105,73 +98,54 @@ def _get_factofrance_header(self):
factor_code = str(self.factor_journal_id.factor_code).ljust(4, " ")
company_name = self.company_id.name.ljust(40, " ")
- create_date = factofrance_date(self.create_date).ljust(8, " ")
- identification_type = "1".ljust(1, " ")
- country_codes = "2".ljust(1, " ")
- company_identifiers = (self.company_id.siret or self.company_id.vat).ljust(
- 14, " "
+ confirm_date = factofrance_date(self.date)
+ identification_type = "1"
+ country_codes = "2"
+ company_identifiers = get_column(
+ self.company_id.siret or self.company_id.vat, 14
)
- file_sequence_number = str(self.id).ljust(16, " ")
+ file_sequence_number = ("000" + self.name)[-3:]
currency = self.company_id.currency_id.name.ljust(3, " ")
operation_type = "000000".ljust(6, " ")
# Create a list with the header parts, initially filled with spaces
header_parts = [" "] * 360
-
- # Insert each value at the specified position
header_parts[0:2] = "100"
- header_parts[
- 3:7
- ] = factor_code # Position 4 (0-based index 3) to 7 (not inclusive)
+ header_parts[3:7] = factor_code # Position 4 to 7 (not inclusive)
header_parts[9:49] = company_name # Position 10 to 49
- header_parts[49:56] = create_date # Position 50 to 56
+ header_parts[49:56] = confirm_date # Position 50 to 56
header_parts[117] = identification_type # Position 118
header_parts[118] = country_codes # Position 119
header_parts[119:133] = company_identifiers # Position 120 to 133
- header_parts[135:151] = file_sequence_number # Position 136 to 151
- header_parts[351:354] = currency # Position 352 to 354
+ header_parts[135:137] = file_sequence_number # Position 136 to 139
+ header_parts[351:353] = currency # Position 352 to 354
header_parts[354:360] = operation_type # Position 355 to 360
# Join the list into a single string
header = "".join(header_parts)
-
return header
- # return string.format(**info)
-
- def _get_factofrance_ender(self, max_row, balance):
- self = self.sudo()
- invoices = self.item_ids.filtered(
- lambda line: not line.is_refund
- and line.move_id.move_type in ("out_invoice")
- )
- credit_notes = self.item_ids.filtered(
- lambda line: line.is_refund and line.move_id.move_type in ("out_refund")
- )
+ def _get_factofrance_ender(self, totals):
info = {
"code": "199",
"factor_code": str(self.factor_journal_id.factor_code),
"company_name": self.company_id.name,
- "create_date": factofrance_date(self.create_date),
- # "number_of_invoices": str(len(invoices.mapped("move_id"))),
- # "total_amount_of_invoices": pad(sum(invoices.mapped("move_id.amount_total")), ),
- # "number_of_credit_notes": len(credit_notes.mapped("move_id")),
- # "total_amount_of_credit_notes": sum(
- # credit_notes.mapped("move_id.amount_total")
- # ),
- # "number_of_payments": len(self.item_ids.mapped("move_id")),
- # "total_amount_of_payments": sum(
- # self.item_ids.mapped("move_id.amount_total")
- # ),
- # "currency": self.company_id.currency_id.name,
+ "confirm_date": factofrance_date(self.date),
+ "currency": self.company_id.currency_id.name,
"operation_type": "000000",
}
end_parts = [" "] * 360
end_parts[0:2] = info.get("code")
- end_parts[2:8] = info.get("factor_code")
+ end_parts[3:8] = info.get("factor_code")
end_parts[9:49] = info.get("company_name")
- end_parts[49:57] = info.get("create_date")
- end_parts[58:353] = "".ljust(395, " ")
+ end_parts[49:56] = info.get("confirm_date")
+ end_parts[57:60] = get_column(totals["number_of_invoices"], 4)
+ end_parts[61:75] = get_column(totals["total_amount_of_invoices"], 15)
+ end_parts[76:79] = get_column(totals["number_of_credit_notes"], 4)
+ end_parts[80:94] = get_column(totals["total_amount_of_credit_notes"], 15)
+ end_parts[95:98] = get_column(totals["number_of_payments"], 4)
+ end_parts[99:113] = get_column(totals["total_amount_of_payments"], 15)
+ end_parts[351:353] = info.get("currency")
end_parts[354:357] = info.get("operation_type")
# Join the list into a single string
end = "".join(end_parts)
@@ -219,51 +193,65 @@ def action_compute_lines(self):
amls.write({"subrogation_id": self.id})
return res
- def _get_factofrance_body(self):
- self = self.sudo()
+ def _prepare_factofrance_body(self):
rows = []
- balance = 0
- lines = self.line_ids
- for line in lines:
+ totals = {
+ "number_of_invoices": 0,
+ "total_amount_of_invoices": 0.00,
+ "number_of_credit_notes": 0,
+ "total_amount_of_credit_notes": 0.00,
+ "number_of_payments": 0,
+ "total_amount_of_payments": 0.00,
+ "balance": 0.00,
+ }
+ for line in self.item_ids:
move = line.move_id
partner = line.move_id.partner_id.commercial_partner_id
if not partner:
raise UserError(f"Pas de partenaire sur la pièce {line.move_id}")
- total = move.amount_total_in_currency_signed
+
+ code = self.get_code(move, line)
+ amount = line.debit or line.credit
+ if code == "101":
+ totals["number_of_invoices"] += 1
+ totals["total_amount_of_invoices"] += amount
+ elif code == "102":
+ totals["number_of_credit_notes"] += 1
+ totals["total_amount_of_credit_notes"] += amount
+ elif code == "103":
+ totals["number_of_payments"] += 1
+ totals["total_amount_of_payments"] += amount
+ totals["balance"] += line.balance
info = {
- "code": str(self.get_code(move, line)) or " ",
- "create_date": factofrance_date(move.create_date) or " ",
+ "code": code or " ",
+ "confirm_date": factofrance_date(self.date),
"factor_code": str(self.factor_journal_id.factor_code) or " ",
- "company_identifiers": self.company_id.siret
- or self.company_id.vat
- or " ",
- "document": "siret" if partner.siret else "vat",
+ "document": partner.siret or partner.vat or "0",
"document2": " ",
- "customer_name": partner.name
- and partner.name.ljust(40, " ")
- or "".ljust(40, " "),
- "customer_street": partner.street
- and partner.street.ljust(40, " ")
- or " ",
- "customer_street2": partner.street2
- and partner.street2.ljust(40, " ")
- or "".ljust(40, " "),
+ "customer_name": (partner.name or " ").ljust(40, " "),
+ "customer_address": " ".join(
+ x for x in [partner.street, partner.street2, partner.street3] if x
+ ),
+ # "customer_street": partner.street
+ # and partner.street.ljust(40, " ")
+ # or " ",
+ # "customer_street2": partner.street2
+ # and partner.street2.ljust(40, " ")
+ # or "".ljust(40, " "),
"customer_zip_code": partner.zip or " ",
- "delivery_office": " ",
+ "delivery_office": partner.city or " ",
"customer_country_code": partner.country_id.code or " ",
"customer_phone": partner.mobile or partner.phone or " ",
"customer_ref": partner.ref
and partner.ref.ljust(10, " ")
or "".ljust(10, " "),
- "date": factofrance_date(move.invoice_date or move.date) or " ",
- "number": move.name[:15].ljust(15, " "),
+ "date": factofrance_date(move.invoice_date or move.date),
+ "number": get_column(move.name.replace("/", ""), 15),
"currency": self.company_id.currency_id.name or " ",
"account_sign": "+" if move.move_type == "out_invoice" else "-",
- "amount": (str(line.debit or line.credit).replace(".", "")).rjust(
- 15, "0"
- )
- or " ",
+ "amount": (str(amount).replace(".", "")).rjust(15, "0") or " ",
+ "payment_mode": "VIR",
"invoice_date_due": factofrance_date(move.invoice_date_due)
if move.move_type == "out_invoice"
else "".ljust(11, " ") or " ",
@@ -272,15 +260,22 @@ def _get_factofrance_body(self):
or "".ljust(40, " "),
"operation_type": get_type_piece(move, line) or " ",
}
- balance += total
+ rows.append(info)
+ return rows, totals
+
+ def _get_factofrance_body(self, rows_info):
+ rows = []
+ for info in rows_info:
line = [" "] * 361
line[0:2] = info.get("code")
- line[3:10] = info.get("create_date")
- line[17:24] = info.get("document")
+ line[3:10] = factofrance_date(self.date)
+ line[11:16] = info.get("factor_code")
+ line[17:25] = info.get("document")
line[26:30] = info.get("document2")
line[31:70] = info.get("customer_name")
- line[111:150] = info.get("customer_street")
- line[151:190] = info.get("customer_street2")
+ line[111:190] = info.get("customer_address")
+ # line[111:150] = info.get("customer_street")
+ # line[151:190] = info.get("customer_street2")
line[191:196] = info.get("customer_zip_code")
line[197:230] = info.get("delivery_office")
line[231:233] = info.get("customer_country_code")
@@ -291,27 +286,17 @@ def _get_factofrance_body(self):
line[277:279] = info.get("currency")
line[280:280] = info.get("account_sign")
line[281:295] = info.get("amount")
- if move.move_type == "out_invoice":
- line[299:306] = info.get("invoice_date_due")
- else:
- line[297:306] = info.get("invoice_date_due")
- line[317:356] = info.get("customer_order_reference")
+ line[296:298] = info.get("payment_mode")
+ line[299:306] = info.get("invoice_date_due")
+ line[307:316] = info.get("customer_order_reference")
line[357:360] = info.get("operation_type")
formatted_string = "".join(line)
-
rows.append(formatted_string)
- return (RETURN.join(rows), len(rows), balance)
-
-
-def get_piece_factor(name, p_type):
- if not p_type:
- return "{}{}".format(name[:15], pad(" ", 15))
- return name[:30]
+ return RETURN.join(rows)
def get_type_piece(move, line):
- journal_type = move.journal_id.type
p_type = " "
move_type = move.move_type
if move_type == "entry":
@@ -326,28 +311,38 @@ def get_type_piece(move, line):
return p_type
-def factofrance_date(date_field):
- return date_field.strftime("%Y%m%d")
+def get_column(value, size):
+ if isinstance(value, int):
+ if value >= pow(10, size):
+ res = "9" * size
+ else:
+ res = str(value or 0).rjust(size, "0")
+ res = res[-size:]
+ elif isinstance(value, float):
+ if value * 100 >= pow(10, size):
+ res = "9" * size
+ else:
+ res = str(round(value * 100) or 0).rjust(size, "0")
+ res = res[-size:]
+ else:
+ res = str(value).ljust(size, " ")
+ res = res[:size]
+ return res
+
+
+def factofrance_date(date_value):
+ return date_value.strftime("%Y%m%d") if date_value else " "
def clean_string(string):
"""Remove all except [A-Z], space, \r, \n
https://www.rapidtables.com/code/text/ascii-table.html"""
- string = string.replace(FORMAT_VERSION, "FORMATVERSION")
string = string.upper()
string = re.sub(r"[\x21-\x2F]|[\x3A-\x40]|[\x5E-\x7F]|\x0A\x0D", r" ", string)
- string = string.replace("FORMATVERSION", FORMAT_VERSION)
+ string = string.replace("False", " ")
return string
-def debug(content, suffix=""):
- mpath = f"/odoo/subrog{suffix}.txt"
- with open(mpath, "wb") as f:
- if isinstance(content, str):
- content = bytes(content, "ascii", "replace")
- f.write(content)
-
-
# def check_column_size(string, fstring=None, info=None):
# print("\n string", string)
# if len(string) != 358:
diff --git a/account_factoring_receivable_balance_factofrance/static/description/index.html b/account_factoring_receivable_balance_factofrance/static/description/index.html
index 5b586d9bb0..f401d54356 100644
--- a/account_factoring_receivable_balance_factofrance/static/description/index.html
+++ b/account_factoring_receivable_balance_factofrance/static/description/index.html
@@ -8,11 +8,10 @@
/*
:Author: David Goodger (goodger@python.org)
-:Id: $Id: html4css1.css 9511 2024-01-13 09:50:07Z milde $
+:Id: $Id: html4css1.css 8954 2022-01-20 10:10:25Z milde $
:Copyright: This stylesheet has been placed in the public domain.
Default cascading style sheet for the HTML output of Docutils.
-Despite the name, some widely supported CSS2 features are used.
See https://docutils.sourceforge.io/docs/howto/html-stylesheets.html for how to
customize this style sheet.
@@ -275,7 +274,7 @@
margin-left: 2em ;
margin-right: 2em }
-pre.code .ln { color: gray; } /* line numbers */
+pre.code .ln { color: grey; } /* line numbers */
pre.code, code { background-color: #eeeeee }
pre.code .comment, code .comment { color: #5C6576 }
pre.code .keyword, code .keyword { color: #3B0D06; font-weight: bold }
@@ -301,7 +300,7 @@
span.pre {
white-space: pre }
-span.problematic, pre.problematic {
+span.problematic {
color: red }
span.section-subtitle {
@@ -444,9 +443,7 @@
OCA, or the Odoo Community Association, is a nonprofit organization whose
mission is to support the collaborative development of Odoo features and
promote its widespread use.