diff --git a/addons/account_payment/__init__.py b/addons/account_payment/__init__.py new file mode 100644 index 00000000000..6cd57f2af05 --- /dev/null +++ b/addons/account_payment/__init__.py @@ -0,0 +1,35 @@ +############################################################################## +# +# Copyright (c) 2004 TINY SPRL. (http://tiny.be) All Rights Reserved. +# Fabien Pinckaers +# +# WARNING: This program as such is intended to be used by professional +# programmers who take the whole responsability of assessing all potential +# consequences resulting from its eventual inadequacies and bugs +# End users who are looking for a ready-to-use solution with commercial +# garantees and support are strongly adviced to contract a Free Software +# Service Company +# +# This program is Free Software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# +############################################################################## + +#---------------------------------------------------------- +# Init Sales +#---------------------------------------------------------- + +import payment +import wizard +import account_move_line diff --git a/addons/account_payment/__terp__.py b/addons/account_payment/__terp__.py new file mode 100644 index 00000000000..894e7ee7336 --- /dev/null +++ b/addons/account_payment/__terp__.py @@ -0,0 +1,17 @@ +{ + "name" : "Payment Management", + "version" : "1.0", + "author" : "Tiny", + "category" : "Generic Modules/Payment", + "depends" : ["account"], + "init_xml" : [], + "description": """ + This is the module which Provide an more efficient way to manage invoice payment, + Provide a basic mechanism to easily plug various automated payment. + """, + "demo_xml" : [], + "update_xml" : ["payment_wizard.xml","payment_view.xml","payment_workflow.xml","payment_sequence.xml"], + "active": False, + "installable": True +} + diff --git a/addons/account_payment/account_move_line.py b/addons/account_payment/account_move_line.py new file mode 100644 index 00000000000..9849318f7f6 --- /dev/null +++ b/addons/account_payment/account_move_line.py @@ -0,0 +1,102 @@ +############################################################################## +# +# Copyright (c) 2005-2006 TINY SPRL. (http://tiny.be) All Rights Reserved. +# +# WARNING: This program as such is intended to be used by professional +# programmers who take the whole responsability of assessing all potential +# consequences resulting from its eventual inadequacies and bugs +# End users who are looking for a ready-to-use solution with commercial +# garantees and support are strongly adviced to contract a Free Software +# Service Company +# +# This program is Free Software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# +############################################################################## + +from osv import fields +from osv import osv + +class account_move_line(osv.osv): + _inherit = "account.move.line" + + def amount_to_pay(self, cr, uid, ids, name, arg={}, context={}): + """ Return the amount still to pay regarding all the payemnt orders (excepting cancelled orders)""" + if not ids: + return {} + cr.execute("SELECT ml.id,ml.credit - (select coalesce(sum(amount),0) from payment_line pl inner join payment_order po on (pl.order = po.id)where move_line = ml.id and po.state != 'cancel') as amount from account_move_line ml where credit > 0 and id in (%s)"% (",".join(map(str,ids)))) + r=dict(cr.fetchall()) + return r + + + def _to_pay_search(self, cr, uid, obj, name, args): + if not len(args): + return [] + query = self.pool.get('account.move.line')._query_get(cr, uid, context={}) + where = ' and '.join(map(lambda x: '''(select l.credit - coalesce(sum(amount),0) + from payment_line pl + inner join payment_order po on (pl.order = po.id) + where move_line = l.id and po.state != 'cancel') '''+x[1]+str(x[2])+' ',args)) + + cr.execute(('''select id + from account_move_line l + where account_id in (select id from account_account where type=%s and active) + and reconcile_id is null + and credit > 0 + and '''+where+' and '+query), ('payable',) ) + + res = cr.fetchall() + if not len(res): + return [('id','=','0')] + return [('id','in',map(lambda x:x[0], res))] + + + def line2bank(self,cr,uid,ids,payment_mode= 'manual',context=None): + """ + Try to return for each account move line a corresponding bank + account according to the payment mode. This work using one of + the bank of the partner defined on the invoice eventually + associated to the line. + Return the first suitable bank for the corresponding partner. + + """ + if not ids: return {} + bank_type= self.pool.get('payment.mode').suitable_bank_types(cr,uid,payment_mode,context=context) + cr.execute('''select DISTINCT l.id,b.id,b.state + from account_invoice i + join account_move m on (i.move_id = m.id) + join account_move_line l on (m.id = l.move_id) + join res_partner p on (p.id = i.partner_id) + join res_partner_bank b on (p.id = b.partner_id) + where l.id in (%s) + ''' % ",".join(map(str,ids)) ) + + r= cr.fetchall() + type_ok=[] + line2bank={}.fromkeys(ids) + for line,bank,t in r: + if not line2bank[line]: + line2bank[line]= bank + if t in bank_type: + type_ok.append(line) + elif (line not in type_ok) and (t in bank_type) : + line2bank[line]= bank + type_ok.append(line) + + return line2bank + + _columns = { + 'amount_to_pay' : fields.function(amount_to_pay, method=True, type='float', string='Amount to pay', fnct_search=_to_pay_search), + } +account_move_line() diff --git a/addons/account_payment/payment.py b/addons/account_payment/payment.py new file mode 100644 index 00000000000..61fab76bd97 --- /dev/null +++ b/addons/account_payment/payment.py @@ -0,0 +1,194 @@ +############################################################################## +# +# Copyright (c) 2005-2006 TINY SPRL. (http://tiny.be) All Rights Reserved. +# +# WARNING: This program as such is intended to be used by professional +# programmers who take the whole responsability of assessing all potential +# consequences resulting from its eventual inadequacies and bugs +# End users who are looking for a ready-to-use solution with commercial +# garantees and support are strongly adviced to contract a Free Software +# Service Company +# +# This program is Free Software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# +############################################################################## + +from osv import fields +from osv import osv +import time +import netsvc +class payment_type(osv.osv): + _name= 'payment.type' + _description= 'Payment type' + _columns= { + 'name': fields.char('Name', size=64, required=True), + 'suitable_bank_types': fields.many2many('res.partner.bank.type', + 'bank_type_payment_type_rel', + 'pay_type_id','bank_type_id', + 'Suitable bank types') + } + + +payment_type() + + +class payment_mode(osv.osv): + _name= 'payment.mode' + _description= 'Payment mode' + _columns= { + 'name': fields.char('Name', size=64, required=True), + 'code': fields.char('Code', size=64, required=True,unique=True,select=True), + 'bank': fields.many2one('res.partner.bank',"Bank account",required=True), + 'journal': fields.many2one('account.journal','Journal',required=True,domain=[('type','=','cash')]), + 'type': fields.many2one('payment.type','Payment type',required=True), + } + + def suitable_bank_types(self,cr,uid,payment_code,context): + """ Return the codes of the bank type that are suitable for the given payment mode""" + cr.execute(""" select t.code + from res_partner_bank_type t + join bank_type_payment_type_rel r on (r.bank_type_id = t.id) + join payment_type pt on (r.pay_type_id = pt.id) + join payment_mode m on (pt.id=m.type) + where m.code = %s""", [payment_code]) + return [x[0] for x in cr.fetchall()] + +payment_mode() + + +class payment_order(osv.osv): + _name = 'payment.order' + _description = 'Payment Order' + _rec_name = 'date' + + def _total(self, cr, uid, ids, name, args, context={}): + if not ids: return {} + cr.execute("""select o.id, coalesce(sum(amount),0) + from payment_order o left join payment_line l on (o.id = l.order) + where o.id in (%s) group by o.id"""% ','.join(map(str,ids))) + return dict(cr.fetchall()) + + def nb_line(self, cr, uid, ids, name, args, context={}): + if not ids: return {} + res= {}.fromkeys(ids,0) + cr.execute("""select "order",count(*) + from payment_line + where "order" in (%s) group by "order" """% ','.join(map(str,ids))) + res.update(dict(cr.fetchall())) + return res + + def mode_get(self, cr, uid, context={}): + pay_type_obj = self.pool.get('payment.mode') + ids = pay_type_obj.search(cr, uid, []) + res = pay_type_obj.read(cr, uid, ids, ['code','name'], context) + return [(r['code'],r['name']) for r in res] + [('manual', 'Manual')] + + _columns = { + 'date_planned': fields.date('Date if fixed'), + 'reference': fields.char('Reference',size=128), + 'mode': fields.selection(mode_get, 'Payment mode',size=16,required=True, select=True), + 'state': fields.selection([('draft', 'Draft'),('open','Open'), + ('cancel','Cancelled'),('done','Done')], 'State', select=True), + 'lines': fields.one2many('payment.line','order','Payment lines'), + 'total': fields.function(_total, string="Total", method=True, type='float'), + 'user_id': fields.many2one('res.users','User',required=True), + 'nb_line': fields.function(nb_line,string='Number of payment',method=True, type='integer'), + 'date_prefered': fields.selection([('now','Directly'),('due','Due date'),('fixed','Fixed date')],"Prefered date",required=True), + 'date_created': fields.date('Creation date',readonly=True), + 'date_done': fields.date('Execution date',readonly=True), + } + + _defaults = { + 'user_id': lambda self,cr,uid,context: uid, + 'mode': lambda *a : 'manual', + 'state': lambda *a: 'draft', + 'date_prefered': lambda *a: 'due', + 'date_created': lambda *a: time.strftime('%Y-%m-%d'), + } + + def set_to_draft(self, cr, uid, ids, *args): + self.write(cr, uid, ids, {'state':'draft'}) + wf_service = netsvc.LocalService("workflow") + for id in ids: + wf_service.trg_create(uid, 'payment.order', id, cr) + return True + + def action_open(self, cr, uid, ids, *args): + for order in self.read(cr,uid,ids,['reference']): + if not order['reference']: + reference = self.pool.get('ir.sequence').get(cr, uid, 'payment.order') + self.write(cr,uid,order['id'],{'reference':reference}) + return True + + def action_done(self, cr, uid, ids, *args): + self.write(cr,uid,ids,{'date_done':time.strftime('%Y-%m-%d')}) + return True + +payment_order() + +class payment_line(osv.osv): + _name = 'payment.line' + _description = 'Payment Line' + _rec_name = 'move_line' + + def partner_payable(self, cr, uid, ids, name, args, context={}): + if not ids: return {} + partners= self.read(cr, uid, ids, ['partner'], context) + partners= dict(map(lambda x: (x['id'],x['partner']),partners)) + debit= self.pool.get('res.partner')._debit_get(cr, uid, partners.values(), name, args, context) + for i in partners: + partners[i]= debit[partners[i]] + return partners + + def translate(self,orig): + return {"to_pay":"credit", + "due_date":"date_maturity", + "partner":"partner_id", + "reference":"ref"}.get(orig,orig) + + def select_by_name(self, cr, uid, ids, name, args, context={}): + if not ids: return {} + cr.execute("""SELECT pl.id, ml.%s + from account_move_line ml + inner join payment_line pl on (ml.id= pl.move_line) + where pl.id in (%s)"""% (self.translate(name),','.join(map(str,ids))) ) + return dict(cr.fetchall()) + + + _columns = { + 'move_line': fields.many2one('account.move.line','Entry line',required=True), + 'amount': fields.float('Payment Amount', digits=(16,2), required=True), + 'bank': fields.many2one('res.partner.bank','Bank account'), + 'order': fields.many2one('payment.order','Order', ondelete='cascade', select=True), + 'partner': fields.function(select_by_name, string="Partner", method=True, type='many2one', obj='res.partner'), + 'to_pay': fields.function(select_by_name, string="To pay", method=True, type='float'), + 'due_date': fields.function(select_by_name, string="Due date", method=True, type='date'), + 'date_created': fields.function(select_by_name, string="Creation date", method=True, type='date'), + 'reference': fields.function(select_by_name, string="Ref", method=True, type='char'), + 'partner_payable': fields.function(partner_payable, string="Partner payable", method=True, type='float'), + } + def onchange_move_line(self, cr, uid, id, move_line, type,context={}): + if not move_line: + return {} + line=self.pool.get('account.move.line').browse(cr,uid,move_line) + return {'value': {'amount': line.amount_to_pay, + 'to_pay': line.amount_to_pay, + 'partner': line.partner_id, + 'reference': line.ref, + 'date_created': line.date_created, + 'bank': self.pool.get('account.move.line').line2bank(cr,uid,[move_line],type,context)[move_line] + }} + +payment_line() diff --git a/addons/account_payment/payment_sequence.xml b/addons/account_payment/payment_sequence.xml new file mode 100644 index 00000000000..aae93d9053c --- /dev/null +++ b/addons/account_payment/payment_sequence.xml @@ -0,0 +1,16 @@ + + + + + Payment order + payment.order + + + + Payment order + payment.order + + %(year)s/ + + + diff --git a/addons/account_payment/payment_view.xml b/addons/account_payment/payment_view.xml new file mode 100644 index 00000000000..01d71bd129c --- /dev/null +++ b/addons/account_payment/payment_view.xml @@ -0,0 +1,181 @@ + + + + + + + + payment.type.form + payment.type + form + +
+ + + + + +
+ + Payment Type + payment.type + form + tree,form + + + + + + payment.mode.form + payment.mode + form + +
+ + + + + + + +
+ + Payment Mode + payment.mode + form + tree,form + + + + + payment.order.form + payment.order + form + +
+ + + + + +