[FIX] account_payment:Update Payment date accourding to the Prefered dade
[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_ml_inv_ref(self, cr, uid, ids, *a):
272         res={}
273         for id in self.browse(cr, uid, ids):
274             res[id.id] = False
275             if id.move_line_id:
276                 if id.move_line_id.invoice:
277                     res[id.id] = id.move_line_id.invoice.id
278         return res
279
280     def _get_ml_maturity_date(self, cr, uid, ids, *a):
281         res={}
282         for id in self.browse(cr, uid, ids):
283             if id.move_line_id:
284                 res[id.id] = id.move_line_id.date_maturity
285             else:
286                 res[id.id] = ""
287         return res
288
289     def _get_ml_created_date(self, cr, uid, ids, *a):
290         res={}
291         for id in self.browse(cr, uid, ids):
292             if id.move_line_id:
293                 res[id.id] = id.move_line_id.date_created
294             else:
295                 res[id.id] = ""
296         return res
297
298     _columns = {
299         'name': fields.char('Your Reference', size=64, required=True),
300         '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 ?'"),
301         'communication2': fields.char('Communication 2', size=64, help='The successor message of Communication.'),
302         '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.'),
303         'amount_currency': fields.float('Amount in Partner Currency', digits=(16,2),
304             required=True, help='Payment amount in the partner currency'),
305         'currency': fields.many2one('res.currency','Partner Currency', required=True),
306         'company_currency': fields.many2one('res.currency', 'Company Currency', readonly=True),
307         'bank_id': fields.many2one('res.partner.bank', 'Destination Bank account'),
308         'order_id': fields.many2one('payment.order', 'Order', required=True,
309             ondelete='cascade', select=True),
310         'partner_id': fields.many2one('res.partner', string="Partner", required=True, help='The Ordering Customer'),
311         'amount': fields.function(_amount, string='Amount in Company Currency',
312             method=True, type='float',
313             help='Payment amount in the company currency'),
314         'ml_date_created': fields.function(_get_ml_created_date, string="Effective Date",
315             method=True, type='date', help="Invoice Effective Date"),
316         'ml_maturity_date': fields.function(_get_ml_maturity_date, method=True, type='date', string='Due Date'),
317         'ml_inv_ref': fields.function(_get_ml_inv_ref, method=True, type='many2one', relation='account.invoice', string='Invoice Ref.'),
318         'info_owner': fields.function(info_owner, string="Owner Account", method=True, type="text", help='Address of the Main Partner'),
319         'info_partner': fields.function(info_partner, string="Destination Account", method=True, type="text", help='Address of the Ordering Customer.'),
320         'date': fields.date('Payment Date', help="If no payment date is specified, the bank will treat this payment line directly"),
321         'create_date': fields.datetime('Created' , readonly=True),
322         'state': fields.selection([('normal','Free'), ('structured','Structured')], 'Communication Type', required=True),
323         'bank_statement_line_id': fields.many2one('account.bank.statement.line', 'Bank statement line')
324     }
325     _defaults = {
326         'name': lambda obj, cursor, user, context: obj.pool.get('ir.sequence'
327             ).get(cursor, user, 'payment.line'),
328         'state': lambda *args: 'normal',
329         'currency': _get_currency,
330         'company_currency': _get_currency,
331     }
332     _sql_constraints = [
333         ('name_uniq', 'UNIQUE(name)', 'The payment line name must be unique!'),
334     ]
335
336     def onchange_move_line(self, cr, uid, ids, move_line_id, payment_type, date_prefered, date_scheduled, currency=False, company_currency=False, context=None):
337         data={}
338
339         data['amount_currency']=data['communication']=data['partner_id']=data['reference']=data['date_created']=data['bank_id']=data['amount']=False
340
341         if move_line_id:
342             line = self.pool.get('account.move.line').browse(cr, uid, move_line_id)
343             data['amount_currency']=line.amount_to_pay
344
345             res = self.onchange_amount(cr, uid, ids, data['amount_currency'], currency,
346                                        company_currency, context)
347             if res:
348                 data['amount'] = res['value']['amount']
349             data['partner_id']=line.partner_id.id
350             temp = line.currency_id and line.currency_id.id or False
351             if not temp:
352                 if line.invoice:
353                     data['currency'] = line.invoice.currency_id.id
354             else:
355                 data['currency'] = temp
356
357             # calling onchange of partner and updating data dictionary
358             temp_dict=self.onchange_partner(cr, uid, ids, line.partner_id.id, payment_type)
359             data.update(temp_dict['value'])
360
361             data['reference']=line.ref
362             data['date_created'] = line.date_created
363             data['communication']=line.ref
364
365             if date_prefered == 'now':
366                 #no payment date => immediate payment
367                 data['date'] = False
368             elif date_prefered == 'due':
369                 data['date'] = line.date_maturity
370             elif date_prefered == 'fixed':
371                 data['date'] = date_scheduled
372         return {'value': data}
373
374     def onchange_amount(self, cr, uid, ids, amount, currency, cmpny_currency, context=None):
375         if (not amount) or (not cmpny_currency):
376             return {'value': {'amount':False}}
377         res = {}
378         currency_obj = self.pool.get('res.currency')
379         company_amount = currency_obj.compute(cr, uid, currency, cmpny_currency, amount)
380         res['amount'] = company_amount
381         return {'value': res}
382
383     def onchange_partner(self, cr, uid, ids, partner_id, payment_type, context=None):
384         data={}
385         data['info_partner']=data['bank_id']=False
386
387         if partner_id:
388             part_obj=self.pool.get('res.partner').browse(cr, uid, partner_id)
389             partner=part_obj.name or ''
390
391             if part_obj.address:
392                 for ads in part_obj.address:
393                     if ads.type=='default':
394                         st=ads.street and ads.street or ''
395                         st1=ads.street2 and ads.street2 or ''
396
397                         if 'zip_id' in ads:
398                             zip_city= ads.zip_id and self.pool.get('res.partner.zip').name_get(cr, uid, [ads.zip_id.id])[0][1] or ''
399                         else:
400                             zip=ads.zip and ads.zip or ''
401                             city= ads.city and ads.city or  ''
402                             zip_city= zip + ' ' + city
403
404                         cntry= ads.country_id and ads.country_id.name or ''
405                         info=partner + "\n" + st + " " + st1 + "\n" + zip_city + "\n" +cntry
406
407                         data['info_partner']=info
408
409             if part_obj.bank_ids and payment_type:
410                 bank_type = self.pool.get('payment.mode').suitable_bank_types(cr, uid, payment_type, context=context)
411                 for bank in part_obj.bank_ids:
412                     if bank.state in bank_type:
413                         data['bank_id'] = bank.id
414                         break
415         return {'value': data}
416
417     def fields_get(self, cr, uid, fields=None, context=None):
418         res = super(payment_line, self).fields_get(cr, uid, fields, context)
419         if 'communication2' in res:
420             res['communication2'].setdefault('states', {})
421             res['communication2']['states']['structured'] = [('readonly', True)]
422             res['communication2']['states']['normal'] = [('readonly', False)]
423
424         return res
425
426 payment_line()
427
428 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
429