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),
39 'company_id': lambda self,cr,uid,c: self.pool.get('res.users').browse(cr, uid, uid, c).company_id.id
42 def suitable_bank_types(self, cr, uid, payment_code=None, context={}):
43 """Return the codes of the bank type that are suitable
44 for the given payment type code"""
47 cr.execute(""" SELECT pb.state
48 FROM res_partner_bank pb
49 JOIN payment_mode pm ON (pm.bank_id = pb.id)
50 WHERE pm.id = %s """, [payment_code])
51 return [x[0] for x in cr.fetchall()]
55 class payment_order(osv.osv):
56 _name = 'payment.order'
57 _description = 'Payment Order'
58 _rec_name = 'reference'
60 def get_wizard(self, type):
61 logger = netsvc.Logger()
62 logger.notifyChannel("warning", netsvc.LOG_WARNING,
63 "No wizard found for the payment type '%s'." % type)
66 def _total(self, cursor, user, ids, name, args, context=None):
70 for order in self.browse(cursor, user, ids, context=context):
72 res[order.id] = reduce(lambda x, y: x + y.amount, order.line_ids, 0.0)
78 '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.'),
79 'reference': fields.char('Reference', size=128, required=1, states={'done': [('readonly', True)]}),
80 'mode': fields.many2one('payment.mode', 'Payment mode', select=True, required=1, states={'done': [('readonly', True)]}, help='Select the Payment Mode to be applied.'),
81 'state': fields.selection([
83 ('open', 'Confirmed'),
84 ('cancel', 'Cancelled'),
85 ('done', 'Done')], 'State', select=True,
86 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\'.'),
87 'line_ids': fields.one2many('payment.line', 'order_id', 'Payment lines', states={'done': [('readonly', True)]}),
88 'total': fields.function(_total, string="Total", method=True, type='float'),
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,
102 'date_prefered': '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 ir_seq_obj = self.pool.get('ir.sequence')
117 for order in self.read(cr, uid, ids, ['reference']):
118 if not order['reference']:
119 reference = ir_seq_obj.get(cr, uid, 'payment.order')
120 self.write(cr, uid, order['id'], {'reference':reference})
123 def set_done(self, cr, uid, ids, *args):
124 wf_service = netsvc.LocalService("workflow")
125 self.write(cr, uid, ids, {'date_done': time.strftime('%Y-%m-%d')})
126 wf_service.trg_validate(uid, 'payment.order', ids[0], 'done', cr)
129 def copy(self, cr, uid, id, default={}, context=None):
133 'reference': self.pool.get('ir.sequence').get(cr, uid, 'payment.order')
135 return super(payment_order, self).copy(cr, uid, id, default, context=context)
137 def write(self, cr, uid, ids, vals, context=None):
140 payment_line_obj = self.pool.get('payment.line')
141 payment_line_ids = []
143 if (vals.get('date_prefered', False) == 'fixed' and not vals.get('date_scheduled', False)) or vals.get('date_scheduled', False):
144 for order in self.browse(cr, uid, ids, context=context):
145 for line in order.line_ids:
146 payment_line_ids.append(line.id)
147 payment_line_obj.write(cr, uid, payment_line_ids, {'date': vals.get('date_scheduled', False)}, context=context)
148 elif vals.get('date_prefered', False) == 'due':
149 vals.update({'date_scheduled': False})
150 for order in self.browse(cr, uid, ids, context=context):
151 for line in order.line_ids:
152 payment_line_obj.write(cr, uid, [line.id], {'date': line.ml_maturity_date}, context=context)
153 elif vals.get('date_prefered', False) == 'now':
154 vals.update({'date_scheduled': False})
155 for order in self.browse(cr, uid, ids, context=context):
156 for line in order.line_ids:
157 payment_line_ids.append(line.id)
158 payment_line_obj.write(cr, uid, payment_line_ids, {'date': False}, context=context)
159 return super(payment_order, self).write(cr, uid, ids, vals, context=context)
163 class payment_line(osv.osv):
164 _name = 'payment.line'
165 _description = 'Payment Line'
167 def translate(self, orig):
169 "due_date": "date_maturity",
170 "reference": "ref"}.get(orig, orig)
172 def info_owner(self, cr, uid, ids, name=None, args=None, context=None):
173 if not ids: return {}
174 partner_zip_obj = self.pool.get('res.partner.zip')
178 for line in self.browse(cr, uid, ids, context=context):
179 owner = line.order_id.mode.bank_id.partner_id
180 result[line.id] = False
182 for ads in owner.address:
183 if ads.type == 'default':
184 st = ads.street and ads.street or ''
185 st1 = ads.street2 and ads.street2 or ''
187 zip_city = ads.zip_id and partner_zip_obj.name_get(cr, uid, [ads.zip_id.id])[0][1] or ''
189 zip = ads.zip and ads.zip or ''
190 city = ads.city and ads.city or ''
191 zip_city = zip + ' ' + city
192 cntry = ads.country_id and ads.country_id.name or ''
193 info = owner.name + "\n" + st + " " + st1 + "\n" + zip_city + "\n" +cntry
194 result[line.id] = info
198 def info_partner(self, cr, uid, ids, name=None, args=None, context=None):
199 if not ids: return {}
200 partner_zip_obj = self.pool.get('res.partner.zip')
204 for line in self.browse(cr, uid, ids, context=context):
205 result[line.id] = False
206 if not line.partner_id:
208 partner = line.partner_id.name or ''
209 if line.partner_id.address:
210 for ads in line.partner_id.address:
211 if ads.type == 'default':
212 st = ads.street and ads.street or ''
213 st1 = ads.street2 and ads.street2 or ''
215 zip_city = ads.zip_id and partner_zip_obj.name_get(cr, uid, [ads.zip_id.id])[0][1] or ''
217 zip = ads.zip and ads.zip or ''
218 city = ads.city and ads.city or ''
219 zip_city = zip + ' ' + city
220 cntry = ads.country_id and ads.country_id.name or ''
221 info = partner + "\n" + st + " " + st1 + "\n" + zip_city + "\n" +cntry
222 result[line.id] = info
226 def select_by_name(self, cr, uid, ids, name, args, context=None):
227 if not ids: return {}
228 partner_obj = self.pool.get('res.partner')
230 cr.execute("""SELECT pl.id, ml.%s
231 FROM account_move_line ml
232 INNER JOIN payment_line pl
233 ON (ml.id = pl.move_line_id)
234 WHERE pl.id IN %%s"""% self.translate(name),
236 res = dict(cr.fetchall())
238 if name == 'partner_id':
240 for p_id, p_name in partner_obj.name_get(cr, uid,
241 filter(lambda x:x and x != 0,res.values()), context=context):
242 partner_name[p_id] = p_name
245 if id in res and partner_name:
246 res[id] = (res[id],partner_name[res[id]])
248 res[id] = (False,False)
251 res.setdefault(id, (False, ""))
254 def _amount(self, cursor, user, ids, name, args, context=None):
257 currency_obj = self.pool.get('res.currency')
262 for line in self.browse(cursor, user, ids, context=context):
264 ctx['date'] = line.order_id.date_done or time.strftime('%Y-%m-%d')
265 res[line.id] = currency_obj.compute(cursor, user, line.currency.id,
266 line.company_currency.id,
267 line.amount_currency, context=ctx)
270 def _get_currency(self, cr, uid, context):
271 user_obj = self.pool.get('res.users')
272 currency_obj = self.pool.get('res.currency')
273 user = user_obj.browse(cr, uid, uid)
276 return user.company_id.currency_id.id
278 return currency_obj.search(cr, uid, [('rate', '=', 1.0)])[0]
280 def _get_date(self, cr, uid, context=None):
283 payment_order_obj = self.pool.get('payment.order')
286 if context.get('order_id') and context['order_id']:
287 order = payment_order_obj.browse(cr, uid, context['order_id'], context)
288 if order.date_prefered == 'fixed':
289 date = order.date_scheduled
291 date = time.strftime('%Y-%m-%d')
294 def _get_ml_inv_ref(self, cr, uid, ids, *a):
296 for id in self.browse(cr, uid, ids):
299 if id.move_line_id.invoice:
300 res[id.id] = id.move_line_id.invoice.id
303 def _get_ml_maturity_date(self, cr, uid, ids, *a):
305 for id in self.browse(cr, uid, ids):
307 res[id.id] = id.move_line_id.date_maturity
312 def _get_ml_created_date(self, cr, uid, ids, *a):
314 for id in self.browse(cr, uid, ids):
316 res[id.id] = id.move_line_id.date_created
322 'name': fields.char('Your Reference', size=64, required=True),
323 '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 ?'"),
324 'communication2': fields.char('Communication 2', size=64, help='The successor message of Communication.'),
325 '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.'),
326 'amount_currency': fields.float('Amount in Partner Currency', digits=(16, 2),
327 required=True, help='Payment amount in the partner currency'),
328 'currency': fields.many2one('res.currency','Partner Currency', required=True),
329 'company_currency': fields.many2one('res.currency', 'Company Currency', readonly=True),
330 'bank_id': fields.many2one('res.partner.bank', 'Destination Bank account'),
331 'order_id': fields.many2one('payment.order', 'Order', required=True,
332 ondelete='cascade', select=True),
333 'partner_id': fields.many2one('res.partner', string="Partner", required=True, help='The Ordering Customer'),
334 'amount': fields.function(_amount, string='Amount in Company Currency',
335 method=True, type='float',
336 help='Payment amount in the company currency'),
337 'ml_date_created': fields.function(_get_ml_created_date, string="Effective Date",
338 method=True, type='date', help="Invoice Effective Date"),
339 'ml_maturity_date': fields.function(_get_ml_maturity_date, method=True, type='date', string='Due Date'),
340 'ml_inv_ref': fields.function(_get_ml_inv_ref, method=True, type='many2one', relation='account.invoice', string='Invoice Ref.'),
341 'info_owner': fields.function(info_owner, string="Owner Account", method=True, type="text", help='Address of the Main Partner'),
342 'info_partner': fields.function(info_partner, string="Destination Account", method=True, type="text", help='Address of the Ordering Customer.'),
343 'date': fields.date('Payment Date', help="If no payment date is specified, the bank will treat this payment line directly"),
344 'create_date': fields.datetime('Created', readonly=True),
345 'state': fields.selection([('normal','Free'), ('structured','Structured')], 'Communication Type', required=True),
346 'bank_statement_line_id': fields.many2one('account.bank.statement.line', 'Bank statement line')
349 'name': lambda obj, cursor, user, context: obj.pool.get('ir.sequence'
350 ).get(cursor, user, 'payment.line'),
352 'currency': _get_currency,
353 'company_currency': _get_currency,
357 ('name_uniq', 'UNIQUE(name)', 'The payment line name must be unique!'),
360 def onchange_move_line(self, cr, uid, ids, move_line_id, payment_type, date_prefered, date_scheduled, currency=False, company_currency=False, context=None):
362 move_line_obj = self.pool.get('account.move.line')
364 data['amount_currency'] = data['communication'] = data['partner_id'] = data['reference'] = data['date_created'] = data['bank_id'] = data['amount'] = False
367 line = move_line_obj.browse(cr, uid, move_line_id)
368 data['amount_currency'] = line.amount_to_pay
370 res = self.onchange_amount(cr, uid, ids, data['amount_currency'], currency,
371 company_currency, context)
373 data['amount'] = res['value']['amount']
374 data['partner_id'] = line.partner_id.id
375 temp = line.currency_id and line.currency_id.id or False
378 data['currency'] = line.invoice.currency_id.id
380 data['currency'] = temp
382 # calling onchange of partner and updating data dictionary
383 temp_dict = self.onchange_partner(cr, uid, ids, line.partner_id.id, payment_type)
384 data.update(temp_dict['value'])
386 data['reference'] = line.ref
387 data['date_created'] = line.date_created
388 data['communication'] = line.ref
390 if date_prefered == 'now':
391 #no payment date => immediate payment
393 elif date_prefered == 'due':
394 data['date'] = line.date_maturity
395 elif date_prefered == 'fixed':
396 data['date'] = date_scheduled
397 return {'value': data}
399 def onchange_amount(self, cr, uid, ids, amount, currency, cmpny_currency, context=None):
400 if (not amount) or (not cmpny_currency):
401 return {'value': {'amount': False}}
403 currency_obj = self.pool.get('res.currency')
404 company_amount = currency_obj.compute(cr, uid, currency, cmpny_currency, amount)
405 res['amount'] = company_amount
406 return {'value': res}
408 def onchange_partner(self, cr, uid, ids, partner_id, payment_type, context=None):
410 partner_zip_obj = self.pool.get('res.partner.zip')
411 partner_obj = self.pool.get('res.partner')
412 payment_mode_obj = self.pool.get('payment.mode')
413 data['info_partner'] = data['bank_id'] = False
416 part_obj = partner_obj.browse(cr, uid, partner_id)
417 partner = part_obj.name or ''
420 for ads in part_obj.address:
421 if ads.type == 'default':
422 st = ads.street and ads.street or ''
423 st1 = ads.street2 and ads.street2 or ''
426 zip_city = ads.zip_id and partner_zip_obj.name_get(cr, uid, [ads.zip_id.id])[0][1] or ''
428 zip = ads.zip and ads.zip or ''
429 city = ads.city and ads.city or ''
430 zip_city = zip + ' ' + city
432 cntry = ads.country_id and ads.country_id.name or ''
433 info = partner + "\n" + st + " " + st1 + "\n" + zip_city + "\n" +cntry
435 data['info_partner'] = info
437 if part_obj.bank_ids and payment_type:
438 bank_type = payment_mode_obj.suitable_bank_types(cr, uid, payment_type, context=context)
439 for bank in part_obj.bank_ids:
440 if bank.state in bank_type:
441 data['bank_id'] = bank.id
443 return {'value': data}
445 def fields_get(self, cr, uid, fields=None, context=None):
446 res = super(payment_line, self).fields_get(cr, uid, fields, context)
447 if 'communication2' in res:
448 res['communication2'].setdefault('states', {})
449 res['communication2']['states']['structured'] = [('readonly', True)]
450 res['communication2']['states']['normal'] = [('readonly', False)]
455 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: