diff --git a/customer_activity_statement/README.rst b/customer_activity_statement/README.rst new file mode 100644 index 00000000..23d318ef --- /dev/null +++ b/customer_activity_statement/README.rst @@ -0,0 +1,74 @@ +.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg + :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 + +================================= +Print Customer Activity Statement +================================= + +The activity statement provides details of all activity on the customer receivables +between two selected dates. This includes all invoices, refunds and payments. +Any outstanding balance dated prior to the chosen statement period will appear +as a forward balance at the top of the statement. The list is displayed in chronological +order and is split by currencies. + +Aging details can be shown in the report, expressed in aging buckets (30 days +due, ...), so the customer can review how much is open, due or overdue. + +Configuration +============= + +Users willing to access to this report should have proper Accounting & Finance rights: + +#. Go to *Settings / Users* and edit your user to add the corresponding access rights as follows. +#. In *Application / Accounting & Finance*, select *Accountant* or *Adviser* options. + +Usage +===== + +To use this module, you need to: + +#. Go to Customers and select one or more +#. Press 'Action > Customer Activity Statement' +#. Indicate if you want to display aging buckets + + +.. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas + :alt: Try me on Runbot + :target: https://runbot.odoo-community.org/runbot/91/9.0 + +Bug Tracker +=========== + +Bugs are tracked on `GitHub Issues +`_. In case of trouble, +please check there if your issue has already been reported. If you spotted it +first, help us smash it by providing detailed and welcomed feedback. + +Credits +======= + +Images +------ + +* Odoo Community Association: `Icon `_. + +Contributors +------------ + +* Miquel Raïch + +Maintainer +---------- + +.. image:: https://odoo-community.org/logo.png + :alt: Odoo Community Association + :target: https://odoo-community.org + +This module is maintained by the OCA. + +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. + +To contribute to this module, please visit https://odoo-community.org. diff --git a/customer_activity_statement/__init__.py b/customer_activity_statement/__init__.py new file mode 100644 index 00000000..7e6f294d --- /dev/null +++ b/customer_activity_statement/__init__.py @@ -0,0 +1,7 @@ +# -*- coding: utf-8 -*- +# Copyright 2017 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 . import report +from . import wizard diff --git a/customer_activity_statement/__openerp__.py b/customer_activity_statement/__openerp__.py new file mode 100644 index 00000000..6e8479c2 --- /dev/null +++ b/customer_activity_statement/__openerp__.py @@ -0,0 +1,23 @@ +# -*- coding: utf-8 -*- +# Copyright 2017 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). + +{ + 'name': 'Customer Activity Statement', + 'version': '9.0.1.0.0', + 'category': 'Accounting & Finance', + 'summary': 'OCA Financial Reports', + 'author': "Eficent, Odoo Community Association (OCA)", + 'website': 'https://github.com/OCA/account-financial-reporting', + 'license': 'AGPL-3', + 'depends': [ + 'account', + ], + 'data': [ + 'views/statement.xml', + 'wizard/customer_activity_statement_wizard.xml', + ], + 'installable': True, + 'application': False, +} diff --git a/customer_activity_statement/report/__init__.py b/customer_activity_statement/report/__init__.py new file mode 100644 index 00000000..8f6b4ad9 --- /dev/null +++ b/customer_activity_statement/report/__init__.py @@ -0,0 +1,6 @@ +# -*- coding: utf-8 -*- +# Copyright 2017 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 . import customer_activity_statement diff --git a/customer_activity_statement/report/customer_activity_statement.py b/customer_activity_statement/report/customer_activity_statement.py new file mode 100644 index 00000000..9b2ec147 --- /dev/null +++ b/customer_activity_statement/report/customer_activity_statement.py @@ -0,0 +1,358 @@ +# -*- coding: utf-8 -*- +# Copyright 2017 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 openerp.tools import DEFAULT_SERVER_DATE_FORMAT +from openerp import api, fields, models + + +class CustomerActivityStatement(models.AbstractModel): + """Model of Customer Activity Statement""" + + _name = 'report.customer_activity_statement.statement' + + def _format_date_to_partner_lang(self, str_date, partner_id): + lang_code = self.env['res.partner'].browse(partner_id).lang + lang_id = self.env['res.lang']._lang_get(lang_code) + lang = self.env['res.lang'].browse(lang_id) + date = datetime.strptime(str_date, DEFAULT_SERVER_DATE_FORMAT).date() + return date.strftime(lang.date_format) + + def _initial_balance_sql_q1(self, partners, date_start): + 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 = 'receivable' + AND l.date <= '%s' AND not l.blocked + GROUP BY l.partner_id, l.currency_id, l.amount_currency, + l.company_id + """ % (partners, 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): + 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() + 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), + self._initial_balance_sql_q2(company_id))) + for row in self.env.cr.dictfetchall(): + res[row.pop('partner_id')].append(row) + return res + + def _display_lines_sql_q1(self, partners, date_start, date_end): + return """ + 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 (%s) AND at.type = 'receivable' + AND '%s' < l.date AND l.date <= '%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 + """ % (partners, date_start, date_end) + + def _display_lines_sql_q2(self, company_id): + return """ + 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 = %s + """ % company_id + + def _get_account_display_lines(self, company_id, partner_ids, date_start, + date_end): + 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() + 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), + 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_q1(self, partners, date_end): + 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 (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 = 'receivable' + AND not l.reconciled 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) + + def _show_buckets_sql_q2(self, today, 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 + """ % (today, today, minus_30, today, minus_30, today, 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 + """ + + _bucket_dates = { + 'today': fields.date.today(), + 'minus_30': fields.date.today() - timedelta(days=30), + 'minus_60': fields.date.today() - timedelta(days=60), + 'minus_90': fields.date.today() - timedelta(days=90), + 'minus_120': fields.date.today() - timedelta(days=120), + } + + def _get_account_show_buckets(self, company_id, partner_ids, date_end): + 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() + self.env.cr.execute("""WITH 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_q1(partners, date_end), + self._show_buckets_sql_q2( + self._bucket_dates['today'], + self._bucket_dates['minus_30'], + self._bucket_dates['minus_60'], + self._bucket_dates['minus_90'], + self._bucket_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 render_html(self, data): + company_id = data['company_id'] + partner_ids = data['partner_ids'] + date_start = data['date_start'] + date_end = data['date_end'] + today = fields.Date.today() + + balance_start_to_display, buckets_to_display = {}, {} + lines_to_display, amount_due = {}, {} + currency_to_display = {} + today_display, date_start_display, date_end_display = {}, {}, {} + + balance_start = self._get_account_initial_balance( + company_id, partner_ids, date_start) + + for partner_id in partner_ids: + balance_start_to_display[partner_id] = {} + for line in balance_start[partner_id]: + currency = self.env['res.currency'].browse(line['currency_id']) + if currency not in balance_start_to_display[partner_id]: + balance_start_to_display[partner_id][currency] = [] + balance_start_to_display[partner_id][currency] = \ + line['balance'] + + lines = self._get_account_display_lines( + company_id, partner_ids, date_start, date_end) + + for partner_id in partner_ids: + lines_to_display[partner_id], amount_due[partner_id] = {}, {} + currency_to_display[partner_id] = {} + today_display[partner_id] = self._format_date_to_partner_lang( + today, partner_id) + date_start_display[partner_id] = self._format_date_to_partner_lang( + date_start, partner_id) + date_end_display[partner_id] = self._format_date_to_partner_lang( + date_end, 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) + line['date_maturity'] = self._format_date_to_partner_lang( + line['date_maturity'], partner_id) + lines_to_display[partner_id][currency].append(line) + + if data['show_aging_buckets']: + buckets = self._get_account_show_buckets( + company_id, partner_ids, date_end) + 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 + + docargs = { + '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, + } + return self.env['report'].render( + 'customer_activity_statement.statement', values=docargs) diff --git a/customer_activity_statement/static/description/Activity_Statement.png b/customer_activity_statement/static/description/Activity_Statement.png new file mode 100644 index 00000000..e730c3a7 Binary files /dev/null and b/customer_activity_statement/static/description/Activity_Statement.png differ diff --git a/customer_activity_statement/static/description/icon.png b/customer_activity_statement/static/description/icon.png new file mode 100644 index 00000000..3a0328b5 Binary files /dev/null and b/customer_activity_statement/static/description/icon.png differ diff --git a/customer_activity_statement/static/description/index.html b/customer_activity_statement/static/description/index.html new file mode 100644 index 00000000..9d218b04 --- /dev/null +++ b/customer_activity_statement/static/description/index.html @@ -0,0 +1,77 @@ +
+
+
+

Customer Activity Statement

+
+
+
+ +
+
+
+

The activity statement provides +details of all activity on the customer receivables between two selected dates. This +includes all invoices, refunds and payments. Any outstanding balance dated prior to +the chosen statement period will appear as a forward balance at the top of the statement. +The list is displayed in chronological order and is split by currencies.

Aging +details can be shown in the report, expressed in aging buckets (30 days due, ...), +so the customer can review how much is open, due or overdue.

+
+
+
+ +
+
+
+

Configuration

+
+
+

To configure this module, you need to: +

    +
  • Go to Settings / Users and edit your user to add the corresponding access rights as follows.
  • +
  • In Application / Accounting & Finance, select Accountant or Adviser options.
  • +
+

+
+
+
+ +
+
+
+

Usage

+
+
+

To use this module, you need to: +

    +
  • Go to Customers and select one or more
  • +
  • Press 'Action > Customer Activity Statement'
  • +
  • Indicate if you want to display aging buckets
  • +
+

+
+
+
+ +
+
+
+

Credits

+
+
+

Contributors

+ +
+
+

Maintainer

+

+ This module is maintained by the OCA.
+ 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.
+ To contribute to this module, please visit http://odoo-community.org.
+ +

+
+
+
\ No newline at end of file diff --git a/customer_activity_statement/tests/__init__.py b/customer_activity_statement/tests/__init__.py new file mode 100644 index 00000000..1b672b5e --- /dev/null +++ b/customer_activity_statement/tests/__init__.py @@ -0,0 +1,6 @@ +# -*- coding: utf-8 -*- +# Copyright 2017 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 . import test_customer_activity_statement diff --git a/customer_activity_statement/tests/test_customer_activity_statement.py b/customer_activity_statement/tests/test_customer_activity_statement.py new file mode 100644 index 00000000..700bbbdc --- /dev/null +++ b/customer_activity_statement/tests/test_customer_activity_statement.py @@ -0,0 +1,67 @@ +# -*- coding: utf-8 -*- +# Copyright 2017 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 openerp.tests.common import TransactionCase + + +class TestCustomerActivityStatement(TransactionCase): + """ + Tests for Customer Activity Statement. + """ + def setUp(self): + super(TestCustomerActivityStatement, self).setUp() + + self.res_users_model = self.env['res.users'] + self.company = self.env.ref('base.main_company') + self.partner1 = self.env.ref('base.res_partner_1') + self.partner2 = self.env.ref('base.res_partner_2') + self.g_account_user = self.env.ref('account.group_account_user') + + self.user = self._create_user('user_1', [self.g_account_user], + self.company).id + + self.statement_model = \ + self.env['report.customer_activity_statement.statement'] + self.wiz = self.env['customer.activity.statement.wizard'] + self.report_name = 'customer_activity_statement.statement' + self.report_title = 'Customer Activity Statement' + + def _create_user(self, login, groups, company): + group_ids = [group.id for group in groups] + user = self.res_users_model.create({ + 'name': login, + 'login': login, + 'password': 'demo', + 'email': 'example@yourcompany.com', + 'company_id': company.id, + 'company_ids': [(4, company.id)], + 'groups_id': [(6, 0, group_ids)] + }) + return user + + def test_customer_activity_statement(self): + + wiz_id = self.wiz.with_context( + active_ids=[self.partner1.id, self.partner2.id], + ).create({}) + + statement = wiz_id.button_export_pdf() + + self.assertDictContainsSubset( + { + 'type': 'ir.actions.report.xml', + 'report_name': self.report_name, + 'report_type': 'qweb-pdf', + }, + statement, + 'There was an error and the PDF report was not generated.' + ) + + data = wiz_id._prepare_activity_statement() + report = self.statement_model.render_html(data) + self.assertIsInstance(report, str, + "There was an error while compiling the report.") + self.assertIn("", report, + "There was an error while compiling the report.") diff --git a/customer_activity_statement/views/statement.xml b/customer_activity_statement/views/statement.xml new file mode 100644 index 00000000..1701cf4c --- /dev/null +++ b/customer_activity_statement/views/statement.xml @@ -0,0 +1,204 @@ + + + + + + + + diff --git a/customer_activity_statement/wizard/__init__.py b/customer_activity_statement/wizard/__init__.py new file mode 100644 index 00000000..8f76a2a2 --- /dev/null +++ b/customer_activity_statement/wizard/__init__.py @@ -0,0 +1,6 @@ +# -*- coding: utf-8 -*- +# Copyright 2017 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 . import customer_activity_statement_wizard diff --git a/customer_activity_statement/wizard/customer_activity_statement_wizard.py b/customer_activity_statement/wizard/customer_activity_statement_wizard.py new file mode 100644 index 00000000..a2a48c98 --- /dev/null +++ b/customer_activity_statement/wizard/customer_activity_statement_wizard.py @@ -0,0 +1,55 @@ +# -*- coding: utf-8 -*- +# Copyright 2017 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, timedelta +from openerp 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.today()-timedelta(days=120))) + 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) + + @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, + } + + def _export(self): + """Export to PDF.""" + data = self._prepare_activity_statement() + return self.env['report'].with_context(landscape=True).get_action( + self, 'customer_activity_statement.statement', data=data) diff --git a/customer_activity_statement/wizard/customer_activity_statement_wizard.xml b/customer_activity_statement/wizard/customer_activity_statement_wizard.xml new file mode 100644 index 00000000..8e381221 --- /dev/null +++ b/customer_activity_statement/wizard/customer_activity_statement_wizard.xml @@ -0,0 +1,51 @@ + + + + + + + + + Customer Activity Statement Wizard + customer.activity.statement.wizard + +
+
+

+ + + + + + + + + + + + + + + +
+
+
+
+