From 91d59c221ee77f9662ba00d18d89a2373e053c74 Mon Sep 17 00:00:00 2001 From: Luc De Meyer Date: Tue, 12 Nov 2019 20:49:26 +0100 Subject: [PATCH] [11.0][ADD]account_overdue --- account_overdue/README.rst | 30 ++ account_overdue/__init__.py | 3 + account_overdue/__manifest__.py | 22 ++ account_overdue/i18n/fr.po | 130 +++++++++ account_overdue/i18n/it.po | 138 +++++++++ account_overdue/i18n/nl.po | 130 +++++++++ account_overdue/models/__init__.py | 1 + account_overdue/models/res_partner.py | 42 +++ account_overdue/report/__init__.py | 1 + .../report/print_overdue_payment.py | 268 ++++++++++++++++++ .../report/print_overdue_report.xml | 41 +++ account_overdue/static/description/icon.png | Bin 0 -> 8113 bytes account_overdue/views/report_overdue.xml | 140 +++++++++ .../views/report_overdue_layout.xml | 73 +++++ .../views/report_overdue_style.xml | 44 +++ account_overdue/views/res_company.xml | 18 ++ account_overdue/views/res_partner.xml | 18 ++ account_overdue/wizard/__init__.py | 1 + .../wizard/overdue_payment_wizard.py | 41 +++ .../wizard/overdue_payment_wizard.xml | 41 +++ 20 files changed, 1182 insertions(+) create mode 100644 account_overdue/README.rst create mode 100644 account_overdue/__init__.py create mode 100644 account_overdue/__manifest__.py create mode 100644 account_overdue/i18n/fr.po create mode 100644 account_overdue/i18n/it.po create mode 100644 account_overdue/i18n/nl.po create mode 100644 account_overdue/models/__init__.py create mode 100644 account_overdue/models/res_partner.py create mode 100644 account_overdue/report/__init__.py create mode 100644 account_overdue/report/print_overdue_payment.py create mode 100644 account_overdue/report/print_overdue_report.xml create mode 100644 account_overdue/static/description/icon.png create mode 100644 account_overdue/views/report_overdue.xml create mode 100644 account_overdue/views/report_overdue_layout.xml create mode 100644 account_overdue/views/report_overdue_style.xml create mode 100644 account_overdue/views/res_company.xml create mode 100644 account_overdue/views/res_partner.xml create mode 100644 account_overdue/wizard/__init__.py create mode 100644 account_overdue/wizard/overdue_payment_wizard.py create mode 100644 account_overdue/wizard/overdue_payment_wizard.xml diff --git a/account_overdue/README.rst b/account_overdue/README.rst new file mode 100644 index 00000000..799a8229 --- /dev/null +++ b/account_overdue/README.rst @@ -0,0 +1,30 @@ +.. image:: https://img.shields.io/badge/license-AGPL--3-blue.png + :target: https://www.gnu.org/licenses/agpl + :alt: License: AGPL-3 + +============================== +Overdue Payments customisation +============================== + +Features +-------- + +* The print wizard allows to print a single payment notice as well as + notices for all customers. +* The print wizard allows to select receivables or both + receivables and payables and hence covers the case of Partners + which are Customer and Supplier at the same time. +* Outstanding amounts are displayed in invoice currency. +* The litigation column has been replaced by an indication that + clearly shows the payments that are overdue. +* The Invoice number is printed for outgoing transactions. +* The payment notice text can be customized with html tags. +* An "Overdue" button is added to the Customer search view to + facilitate the retrieval of Customers with Overdue payments. + +Overdue message template customization +-------------------------------------- +* %{partner_name}s: insert partner name +* %{date}s: insert date +* %{company_name}: insert company.name +* %{user_signature}: insert user signature diff --git a/account_overdue/__init__.py b/account_overdue/__init__.py new file mode 100644 index 00000000..7660e7bf --- /dev/null +++ b/account_overdue/__init__.py @@ -0,0 +1,3 @@ +from . import models +from . import report +from . import wizard diff --git a/account_overdue/__manifest__.py b/account_overdue/__manifest__.py new file mode 100644 index 00000000..f3f46cef --- /dev/null +++ b/account_overdue/__manifest__.py @@ -0,0 +1,22 @@ +# Copyright 2009-2019 Noviat. +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +{ + 'name': 'Overdue Payments customisation', + 'version': '11.0.1.0.0', + 'license': 'AGPL-3', + 'author': 'Noviat', + 'website': 'http://www.noviat.com', + 'category': 'Accounting & Finance', + 'depends': ['account'], + 'data': [ + 'report/print_overdue_report.xml', + 'views/res_partner.xml', + 'views/res_company.xml', + 'views/report_overdue.xml', + 'views/report_overdue_style.xml', + 'views/report_overdue_layout.xml', + 'wizard/overdue_payment_wizard.xml', + ], + 'installable': True, +} diff --git a/account_overdue/i18n/fr.po b/account_overdue/i18n/fr.po new file mode 100644 index 00000000..f288c8b2 --- /dev/null +++ b/account_overdue/i18n/fr.po @@ -0,0 +1,130 @@ +# French translation of Odoo Server +# This file contains the translation of the following modules: +# * account_overdue +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo 10.0\n" +"Report-Msgid-Bugs-To: support@noviat.com\n" +"POT-Creation-Date: 2015-03-29 23:47:54.874000\n" +"PO-Revision-Date: 2018-05-06 21:00:00.874000\n" +"Last-Translator: Luc De Meyer (Noviat nv/sa)\n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Language: nl\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#. module: account_overdue +#: model:ir.actions.act_window,name:account_overdue.overdue_payment_wizard_action +#: model:ir.model,name:account_overdue.model_overdue_payment_wizard +#: view:overdue.payment.wizard:account_overdue.overdue_payment_wizard_view_form +msgid "Print Overdue Payments" +msgstr "Règlements en retard" + +#. module: account_overdue +#: view:overdue.payment.wizard:account_overdue.overdue_payment_wizard_view_form +msgid "Print Overdue Payments Selection Criteria :" +msgstr "Règlements en retard, critères de recherche : " + +#. module: account_overdue +#: view:overdue.payment.wizard:account_overdue.overdue_payment_wizard_view_form +msgid "Cancel" +msgstr "Annuler" + +#. module: account_overdue +#: view:overdue.payment.wizard:account_overdue.overdue_payment_wizard_view_form +msgid "Print" +msgstr "Imprimer" + +#. module: account_overdue +#: model:ir.model.fields,field_description:account_overdue.field_overdue_payment_wizard_partner_select +msgid "Selected Customers" +msgstr "Clients sélectionnés" + +#. module: account_overdue +#: model:ir.model.fields,field_description:account_overdue.field_overdue_payment_wizard_account_select +msgid "Selected Accounts" +msgstr "Comptes sélectionnés" + +#. module: account_overdue +#: model:ir.model.fields,field_description:account_overdue.field_overdue_payment_wizard_company_id +msgid "Company" +msgstr "Société" + +#. module: account_overdue +#: selection:overdue.payment.wizard,partner_select:0 +msgid "All Customers" +msgstr "Tous les clients" + +#. module: account_overdue +#: selection:overdue.payment.wizard,partner_select:0 +msgid "Selected Customers" +msgstr "Clients sélectionnés" + +#. module: account_overdue +#: field:overdue.payment.wizard,account_select:0 +msgid "Selected Accounts" +msgstr "Comptes sélectionnés" + +#. module: account_overdue +#: selection:overdue.payment.wizard,account_select:0 +msgid "Receivable Accounts" +msgstr "Comptes à recevoir" + +#. module: account_overdue +#: selection:overdue.payment.wizard,account_select:0 +msgid "Receivable and Payable Accounts" +msgstr "Comptes à recevoir et à payer" + +#. module: account_overdue +#: code:addons/account_overdue/report/print_overdue_payment.py:1 +#, python-format +msgid "Attn." +msgstr "Attn." + +#. module: account_overdue +#: model:ir.ui.view,arch_db:account_overdue.report_overdue_document +msgid "Total" +msgstr "Total" + +#. module: account_overdue +#: model:ir.ui.view,arch_db:account_overdue.report_overdue_document +msgid "Inv. no" +msgstr "Fact. no" + +#. module: account_overdue +#: model:ir.ui.view,arch_db:account_overdue.report_overdue_document +msgid "Mat. Date" +msgstr "Echéance" + +#. module: account_overdue +#: model:ir.ui.view,arch_db:account_overdue.report_overdue_document +msgid "Date" +msgstr "Date" + +#. module: account_overdue +#: model:ir.ui.view,arch_db:account_overdue.report_overdue_document +msgid "Amount" +msgstr "Montant" + +#. module: account_overdue +#: model:ir.ui.view,arch_db:account_overdue.report_overdue_document +msgid "Paid" +msgstr "Payé" + +#. module: account_overdue +#: model:ir.ui.view,arch_db:account_overdue.report_overdue_document +msgid "Open" +msgstr "Ouvert" + +#. module: account_overdue +#: model:ir.ui.view,arch_db:account_overdue.report_overdue_document +msgid "Days" +msgstr "Jours" + +#. module: account_overdue +#: model:ir.ui.view,arch_db:account_overdue.report_overdue_document +msgid "Total amount overdue" +msgstr "Montant total échu" diff --git a/account_overdue/i18n/it.po b/account_overdue/i18n/it.po new file mode 100644 index 00000000..eab2bf0a --- /dev/null +++ b/account_overdue/i18n/it.po @@ -0,0 +1,138 @@ +# Italian translation of Odoo 8.0. +# This file contains the translation of the following modules: +# * account_overdue +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo 8.0\n" +"Report-Msgid-Bugs-To: support@noviat.com\n" +"POT-Creation-Date: 2015-03-29 23:47:54.874000\n" +"PO-Revision-Date: 2015-03-29 23:47:54.874000\n" +"Last-Translator: Luc De Meyer (Noviat nv/sa)\n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#. module: account_overdue +#: model:ir.actions.act_window,name:account_overdue.action_overdue_payment_wizard +#: model:ir.model,name:account_overdue.model_overdue_payment_wizard +#: view:overdue.payment.wizard:account_overdue.overdue_payment_wizard_view +msgid "Print Overdue Payments" +msgstr "Imprimir pagos vencidos" + +#. module: account_overdue +#: view:overdue.payment.wizard:account_overdue.overdue_payment_wizard_view +msgid "Print Overdue Payments Selection Criteria :" +msgstr "Imprimir Criterios de seleccion de pagos vencidos :" + +#. module: account_overdue +#: view:overdue.payment.wizard:account_overdue.overdue_payment_wizard_view +msgid "Cancel" +msgstr "Annulla" + +#. module: account_overdue +#: view:overdue.payment.wizard:account_overdue.overdue_payment_wizard_view +msgid "Print" +msgstr "Imprimir" + +#. module: account_overdue +#: selection:overdue.payment.wizard,partner_select:0 +msgid "All Customers" +msgstr "Tutti i clienti" + +#. module: account_overdue +#: selection:overdue.payment.wizard,partner_select:0 +msgid "Selected Customers" +msgstr "Clientes escogidos" + +#. module: account_overdue +#: field:overdue.payment.wizard,account_select:0 +msgid "Selected Accounts" +msgstr "Cuentas escogidas" + +#. module: account_overdue +#: selection:overdue.payment.wizard,account_select:0 +msgid "Receivable Accounts" +msgstr "Cuentas por cobrar" + +#. module: account_overdue +#: selection:overdue.payment.wizard,account_select:0 +msgid "Receivable and Payable Accounts" +msgstr "Cuentas por cobrar y pagar" + +#. module: account_overdue +#: code:addons/account_overdue/report/print_overdue_payment.py:94 +#, python-format +msgid "Attn." +msgstr "All'attenzione" + +#. module: account_overdue +#: view:website:account_overdue.report_overdue_document +msgid "Total" +msgstr "Totale" + +#. module: account_overdue +#: view:website:account_overdue.report_overdue_document +msgid "Inv. no" +msgstr "N. Fattura" + +#. module: account_overdue +#: view:website:account_overdue.report_overdue_document +msgid "Mat. Date" +msgstr "Mat. Date" + +#. module: account_overdue +#: view:website:account_overdue.report_overdue_document +msgid "Date" +msgstr "Data" + +#. module: account_overdue +#: view:website:account_overdue.report_overdue_document +msgid "Amount" +msgstr "Ammontare" + +#. module: account_overdue +#: view:website:account_overdue.report_overdue_document +msgid "Paid" +msgstr "Pagado" + +#. module: account_overdue +#: view:website:account_overdue.report_overdue_document +msgid "Open" +msgstr "Abrir" + +#. module: account_overdue +#: view:website:account_overdue.report_overdue_document +msgid "Days" +msgstr "Giorni" + +#. module: account_overdue +#: view:website:account_overdue.report_overdue_document +msgid "Total amount overdue" +msgstr "Ammontare Totale Insoluto" + +#. module: account_overdue +#: field:overdue.payment.wizard,company_id:0 +msgid "Company" +msgstr "Azienda" + +#. module: account_overdue +#: view:website:account_overdue.report_overdue_layout_footer +msgid "Email:" +msgstr "Email:" + +#. module: account_overdue +#: view:website:account_overdue.report_overdue_layout_footer +msgid "Fax:" +msgstr "Fax:" + +#. module: account_overdue +#: view:website:account_overdue.report_overdue_layout_footer +msgid "Web:" +msgstr "Web:" + +#. module: account_overdue +#: view:overdue.payment.wizard:account_overdue.overdue_payment_wizard_view +msgid "or" +msgstr "o" diff --git a/account_overdue/i18n/nl.po b/account_overdue/i18n/nl.po new file mode 100644 index 00000000..17441776 --- /dev/null +++ b/account_overdue/i18n/nl.po @@ -0,0 +1,130 @@ +# Dutch translation of Odoo Server +# This file contains the translation of the following modules: +# * account_overdue +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo 10.0\n" +"Report-Msgid-Bugs-To: support@noviat.com\n" +"POT-Creation-Date: 2015-03-29 23:47:54.874000\n" +"PO-Revision-Date: 2018-05-06 21:00:00.874000\n" +"Last-Translator: Luc De Meyer (Noviat nv/sa)\n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Language: nl\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#. module: account_overdue +#: model:ir.actions.act_window,name:account_overdue.overdue_payment_wizard_action +#: model:ir.model,name:account_overdue.model_overdue_payment_wizard +#: view:overdue.payment.wizard:account_overdue.overdue_payment_wizard_view_form_form +msgid "Print Overdue Payments" +msgstr "Betalingsherinneringen" + +#. module: account_overdue +#: view:overdue.payment.wizard:account_overdue.overdue_payment_wizard_view_form +msgid "Print Overdue Payments Selection Criteria :" +msgstr "Selectiecriteria betalingsherinneringen :" + +#. module: account_overdue +#: view:overdue.payment.wizard:account_overdue.overdue_payment_wizard_view_form +msgid "Cancel" +msgstr "Annuleer" + +#. module: account_overdue +#: view:overdue.payment.wizard:account_overdue.overdue_payment_wizard_view_form +msgid "Print" +msgstr "Afdrukken" + +#. module: account_overdue +#: model:ir.model.fields,field_description:account_overdue.field_overdue_payment_wizard_partner_select +msgid "Selected Customers" +msgstr "Geselecteerde klanten" + +#. module: account_overdue +#: model:ir.model.fields,field_description:account_overdue.field_overdue_payment_wizard_account_select +msgid "Selected Accounts" +msgstr "Geselecteerde grootboekrekeningen" + +#. module: account_overdue +#: model:ir.model.fields,field_description:account_overdue.field_overdue_payment_wizard_company_id +msgid "Company" +msgstr "Bedrijf" + +#. module: account_overdue +#: selection:overdue.payment.wizard,partner_select:0 +msgid "All Customers" +msgstr "Alle klanten" + +#. module: account_overdue +#: selection:overdue.payment.wizard,partner_select:0 +msgid "Selected Customers" +msgstr "Geselecteerde klanten" + +#. module: account_overdue +#: field:overdue.payment.wizard,account_select:0 +msgid "Selected Accounts" +msgstr "Geselecteerde rekeningen" + +#. module: account_overdue +#: selection:overdue.payment.wizard,account_select:0 +msgid "Receivable Accounts" +msgstr "Te ontvangen" + +#. module: account_overdue +#: selection:overdue.payment.wizard,account_select:0 +msgid "Receivable and Payable Accounts" +msgstr "Te ontvangen en te betalen" + +#. module: account_overdue +#: code:addons/account_overdue/report/print_overdue_payment.py:1 +#, python-format +msgid "Attn." +msgstr "T.a.v." + +#. module: account_overdue +#: model:ir.ui.view,arch_db:account_overdue.report_overdue_document +msgid "Total" +msgstr "Totaal" + +#. module: account_overdue +#: model:ir.ui.view,arch_db:account_overdue.report_overdue_document +msgid "Inv. no" +msgstr "Fact. nr" + +#. module: account_overdue +#: model:ir.ui.view,arch_db:account_overdue.report_overdue_document +msgid "Mat. Date" +msgstr "Vervaldatum" + +#. module: account_overdue +#: model:ir.ui.view,arch_db:account_overdue.report_overdue_document +msgid "Date" +msgstr "Datum" + +#. module: account_overdue +#: model:ir.ui.view,arch_db:account_overdue.report_overdue_document +msgid "Amount" +msgstr "Bedrag" + +#. module: account_overdue +#: model:ir.ui.view,arch_db:account_overdue.report_overdue_document +msgid "Paid" +msgstr "Betaald" + +#. module: account_overdue +#: model:ir.ui.view,arch_db:account_overdue.report_overdue_document +msgid "Open" +msgstr "Open" + +#. module: account_overdue +#: model:ir.ui.view,arch_db:account_overdue.report_overdue_document +msgid "Days" +msgstr "Dagen" + +#. module: account_overdue +#: model:ir.ui.view,arch_db:account_overdue.report_overdue_document +msgid "Total amount overdue" +msgstr "Totaal bedrag waarvoor de vervaldatum overschreden is" \ No newline at end of file diff --git a/account_overdue/models/__init__.py b/account_overdue/models/__init__.py new file mode 100644 index 00000000..91fed54d --- /dev/null +++ b/account_overdue/models/__init__.py @@ -0,0 +1 @@ +from . import res_partner diff --git a/account_overdue/models/res_partner.py b/account_overdue/models/res_partner.py new file mode 100644 index 00000000..b37c16cd --- /dev/null +++ b/account_overdue/models/res_partner.py @@ -0,0 +1,42 @@ +# Copyright 2009-2019 Noviat. +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import api, fields, models + + +class ResPartner(models.Model): + _inherit = 'res.partner' + + @api.model + def search(self, args, offset=0, limit=None, order=None, count=False): + if self._context.get('overdue_filter') and not count: + partners = super().search(args) + overdue_moves = self._get_overdue_moves(partners.ids) + overdue_partners = overdue_moves.mapped('partner_id') + partners = partners.filtered( + lambda x: x.id in set(overdue_partners.ids)) + args.extend([('id', 'in', partners.ids)]) + return super().search( + args, offset=offset, limit=limit, order=order, count=count) + + @api.model + def _get_overdue_moves(self, partner_ids, + company=None, account_select='all'): + report_date = fields.Date.today() + dom = [ + ('date_maturity', '<=', report_date), + ('full_reconcile_id', '=', False), + ('partner_id', 'in', partner_ids), + ('partner_id.customer', '=', True), + ] + if account_select == 'receivable': + dom.append(('account_id.internal_type', '=', 'receivable')) + else: + dom.append( + ('account_id.internal_type', 'in', ['receivable', 'payable'])) + if company: + dom.append(('company_id', '=', company.id)) + # Remark: + # we may consider adding an option on the wizard + # to add ('account_id.deprecated', '=', True/False) + return self.env['account.move.line'].search(dom) diff --git a/account_overdue/report/__init__.py b/account_overdue/report/__init__.py new file mode 100644 index 00000000..4f3a8f3a --- /dev/null +++ b/account_overdue/report/__init__.py @@ -0,0 +1 @@ +from . import print_overdue_payment diff --git a/account_overdue/report/print_overdue_payment.py b/account_overdue/report/print_overdue_payment.py new file mode 100644 index 00000000..db6a1418 --- /dev/null +++ b/account_overdue/report/print_overdue_payment.py @@ -0,0 +1,268 @@ +# Copyright 2009-2019 Noviat. +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from datetime import datetime + +from odoo import _, api, fields, models +from odoo.exceptions import UserError +from odoo.tools.translate import translate +from functools import reduce + + +IR_TRANSLATION_NAME = __name__.split('odoo.')[1].replace('.', '/') + '.py' + + +class OverduePayment(models.AbstractModel): + _name = 'report.account_overdue.report_overdue' + _description = 'Overdue Payment Report' + + def _(self, src, lang): + return translate( + self.env.cr, IR_TRANSLATION_NAME, 'code', lang, src) or src + + @api.model + def get_report_values(self, docids, data=None): + """ + Select open AR/AP moves and remove partially reconciled + receivables/payables since these are on the report + via the 'amount_paid'. + + The following logic is used for this removal; + Receivables: keep only Debit moves + Payables: keep only Credit moves + """ + wiz = self.env['overdue.payment.wizard'].browse(data['wiz_id']) + dom = [('company_id', '=', wiz.company_id.id)] + if wiz.partner_select == 'select': + dom.append(('id', 'in', data['partner_ids'])) + partners = self.env['res.partner'].search(dom) + overdue_moves = self.env['res.partner']._get_overdue_moves( + partners.ids, company=wiz.company_id, + account_select=wiz.account_select) + if not overdue_moves: + raise UserError(_( + "No overdue transactions found for your selection!" + )) + self.cpy = wiz.company_id + self.report_date = fields.Datetime.context_timestamp( + self.env.user, datetime.now()) + + def account_type_filter(aml): + types = ['receivable'] + if wiz.account_select == 'all': + types.append('payable') + return aml.account_id.internal_type in types + + open_moves = overdue_moves.filtered(account_type_filter) + + def remove_filter(aml): + if aml.account_id.internal_type == 'receivable' and aml.credit: + if aml.matched_debit_ids: + return True + elif aml.account_id.internal_type == 'payable' and aml.debit: + if aml.matched_credit_ids: + return True + return False + + removes = open_moves.filtered(remove_filter) + amls = open_moves - removes + overdue_partners = amls.mapped('partner_id') + + amls_by_partner = {} + for aml in amls: + pid = aml.partner_id.id + if pid in amls_by_partner: + amls_by_partner[pid] += aml + else: + amls_by_partner[pid] = aml + self.amls_by_partner = amls_by_partner + + # Use the default invoice address of the partner + contacts = self.env['res.partner'] + for partner in overdue_partners: + contact_id = partner.address_get(['invoice'])['invoice'] + contact = self.env['res.partner'].browse(contact_id) + contacts += contact + + return { + 'overdue.payment.wizard': wiz, + 'company': wiz.company_id, + 'partners': contacts, + 'format_vat': self._format_vat, + 'get_company_data': self._get_company_data, + 'get_partner_data': self._get_partner_data, + 'get_address': self._get_address, + 'getLines': self._lines_get, + 'message': self._message, + 'banks': self._banks_get, + } + + def _get_company_data(self): + """ + Support is added for companies not subject to VAT via the + 'kbo_bce_number' field. + Cf. l10n_be_partner_kbo_bce for an example on how to add this field + to res.partner + Adapt this method for use with other localisation modules. + """ + cpy = self.cpy + p = cpy.partner_id + res = '' + p.name + if p.title: + res += ' ' + p.title.name + res += '
' + self._get_address(p) + vat_or_kbo = False + if p.vat: + vat_or_kbo = p.vat + res += '
' + self._format_vat(p.vat) + elif hasattr(p, 'kbo_bce_number') and p.kbo_bce_number: + vat_or_kbo = p.kbo_bce_number + res += '
' + p.kbo_bce_number + if cpy.company_registry: + vat_or_kbo = vat_or_kbo.replace(' ', '').replace('.', '') + cpy_reg = cpy.company_registry.replace(' ', '').replace('.', '') + if cpy_reg not in vat_or_kbo: + res += '
' + cpy.company_registry + return res + + def _get_partner_data(self, p): + p_cpy = p.commercial_partner_id + res = '' + p_cpy.name + if p_cpy.title: + res += ' ' + p_cpy.title.name + res += '' + if p.parent_id and not p.is_company: + if p.name: + res += '
' + self._("Attn.", p.lang) + ' ' + if p.title: + res += p.title.name + ' ' + res += p.name + res += '
' + self._get_address(p) + return res + + def _get_address(self, p): + res = '' + if p.street: + res += p.street + '
' + if p.street2: + res += p.street2 + '
' + if p.zip: + cityline = p.zip + ' ' + else: + cityline = '' + if p.city: + cityline += p.city + if cityline: + res += cityline + if p.country_id: + res += '
' + p.country_id.name + return res + + def _lines_get(self, contact): + partner = contact.commercial_partner_id + amls = self.amls_by_partner[partner.id] + receivables = amls.filtered( + lambda x: x.account_id.internal_type == 'receivable') + payables = amls - receivables + receivables = receivables.sorted(key=lambda r: r.date) + payables = payables.sorted(key=lambda r: r.date) + company_currency = self.cpy.currency_id + lines = [] + entries = (receivables + payables).with_context(lang=contact.lang) + for entry in entries: + currency = entry.currency_id + if currency and currency != company_currency: + amount = entry.amount_currency + amount_residual = entry.amount_residual_currency + else: + amount = entry.balance + amount_residual = entry.amount_residual + amount_paid = amount - amount_residual + maturity_date = datetime.strptime( + entry.date_maturity, '%Y-%m-%d') + maturity_date = fields.Datetime.context_timestamp( + self.env.user, maturity_date) + od_days = str((self.report_date - maturity_date).days) + + line = { + 'invoice_number': entry.invoice_id.number or '-', + 'amount': amount or 0.0, + 'amount_residual': amount_residual or 0.0, + 'amount_paid': amount_paid, + 'currency': currency or company_currency, + 'od_days': od_days, + 'od': maturity_date <= self.report_date and 'X' or '', + 'entry': entry, + } + lines.append(line) + + currencies = list(set([x['currency'] for x in lines])) + totals = [] + for currency in currencies: + lines_currency = [x for x in lines if x['currency'] == currency] + total_amount = reduce( + lambda x, y: x + y, + [x['amount'] for x in lines_currency]) + total_paid = reduce( + lambda x, y: x + y, + [x['amount_paid'] for x in lines_currency]) + total_residual = reduce( + lambda x, y: x + y, + [x['amount_residual'] for x in lines_currency]) + total_overdue = reduce( + lambda x, y: x + y, + [x['od'] and x['amount_residual'] or 0.0 + for x in lines_currency]) + totals.append({ + 'currency': currency, + 'total_amount': total_amount, + 'total_paid': total_paid, + 'total_residual': total_residual, + 'total_overdue': total_overdue + }) + + return {'lines': lines, 'totals': totals} + + def _message_date_string(self, p): + return '%(date)s
' + + def _message(self, p): + lang_code = p.lang or 'en_US' + lang = self.env['res.lang'] + lang_id = lang._lang_get(lang_code) + date_format = lang_id.date_format + report_date = self.report_date.strftime(date_format) + message = self.cpy.with_context({'lang': p.lang}).overdue_msg + if '%(date)s' not in message: + message = self._message_date_string(p) + message + message = message.replace('\n', '
') + user_signature = self.env.user.signature or '' + if message: + message = message % { + 'partner_name': p.name, + 'date': report_date, + 'company_name': self.cpy.name, + 'user_signature': user_signature, + } + return message + + def _format_vat(self, vat): + vat = vat or '' + vat = vat.replace(' ', '').upper() + if vat[0:2] == 'BE': + vat = vat[0:2] + ' ' + '.'.join([vat[2:6], vat[6:9], vat[9:12]]) + return vat + + def _banks_get(self, company, count): + # returns the first x banks (x = count) + banks = company.bank_journal_ids.filtered( + lambda r: r.display_on_footer).mapped('bank_account_id') + banks = banks[:count] + bank_data = [] + for bank in banks: + if bank.acc_type == 'iban': + bank_data.append( + 'IBAN:' + bank.acc_number + ' BIC:' + bank.bank_bic) + else: + bank_data.append(bank.acc_number) + return ' | '.join(bank_data) diff --git a/account_overdue/report/print_overdue_report.xml b/account_overdue/report/print_overdue_report.xml new file mode 100644 index 00000000..ab279f11 --- /dev/null +++ b/account_overdue/report/print_overdue_report.xml @@ -0,0 +1,41 @@ + + + + + + report_overdue + + A4 + 0 + 0 + Portrait + 23 + 23 + 7 + 7 + + 18 + 90 + + + + + + qweb-pdf + account_overdue.report_overdue + + + + res.partner + ir.actions.report + overdue.payment + account_overdue.report_overdue + account_overdue.report_overdue + + + + report + + + + diff --git a/account_overdue/static/description/icon.png b/account_overdue/static/description/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..dae81d8d2359004e4fd32534e1cf235b1c8152b9 GIT binary patch literal 8113 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 H>xlmcCKPR3 literal 0 HcmV?d00001 diff --git a/account_overdue/views/report_overdue.xml b/account_overdue/views/report_overdue.xml new file mode 100644 index 00000000..0aab0f4e --- /dev/null +++ b/account_overdue/views/report_overdue.xml @@ -0,0 +1,140 @@ + + + + + + + + diff --git a/account_overdue/views/report_overdue_layout.xml b/account_overdue/views/report_overdue_layout.xml new file mode 100644 index 00000000..9fa0dd43 --- /dev/null +++ b/account_overdue/views/report_overdue_layout.xml @@ -0,0 +1,73 @@ + + + + + + + + + + diff --git a/account_overdue/views/report_overdue_style.xml b/account_overdue/views/report_overdue_style.xml new file mode 100644 index 00000000..e6023000 --- /dev/null +++ b/account_overdue/views/report_overdue_style.xml @@ -0,0 +1,44 @@ + + + + + + + + diff --git a/account_overdue/views/res_company.xml b/account_overdue/views/res_company.xml new file mode 100644 index 00000000..9f408a86 --- /dev/null +++ b/account_overdue/views/res_company.xml @@ -0,0 +1,18 @@ + + + + + res.company.form + res.company + + + + + + + + + + + + diff --git a/account_overdue/views/res_partner.xml b/account_overdue/views/res_partner.xml new file mode 100644 index 00000000..24817d7a --- /dev/null +++ b/account_overdue/views/res_partner.xml @@ -0,0 +1,18 @@ + + + + + res.partner.select + res.partner + + + + + + + + + + diff --git a/account_overdue/wizard/__init__.py b/account_overdue/wizard/__init__.py new file mode 100644 index 00000000..6faa3849 --- /dev/null +++ b/account_overdue/wizard/__init__.py @@ -0,0 +1 @@ +from . import overdue_payment_wizard diff --git a/account_overdue/wizard/overdue_payment_wizard.py b/account_overdue/wizard/overdue_payment_wizard.py new file mode 100644 index 00000000..9d241214 --- /dev/null +++ b/account_overdue/wizard/overdue_payment_wizard.py @@ -0,0 +1,41 @@ +# Copyright 2009-2019 Noviat. +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import api, fields, models + + +class OverduePaymentWizard(models.TransientModel): + _name = 'overdue.payment.wizard' + _description = 'Print Overdue Payments' + + partner_select = fields.Selection( + selection=[('all', 'All Customers'), + ('select', 'Selected Customers')], + string='Partners', required=True, + default=lambda self: self._default_partner_select()) + account_select = fields.Selection( + selection=[('receivable', 'Receivable Accounts'), + ('all', 'Receivable and Payable Accounts')], + string='Selected Accounts', required=True, default='receivable') + company_id = fields.Many2one( + comodel_name='res.company', string='Company', required=True, + default=lambda self: self._default_company_id()) + + @api.model + def _default_partner_select(self): + return 'select' + + @api.model + def _default_company_id(self): + return self.env.user.company_id + + @api.multi + def overdue_payment_print(self): + ctx = self.env.context + report = ctx.get('ref_report_action') \ + or 'account_overdue.report_print_overdue_action' + data = { + 'partner_ids': ctx.get('active_ids'), + 'wiz_id': self.id, + } + return self.env.ref(report).report_action(self, data=data) diff --git a/account_overdue/wizard/overdue_payment_wizard.xml b/account_overdue/wizard/overdue_payment_wizard.xml new file mode 100644 index 00000000..4a279a1f --- /dev/null +++ b/account_overdue/wizard/overdue_payment_wizard.xml @@ -0,0 +1,41 @@ + + + + + Print Overdue Payments + overdue.payment.wizard + form + 1 + +
+ + + + + + +
+
+
+
+
+ + + Print Overdue Payments + ir.actions.act_window + overdue.payment.wizard + form + form + + new + + + report + + +