From dab31d0989b1320aa08a1d876f4a9c4c5cb1c7b6 Mon Sep 17 00:00:00 2001 From: Stefan Rijnhart Date: Wed, 5 Jun 2013 17:44:55 +0200 Subject: [PATCH 1/9] [ADD] Boilerplate --- account_banking_aggregate_payment/__init__.py | 1 + .../__openerp__.py | 38 +++++++++++++++++++ .../data/payment_mode_type.xml | 37 ++++++++++++++++++ .../model/payment_mode.py | 34 +++++++++++++++++ .../view/payment_mode.xml | 25 ++++++++++++ 5 files changed, 135 insertions(+) create mode 100644 account_banking_aggregate_payment/__init__.py create mode 100644 account_banking_aggregate_payment/__openerp__.py create mode 100644 account_banking_aggregate_payment/data/payment_mode_type.xml create mode 100644 account_banking_aggregate_payment/model/payment_mode.py create mode 100644 account_banking_aggregate_payment/view/payment_mode.xml diff --git a/account_banking_aggregate_payment/__init__.py b/account_banking_aggregate_payment/__init__.py new file mode 100644 index 000000000..16e8b082f --- /dev/null +++ b/account_banking_aggregate_payment/__init__.py @@ -0,0 +1 @@ +import model diff --git a/account_banking_aggregate_payment/__openerp__.py b/account_banking_aggregate_payment/__openerp__.py new file mode 100644 index 000000000..1d0a2513e --- /dev/null +++ b/account_banking_aggregate_payment/__openerp__.py @@ -0,0 +1,38 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# This module copyright (C) 2013 Therp BV (). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## +{ + 'name': 'Account Banking Aggregate Payment', + 'version': '0.1.136', + 'license': 'GPL-3', + 'author': 'Therp BV', + 'website': 'https://launchpad.net/banking-addons', + 'category': 'Banking addons', + 'depends': ['account_banking'], + 'data': [ + 'view/payment_mode.xml', + 'data/payment_mode_type.xml', + ], + }, + 'description': ''' + Allows for aggregating several payments on a single partner + ''', + 'active': False, +} diff --git a/account_banking_aggregate_payment/data/payment_mode_type.xml b/account_banking_aggregate_payment/data/payment_mode_type.xml new file mode 100644 index 000000000..945621a49 --- /dev/null +++ b/account_banking_aggregate_payment/data/payment_mode_type.xml @@ -0,0 +1,37 @@ + + + + + + Aggregate payment order for all accounts + AGG_ALL + + + + + + Aggregate payment order for IBAN accounts + AGG_IBAN + + + + + + Aggregate payment order for domestic accounts + AGG_DMST + + + + + + diff --git a/account_banking_aggregate_payment/model/payment_mode.py b/account_banking_aggregate_payment/model/payment_mode.py new file mode 100644 index 000000000..cb7ae692c --- /dev/null +++ b/account_banking_aggregate_payment/model/payment_mode.py @@ -0,0 +1,34 @@ +o# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# This module copyright (C) 2013 Therp BV (). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## + +from openerp.osv import orm, fields + + +class payment_mode(orm.Model): + _inherit = "payment.mode" + _columns = { + 'aggregate_partner_id': fields.many2one( + 'res.partner', 'Aggregate partner id', + ), + 'chained_mode_id': fields.many2one( + 'payment.mode', 'Chained payment mode'), + } + diff --git a/account_banking_aggregate_payment/view/payment_mode.xml b/account_banking_aggregate_payment/view/payment_mode.xml new file mode 100644 index 000000000..5871a2b36 --- /dev/null +++ b/account_banking_aggregate_payment/view/payment_mode.xml @@ -0,0 +1,25 @@ + + + + + Adaptations for aggregate payment modes + payment.mode + + form + + + + + + + + + + + From 4cbf57ffae557f64b5312ccf623ce6020a893e03 Mon Sep 17 00:00:00 2001 From: Stefan Rijnhart Date: Wed, 5 Jun 2013 19:46:26 +0200 Subject: [PATCH 2/9] [ADD] Full move creation and reconciliation --- .../model/export_aggregate.py | 236 ++++++++++++++++++ .../model/payment_mode.py | 2 +- 2 files changed, 237 insertions(+), 1 deletion(-) create mode 100644 account_banking_aggregate_payment/model/export_aggregate.py diff --git a/account_banking_aggregate_payment/model/export_aggregate.py b/account_banking_aggregate_payment/model/export_aggregate.py new file mode 100644 index 000000000..fb77f105d --- /dev/null +++ b/account_banking_aggregate_payment/model/export_aggregate.py @@ -0,0 +1,236 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# This module copyright (C) 2013 Therp BV (). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## + +from openerp.osv import orm, fields +from openerp.tools.translate import _ +from openerp import netsvc + +class banking_export_aggregate(orm.TransientModel): + _name = 'banking.export.aggregate' + _columns = { + 'payment_order_id': fields.many2one( + 'payment.order', 'Payment order'), + 'reference': fields.char( + 'Reference', size=24), + } + + def reconcile_lines(self, cr, uid, move_line_ids, context=None): + """ + Reconcile move lines lines, really. Talk about core functionality + """ + reconcile_obj = self.pool.get('account.move.reconcile') + account_move_line_obj = self.pool.get('account.move.line') + currency_obj = self.pool.get('res.currency') + lines = account_move_line_obj.browse(cr, uid, move_line_ids, context=context) + + for line in lines[1:]: + if line.account_id != lines[0].account_id: + raise orm.except_orm( + _('Error'), + _('Cannot reconcile between different accounts')) + + if any(lines, + lambda line: line.reconcile_id and line.reconcile_id.line_id): + raise orm.except_orm( + _('Error'), + _('Line is already fully reconciled')) + + currency = lines[0].company_id.currency_id + + partials = [] + line_ids = [] + for line in lines: + if line.id not in line_ids: + line_ids.append(line.id) + if line.reconcile_partial_id: + line_ids += line.reconcile_partial_id.line_partial_ids + if line.reconcile_partial_id.id not in partials: + partials.append(line.reconcile_partial_id.id) + + total = account_move_line_obj.get_balance(cr, uid, line_ids) + is_zero = currency_obj.is_zero(cr, uid, currency, total) + + vals = { + 'type': 'auto', + 'line_id': is_zero and [(6, 0, line_ids)] or [(6, 0, [])], + 'line_partial_ids': is_zero and [(6, 0, [])] or [(6, 0, line_ids)], + } + + if partials: + if len(partials) > 1: + reconcile_obj.unlink( + cr, uid, partials[1:], context=context) + reconcile_obj.write( + cr, uid, partials[0], + vals, context=context) + else: + reconcile_obj.create( + cr, uid, vals, context=context) + + for line_id in line_ids: + netsvc.LocalService("workflow").trg_trigger( + uid, 'account.move.line', line_id, cr) + return True + + def create_aggregate_order(self, cr, uid, ids, context=None): + wiz = self.browse(cr, uid, ids[0], context=context) + account_move_line_obj = self.pool.get('account.move.line') + account_move_obj = self.pool.get('account.move') + payment_order_obj = self.pool.get('payment.order') + payment_order_line_obj = self.pool.get('payment.order.line') + payment_order_ids = context.get('active_ids', []) + if not payment_order_ids: + raise orm.except_orm( + _('Error'), + _('Please select a payment order')) + if len(payment_order_ids) > 1: + raise orm.except_orm( + _('Error'), + _('This operation can only be performed on a single payment order')) + order = payment_order_obj.browse( + cr, uid, payment_order_ids[0], context=context) + if not (order.mode.transfer_journal_id and + order.mode.transfer_account_id): + raise orm.except_orm( + _('Error'), + _('Transfer journal or account are not filled ' + 'in on the payment mode')) + + move_id = account_move_obj.create(cr, uid, { + 'journal_id': order.mode.transfer_journal_id.id, + 'name': 'Aggregate Payment Order', # TODO: get order reference or number + 'reference': 'AGG', # TODO get order reference number or number + }, context=context) + + counter_move_line_ids = [] + for line in order.line_ids: + # basic checks + if not line.move_line_id: + raise orm.except_orm( + _('Error'), + _('No move line provided for line %s') % line.name) + if line.move_line_id.reconcile_id: + raise orm.except_orm( + _('Error'), + _('Move line %s has already been paid/reconciled') % + line.move_line_id.name + ) + + # TODO: take multicurrency into account + + # create the move line on the transfer account + vals = { + 'name': 'Aggregate payment for %s' % ( + line.move_line_id.invoice and + line.move_line_id.invoice.number or + line.move_line_id.name), + 'move_id': move_id, + 'partner_id': line.partner_id and line.partner_id.id or False, + 'account_id': order.mode.transfer_account_id.id, + 'credit': line.amount, + 'debit': 0.0, + 'date': fields.date.context_today(self, cr, uid, context=context), + } + counter_move_line_id = account_move_line_obj.create( + cr, uid, vals, context=context) + counter_move_line_ids.append(counter_move_line_id) + + # create the debit move line on the receivable account + vals.update({ + 'account_id': line.move_line_id.account_id.id, + 'credit': 0.0, + 'debit': line.amount, + }) + reconcile_move_line_id = account_move_line_obj.create( + cr, uid, vals, context=context) + + self.reconcile_lines( + cr, uid, [reconcile_move_line_id, counter_move_line_id], + context=context) + + total = account_move_line_obj.get_balance( + cr, uid, counter_move_line_ids) + + vals = { + 'name': 'Aggregate payment for %s' % ( + line.move_line_id.invoice and + line.move_line_id.invoice.number or + line.move_line_id.name), + 'move_id': move_id, + 'partner_id': order.mode.aggregate_partner_id.id, + 'account_id': order.mode.transfer_account_id.id, + 'credit': 0.0, + 'debit': total, + 'date': fields.date.context_today(self, cr, uid, context=context), + } + aggregate_move_line_id = account_move_line_obj.create( + cr, uid, vals, context=context) + + self.reconcile_lines( + cr, uid, counter_move_line_ids + [aggregate_move_line_id], + context=context) + + # create the credit move line on the aggregate partner + vals.update({ + 'account_id': order.mode.aggregate_partner_id.property_account_payable.id, + 'partner_id': order.mode.aggregate_partner_id.id, + 'credit': total, + 'debit': 0.0, + }) + + payable_move_line = account_move_line_obj.browse( + cr, uid, + account_move_line_obj.create( + cr, uid, vals, context=context), + context=context) + + account_move_obj.post(cr, uid, [move_id], context=context) + + wf_service = netsvc.LocalService('workflow') + wf_service.trg_validate(uid, 'payment.order', order.id, 'sent', cr) + wf_service.trg_validate(uid, 'payment.order', order.id, 'done', cr) + order.refresh() + if order.state != 'done': + raise orm.except_orm( + _('Error'), + _('Payment order workflow does not go into state "done"')) + + payment_order_id = payment_order_obj.create( + cr, uid, { + 'company_id': order.company_id.id, + 'mode': order.mode.aggregate_mode_id.id, + }, context=context) + + payment_order_line_obj.create(cr, uid,{ + 'move_line_id': payable_move_line.id, + 'amount_currency': payable_move_line.amount_to_pay, + 'bank_id': line2bank.get(line.id), # TODO line2bank get it from account_banking + 'order_id': payment_order_id, + 'partner_id': order.mode.aggregate_partner_id.id, + 'communication': False, + 'communication2': payable_move_line.ref, + 'state': 'normal', + 'date': False, + 'currency': (line.journal_id.currency.id or + line.journal_id.company_id.currency_id.id), + }, context=context) + + return ### act_window to payment order{'type': 'ir.actions.act_window_close'} diff --git a/account_banking_aggregate_payment/model/payment_mode.py b/account_banking_aggregate_payment/model/payment_mode.py index cb7ae692c..dc4bf21e8 100644 --- a/account_banking_aggregate_payment/model/payment_mode.py +++ b/account_banking_aggregate_payment/model/payment_mode.py @@ -1,4 +1,4 @@ -o# -*- coding: utf-8 -*- +# -*- coding: utf-8 -*- ############################################################################## # # OpenERP, Open Source Management Solution From 9d03729edf9d7763069d765e9c20d8a87e75b43d Mon Sep 17 00:00:00 2001 From: Stefan Rijnhart Date: Thu, 6 Jun 2013 22:57:50 +0200 Subject: [PATCH 3/9] [ADD] Finish core code and views --- .../__openerp__.py | 6 ++- .../model/__init__.py | 2 + .../model/export_aggregate.py | 41 ++++++++++++++++--- .../view/export_aggregate.xml | 24 +++++++++++ 4 files changed, 67 insertions(+), 6 deletions(-) create mode 100644 account_banking_aggregate_payment/model/__init__.py create mode 100644 account_banking_aggregate_payment/view/export_aggregate.xml diff --git a/account_banking_aggregate_payment/__openerp__.py b/account_banking_aggregate_payment/__openerp__.py index 1d0a2513e..65ff1b329 100644 --- a/account_banking_aggregate_payment/__openerp__.py +++ b/account_banking_aggregate_payment/__openerp__.py @@ -28,11 +28,15 @@ 'depends': ['account_banking'], 'data': [ 'view/payment_mode.xml', + 'view/export_aggregate.xml', 'data/payment_mode_type.xml', ], }, 'description': ''' - Allows for aggregating several payments on a single partner + Allows for aggregating several payments on a single partner by + reconciling the payment lines in a payment order of this type + with a single line on the partner, then proceeds to create a new + order of a user chosen type with only this line in it. ''', 'active': False, } diff --git a/account_banking_aggregate_payment/model/__init__.py b/account_banking_aggregate_payment/model/__init__.py new file mode 100644 index 000000000..94bbdd9f5 --- /dev/null +++ b/account_banking_aggregate_payment/model/__init__.py @@ -0,0 +1,2 @@ +import payment_mode +import export_aggregate diff --git a/account_banking_aggregate_payment/model/export_aggregate.py b/account_banking_aggregate_payment/model/export_aggregate.py index fb77f105d..4454a344c 100644 --- a/account_banking_aggregate_payment/model/export_aggregate.py +++ b/account_banking_aggregate_payment/model/export_aggregate.py @@ -27,11 +27,28 @@ class banking_export_aggregate(orm.TransientModel): _name = 'banking.export.aggregate' _columns = { 'payment_order_id': fields.many2one( - 'payment.order', 'Payment order'), + 'payment.order', 'Payment order', + required=True), 'reference': fields.char( 'Reference', size=24), } + def create(self, cr, uid, vals, context=None): + if context is None: + context = {} + if not vals.get('payment_order_id'): + if not context.get('active_ids'): + raise orm.except_orm( + _('Error'), + _('Please select a payment order')) + if len(context['active_ids']) > 1: + raise orm.except_orm( + _('Error'), + _('Please only select a single payment order')) + vals['payment_order_id'] = context['active_ids'][0] + return self.create( + cr, uid, vals, context=context) + def reconcile_lines(self, cr, uid, move_line_ids, context=None): """ Reconcile move lines lines, really. Talk about core functionality @@ -116,8 +133,8 @@ class banking_export_aggregate(orm.TransientModel): move_id = account_move_obj.create(cr, uid, { 'journal_id': order.mode.transfer_journal_id.id, - 'name': 'Aggregate Payment Order', # TODO: get order reference or number - 'reference': 'AGG', # TODO get order reference number or number + 'name': 'Aggregate Payment Order %s' % order.reference, + 'reference': order.reference, }, context=context) counter_move_line_ids = [] @@ -219,10 +236,13 @@ class banking_export_aggregate(orm.TransientModel): 'mode': order.mode.aggregate_mode_id.id, }, context=context) + lines2bank = payment_order_line_obj.line2bank( + cr, uid, [payable_move_line.id], order.mode.id, context) + payment_order_line_obj.create(cr, uid,{ 'move_line_id': payable_move_line.id, 'amount_currency': payable_move_line.amount_to_pay, - 'bank_id': line2bank.get(line.id), # TODO line2bank get it from account_banking + 'bank_id': lines2bank.get(payable_move_line.id), 'order_id': payment_order_id, 'partner_id': order.mode.aggregate_partner_id.id, 'communication': False, @@ -233,4 +253,15 @@ class banking_export_aggregate(orm.TransientModel): line.journal_id.company_id.currency_id.id), }, context=context) - return ### act_window to payment order{'type': 'ir.actions.act_window_close'} + return { + 'name': payment_order_obj._description, + 'view_type': 'form', + 'view_mode': 'form', + 'res_model': payment_order_obj._name, + 'domain': [], + 'context': context, + 'type': 'ir.actions.act_window', + 'target': 'current', + 'res_id': payment_order_id, + 'nodestroy': True, + } diff --git a/account_banking_aggregate_payment/view/export_aggregate.xml b/account_banking_aggregate_payment/view/export_aggregate.xml new file mode 100644 index 000000000..9a0a16be7 --- /dev/null +++ b/account_banking_aggregate_payment/view/export_aggregate.xml @@ -0,0 +1,24 @@ + + + + + Export aggregate payment order + banking.export.aggregate + form + +
+