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 ##############################################################################
24 from osv import osv, fields
27 class payment_mode(osv.osv):
29 _description= 'Payment Mode'
31 'name': fields.char('Name', size=64, required=True, help='Mode of Payment'),
32 'bank_id': fields.many2one('res.partner.bank', "Bank account",
33 required=True,help='Bank Account for the Payment Mode'),
34 'journal': fields.many2one('account.journal', 'Journal', required=True,
35 domain=[('type', 'in', ('bank','cash'))], help='Bank or Cash Journal for the Payment Mode'),
36 'company_id': fields.many2one('res.company', 'Company',required=True),
37 'partner_id':fields.related('company_id','partner_id',type='many2one',relation='res.partner',string='Partner',store=True,),
41 'company_id': lambda self,cr,uid,c: self.pool.get('res.users').browse(cr, uid, uid, c).company_id.id
44 def suitable_bank_types(self, cr, uid, payment_code=None, context=None):
45 """Return the codes of the bank type that are suitable
46 for the given payment type code"""
49 cr.execute(""" SELECT pb.state
50 FROM res_partner_bank pb
51 JOIN payment_mode pm ON (pm.bank_id = pb.id)
52 WHERE pm.id = %s """, [payment_code])
53 return [x[0] for x in cr.fetchall()]
55 def onchange_company_id (self, cr, uid, ids, company_id=False, context=None):
58 partner_id = self.pool.get('res.company').browse(cr, uid, company_id, context=context).partner_id.id
59 result['partner_id'] = partner_id
60 return {'value': result}
65 class payment_order(osv.osv):
66 _name = 'payment.order'
67 _description = 'Payment Order'
68 _rec_name = 'reference'
72 def get_wizard(self, type):
73 logger = netsvc.Logger()
74 logger.notifyChannel("warning", netsvc.LOG_WARNING,
75 "No wizard found for the payment type '%s'." % type)
78 def _total(self, cursor, user, ids, name, args, context=None):
82 for order in self.browse(cursor, user, ids, context=context):
84 res[order.id] = reduce(lambda x, y: x + y.amount, order.line_ids, 0.0)
90 '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.'),
91 'reference': fields.char('Reference', size=128, required=1, states={'done': [('readonly', True)]}),
92 'mode': fields.many2one('payment.mode', 'Payment mode', select=True, required=1, states={'done': [('readonly', True)]}, help='Select the Payment Mode to be applied.'),
93 'state': fields.selection([
95 ('open', 'Confirmed'),
96 ('cancel', 'Cancelled'),
97 ('done', 'Done')], 'State', select=True,
98 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\'.'),
99 'line_ids': fields.one2many('payment.line', 'order_id', 'Payment lines', states={'done': [('readonly', True)]}),
100 'total': fields.function(_total, string="Total", type='float'),
101 'user_id': fields.many2one('res.users', 'User', required=True, states={'done': [('readonly', True)]}),
102 'date_prefered': fields.selection([
105 ('fixed', 'Fixed date')
106 ], "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."),
107 'date_created': fields.date('Creation date', readonly=True),
108 'date_done': fields.date('Execution date', readonly=True),
109 'company_id': fields.related('mode', 'company_id', type='many2one', relation='res.company', string='Company', store=True, readonly=True),
113 'user_id': lambda self,cr,uid,context: uid,
115 'date_prefered': 'due',
116 'date_created': lambda *a: time.strftime('%Y-%m-%d'),
117 'reference': lambda self,cr,uid,context: self.pool.get('ir.sequence').get(cr, uid, 'payment.order'),
120 def set_to_draft(self, cr, uid, ids, *args):
121 self.write(cr, uid, ids, {'state': 'draft'})
122 wf_service = netsvc.LocalService("workflow")
124 wf_service.trg_create(uid, 'payment.order', id, cr)
127 def action_open(self, cr, uid, ids, *args):
128 ir_seq_obj = self.pool.get('ir.sequence')
130 for order in self.read(cr, uid, ids, ['reference']):
131 if not order['reference']:
132 reference = ir_seq_obj.get(cr, uid, 'payment.order')
133 self.write(cr, uid, order['id'], {'reference':reference})
136 def set_done(self, cr, uid, ids, *args):
137 wf_service = netsvc.LocalService("workflow")
138 self.write(cr, uid, ids, {'date_done': time.strftime('%Y-%m-%d')})
139 wf_service.trg_validate(uid, 'payment.order', ids[0], 'done', cr)
142 def copy(self, cr, uid, id, default={}, context=None):
146 'reference': self.pool.get('ir.sequence').get(cr, uid, 'payment.order')
148 return super(payment_order, self).copy(cr, uid, id, default, context=context)
150 def write(self, cr, uid, ids, vals, context=None):
153 payment_line_obj = self.pool.get('payment.line')
154 payment_line_ids = []
156 if (vals.get('date_prefered', False) == 'fixed' and not vals.get('date_scheduled', False)) or vals.get('date_scheduled', False):
157 for order in self.browse(cr, uid, ids, context=context):
158 for line in order.line_ids:
159 payment_line_ids.append(line.id)
160 payment_line_obj.write(cr, uid, payment_line_ids, {'date': vals.get('date_scheduled', False)}, context=context)
161 elif vals.get('date_prefered', False) == 'due':
162 vals.update({'date_scheduled': False})
163 for order in self.browse(cr, uid, ids, context=context):
164 for line in order.line_ids:
165 payment_line_obj.write(cr, uid, [line.id], {'date': line.ml_maturity_date}, context=context)
166 elif vals.get('date_prefered', False) == 'now':
167 vals.update({'date_scheduled': False})
168 for order in self.browse(cr, uid, ids, context=context):
169 for line in order.line_ids:
170 payment_line_ids.append(line.id)
171 payment_line_obj.write(cr, uid, payment_line_ids, {'date': False}, context=context)
172 return super(payment_order, self).write(cr, uid, ids, vals, context=context)
176 class payment_line(osv.osv):
177 _name = 'payment.line'
178 _description = 'Payment Line'
180 def translate(self, orig):
182 "due_date": "date_maturity",
183 "reference": "ref"}.get(orig, orig)
185 def info_owner(self, cr, uid, ids, name=None, args=None, context=None):
186 if not ids: return {}
187 partner_zip_obj = self.pool.get('res.partner.zip')
191 for line in self.browse(cr, uid, ids, context=context):
192 owner = line.order_id.mode.bank_id.partner_id
193 result[line.id] = False
195 for ads in owner.address:
196 if ads.type == 'default':
197 st = ads.street and ads.street or ''
198 st1 = ads.street2 and ads.street2 or ''
200 zip_city = ads.zip_id and partner_zip_obj.name_get(cr, uid, [ads.zip_id.id])[0][1] or ''
202 zip = ads.zip and ads.zip or ''
203 city = ads.city and ads.city or ''
204 zip_city = zip + ' ' + city
205 cntry = ads.country_id and ads.country_id.name or ''
206 info = owner.name + "\n" + st + " " + st1 + "\n" + zip_city + "\n" +cntry
207 result[line.id] = info
211 def info_partner(self, cr, uid, ids, name=None, args=None, context=None):
212 if not ids: return {}
213 partner_zip_obj = self.pool.get('res.partner.zip')
217 for line in self.browse(cr, uid, ids, context=context):
218 result[line.id] = False
219 if not line.partner_id:
221 partner = line.partner_id.name or ''
222 if line.partner_id.address:
223 for ads in line.partner_id.address:
224 if ads.type == 'default':
225 st = ads.street and ads.street or ''
226 st1 = ads.street2 and ads.street2 or ''
228 zip_city = ads.zip_id and partner_zip_obj.name_get(cr, uid, [ads.zip_id.id])[0][1] or ''
230 zip = ads.zip and ads.zip or ''
231 city = ads.city and ads.city or ''
232 zip_city = zip + ' ' + city
233 cntry = ads.country_id and ads.country_id.name or ''
234 info = partner + "\n" + st + " " + st1 + "\n" + zip_city + "\n" +cntry
235 result[line.id] = info
240 def select_by_name(self, cr, uid, ids, name, args, context=None):
241 if not ids: return {}
242 partner_obj = self.pool.get('res.partner')
244 cr.execute("""SELECT pl.id, ml.%s
245 FROM account_move_line ml
246 INNER JOIN payment_line pl
247 ON (ml.id = pl.move_line_id)
248 WHERE pl.id IN %%s"""% self.translate(name),
250 res = dict(cr.fetchall())
252 if name == 'partner_id':
254 for p_id, p_name in partner_obj.name_get(cr, uid,
255 filter(lambda x:x and x != 0,res.values()), context=context):
256 partner_name[p_id] = p_name
259 if id in res and partner_name:
260 res[id] = (res[id],partner_name[res[id]])
262 res[id] = (False,False)
265 res.setdefault(id, (False, ""))
268 def _amount(self, cursor, user, ids, name, args, context=None):
271 currency_obj = self.pool.get('res.currency')
276 for line in self.browse(cursor, user, ids, context=context):
278 ctx['date'] = line.order_id.date_done or time.strftime('%Y-%m-%d')
279 res[line.id] = currency_obj.compute(cursor, user, line.currency.id,
280 line.company_currency.id,
281 line.amount_currency, context=ctx)
284 def _get_currency(self, cr, uid, context=None):
285 user_obj = self.pool.get('res.users')
286 currency_obj = self.pool.get('res.currency')
287 user = user_obj.browse(cr, uid, uid, context=context)
290 return user.company_id.currency_id.id
292 return currency_obj.search(cr, uid, [('rate', '=', 1.0)])[0]
294 def _get_date(self, cr, uid, context=None):
297 payment_order_obj = self.pool.get('payment.order')
300 if context.get('order_id') and context['order_id']:
301 order = payment_order_obj.browse(cr, uid, context['order_id'], context=context)
302 if order.date_prefered == 'fixed':
303 date = order.date_scheduled
305 date = time.strftime('%Y-%m-%d')
308 def _get_ml_inv_ref(self, cr, uid, ids, *a):
310 for id in self.browse(cr, uid, ids):
313 if id.move_line_id.invoice:
314 res[id.id] = id.move_line_id.invoice.id
317 def _get_ml_maturity_date(self, cr, uid, ids, *a):
319 for id in self.browse(cr, uid, ids):
321 res[id.id] = id.move_line_id.date_maturity
326 def _get_ml_created_date(self, cr, uid, ids, *a):
328 for id in self.browse(cr, uid, ids):
330 res[id.id] = id.move_line_id.date_created
336 'name': fields.char('Your Reference', size=64, required=True),
337 '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 ?'"),
338 'communication2': fields.char('Communication 2', size=64, help='The successor message of Communication.'),
339 '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.'),
340 'amount_currency': fields.float('Amount in Partner Currency', digits=(16, 2),
341 required=True, help='Payment amount in the partner currency'),
342 'currency': fields.many2one('res.currency','Partner Currency', required=True),
343 'company_currency': fields.many2one('res.currency', 'Company Currency', readonly=True),
344 'bank_id': fields.many2one('res.partner.bank', 'Destination Bank Account'),
345 'order_id': fields.many2one('payment.order', 'Order', required=True,
346 ondelete='cascade', select=True),
347 'partner_id': fields.many2one('res.partner', string="Partner", required=True, help='The Ordering Customer'),
348 'amount': fields.function(_amount, string='Amount in Company Currency',
350 help='Payment amount in the company currency'),
351 'ml_date_created': fields.function(_get_ml_created_date, string="Effective Date",
352 type='date', help="Invoice Effective Date"),
353 'ml_maturity_date': fields.function(_get_ml_maturity_date, type='date', string='Due Date'),
354 'ml_inv_ref': fields.function(_get_ml_inv_ref, type='many2one', relation='account.invoice', string='Invoice Ref.'),
355 'info_owner': fields.function(info_owner, string="Owner Account", type="text", help='Address of the Main Partner'),
356 'info_partner': fields.function(info_partner, string="Destination Account", type="text", help='Address of the Ordering Customer.'),
357 'date': fields.date('Payment Date', help="If no payment date is specified, the bank will treat this payment line directly"),
358 'create_date': fields.datetime('Created', readonly=True),
359 'state': fields.selection([('normal','Free'), ('structured','Structured')], 'Communication Type', required=True),
360 'bank_statement_line_id': fields.many2one('account.bank.statement.line', 'Bank statement line'),
361 'company_id': fields.related('order_id', 'company_id', type='many2one', relation='res.company', string='Company', store=True, readonly=True),
364 'name': lambda obj, cursor, user, context: obj.pool.get('ir.sequence'
365 ).get(cursor, user, 'payment.line'),
367 'currency': _get_currency,
368 'company_currency': _get_currency,
372 ('name_uniq', 'UNIQUE(name)', 'The payment line name must be unique!'),
375 def onchange_move_line(self, cr, uid, ids, move_line_id, payment_type, date_prefered, date_scheduled, currency=False, company_currency=False, context=None):
377 move_line_obj = self.pool.get('account.move.line')
379 data['amount_currency'] = data['communication'] = data['partner_id'] = data['bank_id'] = data['amount'] = False
382 line = move_line_obj.browse(cr, uid, move_line_id, context=context)
383 data['amount_currency'] = line.amount_to_pay
385 res = self.onchange_amount(cr, uid, ids, data['amount_currency'], currency,
386 company_currency, context)
388 data['amount'] = res['value']['amount']
389 data['partner_id'] = line.partner_id.id
390 temp = line.currency_id and line.currency_id.id or False
393 data['currency'] = line.invoice.currency_id.id
395 data['currency'] = temp
397 # calling onchange of partner and updating data dictionary
398 temp_dict = self.onchange_partner(cr, uid, ids, line.partner_id.id, payment_type)
399 data.update(temp_dict['value'])
401 data['communication'] = line.ref
403 if date_prefered == 'now':
404 #no payment date => immediate payment
406 elif date_prefered == 'due':
407 data['date'] = line.date_maturity
408 elif date_prefered == 'fixed':
409 data['date'] = date_scheduled
410 return {'value': data}
412 def onchange_amount(self, cr, uid, ids, amount, currency, cmpny_currency, context=None):
413 if (not amount) or (not cmpny_currency):
414 return {'value': {'amount': False}}
416 currency_obj = self.pool.get('res.currency')
417 company_amount = currency_obj.compute(cr, uid, currency, cmpny_currency, amount)
418 res['amount'] = company_amount
419 return {'value': res}
421 def onchange_partner(self, cr, uid, ids, partner_id, payment_type, context=None):
423 partner_zip_obj = self.pool.get('res.partner.zip')
424 partner_obj = self.pool.get('res.partner')
425 payment_mode_obj = self.pool.get('payment.mode')
426 data['info_partner'] = data['bank_id'] = False
429 part_obj = partner_obj.browse(cr, uid, partner_id, context=context)
430 partner = part_obj.name or ''
433 for ads in part_obj.address:
434 if ads.type == 'default':
435 st = ads.street and ads.street or ''
436 st1 = ads.street2 and ads.street2 or ''
439 zip_city = ads.zip_id and partner_zip_obj.name_get(cr, uid, [ads.zip_id.id])[0][1] or ''
441 zip = ads.zip and ads.zip or ''
442 city = ads.city and ads.city or ''
443 zip_city = zip + ' ' + city
445 cntry = ads.country_id and ads.country_id.name or ''
446 info = partner + "\n" + st + " " + st1 + "\n" + zip_city + "\n" +cntry
448 data['info_partner'] = info
450 if part_obj.bank_ids and payment_type:
451 bank_type = payment_mode_obj.suitable_bank_types(cr, uid, payment_type, context=context)
452 for bank in part_obj.bank_ids:
453 if bank.state in bank_type:
454 data['bank_id'] = bank.id
456 return {'value': data}
458 def fields_get(self, cr, uid, fields=None, context=None):
459 res = super(payment_line, self).fields_get(cr, uid, fields, context)
460 if 'communication2' in res:
461 res['communication2'].setdefault('states', {})
462 res['communication2']['states']['structured'] = [('readonly', True)]
463 res['communication2']['states']['normal'] = [('readonly', False)]
468 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: