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 ##############################################################################
23 from osv import osv, fields
26 class payment_mode(osv.osv):
28 _description= 'Payment Mode'
30 'name': fields.char('Name', size=64, required=True, help='Mode of Payment'),
31 'bank_id': fields.many2one('res.partner.bank', "Bank account",
32 required=True,help='Bank Account for the Payment Mode'),
33 'journal': fields.many2one('account.journal', 'Journal', required=True,
34 domain=[('type', '=', 'cash')], help='Cash Journal for the Payment Mode'),
35 'company_id': fields.many2one('res.company', 'Company', required=True),
38 'company_id': lambda self,cr,uid,c: self.pool.get('res.users').browse(cr, uid, uid, c).company_id.id
41 def suitable_bank_types(self, cr, uid, payment_code=None, context={}):
42 """Return the codes of the bank type that are suitable
43 for the given payment type code"""
46 cr.execute(""" select pb.state
47 from res_partner_bank pb
48 join payment_mode pm on (pm.bank_id = pb.id)
49 where pm.id = %s """, [payment_code])
50 return [x[0] for x in cr.fetchall()]
54 class payment_order(osv.osv):
55 _name = 'payment.order'
56 _description = 'Payment Order'
57 _rec_name = 'reference'
59 def get_wizard(self, type):
60 logger = netsvc.Logger()
61 logger.notifyChannel("warning", netsvc.LOG_WARNING,
62 "No wizard found for the payment type '%s'." % type)
65 def _total(self, cursor, user, ids, name, args, context=None):
69 for order in self.browse(cursor, user, ids, context=context):
71 res[order.id] = reduce(lambda x, y: x + y.amount, order.line_ids, 0.0)
77 'date_scheduled': fields.date('Scheduled date if fixed', states={'done':[('readonly',True)]}, help='Select a date if you have chosen Preferred Date to be fixed.'),
78 'reference': fields.char('Reference', size=128, required=1, states={'done':[('readonly',True)]}),
79 'mode': fields.many2one('payment.mode','Payment mode', select=True, required=1, states={'done':[('readonly',True)]}, help='Select the Payment Mode to be applied.'),
80 'state': fields.selection([
83 ('cancel','Cancelled'),
84 ('done','Done')], 'State', select=True,
85 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\'.'),
86 'line_ids': fields.one2many('payment.line', 'order_id', 'Payment lines', states={'done':[('readonly',True)]}),
87 'total': fields.function(_total, string="Total", method=True,
89 'user_id': fields.many2one('res.users', 'User', required=True, states={'done':[('readonly',True)]}),
90 'date_prefered': fields.selection([
93 ('fixed', 'Fixed date')
94 ], "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."),
95 'date_created': fields.date('Creation date', readonly=True),
96 'date_done': fields.date('Execution date', readonly=True),
100 'user_id': lambda self,cr,uid,context: uid,
101 'state': lambda *a: 'draft',
102 'date_prefered': lambda *a: 'due',
103 'date_created': lambda *a: time.strftime('%Y-%m-%d'),
104 'reference': lambda self,cr,uid,context: self.pool.get('ir.sequence').get(cr, uid, 'payment.order'),
107 def set_to_draft(self, cr, uid, ids, *args):
108 self.write(cr, uid, ids, {'state':'draft'})
109 wf_service = netsvc.LocalService("workflow")
111 wf_service.trg_create(uid, 'payment.order', id, cr)
114 def action_open(self, cr, uid, ids, *args):
115 for order in self.read(cr, uid, ids, ['reference']):
116 if not order['reference']:
117 reference = self.pool.get('ir.sequence').get(cr, uid, 'payment.order')
118 self.write(cr, uid, order['id'],{'reference':reference})
121 def set_done(self, cr, uid, id, *args):
122 self.write(cr,uid,id,{'date_done': time.strftime('%Y-%m-%d'),
124 wf_service = netsvc.LocalService("workflow")
125 wf_service.trg_validate(uid, 'payment.order', id, 'done', cr)
128 def copy(self, cr, uid, id, default={}, context=None):
132 'reference': self.pool.get('ir.sequence').get(cr, uid, 'payment.order')
134 return super(payment_order, self).copy(cr, uid, id, default, context=context)
136 def write(self, cr, uid, ids, vals, context=None):
139 payment_line_obj = self.pool.get('payment.line')
140 payment_line_ids = []
141 if (vals.get('date_prefered', False) == 'fixed' and not vals.get('date_scheduled', False)) or vals.get('date_scheduled', False):
142 for order in self.browse(cr, uid, ids, context=context):
143 for line in order.line_ids:
144 payment_line_ids.append(line.id)
145 payment_line_obj.write(cr, uid, payment_line_ids, {'date':vals.get('date_scheduled', False)}, context=context)
146 elif vals.get('date_prefered', False) == 'due':
147 vals.update({'date_scheduled':False})
148 for order in self.browse(cr, uid, ids, context=context):
149 for line in order.line_ids:
150 payment_line_obj.write(cr, uid, [line.id], {'date':line.ml_maturity_date}, context=context)
151 elif vals.get('date_prefered', False) == 'now':
152 vals.update({'date_scheduled':False})
153 for order in self.browse(cr, uid, ids, context=context):
154 for line in order.line_ids:
155 payment_line_ids.append(line.id)
156 payment_line_obj.write(cr, uid, payment_line_ids, {'date': False}, context=context)
157 return super(payment_order, self).write(cr, uid, ids, vals, context=context)
161 class payment_line(osv.osv):
162 _name = 'payment.line'
163 _description = 'Payment Line'
166 def translate(self, orig):
168 "due_date": "date_maturity",
169 "reference": "ref"}.get(orig, orig)
171 def info_owner(self, cr, uid, ids, name=None, args=None, context=None):
172 if not ids: return {}
175 for line in self.browse(cr, uid, ids, context=context):
176 owner=line.order_id.mode.bank_id.partner_id
177 result[line.id]=False
179 for ads in owner.address:
180 if ads.type=='default':
181 st=ads.street and ads.street or ''
182 st1=ads.street2 and ads.street2 or ''
184 zip_city= ads.zip_id and self.pool.get('res.partner.zip').name_get(cr, uid, [ads.zip_id.id])[0][1] or ''
186 zip=ads.zip and ads.zip or ''
187 city= ads.city and ads.city or ''
188 zip_city= zip + ' ' + city
189 cntry= ads.country_id and ads.country_id.name or ''
190 info=owner.name + "\n" + st + " " + st1 + "\n" + zip_city + "\n" +cntry
195 def info_partner(self, cr, uid, ids, name=None, args=None, context=None):
196 if not ids: return {}
199 for line in self.browse(cr, uid, ids, context=context):
200 result[line.id]=False
201 if not line.partner_id:
203 partner = line.partner_id.name or ''
204 if line.partner_id.address:
205 for ads in line.partner_id.address:
206 if ads.type=='default':
207 st=ads.street and ads.street or ''
208 st1=ads.street2 and ads.street2 or ''
210 zip_city= ads.zip_id and self.pool.get('res.partner.zip').name_get(cr, uid, [ads.zip_id.id])[0][1] or ''
212 zip=ads.zip and ads.zip or ''
213 city= ads.city and ads.city or ''
214 zip_city= zip + ' ' + city
215 cntry= ads.country_id and ads.country_id.name or ''
216 info=partner + "\n" + st + " " + st1 + "\n" + zip_city + "\n" +cntry
221 def select_by_name(self, cr, uid, ids, name, args, context=None):
222 if not ids: return {}
224 partner_obj = self.pool.get('res.partner')
225 cr.execute("""SELECT pl.id, ml.%s
226 from account_move_line ml
227 inner join payment_line pl
228 on (ml.id = pl.move_line_id)
229 where pl.id IN %%s"""% self.translate(name),
231 res = dict(cr.fetchall())
233 if name == 'partner_id':
235 for p_id, p_name in partner_obj.name_get(cr, uid,
236 filter(lambda x:x and x != 0,res.values()), context=context):
237 partner_name[p_id] = p_name
240 if id in res and partner_name:
241 res[id] = (res[id],partner_name[res[id]])
243 res[id] = (False,False)
246 res.setdefault(id, (False, ""))
249 def _amount(self, cursor, user, ids, name, args, context=None):
252 currency_obj = self.pool.get('res.currency')
256 for line in self.browse(cursor, user, ids, context=context):
258 ctx['date'] = line.order_id.date_done or time.strftime('%Y-%m-%d')
259 res[line.id] = currency_obj.compute(cursor, user, line.currency.id,
260 line.company_currency.id,
261 line.amount_currency, context=ctx)
264 def _get_currency(self, cr, uid, context):
265 user = self.pool.get('res.users').browse(cr, uid, uid)
267 return user.company_id.currency_id.id
269 return self.pool.get('res.currency').search(cr, uid, [('rate','=',1.0)])[0]
271 def _get_ml_inv_ref(self, cr, uid, ids, *a):
273 for id in self.browse(cr, uid, ids):
276 if id.move_line_id.invoice:
277 res[id.id] = id.move_line_id.invoice.id
280 def _get_ml_maturity_date(self, cr, uid, ids, *a):
282 for id in self.browse(cr, uid, ids):
284 res[id.id] = id.move_line_id.date_maturity
289 def _get_ml_created_date(self, cr, uid, ids, *a):
291 for id in self.browse(cr, uid, ids):
293 res[id.id] = id.move_line_id.date_created
299 'name': fields.char('Your Reference', size=64, required=True),
300 '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 ?'"),
301 'communication2': fields.char('Communication 2', size=64, help='The successor message of Communication.'),
302 '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.'),
303 'amount_currency': fields.float('Amount in Partner Currency', digits=(16,2),
304 required=True, help='Payment amount in the partner currency'),
305 'currency': fields.many2one('res.currency','Partner Currency', required=True),
306 'company_currency': fields.many2one('res.currency', 'Company Currency', readonly=True),
307 'bank_id': fields.many2one('res.partner.bank', 'Destination Bank account'),
308 'order_id': fields.many2one('payment.order', 'Order', required=True,
309 ondelete='cascade', select=True),
310 'partner_id': fields.many2one('res.partner', string="Partner", required=True, help='The Ordering Customer'),
311 'amount': fields.function(_amount, string='Amount in Company Currency',
312 method=True, type='float',
313 help='Payment amount in the company currency'),
314 'ml_date_created': fields.function(_get_ml_created_date, string="Effective Date",
315 method=True, type='date', help="Invoice Effective Date"),
316 'ml_maturity_date': fields.function(_get_ml_maturity_date, method=True, type='date', string='Due Date'),
317 'ml_inv_ref': fields.function(_get_ml_inv_ref, method=True, type='many2one', relation='account.invoice', string='Invoice Ref.'),
318 'info_owner': fields.function(info_owner, string="Owner Account", method=True, type="text", help='Address of the Main Partner'),
319 'info_partner': fields.function(info_partner, string="Destination Account", method=True, type="text", help='Address of the Ordering Customer.'),
320 'date': fields.date('Payment Date', help="If no payment date is specified, the bank will treat this payment line directly"),
321 'create_date': fields.datetime('Created' , readonly=True),
322 'state': fields.selection([('normal','Free'), ('structured','Structured')], 'Communication Type', required=True),
323 'bank_statement_line_id': fields.many2one('account.bank.statement.line', 'Bank statement line')
326 'name': lambda obj, cursor, user, context: obj.pool.get('ir.sequence'
327 ).get(cursor, user, 'payment.line'),
328 'state': lambda *args: 'normal',
329 'currency': _get_currency,
330 'company_currency': _get_currency,
333 ('name_uniq', 'UNIQUE(name)', 'The payment line name must be unique!'),
336 def onchange_move_line(self, cr, uid, ids, move_line_id, payment_type, date_prefered, date_scheduled, currency=False, company_currency=False, context=None):
339 data['amount_currency']=data['communication']=data['partner_id']=data['reference']=data['date_created']=data['bank_id']=data['amount']=False
342 line = self.pool.get('account.move.line').browse(cr, uid, move_line_id)
343 data['amount_currency']=line.amount_to_pay
345 res = self.onchange_amount(cr, uid, ids, data['amount_currency'], currency,
346 company_currency, context)
348 data['amount'] = res['value']['amount']
349 data['partner_id']=line.partner_id.id
350 temp = line.currency_id and line.currency_id.id or False
353 data['currency'] = line.invoice.currency_id.id
355 data['currency'] = temp
357 # calling onchange of partner and updating data dictionary
358 temp_dict=self.onchange_partner(cr, uid, ids, line.partner_id.id, payment_type)
359 data.update(temp_dict['value'])
361 data['reference']=line.ref
362 data['date_created'] = line.date_created
363 data['communication']=line.ref
365 if date_prefered == 'now':
366 #no payment date => immediate payment
368 elif date_prefered == 'due':
369 data['date'] = line.date_maturity
370 elif date_prefered == 'fixed':
371 data['date'] = date_scheduled
372 return {'value': data}
374 def onchange_amount(self, cr, uid, ids, amount, currency, cmpny_currency, context=None):
375 if (not amount) or (not cmpny_currency):
376 return {'value': {'amount':False}}
378 currency_obj = self.pool.get('res.currency')
379 company_amount = currency_obj.compute(cr, uid, currency, cmpny_currency, amount)
380 res['amount'] = company_amount
381 return {'value': res}
383 def onchange_partner(self, cr, uid, ids, partner_id, payment_type, context=None):
385 data['info_partner']=data['bank_id']=False
388 part_obj=self.pool.get('res.partner').browse(cr, uid, partner_id)
389 partner=part_obj.name or ''
392 for ads in part_obj.address:
393 if ads.type=='default':
394 st=ads.street and ads.street or ''
395 st1=ads.street2 and ads.street2 or ''
398 zip_city= ads.zip_id and self.pool.get('res.partner.zip').name_get(cr, uid, [ads.zip_id.id])[0][1] or ''
400 zip=ads.zip and ads.zip or ''
401 city= ads.city and ads.city or ''
402 zip_city= zip + ' ' + city
404 cntry= ads.country_id and ads.country_id.name or ''
405 info=partner + "\n" + st + " " + st1 + "\n" + zip_city + "\n" +cntry
407 data['info_partner']=info
409 if part_obj.bank_ids and payment_type:
410 bank_type = self.pool.get('payment.mode').suitable_bank_types(cr, uid, payment_type, context=context)
411 for bank in part_obj.bank_ids:
412 if bank.state in bank_type:
413 data['bank_id'] = bank.id
415 return {'value': data}
417 def fields_get(self, cr, uid, fields=None, context=None):
418 res = super(payment_line, self).fields_get(cr, uid, fields, context)
419 if 'communication2' in res:
420 res['communication2'].setdefault('states', {})
421 res['communication2']['states']['structured'] = [('readonly', True)]
422 res['communication2']['states']['normal'] = [('readonly', False)]
428 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: