[REF] purchase: search view of purchase order and form view of merge order wizard
[odoo/odoo.git] / addons / account_payment / payment.py
1 # -*- coding: utf-8 -*-
2 ##############################################################################
3 #
4 #    OpenERP, Open Source Management Solution
5 #    Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
6 #
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.
11 #
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.
16 #
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/>.
19 #
20 ##############################################################################
21
22 from osv import fields
23 from osv import osv
24 import time
25 import netsvc
26 import pooler
27
28
29 class payment_type(osv.osv):
30     _name= 'payment.type'
31     _description= 'Payment type'
32     _columns= {
33         'name': fields.char('Name', size=64, required=True,help='Payment Type'),
34         'code': fields.char('Code', size=64, required=True,help='Specifies the Code for Payment Type'),
35         'suitable_bank_types': fields.many2many('res.partner.bank.type',
36             'bank_type_payment_type_rel',
37             'pay_type_id','bank_type_id',
38             'Suitable bank types')
39     }
40
41 payment_type()
42
43
44 class payment_mode(osv.osv):
45     _name= 'payment.mode'
46     _description= 'Payment mode'
47     _columns= {
48         'name': fields.char('Name', size=64, required=True,help='Mode of Payment'),
49         'bank_id': fields.many2one('res.partner.bank', "Bank account",
50             required=True,help='Bank Account for the Payment Mode'),
51         'journal': fields.many2one('account.journal', 'Journal', required=True,
52             domain=[('type', '=', 'cash')],help='Cash Journal for the Payment Mode'),
53         'type': fields.many2one('payment.type','Payment type',required=True,help='Select the Payment Type for the Payment Mode.'),
54     }
55
56     def suitable_bank_types(self,cr,uid,payment_code=None,context={}):
57         """Return the codes of the bank type that are suitable
58         for the given payment type code"""
59         if not payment_code:
60             return []
61         cr.execute(""" select t.code
62             from res_partner_bank_type t
63             join bank_type_payment_type_rel r on (r.bank_type_id = t.id)
64             join payment_type pt on (r.pay_type_id = pt.id)
65             join payment_mode pm on (pm.type = pt.id)
66             where pm.id = %s """, [payment_code])
67         return [x[0] for x in cr.fetchall()]
68
69
70 payment_mode()
71
72
73 class payment_order(osv.osv):
74     _name = 'payment.order'
75     _description = 'Payment Order'
76     _rec_name = 'reference'
77
78     def get_wizard(self,type):
79         logger = netsvc.Logger()
80         logger.notifyChannel("warning", netsvc.LOG_WARNING,
81                 "No wizard found for the payment type '%s'." % type)
82         return None
83
84     def _total(self, cursor, user, ids, name, args, context=None):
85         if not ids:
86             return {}
87         res = {}
88         for order in self.browse(cursor, user, ids, context=context):
89             if order.line_ids:
90                 res[order.id] = reduce(lambda x, y: x + y.amount, order.line_ids, 0.0)
91             else:
92                 res[order.id] = 0.0
93         return res
94
95     _columns = {
96         'date_planned': fields.date('Scheduled date if fixed', states={'done':[('readonly',True)]}, help='Select a date if you have chosen Preferred Date to be fixed.'),
97         'reference': fields.char('Reference', size=128, required=1, states={'done':[('readonly',True)]}),
98         'mode': fields.many2one('payment.mode','Payment mode', select=True, required=1, states={'done':[('readonly',True)]}, help='Select the Payment Mode to be applied.'),
99         'state': fields.selection([
100             ('draft', 'Draft'),
101             ('open','Confirmed'),
102             ('cancel','Cancelled'),
103             ('done','Done')], 'State', select=True,
104             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\'.'),
105         'line_ids': fields.one2many('payment.line','order_id','Payment lines',states={'done':[('readonly',True)]}),
106         'total': fields.function(_total, string="Total", method=True,
107             type='float'),
108         'user_id': fields.many2one('res.users','User', required=True, states={'done':[('readonly',True)]}),
109         'date_prefered': fields.selection([
110             ('now', 'Directly'),
111             ('due', 'Due date'),
112             ('fixed', 'Fixed date')
113             ], "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."),
114         'date_created': fields.date('Creation date', readonly=True),
115         'date_done': fields.date('Execution date', readonly=True),
116     }
117
118     _defaults = {
119         'user_id': lambda self,cr,uid,context: uid,
120         'state': lambda *a: 'draft',
121         'date_prefered': lambda *a: 'due',
122         'date_created': lambda *a: time.strftime('%Y-%m-%d'),
123         'reference': lambda self,cr,uid,context: self.pool.get('ir.sequence').get(cr, uid, 'payment.order'),
124     }
125
126     def set_to_draft(self, cr, uid, ids, *args):
127         self.write(cr, uid, ids, {'state':'draft'})
128         wf_service = netsvc.LocalService("workflow")
129         for id in ids:
130             wf_service.trg_create(uid, 'payment.order', id, cr)
131         return True
132
133     def action_open(self, cr, uid, ids, *args):
134         for order in self.read(cr,uid,ids,['reference']):
135             if not order['reference']:
136                 reference = self.pool.get('ir.sequence').get(cr, uid, 'payment.order')
137                 self.write(cr,uid,order['id'],{'reference':reference})
138         return True
139
140     def set_done(self, cr, uid, id, *args):
141         self.write(cr,uid,id,{'date_done': time.strftime('%Y-%m-%d'),
142             'state': 'done',})
143         wf_service = netsvc.LocalService("workflow")
144         wf_service.trg_validate(uid, 'payment.order', id, 'done', cr)
145         return True
146
147 payment_order()
148
149
150 class payment_line(osv.osv):
151     _name = 'payment.line'
152     _description = 'Payment Line'
153
154     #~ def partner_payable(self, cr, uid, ids, name, args, context={}):
155         #~ if not ids: return {}
156         #~ partners= self.read(cr, uid, ids, ['partner_id'], context)
157         #~ partners= dict(map(lambda x: (x['id'], x['partner_id'][0]), partners))
158         #~ debit = self.pool.get('res.partner')._debit_get(cr, uid,
159                 #~ partners.values(), name, args, context)
160         #~ for i in partners:
161             #~ partners[i] = debit[partners[i]]
162         #~ return partners
163
164     def translate(self, orig):
165         return {
166 #               "to_pay": "credit",
167                 "due_date": "date_maturity",
168                 "reference": "ref"}.get(orig, orig)
169
170     def info_owner(self, cr, uid, ids, name=None, args=None, context=None):
171         if not ids: return {}
172         result = {}
173         info=''
174         for line in self.browse(cr, uid, ids, context=context):
175             owner=line.order_id.mode.bank_id.partner_id
176             result[line.id]=False
177             if owner.address:
178                 for ads in owner.address:
179                     if ads.type=='default':
180                         st=ads.street and ads.street or ''
181                         st1=ads.street2 and ads.street2 or ''
182                         if 'zip_id' in ads:
183                             zip_city= ads.zip_id and self.pool.get('res.partner.zip').name_get(cr,uid,[ads.zip_id.id])[0][1] or ''
184                         else:
185                             zip=ads.zip and ads.zip or ''
186                             city= ads.city and ads.city or  ''
187                             zip_city= zip + ' ' + city
188                         cntry= ads.country_id and ads.country_id.name or ''
189                         info=owner.name + "\n" + st + " " + st1 + "\n" + zip_city + "\n" +cntry
190                         result[line.id]=info
191                         break
192         return result
193
194     def info_partner(self, cr, uid, ids, name=None, args=None, context=None):
195         if not ids: return {}
196         result = {}
197         info=''
198         for line in self.browse(cr, uid, ids, context=context):
199             result[line.id]=False
200             if not line.partner_id:
201                 break
202             partner = line.partner_id.name or ''
203             if line.partner_id.address:
204                 for ads in line.partner_id.address:
205                     if ads.type=='default':
206                         st=ads.street and ads.street or ''
207                         st1=ads.street2 and ads.street2 or ''
208                         if 'zip_id' in ads:
209                             zip_city= ads.zip_id and self.pool.get('res.partner.zip').name_get(cr,uid,[ads.zip_id.id])[0][1] or ''
210                         else:
211                             zip=ads.zip and ads.zip or ''
212                             city= ads.city and ads.city or  ''
213                             zip_city= zip + ' ' + city
214                         cntry= ads.country_id and ads.country_id.name or ''
215                         info=partner + "\n" + st + " " + st1 + "\n" + zip_city + "\n" +cntry
216                         result[line.id]=info
217                         break
218         return result
219
220     def select_by_name(self, cr, uid, ids, name, args, context=None):
221         if not ids: return {}
222
223         partner_obj = self.pool.get('res.partner')
224         cr.execute("""SELECT pl.id, ml.%s
225             from account_move_line ml
226                 inner join payment_line pl
227                 on (ml.id = pl.move_line_id)
228             where pl.id =ANY(%s)""",
229             (self.translate(name),ids,))
230         res = dict(cr.fetchall())
231
232         if name == 'partner_id':
233             partner_name = {}
234             for p_id, p_name in partner_obj.name_get(cr,uid,
235                 filter(lambda x:x and x != 0,res.values()),context=context):
236                 partner_name[p_id] = p_name
237
238             for id in ids:
239                 if id in res and partner_name:
240                     res[id] = (res[id],partner_name[res[id]])
241                 else:
242                     res[id] = (False,False)
243         else:
244             for id in ids:
245                 res.setdefault(id, (False, ""))
246         return res
247
248 #   def _currency(self, cursor, user, ids, name, args, context=None):
249 #       if not ids:
250 #           return {}
251 #       res = {}
252 #
253 #       currency_obj = self.pool.get('res.currency')
254 #       account_obj = self.pool.get('account.account')
255 #       cursor.execute('''SELECT pl.id, ml.currency_id, ml.account_id
256 #       FROM account_move_line ml
257 #           INNER JOIN payment_line pl
258 #               ON (ml.id = pl.move_line_id)
259 #       WHERE pl.id in (''' + ','.join([str(x) for x in ids]) + ')')
260 #
261 #       res2 = {}
262 #       account_ids = []
263 #       for payment_line_id, currency_id, account_id in cursor.fetchall():
264 #           res2[payment_line_id] = [currency_id, account_id]
265 #           account_ids.append(account_id)
266 #
267 #       account2currency_id = {}
268 #       for account in account_obj.browse(cursor, user, account_ids,
269 #               context=context):
270 #           account2currency_id[account.id] = account.company_currency_id.id
271 #
272 #       for payment_line_id in ids:
273 #           if res2[payment_line_id][0]:
274 #               res[payment_line_id] = res2[payment_line_id][0]
275 #           else:
276 #               res[payment_line_id] = \
277 #                       account2currency_id[res2[payment_line_id][1]]
278 #
279 #       currency_names = {}
280 #       for currency_id, name in currency_obj.name_get(cursor, user, res.values(),
281 #               context=context):
282 #           currency_names[currency_id] = name
283 #       for payment_line_id in ids:
284 #           res[payment_line_id] = (res[payment_line_id],
285 #                   currency_names[res[payment_line_id]])
286 #       return res
287 #
288 #   def _to_pay_currency(self, cursor, user, ids, name , args, context=None):
289 #       if not ids:
290 #           return {}
291 #
292 #       cursor.execute('''SELECT pl.id,
293 #           CASE WHEN ml.amount_currency < 0
294 #               THEN - ml.amount_currency
295 #               ELSE ml.credit
296 #           END
297 #       FROM account_move_line ml
298 #           INNER JOIN payment_line pl
299 #               ON (ml.id = pl.move_line_id)
300 #       WHERE pl.id in (''' + ','.join([str(x) for x in ids]) + ')')
301 #       return dict(cursor.fetchall())
302
303     def _amount(self, cursor, user, ids, name, args, context=None):
304         if not ids:
305             return {}
306         currency_obj = self.pool.get('res.currency')
307         if context is None:
308             context = {}
309         res = {}
310         for line in self.browse(cursor, user, ids, context=context):
311             ctx = context.copy()
312             ctx['date'] = line.order_id.date_done or time.strftime('%Y-%m-%d')
313             res[line.id] = currency_obj.compute(cursor, user, line.currency.id,
314                     line.company_currency.id,
315                     line.amount_currency, context=ctx)
316         return res
317
318     def _value_date(self, cursor, user, ids, name, args, context=None):
319         if not ids:
320             return {}
321         res = {}
322         for line in self.browse(cursor, user, ids, context=context):
323             if line.order_id.date_prefered == 'fixed':
324                 res[line.id] = line.order_id.date_planned
325             elif line.order_id.date_prefered == 'due':
326                 res[line.id] = line.due_date or time.strftime('%Y-%m-%d')
327             else:
328                 res[line.id] = time.strftime('%Y-%m-%d')
329         return res
330
331     def _get_currency(self, cr, uid, context):
332         user = self.pool.get('res.users').browse(cr, uid, uid)
333         if user.company_id:
334             return user.company_id.currency_id.id
335         else:
336             return self.pool.get('res.currency').search(cr, uid, [('rate','=',1.0)])[0]
337
338 #   def select_move_lines(*a):
339 #       print a
340 #       return []
341
342 #   def create(self, cr, uid, vals, context):
343 #       print "created!!!"
344 #       vals['company_currency'] = self._get_currency(cr, uid, context)
345 #       return super(payment_line, self).create(cr, uid, vals, context)
346
347     def _get_ml_inv_ref(self, cr, uid, ids, *a):
348         res={}
349         for id in self.browse(cr, uid, ids):
350             res[id.id] = False
351             if id.move_line_id:
352                 if id.move_line_id.invoice:
353                     res[id.id] = id.move_line_id.invoice.id
354         return res
355
356     def _get_ml_maturity_date(self, cr, uid, ids, *a):
357         res={}
358         for id in self.browse(cr, uid, ids):
359             if id.move_line_id:
360                 res[id.id] = id.move_line_id.date_maturity
361             else:
362                 res[id.id] = ""
363         return res
364
365     def _get_ml_created_date(self, cr, uid, ids, *a):
366         res={}
367         for id in self.browse(cr, uid, ids):
368             if id.move_line_id:
369                 res[id.id] = id.move_line_id.date_created
370             else:
371                 res[id.id] = ""
372         return res
373
374     _columns = {
375         'name': fields.char('Your Reference', size=64, required=True),
376         '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 ?'"),
377         'communication2': fields.char('Communication 2', size=64,help='The successor message of Communication.'),
378         '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.'),
379         'amount_currency': fields.float('Amount in Partner Currency', digits=(16,2),
380             required=True, help='Payment amount in the partner currency'),
381 #       'to_pay_currency': fields.function(_to_pay_currency, string='To Pay',
382 #           method=True, type='float',
383 #           help='Amount to pay in the partner currency'),
384 #       'currency': fields.function(_currency, string='Currency',
385 #           method=True, type='many2one', obj='res.currency'),
386         'currency': fields.many2one('res.currency','Partner Currency',required=True),
387         'company_currency': fields.many2one('res.currency','Company Currency',readonly=True),
388         'bank_id': fields.many2one('res.partner.bank', 'Destination Bank account'),
389         'order_id': fields.many2one('payment.order', 'Order', required=True,
390             ondelete='cascade', select=True),
391         'partner_id': fields.many2one('res.partner', string="Partner",required=True,help='The Ordering Customer'),
392         'amount': fields.function(_amount, string='Amount in Company Currency',
393             method=True, type='float',
394             help='Payment amount in the company currency'),
395 #       'to_pay': fields.function(select_by_name, string="To Pay", method=True,
396 #           type='float', help='Amount to pay in the company currency'),
397 #       'due_date': fields.function(select_by_name, string="Due date",
398 #           method=True, type='date'),
399         'ml_date_created': fields.function(_get_ml_created_date, string="Effective Date",
400             method=True, type='date',help="Invoice Effective Date"),
401 #       'reference': fields.function(select_by_name, string="Ref", method=True,
402 #           type='char'),
403         'ml_maturity_date': fields.function(_get_ml_maturity_date, method=True, type='date', string='Maturity Date'),
404         'ml_inv_ref': fields.function(_get_ml_inv_ref, method=True, type='many2one', relation='account.invoice', string='Invoice Ref.'),
405         'info_owner': fields.function(info_owner, string="Owner Account", method=True, type="text",help='Address of the Main Partner'),
406         'info_partner': fields.function(info_partner, string="Destination Account", method=True, type="text",help='Address of the Ordering Customer.'),
407 #        'partner_payable': fields.function(partner_payable, string="Partner payable", method=True, type='float'),
408 #       'value_date': fields.function(_value_date, string='Value Date',
409 #           method=True, type='date'),
410         'date': fields.date('Payment Date',help="If no payment date is specified, the bank will treat this payment line directly"),
411         'create_date': fields.datetime('Created' ,readonly=True),
412         'state': fields.selection([('normal','Free'), ('structured','Structured')], 'Communication Type', required=True)
413     }
414     _defaults = {
415         'name': lambda obj, cursor, user, context: obj.pool.get('ir.sequence'
416             ).get(cursor, user, 'payment.line'),
417         'state': lambda *args: 'normal',
418         'currency': _get_currency,
419         'company_currency': _get_currency,
420     }
421     _sql_constraints = [
422         ('name_uniq', 'UNIQUE(name)', 'The payment line name must be unique!'),
423     ]
424
425     def onchange_move_line(self,cr,uid,ids,move_line_id,payment_type,date_prefered,date_planned,currency=False,company_currency=False,context=None):
426         data={}
427
428         data['amount_currency']=data['communication']=data['partner_id']=data['reference']=data['date_created']=data['bank_id']=data['amount']=False
429
430         if move_line_id:
431             line = self.pool.get('account.move.line').browse(cr,uid,move_line_id)
432             data['amount_currency']=line.amount_to_pay
433
434             res = self.onchange_amount(cr, uid, ids, data['amount_currency'], currency,
435                                        company_currency, context)
436             if res:
437                 data['amount'] = res['value']['amount']
438             data['partner_id']=line.partner_id.id
439             temp = line.currency_id and line.currency_id.id or False
440             if not temp:
441                 if line.invoice:
442                     data['currency'] = line.invoice.currency_id.id
443             else:
444                 data['currency'] = temp
445
446             # calling onchange of partner and updating data dictionary
447             temp_dict=self.onchange_partner(cr,uid,ids,line.partner_id.id,payment_type)
448             data.update(temp_dict['value'])
449
450             data['reference']=line.ref
451             data['date_created'] = line.date_created
452             data['communication']=line.ref
453
454             if date_prefered == 'now':
455                 #no payment date => immediate payment
456                 data['date'] = False
457             elif date_prefered == 'due':
458                 data['date'] = line.date_maturity
459             elif date_prefered == 'fixed':
460                 data['date'] = date_planned
461
462         return {'value': data}
463
464     def onchange_amount(self, cr, uid, ids, amount, currency, cmpny_currency, context=None):
465         if (not amount) or (not cmpny_currency):
466             return {'value': {'amount':False}}
467         res = {}
468         currency_obj = self.pool.get('res.currency')
469         company_amount = currency_obj.compute(cr, uid, currency, cmpny_currency, amount)
470         res['amount'] = company_amount
471         return {'value': res}
472
473     def onchange_partner(self,cr,uid,ids,partner_id,payment_type,context=None):
474         data={}
475         data['info_partner']=data['bank_id']=False
476
477         if partner_id:
478             part_obj=self.pool.get('res.partner').browse(cr,uid,partner_id)
479             partner=part_obj.name or ''
480
481             if part_obj.address:
482                 for ads in part_obj.address:
483                     if ads.type=='default':
484                         st=ads.street and ads.street or ''
485                         st1=ads.street2 and ads.street2 or ''
486
487                         if 'zip_id' in ads:
488                             zip_city= ads.zip_id and self.pool.get('res.partner.zip').name_get(cr,uid,[ads.zip_id.id])[0][1] or ''
489                         else:
490                             zip=ads.zip and ads.zip or ''
491                             city= ads.city and ads.city or  ''
492                             zip_city= zip + ' ' + city
493
494                         cntry= ads.country_id and ads.country_id.name or ''
495                         info=partner + "\n" + st + " " + st1 + "\n" + zip_city + "\n" +cntry
496
497                         data['info_partner']=info
498
499             if part_obj.bank_ids and payment_type:
500                 bank_type = self.pool.get('payment.mode').suitable_bank_types(cr, uid, payment_type, context=context)
501                 for bank in part_obj.bank_ids:
502                     if bank.state in bank_type:
503                         data['bank_id'] = bank.id
504                         break
505
506         return {'value': data}
507
508     def fields_get(self, cr, uid, fields=None, context=None):
509         res = super(payment_line, self).fields_get(cr, uid, fields, context)
510         if 'communication2' in res:
511             res['communication2'].setdefault('states', {})
512             res['communication2']['states']['structured'] = [('readonly', True)]
513             res['communication2']['states']['normal'] = [('readonly', False)]
514
515         return res
516
517 payment_line()
518
519 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
520