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
27 _logger = logging.getLogger(__name__)
29 class payment_mode(osv.osv):
31 _description= 'Payment Mode'
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,),
43 'company_id': lambda self,cr,uid,c: self.pool.get('res.users').browse(cr, uid, uid, c).company_id.id
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"""
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()]
57 def onchange_company_id (self, cr, uid, ids, company_id=False, context=None):
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}
66 class payment_order(osv.osv):
67 _name = 'payment.order'
68 _description = 'Payment Order'
69 _rec_name = 'reference'
73 def get_wizard(self, type):
74 _logger.warning("No wizard found for the payment type '%s'.", type)
77 def _total(self, cursor, user, ids, name, args, context=None):
81 for order in self.browse(cursor, user, ids, context=context):
83 res[order.id] = reduce(lambda x, y: x + y.amount, order.line_ids, 0.0)
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([
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([
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),
112 'user_id': lambda self,cr,uid,context: uid,
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'),
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)
124 def action_open(self, cr, uid, ids, *args):
125 ir_seq_obj = self.pool.get('ir.sequence')
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})
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]])
138 def copy(self, cr, uid, id, default=None, context=None):
144 'reference': self.pool.get('ir.sequence').get(cr, uid, 'payment.order')
146 return super(payment_order, self).copy(cr, uid, id, default, context=context)
148 def write(self, cr, uid, ids, vals, context=None):
151 payment_line_obj = self.pool.get('payment.line')
152 payment_line_ids = []
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)
173 class payment_line(osv.osv):
174 _name = 'payment.line'
175 _description = 'Payment Line'
177 def translate(self, orig):
179 "due_date": "date_maturity",
180 "reference": "ref"}.get(orig, orig)
182 def info_owner(self, cr, uid, ids, name=None, args=None, context=None):
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)
189 def _get_info_partner(self,cr, uid, partner_record, context=None):
190 if not partner_record:
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
200 def info_partner(self, cr, uid, ids, name=None, args=None, context=None):
202 for line in self.browse(cr, uid, ids, context=context):
203 result[line.id] = False
204 if not line.partner_id:
206 result[line.id] = self._get_info_partner(cr, uid, line.partner_id, context=context)
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')
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),
220 res = dict(cr.fetchall())
222 if name == 'partner_id':
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
229 if id in res and partner_name:
230 res[id] = (res[id],partner_name[res[id]])
232 res[id] = (False,False)
235 res.setdefault(id, (False, ""))
238 def _amount(self, cursor, user, ids, name, args, context=None):
241 currency_obj = self.pool.get('res.currency')
246 for line in self.browse(cursor, user, ids, context=context):
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)
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)
260 return user.company_id.currency_id.id
262 return currency_obj.search(cr, uid, [('rate', '=', 1.0)])[0]
264 def _get_date(self, cr, uid, context=None):
267 payment_order_obj = self.pool.get('payment.order')
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
275 date = time.strftime('%Y-%m-%d')
278 def _get_ml_inv_ref(self, cr, uid, ids, *a):
280 for id in self.browse(cr, uid, ids):
283 if id.move_line_id.invoice:
284 res[id.id] = id.move_line_id.invoice.id
287 def _get_ml_maturity_date(self, cr, uid, ids, *a):
289 for id in self.browse(cr, uid, ids):
291 res[id.id] = id.move_line_id.date_maturity
296 def _get_ml_created_date(self, cr, uid, ids, *a):
298 for id in self.browse(cr, uid, ids):
300 res[id.id] = id.move_line_id.date_created
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',
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),
334 'name': lambda obj, cursor, user, context: obj.pool.get('ir.sequence'
335 ).get(cursor, user, 'payment.line'),
337 'currency': _get_currency,
338 'company_currency': _get_currency,
342 ('name_uniq', 'UNIQUE(name)', 'The payment line name must be unique!'),
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):
347 move_line_obj = self.pool.get('account.move.line')
349 data['amount_currency'] = data['communication'] = data['partner_id'] = data['bank_id'] = data['amount'] = False
352 line = move_line_obj.browse(cr, uid, move_line_id, context=context)
353 data['amount_currency'] = line.amount_residual_currency
355 res = self.onchange_amount(cr, uid, ids, data['amount_currency'], currency,
356 company_currency, context)
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
363 data['currency'] = line.invoice.currency_id.id
365 data['currency'] = temp
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'])
371 data['communication'] = line.ref
373 if date_prefered == 'now':
374 #no payment date => immediate payment
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}
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}}
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}
391 def onchange_partner(self, cr, uid, ids, partner_id, payment_type, context=None):
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
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)
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
408 return {'value': data}
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)]
419 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: