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