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
26 from tools.translate import _
28 class payment_mode(osv.osv):
30 _description= 'Payment Mode'
32 'name': fields.char('Name', size=64, required=True, help='Mode of Payment'),
33 'bank_id': fields.many2one('res.partner.bank', "Bank account",
34 required=True,help='Bank Account for the Payment Mode'),
35 'journal': fields.many2one('account.journal', 'Journal', required=True,
36 domain=[('type', 'in', ('bank','cash'))], help='Bank or Cash Journal for the Payment Mode'),
37 'company_id': fields.many2one('res.company', 'Company', required=True),
40 'company_id': lambda self,cr,uid,c: self.pool.get('res.users').browse(cr, uid, uid, c).company_id.id
43 def suitable_bank_types(self, cr, uid, payment_code=None, context=None):
44 """Return the codes of the bank type that are suitable
45 for the given payment type code"""
48 cr.execute(""" SELECT pb.state
49 FROM res_partner_bank pb
50 JOIN payment_mode pm ON (pm.bank_id = pb.id)
51 WHERE pm.id = %s """, [payment_code])
52 return [x[0] for x in cr.fetchall()]
56 class payment_order(osv.osv):
57 _name = 'payment.order'
58 _description = 'Payment Order'
59 _rec_name = 'reference'
61 def get_wizard(self, type):
62 logger = netsvc.Logger()
63 logger.notifyChannel("warning", netsvc.LOG_WARNING,
64 "No wizard found for the payment type '%s'." % type)
67 def _total(self, cursor, user, ids, name, args, context=None):
71 for order in self.browse(cursor, user, ids, context=context):
73 res[order.id] = reduce(lambda x, y: x + y.amount, order.line_ids, 0.0)
79 '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.'),
80 'reference': fields.char('Reference', size=128, required=1, states={'done': [('readonly', True)]}),
81 'mode': fields.many2one('payment.mode', 'Payment mode', select=True, required=1, states={'done': [('readonly', True)]}, help='Select the Payment Mode to be applied.'),
82 'state': fields.selection([
84 ('open', 'Confirmed'),
85 ('cancel', 'Cancelled'),
86 ('done', 'Done')], 'State', select=True,
87 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\'.'),
88 'line_ids': fields.one2many('payment.line', 'order_id', 'Payment lines', states={'done': [('readonly', True)]}),
89 'total': fields.function(_total, string="Total", method=True, type='float'),
90 'user_id': fields.many2one('res.users', 'User', required=True, states={'done': [('readonly', True)]}),
91 'date_prefered': fields.selection([
94 ('fixed', 'Fixed date')
95 ], "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."),
96 'date_created': fields.date('Creation date', readonly=True),
97 'date_done': fields.date('Execution date', readonly=True),
101 'user_id': lambda self,cr,uid,context: uid,
103 'date_prefered': 'due',
104 'date_created': lambda *a: time.strftime('%Y-%m-%d'),
105 'reference': lambda self,cr,uid,context: self.pool.get('ir.sequence').get(cr, uid, 'payment.order'),
108 def set_to_draft(self, cr, uid, ids, *args):
109 self.write(cr, uid, ids, {'state': 'draft'})
110 wf_service = netsvc.LocalService("workflow")
112 wf_service.trg_delete(uid, 'payment.order', id, cr)
113 wf_service.trg_create(uid, 'payment.order', id, cr)
116 def action_open(self, cr, uid, ids, *args):
117 ir_seq_obj = self.pool.get('ir.sequence')
119 for order in self.read(cr, uid, ids, ['reference']):
120 if not order['reference']:
121 reference = ir_seq_obj.get(cr, uid, 'payment.order')
122 self.write(cr, uid, order['id'], {'reference':reference})
125 def action_cancel(self, cr, uid, ids, *args):
126 for order in self.browse(cr, uid, ids):
127 for line in order.line_ids:
128 if line.bank_statement_line_id:
129 raise osv.except_osv(_('Warning !'),
130 _("Payment order line '%s' is already imported in Bank statement '%s' (id: %s).") % (
131 line.name, line.bank_statement_line_id.statement_id.name,
132 line.bank_statement_line_id.statement_id.id))
133 self.write(cr, uid, ids, {'state': 'cancel'})
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
239 def select_by_name(self, cr, uid, ids, name, args, context=None):
240 if not ids: return {}
241 partner_obj = self.pool.get('res.partner')
243 cr.execute("""SELECT pl.id, ml.%s
244 FROM account_move_line ml
245 INNER JOIN payment_line pl
246 ON (ml.id = pl.move_line_id)
247 WHERE pl.id IN %%s"""% self.translate(name),
249 res = dict(cr.fetchall())
251 if name == 'partner_id':
253 for p_id, p_name in partner_obj.name_get(cr, uid,
254 filter(lambda x:x and x != 0,res.values()), context=context):
255 partner_name[p_id] = p_name
258 if id in res and partner_name:
259 res[id] = (res[id],partner_name[res[id]])
261 res[id] = (False,False)
264 res.setdefault(id, (False, ""))
267 def _amount(self, cursor, user, ids, name, args, context=None):
270 currency_obj = self.pool.get('res.currency')
275 for line in self.browse(cursor, user, ids, context=context):
277 ctx['date'] = line.order_id.date_done or time.strftime('%Y-%m-%d')
278 res[line.id] = currency_obj.compute(cursor, user, line.currency.id,
279 line.company_currency.id,
280 line.amount_currency, context=ctx)
283 def _get_currency(self, cr, uid, context=None):
284 user_obj = self.pool.get('res.users')
285 currency_obj = self.pool.get('res.currency')
286 user = user_obj.browse(cr, uid, uid, context=context)
289 return user.company_id.currency_id.id
291 return currency_obj.search(cr, uid, [('rate', '=', 1.0)])[0]
293 def _get_date(self, cr, uid, context=None):
296 payment_order_obj = self.pool.get('payment.order')
299 if context.get('order_id') and context['order_id']:
300 order = payment_order_obj.browse(cr, uid, context['order_id'], context=context)
301 if order.date_prefered == 'fixed':
302 date = order.date_scheduled
304 date = time.strftime('%Y-%m-%d')
307 def _get_ml_inv_ref(self, cr, uid, ids, *a):
309 for id in self.browse(cr, uid, ids):
312 if id.move_line_id.invoice:
313 res[id.id] = id.move_line_id.invoice.id
316 def _get_ml_maturity_date(self, cr, uid, ids, *a):
318 for id in self.browse(cr, uid, ids):
320 res[id.id] = id.move_line_id.date_maturity
325 def _get_ml_created_date(self, cr, uid, ids, *a):
327 for id in self.browse(cr, uid, ids):
329 res[id.id] = id.move_line_id.date_created
335 'name': fields.char('Your Reference', size=64, required=True),
336 '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 ?'"),
337 'communication2': fields.char('Communication 2', size=64, help='The successor message of Communication.'),
338 '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.'),
339 'amount_currency': fields.float('Amount in Partner Currency', digits=(16, 2),
340 required=True, help='Payment amount in the partner currency'),
341 'currency': fields.many2one('res.currency','Partner Currency', required=True),
342 'company_currency': fields.many2one('res.currency', 'Company Currency', readonly=True),
343 'bank_id': fields.many2one('res.partner.bank', 'Destination Bank account'),
344 'order_id': fields.many2one('payment.order', 'Order', required=True,
345 ondelete='cascade', select=True),
346 'partner_id': fields.many2one('res.partner', string="Partner", required=True, help='The Ordering Customer'),
347 'amount': fields.function(_amount, string='Amount in Company Currency',
348 method=True, type='float',
349 help='Payment amount in the company currency'),
350 'ml_date_created': fields.function(_get_ml_created_date, string="Effective Date",
351 method=True, type='date', help="Invoice Effective Date"),
352 'ml_maturity_date': fields.function(_get_ml_maturity_date, method=True, type='date', string='Due Date'),
353 'ml_inv_ref': fields.function(_get_ml_inv_ref, method=True, type='many2one', relation='account.invoice', string='Invoice Ref.'),
354 'info_owner': fields.function(info_owner, string="Owner Account", method=True, type="text", help='Address of the Main Partner'),
355 'info_partner': fields.function(info_partner, string="Destination Account", method=True, type="text", help='Address of the Ordering Customer.'),
356 'date': fields.date('Payment Date', help="If no payment date is specified, the bank will treat this payment line directly"),
357 'create_date': fields.datetime('Created', readonly=True),
358 'state': fields.selection([('normal','Free'), ('structured','Structured')], 'Communication Type', required=True),
359 'bank_statement_line_id': fields.many2one('account.bank.statement.line', 'Bank statement line')
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['reference'] = data['date_created'] = 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['reference'] = line.ref
400 data['date_created'] = line.date_created
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: