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 00000000..dae81d8d Binary files /dev/null and b/account_overdue/static/description/icon.png differ 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 + + +