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 ('cancel', 'Cancelled'),
96 ('open', 'Confirmed'),
97 ('done', 'Done')], 'Status', 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):
187 for line in self.browse(cr, uid, ids, context=context):
188 owner = line.order_id.mode.bank_id.partner_id
189 result[line.id] = self._get_info_partner(cr, uid, owner, context=context)
192 def _get_info_partner(self,cr, uid, partner_record, context=None):
193 if not partner_record:
195 st = partner_record.street or ''
196 st1 = partner_record.street2 or ''
197 zip = partner_record.zip or ''
198 city = partner_record.city or ''
199 zip_city = zip + ' ' + city
200 cntry = partner_record.country_id and partner_record.country_id.name or ''
201 return partner_record.name + "\n" + st + " " + st1 + "\n" + zip_city + "\n" +cntry
203 def info_partner(self, cr, uid, ids, name=None, args=None, context=None):
205 for line in self.browse(cr, uid, ids, context=context):
206 result[line.id] = False
207 if not line.partner_id:
209 result[line.id] = self._get_info_partner(cr, uid, line.partner_id, context=context)
213 def select_by_name(self, cr, uid, ids, name, args, context=None):
214 if not ids: return {}
215 partner_obj = self.pool.get('res.partner')
217 cr.execute("""SELECT pl.id, ml.%s
218 FROM account_move_line ml
219 INNER JOIN payment_line pl
220 ON (ml.id = pl.move_line_id)
221 WHERE pl.id IN %%s"""% self.translate(name),
223 res = dict(cr.fetchall())
225 if name == 'partner_id':
227 for p_id, p_name in partner_obj.name_get(cr, uid,
228 filter(lambda x:x and x != 0,res.values()), context=context):
229 partner_name[p_id] = p_name
232 if id in res and partner_name:
233 res[id] = (res[id],partner_name[res[id]])
235 res[id] = (False,False)
238 res.setdefault(id, (False, ""))
241 def _amount(self, cursor, user, ids, name, args, context=None):
244 currency_obj = self.pool.get('res.currency')
249 for line in self.browse(cursor, user, ids, context=context):
251 ctx['date'] = line.order_id.date_done or time.strftime('%Y-%m-%d')
252 res[line.id] = currency_obj.compute(cursor, user, line.currency.id,
253 line.company_currency.id,
254 line.amount_currency, context=ctx)
257 def _get_currency(self, cr, uid, context=None):
258 user_obj = self.pool.get('res.users')
259 currency_obj = self.pool.get('res.currency')
260 user = user_obj.browse(cr, uid, uid, context=context)
263 return user.company_id.currency_id.id
265 return currency_obj.search(cr, uid, [('rate', '=', 1.0)])[0]
267 def _get_date(self, cr, uid, context=None):
270 payment_order_obj = self.pool.get('payment.order')
273 if context.get('order_id') and context['order_id']:
274 order = payment_order_obj.browse(cr, uid, context['order_id'], context=context)
275 if order.date_prefered == 'fixed':
276 date = order.date_scheduled
278 date = time.strftime('%Y-%m-%d')
281 def _get_ml_inv_ref(self, cr, uid, ids, *a):
283 for id in self.browse(cr, uid, ids):
286 if id.move_line_id.invoice:
287 res[id.id] = id.move_line_id.invoice.id
290 def _get_ml_maturity_date(self, cr, uid, ids, *a):
292 for id in self.browse(cr, uid, ids):
294 res[id.id] = id.move_line_id.date_maturity
299 def _get_ml_created_date(self, cr, uid, ids, *a):
301 for id in self.browse(cr, uid, ids):
303 res[id.id] = id.move_line_id.date_created
309 'name': fields.char('Your Reference', size=64, required=True),
310 '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 ?'"),
311 'communication2': fields.char('Communication 2', size=64, help='The successor message of Communication.'),
312 '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.'),
313 'amount_currency': fields.float('Amount in Partner Currency', digits=(16, 2),
314 required=True, help='Payment amount in the partner currency'),
315 'currency': fields.many2one('res.currency','Partner Currency', required=True),
316 'company_currency': fields.many2one('res.currency', 'Company Currency', readonly=True),
317 'bank_id': fields.many2one('res.partner.bank', 'Destination Bank Account'),
318 'order_id': fields.many2one('payment.order', 'Order', required=True,
319 ondelete='cascade', select=True),
320 'partner_id': fields.many2one('res.partner', string="Partner", required=True, help='The Ordering Customer'),
321 'amount': fields.function(_amount, string='Amount in Company Currency',
323 help='Payment amount in the company currency'),
324 'ml_date_created': fields.function(_get_ml_created_date, string="Effective Date",
325 type='date', help="Invoice Effective Date"),
326 'ml_maturity_date': fields.function(_get_ml_maturity_date, type='date', string='Due Date'),
327 'ml_inv_ref': fields.function(_get_ml_inv_ref, type='many2one', relation='account.invoice', string='Invoice Ref.'),
328 'info_owner': fields.function(info_owner, string="Owner Account", type="text", help='Address of the Main Partner'),
329 'info_partner': fields.function(info_partner, string="Destination Account", type="text", help='Address of the Ordering Customer.'),
330 'date': fields.date('Payment Date', help="If no payment date is specified, the bank will treat this payment line directly"),
331 'create_date': fields.datetime('Created', readonly=True),
332 'state': fields.selection([('normal','Free'), ('structured','Structured')], 'Communication Type', required=True),
333 'bank_statement_line_id': fields.many2one('account.bank.statement.line', 'Bank statement line'),
334 'company_id': fields.related('order_id', 'company_id', type='many2one', relation='res.company', string='Company', store=True, readonly=True),
337 'name': lambda obj, cursor, user, context: obj.pool.get('ir.sequence'
338 ).get(cursor, user, 'payment.line'),
340 'currency': _get_currency,
341 'company_currency': _get_currency,
345 ('name_uniq', 'UNIQUE(name)', 'The payment line name must be unique!'),
348 def onchange_move_line(self, cr, uid, ids, move_line_id, payment_type, date_prefered, date_scheduled, currency=False, company_currency=False, context=None):
350 move_line_obj = self.pool.get('account.move.line')
352 data['amount_currency'] = data['communication'] = data['partner_id'] = data['bank_id'] = data['amount'] = False
355 line = move_line_obj.browse(cr, uid, move_line_id, context=context)
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['communication'] = line.ref
376 if date_prefered == 'now':
377 #no payment date => immediate payment
379 elif date_prefered == 'due':
380 data['date'] = line.date_maturity
381 elif date_prefered == 'fixed':
382 data['date'] = date_scheduled
383 return {'value': data}
385 def onchange_amount(self, cr, uid, ids, amount, currency, cmpny_currency, context=None):
386 if (not amount) or (not cmpny_currency):
387 return {'value': {'amount': False}}
389 currency_obj = self.pool.get('res.currency')
390 company_amount = currency_obj.compute(cr, uid, currency, cmpny_currency, amount)
391 res['amount'] = company_amount
392 return {'value': res}
394 def onchange_partner(self, cr, uid, ids, partner_id, payment_type, context=None):
396 partner_obj = self.pool.get('res.partner')
397 payment_mode_obj = self.pool.get('payment.mode')
398 data['info_partner'] = data['bank_id'] = False
401 part_obj = partner_obj.browse(cr, uid, partner_id, context=context)
402 partner = part_obj.name or ''
403 data['info_partner'] = self._get_info_partner(cr, uid, part_obj, context=context)
405 if part_obj.bank_ids and payment_type:
406 bank_type = payment_mode_obj.suitable_bank_types(cr, uid, payment_type, context=context)
407 for bank in part_obj.bank_ids:
408 if bank.state in bank_type:
409 data['bank_id'] = bank.id
411 return {'value': data}
413 def fields_get(self, cr, uid, fields=None, context=None):
414 res = super(payment_line, self).fields_get(cr, uid, fields, context)
415 if 'communication2' in res:
416 res['communication2'].setdefault('states', {})
417 res['communication2']['states']['structured'] = [('readonly', True)]
418 res['communication2']['states']['normal'] = [('readonly', False)]
423 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: