[IMP] Account_payment: default values of payment date
[odoo/odoo.git] / addons / account_payment / account_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 import time
22
23 from osv import osv, fields
24 import netsvc
25
26 class payment_mode(osv.osv):
27     _name= 'payment.mode'
28     _description= 'Payment Mode'
29     _columns= {
30         'name': fields.char('Name', size=64, required=True, help='Mode of Payment'),
31         'bank_id': fields.many2one('res.partner.bank', "Bank account",
32             required=True,help='Bank Account for the Payment Mode'),
33         'journal': fields.many2one('account.journal', 'Journal', required=True,
34             domain=[('type', '=', 'cash')], help='Cash Journal for the Payment Mode'),
35         'company_id': fields.many2one('res.company', 'Company', required=True),
36     }
37     _defaults = {
38         'company_id': lambda self,cr,uid,c: self.pool.get('res.users').browse(cr, uid, uid, c).company_id.id
39     }
40
41     def suitable_bank_types(self, cr, uid, payment_code=None, context={}):
42         """Return the codes of the bank type that are suitable
43         for the given payment type code"""
44         if not payment_code:
45             return []
46         cr.execute(""" select pb.state
47             from res_partner_bank pb
48             join payment_mode pm on (pm.bank_id = pb.id)
49             where pm.id = %s """, [payment_code])
50         return [x[0] for x in cr.fetchall()]
51
52 payment_mode()
53
54 class payment_order(osv.osv):
55     _name = 'payment.order'
56     _description = 'Payment Order'
57     _rec_name = 'reference'
58
59     def get_wizard(self, type):
60         logger = netsvc.Logger()
61         logger.notifyChannel("warning", netsvc.LOG_WARNING,
62                 "No wizard found for the payment type '%s'." % type)
63         return None
64
65     def _total(self, cursor, user, ids, name, args, context=None):
66         if not ids:
67             return {}
68         res = {}
69         for order in self.browse(cursor, user, ids, context=context):
70             if order.line_ids:
71                 res[order.id] = reduce(lambda x, y: x + y.amount, order.line_ids, 0.0)
72             else:
73                 res[order.id] = 0.0
74         return res
75
76     _columns = {
77         '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.'),
78         'reference': fields.char('Reference', size=128, required=1, states={'done':[('readonly',True)]}),
79         'mode': fields.many2one('payment.mode','Payment mode', select=True, required=1, states={'done':[('readonly',True)]}, help='Select the Payment Mode to be applied.'),
80         'state': fields.selection([
81             ('draft', 'Draft'),
82             ('open','Confirmed'),
83             ('cancel','Cancelled'),
84             ('done','Done')], 'State', select=True,
85             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\'.'),
86         'line_ids': fields.one2many('payment.line', 'order_id', 'Payment lines', states={'done':[('readonly',True)]}),
87         'total': fields.function(_total, string="Total", method=True,
88             type='float'),
89         'user_id': fields.many2one('res.users', 'User', required=True, states={'done':[('readonly',True)]}),
90         'date_prefered': fields.selection([
91             ('now', 'Directly'),
92             ('due', 'Due date'),
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),
97     }
98
99     _defaults = {
100         'user_id': lambda self,cr,uid,context: uid,
101         'state': lambda *a: 'draft',
102         'date_prefered': lambda *a: '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'),
105     }
106
107     def set_to_draft(self, cr, uid, ids, *args):
108         self.write(cr, uid, ids, {'state':'draft'})
109         wf_service = netsvc.LocalService("workflow")
110         for id in ids:
111             wf_service.trg_create(uid, 'payment.order', id, cr)
112         return True
113
114     def action_open(self, cr, uid, ids, *args):
115         for order in self.read(cr, uid, ids, ['reference']):
116             if not order['reference']:
117                 reference = self.pool.get('ir.sequence').get(cr, uid, 'payment.order')
118                 self.write(cr, uid, order['id'],{'reference':reference})
119         return True
120
121     def set_done(self, cr, uid, id, *args):
122         self.write(cr,uid,id,{'date_done': time.strftime('%Y-%m-%d'),
123             'state': 'done',})
124         wf_service = netsvc.LocalService("workflow")
125         wf_service.trg_validate(uid, 'payment.order', id, 'done', cr)
126         return True
127
128     def copy(self, cr, uid, id, default={}, context=None):
129         default.update({
130             'state':'draft',
131             'line_ids': [],
132             'reference': self.pool.get('ir.sequence').get(cr, uid, 'payment.order')
133         })
134         return super(payment_order, self).copy(cr, uid, id, default, context=context)
135
136     def write(self, cr, uid, ids, vals, context=None):
137         if context is None:
138             context = {}
139         payment_line_obj = self.pool.get('payment.line')
140         payment_line_ids = []
141         if (vals.get('date_prefered', False) == 'fixed' and not vals.get('date_scheduled', False)) or vals.get('date_scheduled', False):
142             for order in self.browse(cr, uid, ids, context=context):
143                 for line in order.line_ids:
144                     payment_line_ids.append(line.id)
145             payment_line_obj.write(cr, uid, payment_line_ids, {'date':vals.get('date_scheduled', False)}, context=context)
146         elif vals.get('date_prefered', False) == 'due':
147             vals.update({'date_scheduled':False})
148             for order in self.browse(cr, uid, ids, context=context):
149                 for line in order.line_ids:
150                     payment_line_obj.write(cr, uid, [line.id], {'date':line.ml_maturity_date}, context=context)
151         elif vals.get('date_prefered', False) == 'now':
152             vals.update({'date_scheduled':False})
153             for order in self.browse(cr, uid, ids, context=context):
154                 for line in order.line_ids:
155                     payment_line_ids.append(line.id)
156             payment_line_obj.write(cr, uid, payment_line_ids, {'date': False}, context=context)
157         return super(payment_order, self).write(cr, uid, ids, vals, context=context)
158
159 payment_order()
160
161 class payment_line(osv.osv):
162     _name = 'payment.line'
163     _description = 'Payment Line'
164
165
166     def translate(self, orig):
167         return {
168                 "due_date": "date_maturity",
169                 "reference": "ref"}.get(orig, orig)
170
171     def info_owner(self, cr, uid, ids, name=None, args=None, context=None):
172         if not ids: return {}
173         result = {}
174         info=''
175         for line in self.browse(cr, uid, ids, context=context):
176             owner=line.order_id.mode.bank_id.partner_id
177             result[line.id]=False
178             if owner.address:
179                 for ads in owner.address:
180                     if ads.type=='default':
181                         st=ads.street and ads.street or ''
182                         st1=ads.street2 and ads.street2 or ''
183                         if 'zip_id' in ads:
184                             zip_city= ads.zip_id and self.pool.get('res.partner.zip').name_get(cr, uid, [ads.zip_id.id])[0][1] or ''
185                         else:
186                             zip=ads.zip and ads.zip or ''
187                             city= ads.city and ads.city or  ''
188                             zip_city= zip + ' ' + city
189                         cntry= ads.country_id and ads.country_id.name or ''
190                         info=owner.name + "\n" + st + " " + st1 + "\n" + zip_city + "\n" +cntry
191                         result[line.id]=info
192                         break
193         return result
194
195     def info_partner(self, cr, uid, ids, name=None, args=None, context=None):
196         if not ids: return {}
197         result = {}
198         info=''
199         for line in self.browse(cr, uid, ids, context=context):
200             result[line.id]=False
201             if not line.partner_id:
202                 break
203             partner = line.partner_id.name or ''
204             if line.partner_id.address:
205                 for ads in line.partner_id.address:
206                     if ads.type=='default':
207                         st=ads.street and ads.street or ''
208                         st1=ads.street2 and ads.street2 or ''
209                         if 'zip_id' in ads:
210                             zip_city= ads.zip_id and self.pool.get('res.partner.zip').name_get(cr, uid, [ads.zip_id.id])[0][1] or ''
211                         else:
212                             zip=ads.zip and ads.zip or ''
213                             city= ads.city and ads.city or  ''
214                             zip_city= zip + ' ' + city
215                         cntry= ads.country_id and ads.country_id.name or ''
216                         info=partner + "\n" + st + " " + st1 + "\n" + zip_city + "\n" +cntry
217                         result[line.id]=info
218                         break
219         return result
220
221     def select_by_name(self, cr, uid, ids, name, args, context=None):
222         if not ids: return {}
223
224         partner_obj = self.pool.get('res.partner')
225         cr.execute("""SELECT pl.id, ml.%s
226             from account_move_line ml
227                 inner join payment_line pl
228                 on (ml.id = pl.move_line_id)
229                 where pl.id IN %%s"""% self.translate(name),
230                    (tuple(ids),))
231         res = dict(cr.fetchall())
232
233         if name == 'partner_id':
234             partner_name = {}
235             for p_id, p_name in partner_obj.name_get(cr, uid,
236                 filter(lambda x:x and x != 0,res.values()), context=context):
237                 partner_name[p_id] = p_name
238
239             for id in ids:
240                 if id in res and partner_name:
241                     res[id] = (res[id],partner_name[res[id]])
242                 else:
243                     res[id] = (False,False)
244         else:
245             for id in ids:
246                 res.setdefault(id, (False, ""))
247         return res
248
249     def _amount(self, cursor, user, ids, name, args, context=None):
250         if not ids:
251             return {}
252         currency_obj = self.pool.get('res.currency')
253         if context is None:
254             context = {}
255         res = {}
256         for line in self.browse(cursor, user, ids, context=context):
257             ctx = context.copy()
258             ctx['date'] = line.order_id.date_done or time.strftime('%Y-%m-%d')
259             res[line.id] = currency_obj.compute(cursor, user, line.currency.id,
260                     line.company_currency.id,
261                     line.amount_currency, context=ctx)
262         return res
263
264     def _get_currency(self, cr, uid, context):
265         user = self.pool.get('res.users').browse(cr, uid, uid)
266         if user.company_id:
267             return user.company_id.currency_id.id
268         else:
269             return self.pool.get('res.currency').search(cr, uid, [('rate','=',1.0)])[0]
270
271     def _get_date(self, cr, uid, context=None):
272         if context is None:
273             context = {}
274         date = False
275         if context.get('order_id') and context['order_id']:
276             order = self.pool.get('payment.order').browse(cr, uid, context['order_id'], context)
277             if order.date_prefered == 'fixed':
278                 date = order.date_scheduled
279             else:
280                 date = time.strftime('%Y-%m-%d')
281         return date
282
283     def _get_ml_inv_ref(self, cr, uid, ids, *a):
284         res={}
285         for id in self.browse(cr, uid, ids):
286             res[id.id] = False
287             if id.move_line_id:
288                 if id.move_line_id.invoice:
289                     res[id.id] = id.move_line_id.invoice.id
290         return res
291
292     def _get_ml_maturity_date(self, cr, uid, ids, *a):
293         res={}
294         for id in self.browse(cr, uid, ids):
295             if id.move_line_id:
296                 res[id.id] = id.move_line_id.date_maturity
297             else:
298                 res[id.id] = ""
299         return res
300
301     def _get_ml_created_date(self, cr, uid, ids, *a):
302         res={}
303         for id in self.browse(cr, uid, ids):
304             if id.move_line_id:
305                 res[id.id] = id.move_line_id.date_created
306             else:
307                 res[id.id] = ""
308         return res
309
310     _columns = {
311         'name': fields.char('Your Reference', size=64, required=True),
312         '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 ?'"),
313         'communication2': fields.char('Communication 2', size=64, help='The successor message of Communication.'),
314         '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.'),
315         'amount_currency': fields.float('Amount in Partner Currency', digits=(16,2),
316             required=True, help='Payment amount in the partner currency'),
317         'currency': fields.many2one('res.currency','Partner Currency', required=True),
318         'company_currency': fields.many2one('res.currency', 'Company Currency', readonly=True),
319         'bank_id': fields.many2one('res.partner.bank', 'Destination Bank account'),
320         'order_id': fields.many2one('payment.order', 'Order', required=True,
321             ondelete='cascade', select=True),
322         'partner_id': fields.many2one('res.partner', string="Partner", required=True, help='The Ordering Customer'),
323         'amount': fields.function(_amount, string='Amount in Company Currency',
324             method=True, type='float',
325             help='Payment amount in the company currency'),
326         'ml_date_created': fields.function(_get_ml_created_date, string="Effective Date",
327             method=True, type='date', help="Invoice Effective Date"),
328         'ml_maturity_date': fields.function(_get_ml_maturity_date, method=True, type='date', string='Due Date'),
329         'ml_inv_ref': fields.function(_get_ml_inv_ref, method=True, type='many2one', relation='account.invoice', string='Invoice Ref.'),
330         'info_owner': fields.function(info_owner, string="Owner Account", method=True, type="text", help='Address of the Main Partner'),
331         'info_partner': fields.function(info_partner, string="Destination Account", method=True, type="text", help='Address of the Ordering Customer.'),
332         'date': fields.date('Payment Date', help="If no payment date is specified, the bank will treat this payment line directly"),
333         'create_date': fields.datetime('Created' , readonly=True),
334         'state': fields.selection([('normal','Free'), ('structured','Structured')], 'Communication Type', required=True),
335         'bank_statement_line_id': fields.many2one('account.bank.statement.line', 'Bank statement line')
336     }
337     _defaults = {
338         'name': lambda obj, cursor, user, context: obj.pool.get('ir.sequence'
339             ).get(cursor, user, 'payment.line'),
340         'state': lambda *args: 'normal',
341         'currency': _get_currency,
342         'company_currency': _get_currency,
343         'date': _get_date,
344     }
345     _sql_constraints = [
346         ('name_uniq', 'UNIQUE(name)', 'The payment line name must be unique!'),
347     ]
348
349     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         data={}
351
352         data['amount_currency']=data['communication']=data['partner_id']=data['reference']=data['date_created']=data['bank_id']=data['amount']=False
353
354         if move_line_id:
355             line = self.pool.get('account.move.line').browse(cr, uid, move_line_id)
356             data['amount_currency']=line.amount_to_pay
357
358             res = self.onchange_amount(cr, uid, ids, data['amount_currency'], currency,
359                                        company_currency, context)
360             if res:
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
364             if not temp:
365                 if line.invoice:
366                     data['currency'] = line.invoice.currency_id.id
367             else:
368                 data['currency'] = temp
369
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'])
373
374             data['reference']=line.ref
375             data['date_created'] = line.date_created
376             data['communication']=line.ref
377
378             if date_prefered == 'now':
379                 #no payment date => immediate payment
380                 data['date'] = False
381             elif date_prefered == 'due':
382                 data['date'] = line.date_maturity
383             elif date_prefered == 'fixed':
384                 data['date'] = date_scheduled
385         return {'value': data}
386
387     def onchange_amount(self, cr, uid, ids, amount, currency, cmpny_currency, context=None):
388         if (not amount) or (not cmpny_currency):
389             return {'value': {'amount':False}}
390         res = {}
391         currency_obj = self.pool.get('res.currency')
392         company_amount = currency_obj.compute(cr, uid, currency, cmpny_currency, amount)
393         res['amount'] = company_amount
394         return {'value': res}
395
396     def onchange_partner(self, cr, uid, ids, partner_id, payment_type, context=None):
397         data={}
398         data['info_partner']=data['bank_id']=False
399
400         if partner_id:
401             part_obj=self.pool.get('res.partner').browse(cr, uid, partner_id)
402             partner=part_obj.name or ''
403
404             if part_obj.address:
405                 for ads in part_obj.address:
406                     if ads.type=='default':
407                         st=ads.street and ads.street or ''
408                         st1=ads.street2 and ads.street2 or ''
409
410                         if 'zip_id' in ads:
411                             zip_city= ads.zip_id and self.pool.get('res.partner.zip').name_get(cr, uid, [ads.zip_id.id])[0][1] or ''
412                         else:
413                             zip=ads.zip and ads.zip or ''
414                             city= ads.city and ads.city or  ''
415                             zip_city= zip + ' ' + city
416
417                         cntry= ads.country_id and ads.country_id.name or ''
418                         info=partner + "\n" + st + " " + st1 + "\n" + zip_city + "\n" +cntry
419
420                         data['info_partner']=info
421
422             if part_obj.bank_ids and payment_type:
423                 bank_type = self.pool.get('payment.mode').suitable_bank_types(cr, uid, payment_type, context=context)
424                 for bank in part_obj.bank_ids:
425                     if bank.state in bank_type:
426                         data['bank_id'] = bank.id
427                         break
428         return {'value': data}
429
430     def fields_get(self, cr, uid, fields=None, context=None):
431         res = super(payment_line, self).fields_get(cr, uid, fields, context)
432         if 'communication2' in res:
433             res['communication2'].setdefault('states', {})
434             res['communication2']['states']['structured'] = [('readonly', True)]
435             res['communication2']['states']['normal'] = [('readonly', False)]
436
437         return res
438
439 payment_line()
440
441 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
442