Launchpad automatic translations update.
[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
22 import time
23
24 from osv import osv, fields
25 import netsvc
26
27 class payment_mode(osv.osv):
28     _name= 'payment.mode'
29     _description= 'Payment Mode'
30     _columns = {
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,),
38         
39     }
40     _defaults = {
41         'company_id': lambda self,cr,uid,c: self.pool.get('res.users').browse(cr, uid, uid, c).company_id.id
42     }
43
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"""
47         if not payment_code:
48             return []
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()]
54     
55     def onchange_company_id (self, cr, uid, ids, company_id=False, context=None):
56         result = {}
57         if company_id:
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}
61                 
62
63 payment_mode()
64
65 class payment_order(osv.osv):
66     _name = 'payment.order'
67     _description = 'Payment Order'
68     _rec_name = 'reference'
69     _order = 'id desc'
70
71     #dead code
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)
76         return None
77
78     def _total(self, cursor, user, ids, name, args, context=None):
79         if not ids:
80             return {}
81         res = {}
82         for order in self.browse(cursor, user, ids, context=context):
83             if order.line_ids:
84                 res[order.id] = reduce(lambda x, y: x + y.amount, order.line_ids, 0.0)
85             else:
86                 res[order.id] = 0.0
87         return res
88
89     _columns = {
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([
94             ('draft', 'Draft'),
95             ('open', 'Confirmed'),
96             ('cancel', 'Cancelled'),
97             ('done', 'Done')], 'State', 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([
103             ('now', 'Directly'),
104             ('due', 'Due date'),
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),
110     }
111
112     _defaults = {
113         'user_id': lambda self,cr,uid,context: uid,
114         'state': 'draft',
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'),
118     }
119
120     def set_to_draft(self, cr, uid, ids, *args):
121         self.write(cr, uid, ids, {'state': 'draft'})
122         wf_service = netsvc.LocalService("workflow")
123         for id in ids:
124             wf_service.trg_create(uid, 'payment.order', id, cr)
125         return True
126
127     def action_open(self, cr, uid, ids, *args):
128         ir_seq_obj = self.pool.get('ir.sequence')
129
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})
134         return True
135
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)
140         return True
141
142     def copy(self, cr, uid, id, default={}, context=None):
143         default.update({
144             'state': 'draft',
145             'line_ids': [],
146             'reference': self.pool.get('ir.sequence').get(cr, uid, 'payment.order')
147         })
148         return super(payment_order, self).copy(cr, uid, id, default, context=context)
149
150     def write(self, cr, uid, ids, vals, context=None):
151         if context is None:
152             context = {}
153         payment_line_obj = self.pool.get('payment.line')
154         payment_line_ids = []
155
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)
173
174 payment_order()
175
176 class payment_line(osv.osv):
177     _name = 'payment.line'
178     _description = 'Payment Line'
179
180     def translate(self, orig):
181         return {
182                 "due_date": "date_maturity",
183                 "reference": "ref"}.get(orig, orig)
184
185     def info_owner(self, cr, uid, ids, name=None, args=None, context=None):
186         if not ids: return {}
187         partner_zip_obj = self.pool.get('res.partner.zip')
188
189         result = {}
190         info=''
191         for line in self.browse(cr, uid, ids, context=context):
192             owner = line.order_id.mode.bank_id.partner_id
193             result[line.id] = False
194             if owner.address:
195                 for ads in owner.address:
196                     if ads.type == 'default':
197                         st = ads.street and ads.street or ''
198                         st1 = ads.street2 and ads.street2 or ''
199                         if 'zip_id' in ads:
200                             zip_city = ads.zip_id and partner_zip_obj.name_get(cr, uid, [ads.zip_id.id])[0][1] or ''
201                         else:
202                             zip = ads.zip and ads.zip or ''
203                             city = ads.city and ads.city or  ''
204                             zip_city = zip + ' ' + city
205                         cntry = ads.country_id and ads.country_id.name or ''
206                         info = owner.name + "\n" + st + " " + st1 + "\n" + zip_city + "\n" +cntry
207                         result[line.id] = info
208                         break
209         return result
210
211     def info_partner(self, cr, uid, ids, name=None, args=None, context=None):
212         if not ids: return {}
213         partner_zip_obj = self.pool.get('res.partner.zip')
214         result = {}
215         info = ''
216
217         for line in self.browse(cr, uid, ids, context=context):
218             result[line.id] = False
219             if not line.partner_id:
220                 break
221             partner = line.partner_id.name or ''
222             if line.partner_id.address:
223                 for ads in line.partner_id.address:
224                     if ads.type == 'default':
225                         st = ads.street and ads.street or ''
226                         st1 = ads.street2 and ads.street2 or ''
227                         if 'zip_id' in ads:
228                             zip_city = ads.zip_id and partner_zip_obj.name_get(cr, uid, [ads.zip_id.id])[0][1] or ''
229                         else:
230                             zip = ads.zip and ads.zip or ''
231                             city = ads.city and ads.city or  ''
232                             zip_city = zip + ' ' + city
233                         cntry = ads.country_id and ads.country_id.name or ''
234                         info = partner + "\n" + st + " " + st1 + "\n" + zip_city + "\n" +cntry
235                         result[line.id] = info
236                         break
237         return result
238
239     #dead code
240     def select_by_name(self, cr, uid, ids, name, args, context=None):
241         if not ids: return {}
242         partner_obj = self.pool.get('res.partner')
243
244         cr.execute("""SELECT pl.id, ml.%s
245             FROM account_move_line ml
246                 INNER JOIN payment_line pl
247                 ON (ml.id = pl.move_line_id)
248                 WHERE pl.id IN %%s"""% self.translate(name),
249                    (tuple(ids),))
250         res = dict(cr.fetchall())
251
252         if name == 'partner_id':
253             partner_name = {}
254             for p_id, p_name in partner_obj.name_get(cr, uid,
255                 filter(lambda x:x and x != 0,res.values()), context=context):
256                 partner_name[p_id] = p_name
257
258             for id in ids:
259                 if id in res and partner_name:
260                     res[id] = (res[id],partner_name[res[id]])
261                 else:
262                     res[id] = (False,False)
263         else:
264             for id in ids:
265                 res.setdefault(id, (False, ""))
266         return res
267
268     def _amount(self, cursor, user, ids, name, args, context=None):
269         if not ids:
270             return {}
271         currency_obj = self.pool.get('res.currency')
272         if context is None:
273             context = {}
274         res = {}
275
276         for line in self.browse(cursor, user, ids, context=context):
277             ctx = context.copy()
278             ctx['date'] = line.order_id.date_done or time.strftime('%Y-%m-%d')
279             res[line.id] = currency_obj.compute(cursor, user, line.currency.id,
280                     line.company_currency.id,
281                     line.amount_currency, context=ctx)
282         return res
283
284     def _get_currency(self, cr, uid, context=None):
285         user_obj = self.pool.get('res.users')
286         currency_obj = self.pool.get('res.currency')
287         user = user_obj.browse(cr, uid, uid, context=context)
288
289         if user.company_id:
290             return user.company_id.currency_id.id
291         else:
292             return currency_obj.search(cr, uid, [('rate', '=', 1.0)])[0]
293
294     def _get_date(self, cr, uid, context=None):
295         if context is None:
296             context = {}
297         payment_order_obj = self.pool.get('payment.order')
298         date = False
299
300         if context.get('order_id') and context['order_id']:
301             order = payment_order_obj.browse(cr, uid, context['order_id'], context=context)
302             if order.date_prefered == 'fixed':
303                 date = order.date_scheduled
304             else:
305                 date = time.strftime('%Y-%m-%d')
306         return date
307
308     def _get_ml_inv_ref(self, cr, uid, ids, *a):
309         res = {}
310         for id in self.browse(cr, uid, ids):
311             res[id.id] = False
312             if id.move_line_id:
313                 if id.move_line_id.invoice:
314                     res[id.id] = id.move_line_id.invoice.id
315         return res
316
317     def _get_ml_maturity_date(self, cr, uid, ids, *a):
318         res = {}
319         for id in self.browse(cr, uid, ids):
320             if id.move_line_id:
321                 res[id.id] = id.move_line_id.date_maturity
322             else:
323                 res[id.id] = False
324         return res
325
326     def _get_ml_created_date(self, cr, uid, ids, *a):
327         res = {}
328         for id in self.browse(cr, uid, ids):
329             if id.move_line_id:
330                 res[id.id] = id.move_line_id.date_created
331             else:
332                 res[id.id] = False
333         return res
334
335     _columns = {
336         'name': fields.char('Your Reference', size=64, required=True),
337         '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 ?'"),
338         'communication2': fields.char('Communication 2', size=64, help='The successor message of Communication.'),
339         '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.'),
340         'amount_currency': fields.float('Amount in Partner Currency', digits=(16, 2),
341             required=True, help='Payment amount in the partner currency'),
342         'currency': fields.many2one('res.currency','Partner Currency', required=True),
343         'company_currency': fields.many2one('res.currency', 'Company Currency', readonly=True),
344         'bank_id': fields.many2one('res.partner.bank', 'Destination Bank Account'),
345         'order_id': fields.many2one('payment.order', 'Order', required=True,
346             ondelete='cascade', select=True),
347         'partner_id': fields.many2one('res.partner', string="Partner", required=True, help='The Ordering Customer'),
348         'amount': fields.function(_amount, string='Amount in Company Currency',
349             type='float',
350             help='Payment amount in the company currency'),
351         'ml_date_created': fields.function(_get_ml_created_date, string="Effective Date",
352             type='date', help="Invoice Effective Date"),
353         'ml_maturity_date': fields.function(_get_ml_maturity_date, type='date', string='Due Date'),
354         'ml_inv_ref': fields.function(_get_ml_inv_ref, type='many2one', relation='account.invoice', string='Invoice Ref.'),
355         'info_owner': fields.function(info_owner, string="Owner Account", type="text", help='Address of the Main Partner'),
356         'info_partner': fields.function(info_partner, string="Destination Account", type="text", help='Address of the Ordering Customer.'),
357         'date': fields.date('Payment Date', help="If no payment date is specified, the bank will treat this payment line directly"),
358         'create_date': fields.datetime('Created', readonly=True),
359         'state': fields.selection([('normal','Free'), ('structured','Structured')], 'Communication Type', required=True),
360         'bank_statement_line_id': fields.many2one('account.bank.statement.line', 'Bank statement line'),
361         'company_id': fields.related('order_id', 'company_id', type='many2one', relation='res.company', string='Company', store=True, readonly=True),
362     }
363     _defaults = {
364         'name': lambda obj, cursor, user, context: obj.pool.get('ir.sequence'
365             ).get(cursor, user, 'payment.line'),
366         'state': 'normal',
367         'currency': _get_currency,
368         'company_currency': _get_currency,
369         'date': _get_date,
370     }
371     _sql_constraints = [
372         ('name_uniq', 'UNIQUE(name)', 'The payment line name must be unique!'),
373     ]
374
375     def onchange_move_line(self, cr, uid, ids, move_line_id, payment_type, date_prefered, date_scheduled, currency=False, company_currency=False, context=None):
376         data = {}
377         move_line_obj = self.pool.get('account.move.line')
378
379         data['amount_currency'] = data['communication'] = data['partner_id'] = data['bank_id'] = data['amount'] = False
380
381         if move_line_id:
382             line = move_line_obj.browse(cr, uid, move_line_id, context=context)
383             data['amount_currency'] = line.amount_to_pay
384
385             res = self.onchange_amount(cr, uid, ids, data['amount_currency'], currency,
386                                        company_currency, context)
387             if res:
388                 data['amount'] = res['value']['amount']
389             data['partner_id'] = line.partner_id.id
390             temp = line.currency_id and line.currency_id.id or False
391             if not temp:
392                 if line.invoice:
393                     data['currency'] = line.invoice.currency_id.id
394             else:
395                 data['currency'] = temp
396
397             # calling onchange of partner and updating data dictionary
398             temp_dict = self.onchange_partner(cr, uid, ids, line.partner_id.id, payment_type)
399             data.update(temp_dict['value'])
400
401             data['communication'] = line.ref
402
403             if date_prefered == 'now':
404                 #no payment date => immediate payment
405                 data['date'] = False
406             elif date_prefered == 'due':
407                 data['date'] = line.date_maturity
408             elif date_prefered == 'fixed':
409                 data['date'] = date_scheduled
410         return {'value': data}
411
412     def onchange_amount(self, cr, uid, ids, amount, currency, cmpny_currency, context=None):
413         if (not amount) or (not cmpny_currency):
414             return {'value': {'amount': False}}
415         res = {}
416         currency_obj = self.pool.get('res.currency')
417         company_amount = currency_obj.compute(cr, uid, currency, cmpny_currency, amount)
418         res['amount'] = company_amount
419         return {'value': res}
420
421     def onchange_partner(self, cr, uid, ids, partner_id, payment_type, context=None):
422         data = {}
423         partner_zip_obj = self.pool.get('res.partner.zip')
424         partner_obj = self.pool.get('res.partner')
425         payment_mode_obj = self.pool.get('payment.mode')
426         data['info_partner'] = data['bank_id'] = False
427
428         if partner_id:
429             part_obj = partner_obj.browse(cr, uid, partner_id, context=context)
430             partner = part_obj.name or ''
431
432             if part_obj.address:
433                 for ads in part_obj.address:
434                     if ads.type == 'default':
435                         st = ads.street and ads.street or ''
436                         st1 = ads.street2 and ads.street2 or ''
437
438                         if 'zip_id' in ads:
439                             zip_city = ads.zip_id and partner_zip_obj.name_get(cr, uid, [ads.zip_id.id])[0][1] or ''
440                         else:
441                             zip = ads.zip and ads.zip or ''
442                             city = ads.city and ads.city or  ''
443                             zip_city = zip + ' ' + city
444
445                         cntry = ads.country_id and ads.country_id.name or ''
446                         info = partner + "\n" + st + " " + st1 + "\n" + zip_city + "\n" +cntry
447
448                         data['info_partner'] = info
449
450             if part_obj.bank_ids and payment_type:
451                 bank_type = payment_mode_obj.suitable_bank_types(cr, uid, payment_type, context=context)
452                 for bank in part_obj.bank_ids:
453                     if bank.state in bank_type:
454                         data['bank_id'] = bank.id
455                         break
456         return {'value': data}
457
458     def fields_get(self, cr, uid, fields=None, context=None):
459         res = super(payment_line, self).fields_get(cr, uid, fields, context)
460         if 'communication2' in res:
461             res['communication2'].setdefault('states', {})
462             res['communication2']['states']['structured'] = [('readonly', True)]
463             res['communication2']['states']['normal'] = [('readonly', False)]
464         return res
465
466 payment_line()
467
468 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: