1 # -*- coding: utf-8 -*-
2 ##############################################################################
4 # OpenERP, Open Source Management Solution
5 # Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
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.
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.
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/>.
20 ##############################################################################
25 from openerp.osv import fields, osv
26 from openerp import netsvc
28 _logger = logging.getLogger(__name__)
30 class payment_mode(osv.osv):
32 _description= 'Payment Mode'
34 'name': fields.char('Name', size=64, required=True, help='Mode of Payment'),
35 'bank_id': fields.many2one('res.partner.bank', "Bank account",
36 required=True,help='Bank Account for the Payment Mode'),
37 'journal': fields.many2one('account.journal', 'Journal', required=True,
38 domain=[('type', 'in', ('bank','cash'))], help='Bank or Cash Journal for the Payment Mode'),
39 'company_id': fields.many2one('res.company', 'Company',required=True),
40 'partner_id':fields.related('company_id','partner_id',type='many2one',relation='res.partner',string='Partner',store=True,),
44 'company_id': lambda self,cr,uid,c: self.pool.get('res.users').browse(cr, uid, uid, c).company_id.id
47 def suitable_bank_types(self, cr, uid, payment_code=None, context=None):
48 """Return the codes of the bank type that are suitable
49 for the given payment type code"""
52 cr.execute(""" SELECT pb.state
53 FROM res_partner_bank pb
54 JOIN payment_mode pm ON (pm.bank_id = pb.id)
55 WHERE pm.id = %s """, [payment_code])
56 return [x[0] for x in cr.fetchall()]
58 def onchange_company_id (self, cr, uid, ids, company_id=False, context=None):
61 partner_id = self.pool.get('res.company').browse(cr, uid, company_id, context=context).partner_id.id
62 result['partner_id'] = partner_id
63 return {'value': result}
68 class payment_order(osv.osv):
69 _name = 'payment.order'
70 _description = 'Payment Order'
71 _rec_name = 'reference'
75 def get_wizard(self, type):
76 _logger.warning("No wizard found for the payment type '%s'.", type)
79 def _total(self, cursor, user, ids, name, args, context=None):
83 for order in self.browse(cursor, user, ids, context=context):
85 res[order.id] = reduce(lambda x, y: x + y.amount, order.line_ids, 0.0)
91 'date_scheduled': fields.date('Scheduled Date', states={'done':[('readonly', True)]}, help='Select a date if you have chosen Preferred Date to be fixed.'),
92 'reference': fields.char('Reference', size=128, required=1, states={'done': [('readonly', True)]}),
93 'mode': fields.many2one('payment.mode', 'Payment Mode', select=True, required=1, states={'done': [('readonly', True)]}, help='Select the Payment Mode to be applied.'),
94 'state': fields.selection([
96 ('cancel', 'Cancelled'),
97 ('open', 'Confirmed'),
98 ('done', 'Done')], 'Status', select=True,
99 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\'.'),
100 'line_ids': fields.one2many('payment.line', 'order_id', 'Payment lines', states={'done': [('readonly', True)]}),
101 'total': fields.function(_total, string="Total", type='float'),
102 'user_id': fields.many2one('res.users', 'Responsible', required=True, states={'done': [('readonly', True)]}),
103 'date_prefered': fields.selection([
106 ('fixed', 'Fixed date')
107 ], "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."),
108 'date_created': fields.date('Creation Date', readonly=True),
109 'date_done': fields.date('Execution Date', readonly=True),
110 'company_id': fields.related('mode', 'company_id', type='many2one', relation='res.company', string='Company', store=True, readonly=True),
114 'user_id': lambda self,cr,uid,context: uid,
116 'date_prefered': 'due',
117 'date_created': lambda *a: time.strftime('%Y-%m-%d'),
118 'reference': lambda self,cr,uid,context: self.pool.get('ir.sequence').get(cr, uid, 'payment.order'),
121 def set_to_draft(self, cr, uid, ids, *args):
122 self.write(cr, uid, ids, {'state': 'draft'})
123 wf_service = netsvc.LocalService("workflow")
125 wf_service.trg_create(uid, 'payment.order', id, cr)
128 def action_open(self, cr, uid, ids, *args):
129 ir_seq_obj = self.pool.get('ir.sequence')
131 for order in self.read(cr, uid, ids, ['reference']):
132 if not order['reference']:
133 reference = ir_seq_obj.get(cr, uid, 'payment.order')
134 self.write(cr, uid, order['id'], {'reference':reference})
137 def set_done(self, cr, uid, ids, *args):
138 wf_service = netsvc.LocalService("workflow")
139 self.write(cr, uid, ids, {'date_done': time.strftime('%Y-%m-%d')})
140 wf_service.trg_validate(uid, 'payment.order', ids[0], 'done', cr)
143 def copy(self, cr, uid, id, default=None, context=None):
149 'reference': self.pool.get('ir.sequence').get(cr, uid, 'payment.order')
151 return super(payment_order, self).copy(cr, uid, id, default, context=context)
153 def write(self, cr, uid, ids, vals, context=None):
156 payment_line_obj = self.pool.get('payment.line')
157 payment_line_ids = []
159 if (vals.get('date_prefered', False) == 'fixed' and not vals.get('date_scheduled', False)) or vals.get('date_scheduled', False):
160 for order in self.browse(cr, uid, ids, context=context):
161 for line in order.line_ids:
162 payment_line_ids.append(line.id)
163 payment_line_obj.write(cr, uid, payment_line_ids, {'date': vals.get('date_scheduled', False)}, context=context)
164 elif vals.get('date_prefered', False) == 'due':
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_obj.write(cr, uid, [line.id], {'date': line.ml_maturity_date}, context=context)
169 elif vals.get('date_prefered', False) == 'now':
170 vals.update({'date_scheduled': False})
171 for order in self.browse(cr, uid, ids, context=context):
172 for line in order.line_ids:
173 payment_line_ids.append(line.id)
174 payment_line_obj.write(cr, uid, payment_line_ids, {'date': False}, context=context)
175 return super(payment_order, self).write(cr, uid, ids, vals, context=context)
179 class payment_line(osv.osv):
180 _name = 'payment.line'
181 _description = 'Payment Line'
183 def translate(self, orig):
185 "due_date": "date_maturity",
186 "reference": "ref"}.get(orig, orig)
188 def info_owner(self, cr, uid, ids, name=None, args=None, context=None):
190 for line in self.browse(cr, uid, ids, context=context):
191 owner = line.order_id.mode.bank_id.partner_id
192 result[line.id] = self._get_info_partner(cr, uid, owner, context=context)
195 def _get_info_partner(self,cr, uid, partner_record, context=None):
196 if not partner_record:
198 st = partner_record.street or ''
199 st1 = partner_record.street2 or ''
200 zip = partner_record.zip or ''
201 city = partner_record.city or ''
202 zip_city = zip + ' ' + city
203 cntry = partner_record.country_id and partner_record.country_id.name or ''
204 return partner_record.name + "\n" + st + " " + st1 + "\n" + zip_city + "\n" +cntry
206 def info_partner(self, cr, uid, ids, name=None, args=None, context=None):
208 for line in self.browse(cr, uid, ids, context=context):
209 result[line.id] = False
210 if not line.partner_id:
212 result[line.id] = self._get_info_partner(cr, uid, line.partner_id, context=context)
216 def select_by_name(self, cr, uid, ids, name, args, context=None):
217 if not ids: return {}
218 partner_obj = self.pool.get('res.partner')
220 cr.execute("""SELECT pl.id, ml.%s
221 FROM account_move_line ml
222 INNER JOIN payment_line pl
223 ON (ml.id = pl.move_line_id)
224 WHERE pl.id IN %%s"""% self.translate(name),
226 res = dict(cr.fetchall())
228 if name == 'partner_id':
230 for p_id, p_name in partner_obj.name_get(cr, uid,
231 filter(lambda x:x and x != 0,res.values()), context=context):
232 partner_name[p_id] = p_name
235 if id in res and partner_name:
236 res[id] = (res[id],partner_name[res[id]])
238 res[id] = (False,False)
241 res.setdefault(id, (False, ""))
244 def _amount(self, cursor, user, ids, name, args, context=None):
247 currency_obj = self.pool.get('res.currency')
252 for line in self.browse(cursor, user, ids, context=context):
254 ctx['date'] = line.order_id.date_done or time.strftime('%Y-%m-%d')
255 res[line.id] = currency_obj.compute(cursor, user, line.currency.id,
256 line.company_currency.id,
257 line.amount_currency, context=ctx)
260 def _get_currency(self, cr, uid, context=None):
261 user_obj = self.pool.get('res.users')
262 currency_obj = self.pool.get('res.currency')
263 user = user_obj.browse(cr, uid, uid, context=context)
266 return user.company_id.currency_id.id
268 return currency_obj.search(cr, uid, [('rate', '=', 1.0)])[0]
270 def _get_date(self, cr, uid, context=None):
273 payment_order_obj = self.pool.get('payment.order')
276 if context.get('order_id') and context['order_id']:
277 order = payment_order_obj.browse(cr, uid, context['order_id'], context=context)
278 if order.date_prefered == 'fixed':
279 date = order.date_scheduled
281 date = time.strftime('%Y-%m-%d')
284 def _get_ml_inv_ref(self, cr, uid, ids, *a):
286 for id in self.browse(cr, uid, ids):
289 if id.move_line_id.invoice:
290 res[id.id] = id.move_line_id.invoice.id
293 def _get_ml_maturity_date(self, cr, uid, ids, *a):
295 for id in self.browse(cr, uid, ids):
297 res[id.id] = id.move_line_id.date_maturity
302 def _get_ml_created_date(self, cr, uid, ids, *a):
304 for id in self.browse(cr, uid, ids):
306 res[id.id] = id.move_line_id.date_created
312 'name': fields.char('Your Reference', size=64, required=True),
313 '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 ?'"),
314 'communication2': fields.char('Communication 2', size=64, help='The successor message of Communication.'),
315 '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.'),
316 'amount_currency': fields.float('Amount in Partner Currency', digits=(16, 2),
317 required=True, help='Payment amount in the partner currency'),
318 'currency': fields.many2one('res.currency','Partner Currency', required=True),
319 'company_currency': fields.many2one('res.currency', 'Company Currency', readonly=True),
320 'bank_id': fields.many2one('res.partner.bank', 'Destination Bank Account'),
321 'order_id': fields.many2one('payment.order', 'Order', required=True,
322 ondelete='cascade', select=True),
323 'partner_id': fields.many2one('res.partner', string="Partner", required=True, help='The Ordering Customer'),
324 'amount': fields.function(_amount, string='Amount in Company Currency',
326 help='Payment amount in the company currency'),
327 'ml_date_created': fields.function(_get_ml_created_date, string="Effective Date",
328 type='date', help="Invoice Effective Date"),
329 'ml_maturity_date': fields.function(_get_ml_maturity_date, type='date', string='Due Date'),
330 'ml_inv_ref': fields.function(_get_ml_inv_ref, type='many2one', relation='account.invoice', string='Invoice Ref.'),
331 'info_owner': fields.function(info_owner, string="Owner Account", type="text", help='Address of the Main Partner'),
332 'info_partner': fields.function(info_partner, string="Destination Account", type="text", help='Address of the Ordering Customer.'),
333 'date': fields.date('Payment Date', help="If no payment date is specified, the bank will treat this payment line directly"),
334 'create_date': fields.datetime('Created', readonly=True),
335 'state': fields.selection([('normal','Free'), ('structured','Structured')], 'Communication Type', required=True),
336 'bank_statement_line_id': fields.many2one('account.bank.statement.line', 'Bank statement line'),
337 'company_id': fields.related('order_id', 'company_id', type='many2one', relation='res.company', string='Company', store=True, readonly=True),
340 'name': lambda obj, cursor, user, context: obj.pool.get('ir.sequence'
341 ).get(cursor, user, 'payment.line'),
343 'currency': _get_currency,
344 'company_currency': _get_currency,
348 ('name_uniq', 'UNIQUE(name)', 'The payment line name must be unique!'),
351 def onchange_move_line(self, cr, uid, ids, move_line_id, payment_type, date_prefered, date_scheduled, currency=False, company_currency=False, context=None):
353 move_line_obj = self.pool.get('account.move.line')
355 data['amount_currency'] = data['communication'] = data['partner_id'] = data['bank_id'] = data['amount'] = False
358 line = move_line_obj.browse(cr, uid, move_line_id, context=context)
359 data['amount_currency'] = line.amount_to_pay
361 res = self.onchange_amount(cr, uid, ids, data['amount_currency'], currency,
362 company_currency, context)
364 data['amount'] = res['value']['amount']
365 data['partner_id'] = line.partner_id.id
366 temp = line.currency_id and line.currency_id.id or False
369 data['currency'] = line.invoice.currency_id.id
371 data['currency'] = temp
373 # calling onchange of partner and updating data dictionary
374 temp_dict = self.onchange_partner(cr, uid, ids, line.partner_id.id, payment_type)
375 data.update(temp_dict['value'])
377 data['communication'] = line.ref
379 if date_prefered == 'now':
380 #no payment date => immediate payment
382 elif date_prefered == 'due':
383 data['date'] = line.date_maturity
384 elif date_prefered == 'fixed':
385 data['date'] = date_scheduled
386 return {'value': data}
388 def onchange_amount(self, cr, uid, ids, amount, currency, cmpny_currency, context=None):
389 if (not amount) or (not cmpny_currency):
390 return {'value': {'amount': False}}
392 currency_obj = self.pool.get('res.currency')
393 company_amount = currency_obj.compute(cr, uid, currency, cmpny_currency, amount)
394 res['amount'] = company_amount
395 return {'value': res}
397 def onchange_partner(self, cr, uid, ids, partner_id, payment_type, context=None):
399 partner_obj = self.pool.get('res.partner')
400 payment_mode_obj = self.pool.get('payment.mode')
401 data['info_partner'] = data['bank_id'] = False
404 part_obj = partner_obj.browse(cr, uid, partner_id, context=context)
405 partner = part_obj.name or ''
406 data['info_partner'] = self._get_info_partner(cr, uid, part_obj, context=context)
408 if part_obj.bank_ids and payment_type:
409 bank_type = payment_mode_obj.suitable_bank_types(cr, uid, payment_type, context=context)
410 for bank in part_obj.bank_ids:
411 if bank.state in bank_type:
412 data['bank_id'] = bank.id
414 return {'value': data}
416 def fields_get(self, cr, uid, fields=None, context=None):
417 res = super(payment_line, self).fields_get(cr, uid, fields, context)
418 if 'communication2' in res:
419 res['communication2'].setdefault('states', {})
420 res['communication2']['states']['structured'] = [('readonly', True)]
421 res['communication2']['states']['normal'] = [('readonly', False)]
426 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: