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'
71 def get_wizard(self, type):
72 logger = netsvc.Logger()
73 logger.notifyChannel("warning", netsvc.LOG_WARNING,
74 "No wizard found for the payment type '%s'." % type)
77 def _total(self, cursor, user, ids, name, args, context=None):
81 for order in self.browse(cursor, user, ids, context=context):
83 res[order.id] = reduce(lambda x, y: x + y.amount, order.line_ids, 0.0)
89 '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.'),
90 'reference': fields.char('Reference', size=128, required=1, states={'done': [('readonly', True)]}),
91 'mode': fields.many2one('payment.mode', 'Payment mode', select=True, required=1, states={'done': [('readonly', True)]}, help='Select the Payment Mode to be applied.'),
92 'state': fields.selection([
94 ('open', 'Confirmed'),
95 ('cancel', 'Cancelled'),
96 ('done', 'Done')], 'State', select=True,
97 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\'.'),
98 'line_ids': fields.one2many('payment.line', 'order_id', 'Payment lines', states={'done': [('readonly', True)]}),
99 'total': fields.function(_total, string="Total", type='float'),
100 'user_id': fields.many2one('res.users', 'User', required=True, states={'done': [('readonly', True)]}),
101 'date_prefered': fields.selection([
104 ('fixed', 'Fixed date')
105 ], "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."),
106 'date_created': fields.date('Creation date', readonly=True),
107 'date_done': fields.date('Execution date', readonly=True),
108 'company_id': fields.related('mode', 'company_id', type='many2one', relation='res.company', string='Company', store=True, readonly=True),
112 'user_id': lambda self,cr,uid,context: uid,
114 'date_prefered': 'due',
115 'date_created': lambda *a: time.strftime('%Y-%m-%d'),
116 'reference': lambda self,cr,uid,context: self.pool.get('ir.sequence').get(cr, uid, 'payment.order'),
119 def set_to_draft(self, cr, uid, ids, *args):
120 self.write(cr, uid, ids, {'state': 'draft'})
121 wf_service = netsvc.LocalService("workflow")
123 wf_service.trg_create(uid, 'payment.order', id, cr)
126 def action_open(self, cr, uid, ids, *args):
127 ir_seq_obj = self.pool.get('ir.sequence')
129 for order in self.read(cr, uid, ids, ['reference']):
130 if not order['reference']:
131 reference = ir_seq_obj.get(cr, uid, 'payment.order')
132 self.write(cr, uid, order['id'], {'reference':reference})
135 def set_done(self, cr, uid, ids, *args):
136 wf_service = netsvc.LocalService("workflow")
137 self.write(cr, uid, ids, {'date_done': time.strftime('%Y-%m-%d')})
138 wf_service.trg_validate(uid, 'payment.order', ids[0], 'done', cr)
141 def copy(self, cr, uid, id, default={}, context=None):
145 'reference': self.pool.get('ir.sequence').get(cr, uid, 'payment.order')
147 return super(payment_order, self).copy(cr, uid, id, default, context=context)
149 def write(self, cr, uid, ids, vals, context=None):
152 payment_line_obj = self.pool.get('payment.line')
153 payment_line_ids = []
155 if (vals.get('date_prefered', False) == 'fixed' and not vals.get('date_scheduled', False)) or vals.get('date_scheduled', False):
156 for order in self.browse(cr, uid, ids, context=context):
157 for line in order.line_ids:
158 payment_line_ids.append(line.id)
159 payment_line_obj.write(cr, uid, payment_line_ids, {'date': vals.get('date_scheduled', False)}, context=context)
160 elif vals.get('date_prefered', False) == 'due':
161 vals.update({'date_scheduled': False})
162 for order in self.browse(cr, uid, ids, context=context):
163 for line in order.line_ids:
164 payment_line_obj.write(cr, uid, [line.id], {'date': line.ml_maturity_date}, context=context)
165 elif vals.get('date_prefered', False) == 'now':
166 vals.update({'date_scheduled': False})
167 for order in self.browse(cr, uid, ids, context=context):
168 for line in order.line_ids:
169 payment_line_ids.append(line.id)
170 payment_line_obj.write(cr, uid, payment_line_ids, {'date': False}, context=context)
171 return super(payment_order, self).write(cr, uid, ids, vals, context=context)
175 class payment_line(osv.osv):
176 _name = 'payment.line'
177 _description = 'Payment Line'
179 def translate(self, orig):
181 "due_date": "date_maturity",
182 "reference": "ref"}.get(orig, orig)
184 def info_owner(self, cr, uid, ids, name=None, args=None, context=None):
185 if not ids: return {}
186 partner_zip_obj = self.pool.get('res.partner.zip')
190 for line in self.browse(cr, uid, ids, context=context):
191 owner = line.order_id.mode.bank_id.partner_id
192 result[line.id] = False
194 for ads in owner.address:
195 if ads.type == 'default':
196 st = ads.street and ads.street or ''
197 st1 = ads.street2 and ads.street2 or ''
199 zip_city = ads.zip_id and partner_zip_obj.name_get(cr, uid, [ads.zip_id.id])[0][1] or ''
201 zip = ads.zip and ads.zip or ''
202 city = ads.city and ads.city or ''
203 zip_city = zip + ' ' + city
204 cntry = ads.country_id and ads.country_id.name or ''
205 info = owner.name + "\n" + st + " " + st1 + "\n" + zip_city + "\n" +cntry
206 result[line.id] = info
210 def info_partner(self, cr, uid, ids, name=None, args=None, context=None):
211 if not ids: return {}
212 partner_zip_obj = self.pool.get('res.partner.zip')
216 for line in self.browse(cr, uid, ids, context=context):
217 result[line.id] = False
218 if not line.partner_id:
220 partner = line.partner_id.name or ''
221 if line.partner_id.address:
222 for ads in line.partner_id.address:
223 if ads.type == 'default':
224 st = ads.street and ads.street or ''
225 st1 = ads.street2 and ads.street2 or ''
227 zip_city = ads.zip_id and partner_zip_obj.name_get(cr, uid, [ads.zip_id.id])[0][1] or ''
229 zip = ads.zip and ads.zip or ''
230 city = ads.city and ads.city or ''
231 zip_city = zip + ' ' + city
232 cntry = ads.country_id and ads.country_id.name or ''
233 info = partner + "\n" + st + " " + st1 + "\n" + zip_city + "\n" +cntry
234 result[line.id] = info
238 def select_by_name(self, cr, uid, ids, name, args, context=None):
239 if not ids: return {}
240 partner_obj = self.pool.get('res.partner')
242 cr.execute("""SELECT pl.id, ml.%s
243 FROM account_move_line ml
244 INNER JOIN payment_line pl
245 ON (ml.id = pl.move_line_id)
246 WHERE pl.id IN %%s"""% self.translate(name),
248 res = dict(cr.fetchall())
250 if name == 'partner_id':
252 for p_id, p_name in partner_obj.name_get(cr, uid,
253 filter(lambda x:x and x != 0,res.values()), context=context):
254 partner_name[p_id] = p_name
257 if id in res and partner_name:
258 res[id] = (res[id],partner_name[res[id]])
260 res[id] = (False,False)
263 res.setdefault(id, (False, ""))
266 def _amount(self, cursor, user, ids, name, args, context=None):
269 currency_obj = self.pool.get('res.currency')
274 for line in self.browse(cursor, user, ids, context=context):
276 ctx['date'] = line.order_id.date_done or time.strftime('%Y-%m-%d')
277 res[line.id] = currency_obj.compute(cursor, user, line.currency.id,
278 line.company_currency.id,
279 line.amount_currency, context=ctx)
282 def _get_currency(self, cr, uid, context=None):
283 user_obj = self.pool.get('res.users')
284 currency_obj = self.pool.get('res.currency')
285 user = user_obj.browse(cr, uid, uid, context=context)
288 return user.company_id.currency_id.id
290 return currency_obj.search(cr, uid, [('rate', '=', 1.0)])[0]
292 def _get_date(self, cr, uid, context=None):
295 payment_order_obj = self.pool.get('payment.order')
298 if context.get('order_id') and context['order_id']:
299 order = payment_order_obj.browse(cr, uid, context['order_id'], context=context)
300 if order.date_prefered == 'fixed':
301 date = order.date_scheduled
303 date = time.strftime('%Y-%m-%d')
306 def _get_ml_inv_ref(self, cr, uid, ids, *a):
308 for id in self.browse(cr, uid, ids):
311 if id.move_line_id.invoice:
312 res[id.id] = id.move_line_id.invoice.id
315 def _get_ml_maturity_date(self, cr, uid, ids, *a):
317 for id in self.browse(cr, uid, ids):
319 res[id.id] = id.move_line_id.date_maturity
324 def _get_ml_created_date(self, cr, uid, ids, *a):
326 for id in self.browse(cr, uid, ids):
328 res[id.id] = id.move_line_id.date_created
334 'name': fields.char('Your Reference', size=64, required=True),
335 '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 ?'"),
336 'communication2': fields.char('Communication 2', size=64, help='The successor message of Communication.'),
337 '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.'),
338 'amount_currency': fields.float('Amount in Partner Currency', digits=(16, 2),
339 required=True, help='Payment amount in the partner currency'),
340 'currency': fields.many2one('res.currency','Partner Currency', required=True),
341 'company_currency': fields.many2one('res.currency', 'Company Currency', readonly=True),
342 'bank_id': fields.many2one('res.partner.bank', 'Destination Bank Account'),
343 'order_id': fields.many2one('payment.order', 'Order', required=True,
344 ondelete='cascade', select=True),
345 'partner_id': fields.many2one('res.partner', string="Partner", required=True, help='The Ordering Customer'),
346 'amount': fields.function(_amount, string='Amount in Company Currency',
348 help='Payment amount in the company currency'),
349 'ml_date_created': fields.function(_get_ml_created_date, string="Effective Date",
350 type='date', help="Invoice Effective Date"),
351 'ml_maturity_date': fields.function(_get_ml_maturity_date, type='date', string='Due Date'),
352 'ml_inv_ref': fields.function(_get_ml_inv_ref, type='many2one', relation='account.invoice', string='Invoice Ref.'),
353 'info_owner': fields.function(info_owner, string="Owner Account", type="text", help='Address of the Main Partner'),
354 'info_partner': fields.function(info_partner, string="Destination Account", type="text", help='Address of the Ordering Customer.'),
355 'date': fields.date('Payment Date', help="If no payment date is specified, the bank will treat this payment line directly"),
356 'create_date': fields.datetime('Created', readonly=True),
357 'state': fields.selection([('normal','Free'), ('structured','Structured')], 'Communication Type', required=True),
358 'bank_statement_line_id': fields.many2one('account.bank.statement.line', 'Bank statement line'),
359 'company_id': fields.related('order_id', 'company_id', type='many2one', relation='res.company', string='Company', store=True, readonly=True),
362 'name': lambda obj, cursor, user, context: obj.pool.get('ir.sequence'
363 ).get(cursor, user, 'payment.line'),
365 'currency': _get_currency,
366 'company_currency': _get_currency,
370 ('name_uniq', 'UNIQUE(name)', 'The payment line name must be unique!'),
373 def onchange_move_line(self, cr, uid, ids, move_line_id, payment_type, date_prefered, date_scheduled, currency=False, company_currency=False, context=None):
375 move_line_obj = self.pool.get('account.move.line')
377 data['amount_currency'] = data['communication'] = data['partner_id'] = data['bank_id'] = data['amount'] = False
380 line = move_line_obj.browse(cr, uid, move_line_id, context=context)
381 data['amount_currency'] = line.amount_to_pay
383 res = self.onchange_amount(cr, uid, ids, data['amount_currency'], currency,
384 company_currency, context)
386 data['amount'] = res['value']['amount']
387 data['partner_id'] = line.partner_id.id
388 temp = line.currency_id and line.currency_id.id or False
391 data['currency'] = line.invoice.currency_id.id
393 data['currency'] = temp
395 # calling onchange of partner and updating data dictionary
396 temp_dict = self.onchange_partner(cr, uid, ids, line.partner_id.id, payment_type)
397 data.update(temp_dict['value'])
399 data['communication'] = line.ref
401 if date_prefered == 'now':
402 #no payment date => immediate payment
404 elif date_prefered == 'due':
405 data['date'] = line.date_maturity
406 elif date_prefered == 'fixed':
407 data['date'] = date_scheduled
408 return {'value': data}
410 def onchange_amount(self, cr, uid, ids, amount, currency, cmpny_currency, context=None):
411 if (not amount) or (not cmpny_currency):
412 return {'value': {'amount': False}}
414 currency_obj = self.pool.get('res.currency')
415 company_amount = currency_obj.compute(cr, uid, currency, cmpny_currency, amount)
416 res['amount'] = company_amount
417 return {'value': res}
419 def onchange_partner(self, cr, uid, ids, partner_id, payment_type, context=None):
421 partner_zip_obj = self.pool.get('res.partner.zip')
422 partner_obj = self.pool.get('res.partner')
423 payment_mode_obj = self.pool.get('payment.mode')
424 data['info_partner'] = data['bank_id'] = False
427 part_obj = partner_obj.browse(cr, uid, partner_id, context=context)
428 partner = part_obj.name or ''
431 for ads in part_obj.address:
432 if ads.type == 'default':
433 st = ads.street and ads.street or ''
434 st1 = ads.street2 and ads.street2 or ''
437 zip_city = ads.zip_id and partner_zip_obj.name_get(cr, uid, [ads.zip_id.id])[0][1] or ''
439 zip = ads.zip and ads.zip or ''
440 city = ads.city and ads.city or ''
441 zip_city = zip + ' ' + city
443 cntry = ads.country_id and ads.country_id.name or ''
444 info = partner + "\n" + st + " " + st1 + "\n" + zip_city + "\n" +cntry
446 data['info_partner'] = info
448 if part_obj.bank_ids and payment_type:
449 bank_type = payment_mode_obj.suitable_bank_types(cr, uid, payment_type, context=context)
450 for bank in part_obj.bank_ids:
451 if bank.state in bank_type:
452 data['bank_id'] = bank.id
454 return {'value': data}
456 def fields_get(self, cr, uid, fields=None, context=None):
457 res = super(payment_line, self).fields_get(cr, uid, fields, context)
458 if 'communication2' in res:
459 res['communication2'].setdefault('states', {})
460 res['communication2']['states']['structured'] = [('readonly', True)]
461 res['communication2']['states']['normal'] = [('readonly', False)]
466 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: