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_date(self, cr, uid, context=None):
275 if context.get('order_id') and context['order_id']:
276 order = self.pool.get('payment.order').browse(cr, uid, context['order_id'], context)
277 if order.date_prefered == 'fixed':
278 date = order.date_scheduled
280 date = time.strftime('%Y-%m-%d')
283 def _get_ml_inv_ref(self, cr, uid, ids, *a):
285 for id in self.browse(cr, uid, ids):
288 if id.move_line_id.invoice:
289 res[id.id] = id.move_line_id.invoice.id
292 def _get_ml_maturity_date(self, cr, uid, ids, *a):
294 for id in self.browse(cr, uid, ids):
296 res[id.id] = id.move_line_id.date_maturity
301 def _get_ml_created_date(self, cr, uid, ids, *a):
303 for id in self.browse(cr, uid, ids):
305 res[id.id] = id.move_line_id.date_created
311 'name': fields.char('Your Reference', size=64, required=True),
312 '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 ?'"),
313 'communication2': fields.char('Communication 2', size=64, help='The successor message of Communication.'),
314 '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.'),
315 'amount_currency': fields.float('Amount in Partner Currency', digits=(16,2),
316 required=True, help='Payment amount in the partner currency'),
317 'currency': fields.many2one('res.currency','Partner Currency', required=True),
318 'company_currency': fields.many2one('res.currency', 'Company Currency', readonly=True),
319 'bank_id': fields.many2one('res.partner.bank', 'Destination Bank account'),
320 'order_id': fields.many2one('payment.order', 'Order', required=True,
321 ondelete='cascade', select=True),
322 'partner_id': fields.many2one('res.partner', string="Partner", required=True, help='The Ordering Customer'),
323 'amount': fields.function(_amount, string='Amount in Company Currency',
324 method=True, type='float',
325 help='Payment amount in the company currency'),
326 'ml_date_created': fields.function(_get_ml_created_date, string="Effective Date",
327 method=True, type='date', help="Invoice Effective Date"),
328 'ml_maturity_date': fields.function(_get_ml_maturity_date, method=True, type='date', string='Due Date'),
329 'ml_inv_ref': fields.function(_get_ml_inv_ref, method=True, type='many2one', relation='account.invoice', string='Invoice Ref.'),
330 'info_owner': fields.function(info_owner, string="Owner Account", method=True, type="text", help='Address of the Main Partner'),
331 'info_partner': fields.function(info_partner, string="Destination Account", method=True, type="text", help='Address of the Ordering Customer.'),
332 'date': fields.date('Payment Date', help="If no payment date is specified, the bank will treat this payment line directly"),
333 'create_date': fields.datetime('Created' , readonly=True),
334 'state': fields.selection([('normal','Free'), ('structured','Structured')], 'Communication Type', required=True),
335 'bank_statement_line_id': fields.many2one('account.bank.statement.line', 'Bank statement line')
338 'name': lambda obj, cursor, user, context: obj.pool.get('ir.sequence'
339 ).get(cursor, user, 'payment.line'),
340 'state': lambda *args: 'normal',
341 'currency': _get_currency,
342 'company_currency': _get_currency,
346 ('name_uniq', 'UNIQUE(name)', 'The payment line name must be unique!'),
349 def onchange_move_line(self, cr, uid, ids, move_line_id, payment_type, date_prefered, date_scheduled, currency=False, company_currency=False, context=None):
352 data['amount_currency']=data['communication']=data['partner_id']=data['reference']=data['date_created']=data['bank_id']=data['amount']=False
355 line = self.pool.get('account.move.line').browse(cr, uid, move_line_id)
356 data['amount_currency']=line.amount_to_pay
358 res = self.onchange_amount(cr, uid, ids, data['amount_currency'], currency,
359 company_currency, context)
361 data['amount'] = res['value']['amount']
362 data['partner_id']=line.partner_id.id
363 temp = line.currency_id and line.currency_id.id or False
366 data['currency'] = line.invoice.currency_id.id
368 data['currency'] = temp
370 # calling onchange of partner and updating data dictionary
371 temp_dict=self.onchange_partner(cr, uid, ids, line.partner_id.id, payment_type)
372 data.update(temp_dict['value'])
374 data['reference']=line.ref
375 data['date_created'] = line.date_created
376 data['communication']=line.ref
378 if date_prefered == 'now':
379 #no payment date => immediate payment
381 elif date_prefered == 'due':
382 data['date'] = line.date_maturity
383 elif date_prefered == 'fixed':
384 data['date'] = date_scheduled
385 return {'value': data}
387 def onchange_amount(self, cr, uid, ids, amount, currency, cmpny_currency, context=None):
388 if (not amount) or (not cmpny_currency):
389 return {'value': {'amount':False}}
391 currency_obj = self.pool.get('res.currency')
392 company_amount = currency_obj.compute(cr, uid, currency, cmpny_currency, amount)
393 res['amount'] = company_amount
394 return {'value': res}
396 def onchange_partner(self, cr, uid, ids, partner_id, payment_type, context=None):
398 data['info_partner']=data['bank_id']=False
401 part_obj=self.pool.get('res.partner').browse(cr, uid, partner_id)
402 partner=part_obj.name or ''
405 for ads in part_obj.address:
406 if ads.type=='default':
407 st=ads.street and ads.street or ''
408 st1=ads.street2 and ads.street2 or ''
411 zip_city= ads.zip_id and self.pool.get('res.partner.zip').name_get(cr, uid, [ads.zip_id.id])[0][1] or ''
413 zip=ads.zip and ads.zip or ''
414 city= ads.city and ads.city or ''
415 zip_city= zip + ' ' + city
417 cntry= ads.country_id and ads.country_id.name or ''
418 info=partner + "\n" + st + " " + st1 + "\n" + zip_city + "\n" +cntry
420 data['info_partner']=info
422 if part_obj.bank_ids and payment_type:
423 bank_type = self.pool.get('payment.mode').suitable_bank_types(cr, uid, payment_type, context=context)
424 for bank in part_obj.bank_ids:
425 if bank.state in bank_type:
426 data['bank_id'] = bank.id
428 return {'value': data}
430 def fields_get(self, cr, uid, fields=None, context=None):
431 res = super(payment_line, self).fields_get(cr, uid, fields, context)
432 if 'communication2' in res:
433 res['communication2'].setdefault('states', {})
434 res['communication2']['states']['structured'] = [('readonly', True)]
435 res['communication2']['states']['normal'] = [('readonly', False)]
441 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: