From 710ef4aa7adf974ad8f2361be5b9b5808ad5494d Mon Sep 17 00:00:00 2001 From: Graeme Gellatly Date: Mon, 29 Oct 2018 23:55:41 +1300 Subject: [PATCH] WIP Statements --- .../customer_activity_statement_wizard.py | 60 ----- .../report/customer_outstanding_statement.py | 248 ++---------------- .../customer_outstanding_statement_wizard.py | 56 ---- .../README.rst | 0 .../__init__.py | 2 + .../__manifest__.py | 1 + .../demo/account_payment_term.xml | 13 + .../i18n/ca.po | 0 .../i18n/customer_activity_statement.pot | 0 .../i18n/de.po | 0 .../i18n/es.po | 0 .../i18n/fr.po | 0 .../i18n/hr_HR.po | 0 .../i18n/it.po | 0 .../i18n/nl.po | 0 .../i18n/nl_NL.po | 0 .../i18n/pt.po | 0 .../i18n/ro.po | 0 customer_statement/models/__init__.py | 1 + .../models/account_payment_term.py | 18 ++ .../report/__init__.py | 0 .../report/customer_activity_statement.py | 207 +++++++++++++++ .../report/statement_common.py | 144 +++++----- .../static/description/Activity_Statement.png | Bin .../static/description/icon.png | Bin .../static/description/index.html | 0 .../static/src/less/layout_statement.less | 0 .../tests/__init__.py | 0 .../tests/test_customer_activity_statement.py | 0 .../views/account_payment_term.xml | 34 +++ .../views/statement.xml | 0 .../wizard/__init__.py | 0 .../customer_activity_statement_wizard.py | 38 +++ .../customer_activity_statement_wizard.xml | 0 .../customer_outstanding_statement_wizard.py | 27 ++ customer_statement/wizard/statement_common.py | 47 ++++ 36 files changed, 473 insertions(+), 423 deletions(-) delete mode 100644 customer_activity_statement/wizard/customer_activity_statement_wizard.py delete mode 100644 customer_outstanding_statement/wizard/customer_outstanding_statement_wizard.py rename {customer_activity_statement => customer_statement}/README.rst (100%) rename {customer_activity_statement => customer_statement}/__init__.py (72%) rename {customer_activity_statement => customer_statement}/__manifest__.py (94%) create mode 100644 customer_statement/demo/account_payment_term.xml rename {customer_activity_statement => customer_statement}/i18n/ca.po (100%) rename {customer_activity_statement => customer_statement}/i18n/customer_activity_statement.pot (100%) rename {customer_activity_statement => customer_statement}/i18n/de.po (100%) rename {customer_activity_statement => customer_statement}/i18n/es.po (100%) rename {customer_activity_statement => customer_statement}/i18n/fr.po (100%) rename {customer_activity_statement => customer_statement}/i18n/hr_HR.po (100%) rename {customer_activity_statement => customer_statement}/i18n/it.po (100%) rename {customer_activity_statement => customer_statement}/i18n/nl.po (100%) rename {customer_activity_statement => customer_statement}/i18n/nl_NL.po (100%) rename {customer_activity_statement => customer_statement}/i18n/pt.po (100%) rename {customer_activity_statement => customer_statement}/i18n/ro.po (100%) create mode 100644 customer_statement/models/__init__.py create mode 100644 customer_statement/models/account_payment_term.py rename {customer_activity_statement => customer_statement}/report/__init__.py (100%) create mode 100644 customer_statement/report/customer_activity_statement.py rename customer_activity_statement/report/customer_activity_statement.py => customer_statement/report/statement_common.py (77%) rename {customer_activity_statement => customer_statement}/static/description/Activity_Statement.png (100%) rename {customer_activity_statement => customer_statement}/static/description/icon.png (100%) rename {customer_activity_statement => customer_statement}/static/description/index.html (100%) rename {customer_activity_statement => customer_statement}/static/src/less/layout_statement.less (100%) rename {customer_activity_statement => customer_statement}/tests/__init__.py (100%) rename {customer_activity_statement => customer_statement}/tests/test_customer_activity_statement.py (100%) create mode 100644 customer_statement/views/account_payment_term.xml rename {customer_activity_statement => customer_statement}/views/statement.xml (100%) rename {customer_activity_statement => customer_statement}/wizard/__init__.py (100%) create mode 100644 customer_statement/wizard/customer_activity_statement_wizard.py rename {customer_activity_statement => customer_statement}/wizard/customer_activity_statement_wizard.xml (100%) create mode 100644 customer_statement/wizard/customer_outstanding_statement_wizard.py create mode 100644 customer_statement/wizard/statement_common.py diff --git a/customer_activity_statement/wizard/customer_activity_statement_wizard.py b/customer_activity_statement/wizard/customer_activity_statement_wizard.py deleted file mode 100644 index 494e98e3..00000000 --- a/customer_activity_statement/wizard/customer_activity_statement_wizard.py +++ /dev/null @@ -1,60 +0,0 @@ -# Copyright 2018 Eficent Business and IT Consulting Services S.L. -# (http://www.eficent.com) -# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). - -from datetime import date -from odoo import api, fields, models - - -class CustomerActivityStatementWizard(models.TransientModel): - """Customer Activity Statement wizard.""" - - _name = 'customer.activity.statement.wizard' - _description = 'Customer Activity Statement Wizard' - - company_id = fields.Many2one( - comodel_name='res.company', - default=lambda self: self.env.user.company_id, - string='Company' - ) - - date_start = fields.Date(required=True, - default=fields.Date.to_string( - date(date.today().year, 1, 1))) - date_end = fields.Date(required=True, - default=fields.Date.to_string(date.today())) - show_aging_buckets = fields.Boolean(string='Include Aging Buckets', - default=True) - number_partner_ids = fields.Integer( - default=lambda self: len(self._context['active_ids']) - ) - filter_partners_non_due = fields.Boolean( - string='Don\'t show partners with no due entries', default=True) - account_type = fields.Selection( - [('receivable', 'Receivable'), - ('payable', 'Payable')], string='Account type', default='receivable') - - @api.multi - def button_export_pdf(self): - self.ensure_one() - return self._export() - - def _prepare_activity_statement(self): - self.ensure_one() - return { - 'date_start': self.date_start, - 'date_end': self.date_end, - 'company_id': self.company_id.id, - 'partner_ids': self._context['active_ids'], - 'show_aging_buckets': self.show_aging_buckets, - 'filter_non_due_partners': self.filter_partners_non_due, - 'account_type': self.account_type, - } - - def _export(self): - """Export to PDF.""" - data = self._prepare_activity_statement() - return self.env.ref( - 'customer_activity_statement' - '.action_print_customer_activity_statement').report_action( - self, data=data) diff --git a/customer_outstanding_statement/report/customer_outstanding_statement.py b/customer_outstanding_statement/report/customer_outstanding_statement.py index b58bb953..b8a0861c 100644 --- a/customer_outstanding_statement/report/customer_outstanding_statement.py +++ b/customer_outstanding_statement/report/customer_outstanding_statement.py @@ -10,16 +10,11 @@ from odoo import api, fields, models class CustomerOutstandingStatement(models.AbstractModel): """Model of Customer Outstanding Statement""" + _inherit = 'report.statement.common' _name = 'report.customer_outstanding_statement.statement' - def _format_date_to_partner_lang(self, str_date, partner_id): - lang_code = self.env['res.partner'].browse(partner_id).lang - lang = self.env['res.lang']._lang_get(lang_code) - date = datetime.strptime(str_date, DEFAULT_SERVER_DATE_FORMAT).date() - return date.strftime(lang.date_format) - def _display_lines_sql_q0(self, date_end): - return """ + return self._cr.mogrify(""" SELECT l1.id, CASE WHEN l1.reconciled = TRUE and l1.balance > 0.0 THEN max(pd.max_date) @@ -32,20 +27,22 @@ class CustomerOutstandingStatement(models.AbstractModel): FROM account_partial_reconcile pr INNER JOIN account_move_line l2 ON pr.credit_move_id = l2.id - WHERE l2.date <= '%s' + WHERE l2.date <= %(date_end)s ) as pd ON pd.debit_move_id = l1.id LEFT JOIN (SELECT pr.* FROM account_partial_reconcile pr INNER JOIN account_move_line l2 ON pr.debit_move_id = l2.id - WHERE l2.date <= '%s' + WHERE l2.date <= %(date_end)s ) as pc ON pc.credit_move_id = l1.id GROUP BY l1.id - """ % (date_end, date_end) + """, locals() + ) def _display_lines_sql_q1(self, partners, date_end, account_type): - return """ - SELECT m.name as move_id, l.partner_id, l.date, l.name, + partners = tuple(partners) + return self._cr.mogrify(""" + SELECT m.name AS move_id, l.partner_id, l.date, l.name, l.ref, l.blocked, l.currency_id, l.company_id, CASE WHEN (l.currency_id is not null AND l.amount_currency > 0.0) THEN avg(l.amount_currency) @@ -75,25 +72,25 @@ class CustomerOutstandingStatement(models.AbstractModel): FROM account_partial_reconcile pr INNER JOIN account_move_line l2 ON pr.credit_move_id = l2.id - WHERE l2.date <= '%s' + WHERE l2.date <= %(date_end)s ) as pd ON pd.debit_move_id = l.id LEFT JOIN (SELECT pr.* FROM account_partial_reconcile pr INNER JOIN account_move_line l2 ON pr.debit_move_id = l2.id - WHERE l2.date <= '%s' + WHERE l2.date <= %(date_end)s ) as pc ON pc.credit_move_id = l.id - WHERE l.partner_id IN (%s) AND at.type = '%s' + WHERE l.partner_id IN %(partners)s AND at.type = %(account_type)s AND (Q0.reconciled_date is null or - Q0.reconciled_date > '%s') - AND l.date <= '%s' + Q0.reconciled_date > %(date_end)s) + AND l.date <= %(date_end)s GROUP BY l.partner_id, m.name, l.date, l.date_maturity, l.name, l.ref, l.blocked, l.currency_id, l.balance, l.amount_currency, l.company_id - """ % (date_end, date_end, partners, account_type, date_end, date_end) + """, locals()) def _display_lines_sql_q2(self): - return """ + return self._cr.mogrify(""" SELECT partner_id, currency_id, move_id, date, date_maturity, debit, credit, name, ref, blocked, company_id, CASE WHEN currency_id is not null @@ -101,27 +98,28 @@ class CustomerOutstandingStatement(models.AbstractModel): ELSE open_amount END as open_amount FROM Q1 - """ + """, locals()) def _display_lines_sql_q3(self, company_id): - return """ + return self._cr.mogrify(""" SELECT Q2.partner_id, move_id, date, date_maturity, Q2.name, ref, debit, credit, debit-credit AS amount, blocked, COALESCE(Q2.currency_id, c.currency_id) AS currency_id, open_amount FROM Q2 JOIN res_company c ON (c.id = Q2.company_id) - WHERE c.id = %s - """ % company_id + WHERE c.id = %(company_id)s + """, locals()) def _get_account_display_lines(self, company_id, partner_ids, date_end, account_type): res = dict(map(lambda x: (x, []), partner_ids)) - partners = ', '.join([str(i) for i in partner_ids]) - date_end = datetime.strptime( - date_end, DEFAULT_SERVER_DATE_FORMAT).date() + partners = tuple(partner_ids) # pylint: disable=E8103 self.env.cr.execute(""" - WITH Q0 as (%s), Q1 AS (%s), Q2 AS (%s), Q3 AS (%s) + WITH Q0 as (%s), + Q1 AS (%s), + Q2 AS (%s), + Q3 AS (%s) SELECT partner_id, currency_id, move_id, date, date_maturity, debit, credit, amount, open_amount, name, ref, blocked FROM Q3 @@ -134,192 +132,6 @@ class CustomerOutstandingStatement(models.AbstractModel): res[row.pop('partner_id')].append(row) return res - def _show_buckets_sql_q0(self, date_end): - return """ - SELECT l1.id, - CASE WHEN l1.reconciled = TRUE and l1.balance > 0.0 - THEN max(pd.max_date) - WHEN l1.reconciled = TRUE and l1.balance < 0.0 - THEN max(pc.max_date) - ELSE null - END as reconciled_date - FROM account_move_line l1 - LEFT JOIN (SELECT pr.* - FROM account_partial_reconcile pr - INNER JOIN account_move_line l2 - ON pr.credit_move_id = l2.id - WHERE l2.date <= '%s' - ) as pd ON pd.debit_move_id = l1.id - LEFT JOIN (SELECT pr.* - FROM account_partial_reconcile pr - INNER JOIN account_move_line l2 - ON pr.debit_move_id = l2.id - WHERE l2.date <= '%s' - ) as pc ON pc.credit_move_id = l1.id - GROUP BY l1.id - """ % (date_end, date_end) - - def _show_buckets_sql_q1(self, partners, date_end, account_type): - return """ - SELECT l.partner_id, l.currency_id, l.company_id, l.move_id, - CASE WHEN l.balance > 0.0 - THEN l.balance - sum(coalesce(pd.amount, 0.0)) - ELSE l.balance + sum(coalesce(pc.amount, 0.0)) - END AS open_due, - CASE WHEN l.balance > 0.0 - THEN l.amount_currency - sum(coalesce(pd.amount_currency, 0.0)) - ELSE l.amount_currency + sum(coalesce(pc.amount_currency, 0.0)) - END AS open_due_currency, - CASE WHEN l.date_maturity is null - THEN l.date - ELSE l.date_maturity - END as date_maturity - FROM account_move_line l - JOIN account_account_type at ON (at.id = l.user_type_id) - JOIN account_move m ON (l.move_id = m.id) - LEFT JOIN Q0 ON Q0.id = l.id - LEFT JOIN (SELECT pr.* - FROM account_partial_reconcile pr - INNER JOIN account_move_line l2 - ON pr.credit_move_id = l2.id - WHERE l2.date <= '%s' - ) as pd ON pd.debit_move_id = l.id - LEFT JOIN (SELECT pr.* - FROM account_partial_reconcile pr - INNER JOIN account_move_line l2 - ON pr.debit_move_id = l2.id - WHERE l2.date <= '%s' - ) as pc ON pc.credit_move_id = l.id - WHERE l.partner_id IN (%s) AND at.type = '%s' - AND (Q0.reconciled_date is null or - Q0.reconciled_date > '%s') - AND l.date <= '%s' AND not l.blocked - GROUP BY l.partner_id, l.currency_id, l.date, l.date_maturity, - l.amount_currency, l.balance, l.move_id, - l.company_id - """ % (date_end, date_end, partners, account_type, date_end, date_end) - - def _show_buckets_sql_q2(self, date_end, minus_30, minus_60, minus_90, - minus_120): - return """ - SELECT partner_id, currency_id, date_maturity, open_due, - open_due_currency, move_id, company_id, - CASE - WHEN '%s' <= date_maturity AND currency_id is null - THEN open_due - WHEN '%s' <= date_maturity AND currency_id is not null - THEN open_due_currency - ELSE 0.0 - END as current, - CASE - WHEN '%s' < date_maturity AND date_maturity < '%s' - AND currency_id is null THEN open_due - WHEN '%s' < date_maturity AND date_maturity < '%s' - AND currency_id is not null - THEN open_due_currency - ELSE 0.0 - END as b_1_30, - CASE - WHEN '%s' < date_maturity AND date_maturity <= '%s' - AND currency_id is null THEN open_due - WHEN '%s' < date_maturity AND date_maturity <= '%s' - AND currency_id is not null - THEN open_due_currency - ELSE 0.0 - END as b_30_60, - CASE - WHEN '%s' < date_maturity AND date_maturity <= '%s' - AND currency_id is null THEN open_due - WHEN '%s' < date_maturity AND date_maturity <= '%s' - AND currency_id is not null - THEN open_due_currency - ELSE 0.0 - END as b_60_90, - CASE - WHEN '%s' < date_maturity AND date_maturity <= '%s' - AND currency_id is null THEN open_due - WHEN '%s' < date_maturity AND date_maturity <= '%s' - AND currency_id is not null - THEN open_due_currency - ELSE 0.0 - END as b_90_120, - CASE - WHEN date_maturity <= '%s' AND currency_id is null - THEN open_due - WHEN date_maturity <= '%s' AND currency_id is not null - THEN open_due_currency - ELSE 0.0 - END as b_over_120 - FROM Q1 - GROUP BY partner_id, currency_id, date_maturity, open_due, - open_due_currency, move_id, company_id - """ % (date_end, date_end, minus_30, date_end, minus_30, date_end, - minus_60, minus_30, minus_60, minus_30, minus_90, minus_60, - minus_90, minus_60, minus_120, minus_90, minus_120, minus_90, - minus_120, minus_120) - - def _show_buckets_sql_q3(self, company_id): - return """ - SELECT Q2.partner_id, current, b_1_30, b_30_60, b_60_90, b_90_120, - b_over_120, - COALESCE(Q2.currency_id, c.currency_id) AS currency_id - FROM Q2 - JOIN res_company c ON (c.id = Q2.company_id) - WHERE c.id = %s - """ % company_id - - def _show_buckets_sql_q4(self): - return """ - SELECT partner_id, currency_id, sum(current) as current, - sum(b_1_30) as b_1_30, - sum(b_30_60) as b_30_60, - sum(b_60_90) as b_60_90, - sum(b_90_120) as b_90_120, - sum(b_over_120) as b_over_120 - FROM Q3 - GROUP BY partner_id, currency_id - """ - - def _get_bucket_dates(self, date_end): - return { - 'date_end': date_end, - 'minus_30': date_end - timedelta(days=30), - 'minus_60': date_end - timedelta(days=60), - 'minus_90': date_end - timedelta(days=90), - 'minus_120': date_end - timedelta(days=120), - } - - def _get_account_show_buckets(self, company_id, partner_ids, date_end, - account_type): - res = dict(map(lambda x: (x, []), partner_ids)) - partners = ', '.join([str(i) for i in partner_ids]) - date_end = datetime.strptime( - date_end, DEFAULT_SERVER_DATE_FORMAT).date() - full_dates = self._get_bucket_dates(date_end) - # pylint: disable=E8103 - self.env.cr.execute(""" - WITH Q0 AS (%s), Q1 AS (%s), Q2 AS (%s), Q3 AS (%s), Q4 AS (%s) - SELECT partner_id, currency_id, current, b_1_30, b_30_60, b_60_90, - b_90_120, b_over_120, - current+b_1_30+b_30_60+b_60_90+b_90_120+b_over_120 - AS balance - FROM Q4 - GROUP BY partner_id, currency_id, current, b_1_30, b_30_60, b_60_90, - b_90_120, b_over_120""" % ( - self._show_buckets_sql_q0(date_end), - self._show_buckets_sql_q1(partners, date_end, account_type), - self._show_buckets_sql_q2( - full_dates['date_end'], - full_dates['minus_30'], - full_dates['minus_60'], - full_dates['minus_90'], - full_dates['minus_120']), - self._show_buckets_sql_q3(company_id), - self._show_buckets_sql_q4())) - for row in self.env.cr.dictfetchall(): - res[row.pop('partner_id')].append(row) - return res - @api.multi def get_report_values(self, docids, data): company_id = data['company_id'] @@ -359,16 +171,8 @@ class CustomerOutstandingStatement(models.AbstractModel): lines_to_display[partner_id][currency].append(line) if data['show_aging_buckets']: - buckets = self._get_account_show_buckets( + buckets_to_display = self._get_account_show_buckets( company_id, partner_ids, date_end, account_type) - for partner_id in partner_ids: - buckets_to_display[partner_id] = {} - for line in buckets[partner_id]: - currency = self.env['res.currency'].browse( - line['currency_id']) - if currency not in buckets_to_display[partner_id]: - buckets_to_display[partner_id][currency] = [] - buckets_to_display[partner_id][currency] = line return { 'doc_ids': partner_ids, diff --git a/customer_outstanding_statement/wizard/customer_outstanding_statement_wizard.py b/customer_outstanding_statement/wizard/customer_outstanding_statement_wizard.py deleted file mode 100644 index fba83785..00000000 --- a/customer_outstanding_statement/wizard/customer_outstanding_statement_wizard.py +++ /dev/null @@ -1,56 +0,0 @@ -# Copyright 2018 Eficent Business and IT Consulting Services S.L. -# (http://www.eficent.com) -# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). - -from datetime import date -from odoo import api, fields, models - - -class CustomerOutstandingStatementWizard(models.TransientModel): - """Customer Outstanding Statement wizard.""" - - _name = 'customer.outstanding.statement.wizard' - _description = 'Customer Outstanding Statement Wizard' - - company_id = fields.Many2one( - comodel_name='res.company', - default=lambda self: self.env.user.company_id, - string='Company' - ) - - date_end = fields.Date(required=True, - default=fields.Date.to_string(date.today())) - show_aging_buckets = fields.Boolean(string='Include Aging Buckets', - default=True) - number_partner_ids = fields.Integer( - default=lambda self: len(self._context['active_ids']) - ) - filter_partners_non_due = fields.Boolean( - string='Don\'t show partners with no due entries', default=True) - account_type = fields.Selection( - [('receivable', 'Receivable'), - ('payable', 'Payable')], string='Account type', default='receivable') - - @api.multi - def button_export_pdf(self): - self.ensure_one() - return self._export() - - def _prepare_outstanding_statement(self): - self.ensure_one() - return { - 'date_end': self.date_end, - 'company_id': self.company_id.id, - 'partner_ids': self._context['active_ids'], - 'show_aging_buckets': self.show_aging_buckets, - 'filter_non_due_partners': self.filter_partners_non_due, - 'account_type': self.account_type, - } - - def _export(self): - """Export to PDF.""" - data = self._prepare_outstanding_statement() - return self.env.ref( - 'customer_outstanding_statement' - '.action_print_customer_outstanding_statement').report_action( - self, data=data) diff --git a/customer_activity_statement/README.rst b/customer_statement/README.rst similarity index 100% rename from customer_activity_statement/README.rst rename to customer_statement/README.rst diff --git a/customer_activity_statement/__init__.py b/customer_statement/__init__.py similarity index 72% rename from customer_activity_statement/__init__.py rename to customer_statement/__init__.py index 997d84a6..56d9f4d0 100644 --- a/customer_activity_statement/__init__.py +++ b/customer_statement/__init__.py @@ -2,3 +2,5 @@ from . import report from . import wizard +from . import models +from . import wizards diff --git a/customer_activity_statement/__manifest__.py b/customer_statement/__manifest__.py similarity index 94% rename from customer_activity_statement/__manifest__.py rename to customer_statement/__manifest__.py index f3170e53..c09e9927 100644 --- a/customer_activity_statement/__manifest__.py +++ b/customer_statement/__manifest__.py @@ -14,6 +14,7 @@ 'account_invoicing', ], 'data': [ + 'views/account_payment_term.xml', 'views/statement.xml', 'wizard/customer_activity_statement_wizard.xml', ], diff --git a/customer_statement/demo/account_payment_term.xml b/customer_statement/demo/account_payment_term.xml new file mode 100644 index 00000000..21d7c8e8 --- /dev/null +++ b/customer_statement/demo/account_payment_term.xml @@ -0,0 +1,13 @@ + + + + + + + + diff --git a/customer_activity_statement/i18n/ca.po b/customer_statement/i18n/ca.po similarity index 100% rename from customer_activity_statement/i18n/ca.po rename to customer_statement/i18n/ca.po diff --git a/customer_activity_statement/i18n/customer_activity_statement.pot b/customer_statement/i18n/customer_activity_statement.pot similarity index 100% rename from customer_activity_statement/i18n/customer_activity_statement.pot rename to customer_statement/i18n/customer_activity_statement.pot diff --git a/customer_activity_statement/i18n/de.po b/customer_statement/i18n/de.po similarity index 100% rename from customer_activity_statement/i18n/de.po rename to customer_statement/i18n/de.po diff --git a/customer_activity_statement/i18n/es.po b/customer_statement/i18n/es.po similarity index 100% rename from customer_activity_statement/i18n/es.po rename to customer_statement/i18n/es.po diff --git a/customer_activity_statement/i18n/fr.po b/customer_statement/i18n/fr.po similarity index 100% rename from customer_activity_statement/i18n/fr.po rename to customer_statement/i18n/fr.po diff --git a/customer_activity_statement/i18n/hr_HR.po b/customer_statement/i18n/hr_HR.po similarity index 100% rename from customer_activity_statement/i18n/hr_HR.po rename to customer_statement/i18n/hr_HR.po diff --git a/customer_activity_statement/i18n/it.po b/customer_statement/i18n/it.po similarity index 100% rename from customer_activity_statement/i18n/it.po rename to customer_statement/i18n/it.po diff --git a/customer_activity_statement/i18n/nl.po b/customer_statement/i18n/nl.po similarity index 100% rename from customer_activity_statement/i18n/nl.po rename to customer_statement/i18n/nl.po diff --git a/customer_activity_statement/i18n/nl_NL.po b/customer_statement/i18n/nl_NL.po similarity index 100% rename from customer_activity_statement/i18n/nl_NL.po rename to customer_statement/i18n/nl_NL.po diff --git a/customer_activity_statement/i18n/pt.po b/customer_statement/i18n/pt.po similarity index 100% rename from customer_activity_statement/i18n/pt.po rename to customer_statement/i18n/pt.po diff --git a/customer_activity_statement/i18n/ro.po b/customer_statement/i18n/ro.po similarity index 100% rename from customer_activity_statement/i18n/ro.po rename to customer_statement/i18n/ro.po diff --git a/customer_statement/models/__init__.py b/customer_statement/models/__init__.py new file mode 100644 index 00000000..2e019d67 --- /dev/null +++ b/customer_statement/models/__init__.py @@ -0,0 +1 @@ +from . import account_payment_term diff --git a/customer_statement/models/account_payment_term.py b/customer_statement/models/account_payment_term.py new file mode 100644 index 00000000..cd4a0d98 --- /dev/null +++ b/customer_statement/models/account_payment_term.py @@ -0,0 +1,18 @@ +# -*- coding: utf-8 -*- +# Copyright 2018 Graeme Gellatly +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import api, fields, models, _ + + +class AccountPaymentTerm(models.Model): + + _inherit = 'account.payment.term' + + aging_periods = fields.Integer(string='Aging Periods to show', default=4) + + @api.constrains('aging_periods') + def _aging_period_limits(self): + periods = self.mapped('aging_periods') + if max(periods) > 5 or min(periods) < 0: + raise ValidationError(_('Only between 0 and 5 Aging periods allowed')) diff --git a/customer_activity_statement/report/__init__.py b/customer_statement/report/__init__.py similarity index 100% rename from customer_activity_statement/report/__init__.py rename to customer_statement/report/__init__.py diff --git a/customer_statement/report/customer_activity_statement.py b/customer_statement/report/customer_activity_statement.py new file mode 100644 index 00000000..cf20ffd1 --- /dev/null +++ b/customer_statement/report/customer_activity_statement.py @@ -0,0 +1,207 @@ +# Copyright 2018 Eficent Business and IT Consulting Services S.L. +# (http://www.eficent.com) +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). + +from datetime import datetime, timedelta +from odoo.tools.misc import DEFAULT_SERVER_DATE_FORMAT +from odoo import api, fields, models +from collections import defaultdict + + +class CustomerActivityStatement(models.AbstractModel): + """Model of Customer Activity Statement""" + + _inherit = 'report.statement.common' + _name = 'report.customer_activity_statement.statement' + + def _initial_balance_sql_q1(self, partners, date_start, account_type): + return 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 * (-1)) + ELSE sum(l.credit) + END as credit + FROM account_move_line l + JOIN account_account_type at ON (at.id = l.user_type_id) + JOIN account_move m ON (l.move_id = m.id) + WHERE l.partner_id IN %(partners)s AND at.type = %(account_type)s + AND l.date < %(date_start)s AND not l.blocked + GROUP BY l.partner_id, l.currency_id, l.amount_currency, + l.company_id + """, locals()) + + def _initial_balance_sql_q2(self, company_id): + return """ + SELECT Q1.partner_id, debit-credit AS balance, + COALESCE(Q1.currency_id, c.currency_id) AS currency_id + FROM Q1 + JOIN res_company c ON (c.id = Q1.company_id) + WHERE c.id = %s + """ % company_id + + def _get_account_initial_balance(self, company_id, partner_ids, + date_start, account_type, currencies): + balance_start = dict(map(lambda x: (x, []), partner_ids)) + balance_start_to_display = defaultdict({}) + 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""" % (self._initial_balance_sql_q1(partners, date_start, + account_type), + self._initial_balance_sql_q2(company_id))) + for row in self.env.cr.dictfetchall(): + balance_start[row.pop('partner_id')].append(row) + + for partner_id, line in balance_start.items(): + currency = currencies.get( + line['currency_id'], + self.env['res.currency'].browse(line['currency_id']) + ) + balance_start_to_display[partner_id][currency] = \ + line['balance'] + return balance_start_to_display + + def _display_lines_sql_q1(self, partners, date_start, date_end, + account_type): + return self._cr.mogrify(""" + SELECT m.name AS move_id, l.partner_id, l.date, l.name, + l.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, + CASE WHEN l.date_maturity is null + THEN l.date + ELSE l.date_maturity + END as date_maturity + FROM account_move_line l + JOIN account_account_type at ON (at.id = l.user_type_id) + JOIN account_move m ON (l.move_id = m.id) + WHERE l.partner_id IN %(partners)s AND at.type = %(account_type)s + AND %(date_start)s <= l.date AND l.date <= %(date_end)s + GROUP BY l.partner_id, m.name, l.date, l.date_maturity, l.name, + l.ref, l.blocked, l.currency_id, + l.amount_currency, l.company_id + """, locals()) + + def _display_lines_sql_q2(self, company_id): + return self._cr.mogrify(""" + SELECT Q1.partner_id, move_id, date, date_maturity, Q1.name, ref, + debit, credit, debit-credit as amount, blocked, + COALESCE(Q1.currency_id, c.currency_id) AS currency_id + FROM Q1 + JOIN res_company c ON (c.id = Q1.company_id) + WHERE c.id = %(company_id)s + """, locals()) + + def _get_account_display_lines(self, company_id, partner_ids, date_start, + date_end, account_type): + res = dict(map(lambda x: (x, []), partner_ids)) + partners = tuple(partner_ids) + + # pylint: disable=E8103 + self.env.cr.execute(""" + WITH Q1 AS (%s), + Q2 AS (%s) + SELECT partner_id, move_id, date, date_maturity, name, ref, debit, + credit, amount, blocked, currency_id + FROM Q2 + ORDER BY date, date_maturity, move_id""" % ( + self._display_lines_sql_q1(partners, date_start, date_end, + account_type), + self._display_lines_sql_q2(company_id))) + for row in self.env.cr.dictfetchall(): + res[row.pop('partner_id')].append(row) + return res + + @api.multi + def get_report_values(self, docids, data): + if not data: + wiz = self.env["customer.activity.statement.wizard"].with_context( + active_ids=docids, model="res.partner" + ) + data = wiz.create({})._prepare_statement() + company_id = data['company_id'] + partner_ids = data['partner_ids'] + date_start = data['date_start'] + date_end = data['date_end'] + account_type = data['account_type'] + today = fields.Date.today() + + # There should be relatively few of these, so to speed performance we cache them + self._cr.execute(""" + SELECT p.id, l.date_format + FROM res_partner p LEFT JOIN res_lang l ON p.lang=l.id + WHERE p.id IN %(partner_ids)s""", {"partner_ids": tuple(partner_ids)}) + date_formats = {r[0]: r[1] for r in self._cr.fetchall()} + currencies = {x.id: x for x in self.env['res.currency'].search([])} + + + buckets_to_display = {} + lines_to_display = defaultdict({}) + amount_due = defaultdict({}) + currency_to_display = defaultdict({}) + today_display, date_start_display, date_end_display = {}, {}, {} + + balance_start_to_display = self._get_account_initial_balance( + company_id, partner_ids, date_start, account_type, currencies) + + lines = self._get_account_display_lines( + company_id, partner_ids, date_start, date_end, account_type) + + for partner_id in partner_ids: + today_display[partner_id] = self._format_date_to_partner_lang( + today, partner_id, date_formats['partner_id']) + date_start_display[partner_id] = self._format_date_to_partner_lang( + date_start, partner_id, date_formats['partner_id']) + date_end_display[partner_id] = self._format_date_to_partner_lang( + date_end, partner_id, date_formats['partner_id']) + + for line in lines[partner_id]: + currency = self.env['res.currency'].browse(line['currency_id']) + if currency not in lines_to_display[partner_id]: + lines_to_display[partner_id][currency] = [] + currency_to_display[partner_id][currency] = currency + if currency in balance_start_to_display[partner_id]: + amount_due[partner_id][currency] = \ + balance_start_to_display[partner_id][currency] + else: + amount_due[partner_id][currency] = 0.0 + if not line['blocked']: + amount_due[partner_id][currency] += line['amount'] + line['balance'] = amount_due[partner_id][currency] + line['date'] = self._format_date_to_partner_lang( + line['date'], partner_id, date_formats['partner_id']) + line['date_maturity'] = self._format_date_to_partner_lang( + line['date_maturity'], partner_id, date_formats['partner_id']) + lines_to_display[partner_id][currency].append(line) + + if data['show_aging_buckets']: + buckets_to_display = self._get_account_show_buckets( + company_id, partner_ids, date_end, account_type) + + return { + 'doc_ids': partner_ids, + 'doc_model': 'res.partner', + 'docs': self.env['res.partner'].browse(partner_ids), + 'Amount_Due': amount_due, + 'Balance_forward': balance_start_to_display, + 'Lines': lines_to_display, + 'Buckets': buckets_to_display, + 'Currencies': currency_to_display, + 'Show_Buckets': data['show_aging_buckets'], + 'Filter_non_due_partners': data['filter_non_due_partners'], + 'Date_start': date_start_display, + 'Date_end': date_end_display, + 'Date': today_display, + 'account_type': account_type, + } diff --git a/customer_activity_statement/report/customer_activity_statement.py b/customer_statement/report/statement_common.py similarity index 77% rename from customer_activity_statement/report/customer_activity_statement.py rename to customer_statement/report/statement_common.py index 104484b7..663938c6 100644 --- a/customer_activity_statement/report/customer_activity_statement.py +++ b/customer_statement/report/statement_common.py @@ -7,46 +7,18 @@ from odoo.tools.misc import DEFAULT_SERVER_DATE_FORMAT from odoo import api, fields, models -class CustomerActivityStatement(models.AbstractModel): - """Model of Customer Activity Statement""" +class ReportStatementCommon(models.AbstractModel): + """Abstract Report Statement for use in other models""" - _name = 'report.customer_activity_statement.statement' + _name = 'report.statement.common' - def _format_date_to_partner_lang(self, str_date, partner_id): - lang_code = self.env['res.partner'].browse(partner_id).lang - lang = self.env['res.lang']._lang_get(lang_code) - date = datetime.strptime(str_date, DEFAULT_SERVER_DATE_FORMAT).date() + def _format_date_to_partner_lang(self, date, partner_id, date_formats=DEFAULT_SERVER_DATE_FORMAT): + """ + Note: Function kept for backwards compatibility in V12 in case of override. + Consider deprecating in v13 migration or removing + """ return date.strftime(lang.date_format) - def _initial_balance_sql_q1(self, partners, date_start, account_type): - return """ - 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 * (-1)) - ELSE sum(l.credit) - END as credit - FROM account_move_line l - JOIN account_account_type at ON (at.id = l.user_type_id) - JOIN account_move m ON (l.move_id = m.id) - WHERE l.partner_id IN (%s) AND at.type = '%s' - AND l.date < '%s' AND not l.blocked - GROUP BY l.partner_id, l.currency_id, l.amount_currency, - l.company_id - """ % (partners, account_type, date_start) - - def _initial_balance_sql_q2(self, company_id): - return """ - SELECT Q1.partner_id, debit-credit AS balance, - COALESCE(Q1.currency_id, c.currency_id) AS currency_id - FROM Q1 - JOIN res_company c ON (c.id = Q1.company_id) - WHERE c.id = %s - """ % company_id - def _get_account_initial_balance(self, company_id, partner_ids, date_start, account_type): res = dict(map(lambda x: (x, []), partner_ids)) @@ -100,29 +72,8 @@ class CustomerActivityStatement(models.AbstractModel): WHERE c.id = %s """ % company_id - def _get_account_display_lines(self, company_id, partner_ids, date_start, - date_end, account_type): - res = dict(map(lambda x: (x, []), partner_ids)) - partners = ', '.join([str(i) for i in partner_ids]) - date_start = datetime.strptime( - date_start, DEFAULT_SERVER_DATE_FORMAT).date() - date_end = datetime.strptime( - date_end, DEFAULT_SERVER_DATE_FORMAT).date() - # pylint: disable=E8103 - self.env.cr.execute("""WITH Q1 AS (%s), Q2 AS (%s) - SELECT partner_id, move_id, date, date_maturity, name, ref, debit, - credit, amount, blocked, currency_id - FROM Q2 - ORDER BY date, date_maturity, move_id""" % ( - self._display_lines_sql_q1(partners, date_start, date_end, - account_type), - self._display_lines_sql_q2(company_id))) - for row in self.env.cr.dictfetchall(): - res[row.pop('partner_id')].append(row) - return res - def _show_buckets_sql_q0(self, date_end): - return """ + return self._cr_mogrify(""" SELECT l1.id, CASE WHEN l1.reconciled = TRUE and l1.balance > 0.0 THEN max(pd.max_date) @@ -135,19 +86,19 @@ class CustomerActivityStatement(models.AbstractModel): FROM account_partial_reconcile pr INNER JOIN account_move_line l2 ON pr.credit_move_id = l2.id - WHERE l2.date <= '%s' + WHERE l2.date <= %(date_end)s ) as pd ON pd.debit_move_id = l1.id LEFT JOIN (SELECT pr.* FROM account_partial_reconcile pr INNER JOIN account_move_line l2 ON pr.debit_move_id = l2.id - WHERE l2.date <= '%s' + WHERE l2.date <= %(date_end)s ) as pc ON pc.credit_move_id = l1.id GROUP BY l1.id - """ % (date_end, date_end) + """, locals()) def _show_buckets_sql_q1(self, partners, date_end, account_type): - return """ + return self._cr.mogrify(""" SELECT l.partner_id, l.currency_id, l.company_id, l.move_id, CASE WHEN l.balance > 0.0 THEN l.balance - sum(coalesce(pd.amount, 0.0)) @@ -169,22 +120,22 @@ class CustomerActivityStatement(models.AbstractModel): FROM account_partial_reconcile pr INNER JOIN account_move_line l2 ON pr.credit_move_id = l2.id - WHERE l2.date <= '%s' + WHERE l2.date <= %(date_end)s ) as pd ON pd.debit_move_id = l.id LEFT JOIN (SELECT pr.* FROM account_partial_reconcile pr INNER JOIN account_move_line l2 ON pr.debit_move_id = l2.id - WHERE l2.date <= '%s' + WHERE l2.date <= %(date_end)s ) as pc ON pc.credit_move_id = l.id - WHERE l.partner_id IN (%s) AND at.type = '%s' + WHERE l.partner_id IN %(partners)s AND at.type = %(account_type)s AND (Q0.reconciled_date is null or - Q0.reconciled_date > '%s') - AND l.date <= '%s' AND not l.blocked + Q0.reconciled_date > %(date_end)s) + AND l.date <= %(date_end)s AND not l.blocked GROUP BY l.partner_id, l.currency_id, l.date, l.date_maturity, l.amount_currency, l.balance, l.move_id, l.company_id - """ % (date_end, date_end, partners, account_type, date_end, date_end) + """, locals()) def _show_buckets_sql_q2(self, date_end, minus_30, minus_60, minus_90, minus_120): @@ -246,14 +197,14 @@ class CustomerActivityStatement(models.AbstractModel): minus_120, minus_120) def _show_buckets_sql_q3(self, company_id): - return """ + return self._cr.mogrify(""" SELECT Q2.partner_id, current, b_1_30, b_30_60, b_60_90, b_90_120, b_over_120, COALESCE(Q2.currency_id, c.currency_id) AS currency_id FROM Q2 JOIN res_company c ON (c.id = Q2.company_id) - WHERE c.id = %s - """ % company_id + WHERE c.id = %(company_id)s + """, locals()) def _show_buckets_sql_q4(self): return """ @@ -267,7 +218,13 @@ class CustomerActivityStatement(models.AbstractModel): GROUP BY partner_id, currency_id """ - def _get_bucket_dates(self, date_end): + def _get_bucket_dates(self, date_end, aging_type): + return getattr( + self, '_get_bucket_dates_%s' % aging_type, + self._get_bucket_dates_days + )(date_end) + + def _get_bucket_dates_days(date_end): return { 'date_end': date_end, 'minus_30': date_end - timedelta(days=30), @@ -276,9 +233,23 @@ class CustomerActivityStatement(models.AbstractModel): 'minus_120': date_end - timedelta(days=120), } + def _get_bucket_dates_months(date_end): + res = {} + d = date_end + for k in ( + "date_end", + "minus_30", + "minus_60", + "minus_90", + "minus_120", + ): + res[k] = d + d = d.replace(day=1) - timedelta(days=1) + return res + def _get_account_show_buckets(self, company_id, partner_ids, date_end, account_type): - res = dict(map(lambda x: (x, []), partner_ids)) + buckets = dict(map(lambda x: (x, []), partner_ids)) partners = ', '.join([str(i) for i in partner_ids]) date_end = datetime.strptime( date_end, DEFAULT_SERVER_DATE_FORMAT).date() @@ -301,14 +272,24 @@ class CustomerActivityStatement(models.AbstractModel): full_dates['minus_60'], full_dates['minus_90'], full_dates['minus_120']), - self._show_buckets_sql_q3(company_id), + self._show_buckets_sql + _q3(company_id), self._show_buckets_sql_q4())) for row in self.env.cr.dictfetchall(): - res[row.pop('partner_id')].append(row) - return res + buckets[row.pop('partner_id')].append(row) + for partner_id in partner_ids: + buckets_to_display[partner_id] = {} + for line in buckets[partner_id]: + currency = self.env['res.currency'].browse( + line['currency_id']) + if currency not in buckets_to_display[partner_id]: + buckets_to_display[partner_id][currency] = [] + buckets_to_display[partner_id][currency] = line + return buckets_to_display @api.multi def get_report_values(self, docids, data): + company_id = data['company_id'] partner_ids = data['partner_ids'] date_start = data['date_start'] @@ -365,16 +346,9 @@ class CustomerActivityStatement(models.AbstractModel): lines_to_display[partner_id][currency].append(line) if data['show_aging_buckets']: - buckets = self._get_account_show_buckets( + buckets_to_display = self._get_account_show_buckets( company_id, partner_ids, date_end, account_type) - for partner_id in partner_ids: - buckets_to_display[partner_id] = {} - for line in buckets[partner_id]: - currency = self.env['res.currency'].browse( - line['currency_id']) - if currency not in buckets_to_display[partner_id]: - buckets_to_display[partner_id][currency] = [] - buckets_to_display[partner_id][currency] = line + return { 'doc_ids': partner_ids, diff --git a/customer_activity_statement/static/description/Activity_Statement.png b/customer_statement/static/description/Activity_Statement.png similarity index 100% rename from customer_activity_statement/static/description/Activity_Statement.png rename to customer_statement/static/description/Activity_Statement.png diff --git a/customer_activity_statement/static/description/icon.png b/customer_statement/static/description/icon.png similarity index 100% rename from customer_activity_statement/static/description/icon.png rename to customer_statement/static/description/icon.png diff --git a/customer_activity_statement/static/description/index.html b/customer_statement/static/description/index.html similarity index 100% rename from customer_activity_statement/static/description/index.html rename to customer_statement/static/description/index.html diff --git a/customer_activity_statement/static/src/less/layout_statement.less b/customer_statement/static/src/less/layout_statement.less similarity index 100% rename from customer_activity_statement/static/src/less/layout_statement.less rename to customer_statement/static/src/less/layout_statement.less diff --git a/customer_activity_statement/tests/__init__.py b/customer_statement/tests/__init__.py similarity index 100% rename from customer_activity_statement/tests/__init__.py rename to customer_statement/tests/__init__.py diff --git a/customer_activity_statement/tests/test_customer_activity_statement.py b/customer_statement/tests/test_customer_activity_statement.py similarity index 100% rename from customer_activity_statement/tests/test_customer_activity_statement.py rename to customer_statement/tests/test_customer_activity_statement.py diff --git a/customer_statement/views/account_payment_term.xml b/customer_statement/views/account_payment_term.xml new file mode 100644 index 00000000..aa9f556b --- /dev/null +++ b/customer_statement/views/account_payment_term.xml @@ -0,0 +1,34 @@ + + + + + + + account.payment.term.form (in customer_activity_statement) + account.payment.term + + + + + + + + account.payment.term.search (in customer_activity_statement) + account.payment.term + + + + + + + + account.payment.term.tree (in customer_activity_statement) + account.payment.term + + + + + + + diff --git a/customer_activity_statement/views/statement.xml b/customer_statement/views/statement.xml similarity index 100% rename from customer_activity_statement/views/statement.xml rename to customer_statement/views/statement.xml diff --git a/customer_activity_statement/wizard/__init__.py b/customer_statement/wizard/__init__.py similarity index 100% rename from customer_activity_statement/wizard/__init__.py rename to customer_statement/wizard/__init__.py diff --git a/customer_statement/wizard/customer_activity_statement_wizard.py b/customer_statement/wizard/customer_activity_statement_wizard.py new file mode 100644 index 00000000..6fa7ba21 --- /dev/null +++ b/customer_statement/wizard/customer_activity_statement_wizard.py @@ -0,0 +1,38 @@ +# Copyright 2018 Eficent Business and IT Consulting Services S.L. +# (http://www.eficent.com) +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). + +from datetime import date +from odoo import api, fields, models + + +class CustomerActivityStatementWizard(models.TransientModel): + """Customer Activity Statement wizard.""" + + _inherit = 'statement.common' + _name = 'customer.activity.statement.wizard' + _description = 'Customer Activity Statement Wizard' + + + def _get_date_start(self): + for record in self: + record.date_start = fields.Date.context_today() - relativedelta(day=1) + + date_start = fields.Date(required=True, default='_get_date_start') + + account_type = fields.Selection( + [('receivable', 'Receivable'), + ('payable', 'Payable')], string='Account type', default='receivable') + + def _export(self): + """Export to PDF.""" + data = self._prepare_statement() + return self.env.ref( + 'customer_statement' + '.action_print_customer_activity_statement').report_action( + self, data=data) + + def _prepare_activity_statement(self): + res = super()._prepare_activity_statement() + res.update({'date_start': self.date_start}) + return res \ No newline at end of file diff --git a/customer_activity_statement/wizard/customer_activity_statement_wizard.xml b/customer_statement/wizard/customer_activity_statement_wizard.xml similarity index 100% rename from customer_activity_statement/wizard/customer_activity_statement_wizard.xml rename to customer_statement/wizard/customer_activity_statement_wizard.xml diff --git a/customer_statement/wizard/customer_outstanding_statement_wizard.py b/customer_statement/wizard/customer_outstanding_statement_wizard.py new file mode 100644 index 00000000..eff286b6 --- /dev/null +++ b/customer_statement/wizard/customer_outstanding_statement_wizard.py @@ -0,0 +1,27 @@ +# Copyright 2018 Eficent Business and IT Consulting Services S.L. +# (http://www.eficent.com) +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). + +from datetime import date +from odoo import api, fields, models + + +class CustomerOutstandingStatementWizard(models.TransientModel): + """Customer Outstanding Statement wizard.""" + + _name = 'customer.outstanding.statement.wizard' + _description = 'Customer Outstanding Statement Wizard' + + company_id = fields.Many2one( + comodel_name='res.company', + default=lambda self: self.env.user.company_id, + string='Company' + ) + + def _export(self): + """Export to PDF.""" + data = self._prepare_statement() + return self.env.ref( + 'customer_statement' + '.action_print_customer_outstanding_statement').report_action( + self, data=data) diff --git a/customer_statement/wizard/statement_common.py b/customer_statement/wizard/statement_common.py new file mode 100644 index 00000000..79bbfcda --- /dev/null +++ b/customer_statement/wizard/statement_common.py @@ -0,0 +1,47 @@ +# -*- coding: utf-8 -*- +# Copyright 2018 Graeme Gellatly +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import api, fields, models, _ + + +class StatementCommon(models.AbstractModel): + + _name = 'statement.common' + + name = fields.Char() + company_id = fields.Many2one( + comodel_name='res.company', + default=lambda self: self.env.user.company_id, + string='Company' + ) + date_end = fields.Date(required=True, + default=fields.Date.context_today) + show_aging_buckets = fields.Selection( + [('auto', 'Auto'), ('always', 'Always'), ('never', 'Never')], + string='Include Aging Buckets', default=True) + number_partner_ids = fields.Integer( + default=lambda self: len(self._context['active_ids']) + ) + filter_partners_non_due = fields.Boolean( + string='Don\'t show partners with no due entries', default=True) + + @api.multi + def button_export_pdf(self): + self.ensure_one() + return self._export() + + def _prepare_statement(self): + self.ensure_one() + return { + 'date_start': self.date_start, + 'date_end': self.date_end, + 'company_id': self.company_id.id, + 'partner_ids': self._context['active_ids'], + 'show_aging_buckets': self.show_aging_buckets, + 'filter_non_due_partners': self.filter_partners_non_due, + 'account_type': self.account_type, + } + + def _export(self): + raise NotImplementedError