diff --git a/partner_statement/__manifest__.py b/partner_statement/__manifest__.py index d9ea94bd6319..d58321e2d865 100644 --- a/partner_statement/__manifest__.py +++ b/partner_statement/__manifest__.py @@ -7,6 +7,7 @@ "category": "Accounting & Finance", "summary": "OCA Financial Reports", "author": "ForgeFlow, Odoo Community Association (OCA)", + "maintainers": ["MiquelRForgeFlow"], "website": "https://github.com/OCA/account-financial-reporting", "license": "AGPL-3", "depends": ["account", "report_xlsx", "report_xlsx_helper"], diff --git a/partner_statement/report/activity_statement.py b/partner_statement/report/activity_statement.py index a6be08bc17ac..ad0a5d80b48e 100644 --- a/partner_statement/report/activity_statement.py +++ b/partner_statement/report/activity_statement.py @@ -17,37 +17,95 @@ def _initial_balance_sql_q1(self, partners, date_start, account_type): return str( self._cr.mogrify( """ - SELECT l.partner_id, l.currency_id, l.company_id, - CASE WHEN l.currency_id is not null AND l.amount_currency > 0.0 - THEN sum(l.amount_currency) - ELSE sum(l.debit) - END as debit, - CASE WHEN l.currency_id is not null AND l.amount_currency < 0.0 - THEN sum(-l.amount_currency) - ELSE sum(l.credit) - END as credit - FROM account_move_line l - JOIN account_move m ON (l.move_id = m.id) + SELECT l.partner_id, l.currency_id, l.company_id, l.id, + (abs(COALESCE(l.balance, 0.0)) + sum( + coalesce(pr.pr_sign, 0.0) * coalesce(pr.amount, 0.0)) + ) * sign(COALESCE(l.balance, 0.0)) AS open_amount, + (abs(COALESCE(l.amount_currency, 0.0)) + sum( + coalesce(pr.pr_sign, 0.0) * CASE + WHEN pr.currency_id IS NOT NULL AND pr.currency_id = l.currency_id + THEN coalesce(pr.amount_currency, 0.0) + WHEN cur.id IS NOT NULL AND ROUND( + abs(COALESCE(l.balance, 0.0)), cur.decimal_places) > 0.0 + THEN ROUND(coalesce(pr.amount, 0.0) * + COALESCE(l.amount_currency, 0.0) / NULLIF(l.balance, 0.0), + cur.decimal_places) + ELSE ROUND(coalesce(pr.amount, 0.0) * + COALESCE(( + SELECT r.rate FROM res_currency_rate r + JOIN account_move_line aml + ON pr.credit_move_id = aml.id + WHERE r.currency_id = l.currency_id + AND r.name <= aml.date + AND (r.company_id IS NULL + OR r.company_id = l.company_id) + ORDER BY r.company_id, r.name DESC LIMIT 1), 1.0), + cur.decimal_places) + END) + ) * sign(COALESCE(l.amount_currency, 0.0)) AS open_amount_currency + FROM ( + SELECT l.*, CASE + WHEN l.debit = 0.0 AND l.credit = 0.0 AND l.currency_id IS NOT NULL + AND ROUND(COALESCE(l.amount_currency, 0.0), + cur.decimal_places) > 0.0 THEN 1 + WHEN l.debit = 0.0 AND l.credit = 0.0 AND l.currency_id IS NOT NULL + AND ROUND(COALESCE(l.amount_currency, 0.0), + cur.decimal_places) < 0.0 THEN -1 + WHEN l.balance > 0.0 THEN 1 ELSE -1 END as sign + FROM account_move_line l + LEFT JOIN res_currency cur ON cur.id = l.currency_id + ) l + JOIN account_move m ON l.move_id = m.id + LEFT JOIN res_currency cur ON cur.id = l.currency_id + LEFT JOIN LATERAL (SELECT pr.*, + CASE WHEN pr.credit_move_id = l.id THEN l.sign + ELSE -l.sign END AS pr_sign + FROM account_partial_reconcile pr + WHERE pr.max_date < %(date_start)s AND ( + (pr.debit_move_id = l.id) OR (pr.credit_move_id = l.id)) + ) as pr ON TRUE WHERE l.partner_id IN %(partners)s AND l.account_internal_type = %(account_type)s - AND l.date < %(date_start)s AND not l.blocked + AND ( + (pr.id IS NOT NULL AND pr.max_date < %(date_start)s) OR + (pr.id IS NULL) + ) AND l.date < %(date_start)s AND not l.blocked AND m.state IN ('posted') - GROUP BY l.partner_id, l.currency_id, l.amount_currency, l.company_id + GROUP BY l.partner_id, l.currency_id, l.company_id, + l.balance, l.amount_currency, l.id """, locals(), ), "utf-8", ) - def _initial_balance_sql_q2(self, company_id): + def _initial_balance_sql_q2(self): return str( self._cr.mogrify( """ - SELECT Q1.partner_id, debit-credit AS balance, - COALESCE(Q1.currency_id, c.currency_id) AS currency_id + SELECT Q1.partner_id, Q1.currency_id, + sum(CASE WHEN Q1.currency_id is not null + THEN Q1.open_amount_currency + ELSE Q1.open_amount + END) as balance, Q1.company_id FROM Q1 - JOIN res_company c ON (c.id = Q1.company_id) - WHERE c.id = %(company_id)s + GROUP BY Q1.partner_id, Q1.currency_id, Q1.company_id""", + locals(), + ), + "utf-8", + ) + + def _initial_balance_sql_q3(self, company_id): + return str( + self._cr.mogrify( + """ + SELECT Q2.partner_id, Q2.balance, + COALESCE(Q2.currency_id, c.currency_id) AS currency_id + FROM Q2 + JOIN res_company c ON (c.id = Q2.company_id) + JOIN res_currency cur ON cur.id = COALESCE(Q2.currency_id, c.currency_id) + WHERE c.id = %(company_id)s AND + round(Q2.balance, cur.decimal_places) != 0.0 """, locals(), ), @@ -61,12 +119,16 @@ def _get_account_initial_balance( partners = tuple(partner_ids) # pylint: disable=E8103 self.env.cr.execute( - """WITH Q1 AS (%s), Q2 AS (%s) - SELECT partner_id, currency_id, balance - FROM Q2""" + """WITH Q1 AS (%s), + Q2 AS (%s), + Q3 AS (%s) + SELECT partner_id, currency_id, sum(balance) as balance + FROM Q2 + GROUP BY partner_id, currency_id""" % ( self._initial_balance_sql_q1(partners, date_start, account_type), - self._initial_balance_sql_q2(company_id), + self._initial_balance_sql_q2(), + self._initial_balance_sql_q3(company_id), ) ) for row in self.env.cr.dictfetchall(): @@ -78,6 +140,7 @@ def _display_lines_sql_q1(self, partners, date_start, date_end, account_type): self._cr.mogrify( """ SELECT m.name AS move_id, l.partner_id, l.date, + array_agg(l.id ORDER BY l.id) as ids, CASE WHEN (aj.type IN ('sale', 'purchase')) THEN l.name ELSE '/' @@ -90,16 +153,16 @@ def _display_lines_sql_q1(self, partners, date_start, date_end, account_type): WHEN (aj.type in ('bank', 'cash')) THEN 'Payment' ELSE '' - END as ref, + END as case_ref, l.blocked, l.currency_id, l.company_id, - CASE WHEN (l.currency_id is not null AND l.amount_currency > 0.0) - THEN sum(l.amount_currency) - ELSE sum(l.debit) - END as debit, - CASE WHEN (l.currency_id is not null AND l.amount_currency < 0.0) - THEN sum(l.amount_currency * (-1)) - ELSE sum(l.credit) - END as credit, + sum(CASE WHEN (l.currency_id is not null AND l.amount_currency > 0.0) + THEN l.amount_currency + ELSE l.debit + END) as debit, + sum(CASE WHEN (l.currency_id is not null AND l.amount_currency < 0.0) + THEN l.amount_currency * (-1) + ELSE l.credit + END) as credit, CASE WHEN l.date_maturity is null THEN l.date ELSE l.date_maturity @@ -112,21 +175,8 @@ def _display_lines_sql_q1(self, partners, date_start, date_end, account_type): AND %(date_start)s <= l.date AND l.date <= %(date_end)s AND m.state IN ('posted') - GROUP BY l.partner_id, m.name, l.date, l.date_maturity, - CASE WHEN (aj.type IN ('sale', 'purchase')) - THEN l.name - ELSE '/' - END, - CASE - WHEN (aj.type IN ('sale', 'purchase')) AND l.name IS NOT NULL - THEN l.ref - WHEN aj.type IN ('sale', 'purchase') AND l.name IS NULL - THEN m.ref - WHEN (aj.type in ('bank', 'cash')) - THEN 'Payment' - ELSE '' - END, - l.blocked, l.currency_id, l.amount_currency, l.company_id + GROUP BY l.partner_id, m.name, l.date, l.date_maturity, l.name, + aj.type, case_ref, l.blocked, l.currency_id, l.company_id """, locals(), ), @@ -138,7 +188,7 @@ def _display_lines_sql_q2(self, company_id): self._cr.mogrify( """ SELECT Q1.partner_id, Q1.move_id, Q1.date, Q1.date_maturity, - Q1.name, Q1.ref, Q1.debit, Q1.credit, + Q1.name, Q1.case_ref as ref, Q1.debit, Q1.credit, Q1.ids, Q1.debit-Q1.credit as amount, Q1.blocked, COALESCE(Q1.currency_id, c.currency_id) AS currency_id FROM Q1 @@ -161,8 +211,9 @@ def _get_account_display_lines( """ WITH Q1 AS (%s), Q2 AS (%s) - SELECT partner_id, move_id, date, date_maturity, name, ref, debit, - credit, amount, blocked, currency_id + SELECT partner_id, move_id, date, date_maturity, ids, + COALESCE(name, '') as name, COALESCE(ref, '') as ref, + debit, credit, amount, blocked, currency_id FROM Q2 ORDER BY date, date_maturity, move_id""" % ( diff --git a/partner_statement/report/activity_statement_xlsx.py b/partner_statement/report/activity_statement_xlsx.py index 71c0626cd60b..221a7cc11f7d 100644 --- a/partner_statement/report/activity_statement_xlsx.py +++ b/partner_statement/report/activity_statement_xlsx.py @@ -2,7 +2,7 @@ # Copyright 2021 ForgeFlow S.L. # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). -from odoo import _, fields, models +from odoo import _, models class ActivityStatementXslx(models.AbstractModel): @@ -34,7 +34,7 @@ def _write_currency_lines(self, row_pos, sheet, partner, currency, data): ) ) sheet.merge_range( - row_pos, 0, row_pos, 6, statement_header, self.format_right_bold + row_pos, 0, row_pos, 6, statement_header, self.format_left_bold ) row_pos += 1 sheet.write( @@ -44,10 +44,12 @@ def _write_currency_lines(self, row_pos, sheet, partner, currency, data): sheet.merge_range( row_pos, 2, row_pos, 4, _("Description"), self.format_theader_yellow_center ) - sheet.write(row_pos, 5, _("Open Amount"), self.format_theader_yellow_center) + sheet.write(row_pos, 5, _("Original Amount"), self.format_theader_yellow_center) sheet.write(row_pos, 6, _("Balance"), self.format_theader_yellow_center) row_pos += 1 - sheet.write(row_pos, 1, partner_data.get("start"), self.format_tcell_date_left) + sheet.write( + row_pos, 1, partner_data.get("prior_day"), self.format_tcell_date_left + ) sheet.merge_range( row_pos, 2, row_pos, 4, _("Balance Forward"), self.format_tcell_left ) @@ -59,7 +61,7 @@ def _write_currency_lines(self, row_pos, sheet, partner, currency, data): name_to_show = ( line.get("name", "") == "/" or not line.get("name", "") ) and line.get("ref", "") - if line.get("name", "") != "/": + if line.get("name", "") and line.get("name", "") != "/": if not line.get("ref", ""): name_to_show = line.get("name", "") else: @@ -166,7 +168,7 @@ def generate_xlsx_report(self, workbook, data, objects): sheet.write( row_pos, 2, - fields.Date.from_string(data.get("date_end")), + data.get("data", {}).get(partners.ids[0], {}).get("today"), self.format_date_left, ) self._size_columns(sheet) diff --git a/partner_statement/report/outstanding_statement.py b/partner_statement/report/outstanding_statement.py index 7f4063406079..4514afa89f13 100644 --- a/partner_statement/report/outstanding_statement.py +++ b/partner_statement/report/outstanding_statement.py @@ -102,7 +102,7 @@ def _display_lines_sql_q2(self): CASE WHEN Q1.currency_id is not null THEN Q1.open_amount_currency ELSE Q1.open_amount - END as open_amount + END as open_amount, Q1.id FROM Q1 """, locals(), @@ -118,7 +118,7 @@ def _display_lines_sql_q3(self, company_id): Q2.name, Q2.ref, Q2.debit, Q2.credit, Q2.debit-Q2.credit AS amount, blocked, COALESCE(Q2.currency_id, c.currency_id) AS currency_id, - Q2.open_amount + Q2.open_amount, Q2.id FROM Q2 JOIN res_company c ON (c.id = Q2.company_id) JOIN res_currency cur ON cur.id = COALESCE(Q2.currency_id, c.currency_id) @@ -142,7 +142,8 @@ def _get_account_display_lines( Q2 AS (%s), Q3 AS (%s) SELECT partner_id, currency_id, move_id, date, date_maturity, debit, - credit, amount, open_amount, name, ref, blocked + credit, amount, open_amount, COALESCE(name, '') as name, + COALESCE(ref, '') as ref, blocked, id FROM Q3 ORDER BY date, date_maturity, move_id""" % ( diff --git a/partner_statement/report/outstanting_statement_xlsx.py b/partner_statement/report/outstanting_statement_xlsx.py index e0b96a6c2025..23f047663959 100644 --- a/partner_statement/report/outstanting_statement_xlsx.py +++ b/partner_statement/report/outstanting_statement_xlsx.py @@ -2,7 +2,7 @@ # Copyright 2021 ForgeFlow S.L. # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). -from odoo import _, fields, models +from odoo import _, models class OutstandingStatementXslx(models.AbstractModel): @@ -33,7 +33,7 @@ def _write_currency_lines(self, row_pos, sheet, partner, currency, data): ) ) sheet.merge_range( - row_pos, 0, row_pos, 6, statement_header, self.format_right_bold + row_pos, 0, row_pos, 6, statement_header, self.format_left_bold ) row_pos += 1 sheet.write( @@ -50,7 +50,7 @@ def _write_currency_lines(self, row_pos, sheet, partner, currency, data): name_to_show = ( line.get("name", "") == "/" or not line.get("name", "") ) and line.get("ref", "") - if line.get("name", "") != "/": + if line.get("name", "") and line.get("name", "") != "/": if not line.get("ref", ""): name_to_show = line.get("name", "") else: @@ -161,7 +161,7 @@ def generate_xlsx_report(self, workbook, data, objects): sheet.write( row_pos, 2, - fields.Date.from_string(data.get("date_end")), + data.get("data", {}).get(partners.ids[0], {}).get("today"), self.format_date_left, ) self._size_columns(sheet) diff --git a/partner_statement/report/report_statement_common.py b/partner_statement/report/report_statement_common.py index fbada72af709..0f090ef58eb7 100644 --- a/partner_statement/report/report_statement_common.py +++ b/partner_statement/report/report_statement_common.py @@ -360,6 +360,7 @@ def _get_report_values(self, docids, data=None): res = {} # get base data + prior_day = date_start - timedelta(days=1) if date_start else None lines = self._get_account_display_lines( company_id, partner_ids, date_start, date_end, account_type ) @@ -375,7 +376,7 @@ def _get_report_values(self, docids, data=None): else: bucket_labels = {} - # organise and format for report + # organize and format for report format_date = self._format_date_to_partner_lang partners_to_remove = set() for partner_id in partner_ids: @@ -385,6 +386,9 @@ def _get_report_values(self, docids, data=None): date_start, date_formats.get(partner_id, default_fmt) ), "end": format_date(date_end, date_formats.get(partner_id, default_fmt)), + "prior_day": format_date( + prior_day, date_formats.get(partner_id, default_fmt) + ), "currencies": {}, } currency_dict = res[partner_id]["currencies"] diff --git a/partner_statement/views/activity_statement.xml b/partner_statement/views/activity_statement.xml index 67b9d4cabcbb..35af895055e1 100644 --- a/partner_statement/views/activity_statement.xml +++ b/partner_statement/views/activity_statement.xml @@ -50,7 +50,7 @@