1 # -*- coding: utf-8 -*-
2 ##############################################################################
4 # OpenERP, Open Source Management Solution
5 # Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
7 # This program is free software: you can redistribute it and/or modify
8 # it under the terms of the GNU Affero General Public License as
9 # published by the Free Software Foundation, either version 3 of the
10 # License, or (at your option) any later version.
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU Affero General Public License for more details.
17 # You should have received a copy of the GNU Affero General Public License
18 # along with this program. If not, see <http://www.gnu.org/licenses/>.
20 ##############################################################################
22 from osv import fields
29 class payment_type(osv.osv):
31 _description= 'Payment type'
33 'name': fields.char('Name', size=64, required=True,help='Payment Type'),
34 'code': fields.char('Code', size=64, required=True,help='Specifies the Code for Payment Type'),
35 'suitable_bank_types': fields.many2many('res.partner.bank.type',
36 'bank_type_payment_type_rel',
37 'pay_type_id','bank_type_id',
38 'Suitable bank types')
44 class payment_mode(osv.osv):
46 _description= 'Payment mode'
48 'name': fields.char('Name', size=64, required=True,help='Mode of Payment'),
49 'bank_id': fields.many2one('res.partner.bank', "Bank account",
50 required=True,help='Bank Account for the Payment Mode'),
51 'journal': fields.many2one('account.journal', 'Journal', required=True,
52 domain=[('type', '=', 'cash')],help='Cash Journal for the Payment Mode'),
53 'type': fields.many2one('payment.type','Payment type',required=True,help='Select the Payment Type for the Payment Mode.'),
56 def suitable_bank_types(self,cr,uid,payment_code=None,context={}):
57 """Return the codes of the bank type that are suitable
58 for the given payment type code"""
61 cr.execute(""" select t.code
62 from res_partner_bank_type t
63 join bank_type_payment_type_rel r on (r.bank_type_id = t.id)
64 join payment_type pt on (r.pay_type_id = pt.id)
65 join payment_mode pm on (pm.type = pt.id)
66 where pm.id = %s """, [payment_code])
67 return [x[0] for x in cr.fetchall()]
73 class payment_order(osv.osv):
74 _name = 'payment.order'
75 _description = 'Payment Order'
76 _rec_name = 'reference'
78 def get_wizard(self,type):
79 logger = netsvc.Logger()
80 logger.notifyChannel("warning", netsvc.LOG_WARNING,
81 "No wizard found for the payment type '%s'." % type)
84 def _total(self, cursor, user, ids, name, args, context=None):
88 for order in self.browse(cursor, user, ids, context=context):
90 res[order.id] = reduce(lambda x, y: x + y.amount, order.line_ids, 0.0)
96 'date_planned': fields.date('Scheduled date if fixed', states={'done':[('readonly',True)]}, help='Select a date if you have chosen Preferred Date to be fixed.'),
97 'reference': fields.char('Reference', size=128, required=1, states={'done':[('readonly',True)]}),
98 'mode': fields.many2one('payment.mode','Payment mode', select=True, required=1, states={'done':[('readonly',True)]}, help='Select the Payment Mode to be applied.'),
99 'state': fields.selection([
101 ('open','Confirmed'),
102 ('cancel','Cancelled'),
103 ('done','Done')], 'State', select=True,
104 help='When an order is placed the state is \'Draft\'.\n Once the bank is confirmed the state is set to \'Confirmed\'.\n Then the order is paid the state is \'Done\'.'),
105 'line_ids': fields.one2many('payment.line','order_id','Payment lines',states={'done':[('readonly',True)]}),
106 'total': fields.function(_total, string="Total", method=True,
108 'user_id': fields.many2one('res.users','User', required=True, states={'done':[('readonly',True)]}),
109 'date_prefered': fields.selection([
112 ('fixed', 'Fixed date')
113 ], "Preferred date", change_default=True, required=True, states={'done':[('readonly',True)]}, help="Choose an option for the Payment Order:'Fixed' stands for a date specified by you.'Directly' stands for the direct execution.'Due date' stands for the scheduled date of execution."),
114 'date_created': fields.date('Creation date', readonly=True),
115 'date_done': fields.date('Execution date', readonly=True),
119 'user_id': lambda self,cr,uid,context: uid,
120 'state': lambda *a: 'draft',
121 'date_prefered': lambda *a: 'due',
122 'date_created': lambda *a: time.strftime('%Y-%m-%d'),
123 'reference': lambda self,cr,uid,context: self.pool.get('ir.sequence').get(cr, uid, 'payment.order'),
126 def set_to_draft(self, cr, uid, ids, *args):
127 self.write(cr, uid, ids, {'state':'draft'})
128 wf_service = netsvc.LocalService("workflow")
130 wf_service.trg_create(uid, 'payment.order', id, cr)
133 def action_open(self, cr, uid, ids, *args):
134 for order in self.read(cr,uid,ids,['reference']):
135 if not order['reference']:
136 reference = self.pool.get('ir.sequence').get(cr, uid, 'payment.order')
137 self.write(cr,uid,order['id'],{'reference':reference})
140 def set_done(self, cr, uid, id, *args):
141 self.write(cr,uid,id,{'date_done': time.strftime('%Y-%m-%d'),
143 wf_service = netsvc.LocalService("workflow")
144 wf_service.trg_validate(uid, 'payment.order', id, 'done', cr)
150 class payment_line(osv.osv):
151 _name = 'payment.line'
152 _description = 'Payment Line'
154 #~ def partner_payable(self, cr, uid, ids, name, args, context={}):
155 #~ if not ids: return {}
156 #~ partners= self.read(cr, uid, ids, ['partner_id'], context)
157 #~ partners= dict(map(lambda x: (x['id'], x['partner_id'][0]), partners))
158 #~ debit = self.pool.get('res.partner')._debit_get(cr, uid,
159 #~ partners.values(), name, args, context)
160 #~ for i in partners:
161 #~ partners[i] = debit[partners[i]]
164 def translate(self, orig):
166 # "to_pay": "credit",
167 "due_date": "date_maturity",
168 "reference": "ref"}.get(orig, orig)
170 def info_owner(self, cr, uid, ids, name=None, args=None, context=None):
171 if not ids: return {}
174 for line in self.browse(cr, uid, ids, context=context):
175 owner=line.order_id.mode.bank_id.partner_id
176 result[line.id]=False
178 for ads in owner.address:
179 if ads.type=='default':
180 st=ads.street and ads.street or ''
181 st1=ads.street2 and ads.street2 or ''
183 zip_city= ads.zip_id and self.pool.get('res.partner.zip').name_get(cr,uid,[ads.zip_id.id])[0][1] or ''
185 zip=ads.zip and ads.zip or ''
186 city= ads.city and ads.city or ''
187 zip_city= zip + ' ' + city
188 cntry= ads.country_id and ads.country_id.name or ''
189 info=owner.name + "\n" + st + " " + st1 + "\n" + zip_city + "\n" +cntry
194 def info_partner(self, cr, uid, ids, name=None, args=None, context=None):
195 if not ids: return {}
198 for line in self.browse(cr, uid, ids, context=context):
199 result[line.id]=False
200 if not line.partner_id:
202 partner = line.partner_id.name or ''
203 if line.partner_id.address:
204 for ads in line.partner_id.address:
205 if ads.type=='default':
206 st=ads.street and ads.street or ''
207 st1=ads.street2 and ads.street2 or ''
209 zip_city= ads.zip_id and self.pool.get('res.partner.zip').name_get(cr,uid,[ads.zip_id.id])[0][1] or ''
211 zip=ads.zip and ads.zip or ''
212 city= ads.city and ads.city or ''
213 zip_city= zip + ' ' + city
214 cntry= ads.country_id and ads.country_id.name or ''
215 info=partner + "\n" + st + " " + st1 + "\n" + zip_city + "\n" +cntry
220 def select_by_name(self, cr, uid, ids, name, args, context=None):
221 if not ids: return {}
223 partner_obj = self.pool.get('res.partner')
224 cr.execute("""SELECT pl.id, ml.%s
225 from account_move_line ml
226 inner join payment_line pl
227 on (ml.id = pl.move_line_id)
228 where pl.id =ANY(%s)""",
229 (self.translate(name),ids,))
230 res = dict(cr.fetchall())
232 if name == 'partner_id':
234 for p_id, p_name in partner_obj.name_get(cr,uid,
235 filter(lambda x:x and x != 0,res.values()),context=context):
236 partner_name[p_id] = p_name
239 if id in res and partner_name:
240 res[id] = (res[id],partner_name[res[id]])
242 res[id] = (False,False)
245 res.setdefault(id, (False, ""))
248 # def _currency(self, cursor, user, ids, name, args, context=None):
253 # currency_obj = self.pool.get('res.currency')
254 # account_obj = self.pool.get('account.account')
255 # cursor.execute('''SELECT pl.id, ml.currency_id, ml.account_id
256 # FROM account_move_line ml
257 # INNER JOIN payment_line pl
258 # ON (ml.id = pl.move_line_id)
259 # WHERE pl.id in (''' + ','.join([str(x) for x in ids]) + ')')
263 # for payment_line_id, currency_id, account_id in cursor.fetchall():
264 # res2[payment_line_id] = [currency_id, account_id]
265 # account_ids.append(account_id)
267 # account2currency_id = {}
268 # for account in account_obj.browse(cursor, user, account_ids,
270 # account2currency_id[account.id] = account.company_currency_id.id
272 # for payment_line_id in ids:
273 # if res2[payment_line_id][0]:
274 # res[payment_line_id] = res2[payment_line_id][0]
276 # res[payment_line_id] = \
277 # account2currency_id[res2[payment_line_id][1]]
279 # currency_names = {}
280 # for currency_id, name in currency_obj.name_get(cursor, user, res.values(),
282 # currency_names[currency_id] = name
283 # for payment_line_id in ids:
284 # res[payment_line_id] = (res[payment_line_id],
285 # currency_names[res[payment_line_id]])
288 # def _to_pay_currency(self, cursor, user, ids, name , args, context=None):
292 # cursor.execute('''SELECT pl.id,
293 # CASE WHEN ml.amount_currency < 0
294 # THEN - ml.amount_currency
297 # FROM account_move_line ml
298 # INNER JOIN payment_line pl
299 # ON (ml.id = pl.move_line_id)
300 # WHERE pl.id in (''' + ','.join([str(x) for x in ids]) + ')')
301 # return dict(cursor.fetchall())
303 def _amount(self, cursor, user, ids, name, args, context=None):
306 currency_obj = self.pool.get('res.currency')
310 for line in self.browse(cursor, user, ids, context=context):
312 ctx['date'] = line.order_id.date_done or time.strftime('%Y-%m-%d')
313 res[line.id] = currency_obj.compute(cursor, user, line.currency.id,
314 line.company_currency.id,
315 line.amount_currency, context=ctx)
318 def _value_date(self, cursor, user, ids, name, args, context=None):
322 for line in self.browse(cursor, user, ids, context=context):
323 if line.order_id.date_prefered == 'fixed':
324 res[line.id] = line.order_id.date_planned
325 elif line.order_id.date_prefered == 'due':
326 res[line.id] = line.due_date or time.strftime('%Y-%m-%d')
328 res[line.id] = time.strftime('%Y-%m-%d')
331 def _get_currency(self, cr, uid, context):
332 user = self.pool.get('res.users').browse(cr, uid, uid)
334 return user.company_id.currency_id.id
336 return self.pool.get('res.currency').search(cr, uid, [('rate','=',1.0)])[0]
338 # def select_move_lines(*a):
342 # def create(self, cr, uid, vals, context):
344 # vals['company_currency'] = self._get_currency(cr, uid, context)
345 # return super(payment_line, self).create(cr, uid, vals, context)
347 def _get_ml_inv_ref(self, cr, uid, ids, *a):
349 for id in self.browse(cr, uid, ids):
352 if id.move_line_id.invoice:
353 res[id.id] = id.move_line_id.invoice.id
356 def _get_ml_maturity_date(self, cr, uid, ids, *a):
358 for id in self.browse(cr, uid, ids):
360 res[id.id] = id.move_line_id.date_maturity
365 def _get_ml_created_date(self, cr, uid, ids, *a):
367 for id in self.browse(cr, uid, ids):
369 res[id.id] = id.move_line_id.date_created
375 'name': fields.char('Your Reference', size=64, required=True),
376 'communication': fields.char('Communication', size=64, required=True,help="Used as the message between ordering customer and current company. Depicts 'What do you want to say to the recipient about this order ?'"),
377 'communication2': fields.char('Communication 2', size=64,help='The successor message of Communication.'),
378 'move_line_id': fields.many2one('account.move.line','Entry line', domain=[('reconcile_id','=', False), ('account_id.type', '=','payable')],help='This Entry Line will be referred for the information of the ordering customer.'),
379 'amount_currency': fields.float('Amount in Partner Currency', digits=(16,2),
380 required=True, help='Payment amount in the partner currency'),
381 # 'to_pay_currency': fields.function(_to_pay_currency, string='To Pay',
382 # method=True, type='float',
383 # help='Amount to pay in the partner currency'),
384 # 'currency': fields.function(_currency, string='Currency',
385 # method=True, type='many2one', obj='res.currency'),
386 'currency': fields.many2one('res.currency','Partner Currency',required=True),
387 'company_currency': fields.many2one('res.currency','Company Currency',readonly=True),
388 'bank_id': fields.many2one('res.partner.bank', 'Destination Bank account'),
389 'order_id': fields.many2one('payment.order', 'Order', required=True,
390 ondelete='cascade', select=True),
391 'partner_id': fields.many2one('res.partner', string="Partner",required=True,help='The Ordering Customer'),
392 'amount': fields.function(_amount, string='Amount in Company Currency',
393 method=True, type='float',
394 help='Payment amount in the company currency'),
395 # 'to_pay': fields.function(select_by_name, string="To Pay", method=True,
396 # type='float', help='Amount to pay in the company currency'),
397 # 'due_date': fields.function(select_by_name, string="Due date",
398 # method=True, type='date'),
399 'ml_date_created': fields.function(_get_ml_created_date, string="Effective Date",
400 method=True, type='date',help="Invoice Effective Date"),
401 # 'reference': fields.function(select_by_name, string="Ref", method=True,
403 'ml_maturity_date': fields.function(_get_ml_maturity_date, method=True, type='date', string='Maturity Date'),
404 'ml_inv_ref': fields.function(_get_ml_inv_ref, method=True, type='many2one', relation='account.invoice', string='Invoice Ref.'),
405 'info_owner': fields.function(info_owner, string="Owner Account", method=True, type="text",help='Address of the Main Partner'),
406 'info_partner': fields.function(info_partner, string="Destination Account", method=True, type="text",help='Address of the Ordering Customer.'),
407 # 'partner_payable': fields.function(partner_payable, string="Partner payable", method=True, type='float'),
408 # 'value_date': fields.function(_value_date, string='Value Date',
409 # method=True, type='date'),
410 'date': fields.date('Payment Date',help="If no payment date is specified, the bank will treat this payment line directly"),
411 'create_date': fields.datetime('Created' ,readonly=True),
412 'state': fields.selection([('normal','Free'), ('structured','Structured')], 'Communication Type', required=True)
415 'name': lambda obj, cursor, user, context: obj.pool.get('ir.sequence'
416 ).get(cursor, user, 'payment.line'),
417 'state': lambda *args: 'normal',
418 'currency': _get_currency,
419 'company_currency': _get_currency,
422 ('name_uniq', 'UNIQUE(name)', 'The payment line name must be unique!'),
425 def onchange_move_line(self,cr,uid,ids,move_line_id,payment_type,date_prefered,date_planned,currency=False,company_currency=False,context=None):
428 data['amount_currency']=data['communication']=data['partner_id']=data['reference']=data['date_created']=data['bank_id']=data['amount']=False
431 line = self.pool.get('account.move.line').browse(cr,uid,move_line_id)
432 data['amount_currency']=line.amount_to_pay
434 res = self.onchange_amount(cr, uid, ids, data['amount_currency'], currency,
435 company_currency, context)
437 data['amount'] = res['value']['amount']
438 data['partner_id']=line.partner_id.id
439 temp = line.currency_id and line.currency_id.id or False
442 data['currency'] = line.invoice.currency_id.id
444 data['currency'] = temp
446 # calling onchange of partner and updating data dictionary
447 temp_dict=self.onchange_partner(cr,uid,ids,line.partner_id.id,payment_type)
448 data.update(temp_dict['value'])
450 data['reference']=line.ref
451 data['date_created'] = line.date_created
452 data['communication']=line.ref
454 if date_prefered == 'now':
455 #no payment date => immediate payment
457 elif date_prefered == 'due':
458 data['date'] = line.date_maturity
459 elif date_prefered == 'fixed':
460 data['date'] = date_planned
462 return {'value': data}
464 def onchange_amount(self, cr, uid, ids, amount, currency, cmpny_currency, context=None):
465 if (not amount) or (not cmpny_currency):
466 return {'value': {'amount':False}}
468 currency_obj = self.pool.get('res.currency')
469 company_amount = currency_obj.compute(cr, uid, currency, cmpny_currency, amount)
470 res['amount'] = company_amount
471 return {'value': res}
473 def onchange_partner(self,cr,uid,ids,partner_id,payment_type,context=None):
475 data['info_partner']=data['bank_id']=False
478 part_obj=self.pool.get('res.partner').browse(cr,uid,partner_id)
479 partner=part_obj.name or ''
482 for ads in part_obj.address:
483 if ads.type=='default':
484 st=ads.street and ads.street or ''
485 st1=ads.street2 and ads.street2 or ''
488 zip_city= ads.zip_id and self.pool.get('res.partner.zip').name_get(cr,uid,[ads.zip_id.id])[0][1] or ''
490 zip=ads.zip and ads.zip or ''
491 city= ads.city and ads.city or ''
492 zip_city= zip + ' ' + city
494 cntry= ads.country_id and ads.country_id.name or ''
495 info=partner + "\n" + st + " " + st1 + "\n" + zip_city + "\n" +cntry
497 data['info_partner']=info
499 if part_obj.bank_ids and payment_type:
500 bank_type = self.pool.get('payment.mode').suitable_bank_types(cr, uid, payment_type, context=context)
501 for bank in part_obj.bank_ids:
502 if bank.state in bank_type:
503 data['bank_id'] = bank.id
506 return {'value': data}
508 def fields_get(self, cr, uid, fields=None, context=None):
509 res = super(payment_line, self).fields_get(cr, uid, fields, context)
510 if 'communication2' in res:
511 res['communication2'].setdefault('states', {})
512 res['communication2']['states']['structured'] = [('readonly', True)]
513 res['communication2']['states']['normal'] = [('readonly', False)]
519 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: