[MERGE] Merged Valencia's branch for the fix of variable declaration inside mrp_repai...
[odoo/odoo.git] / addons / mrp_repair / mrp_repair.py
1 # -*- encoding: utf-8 -*-
2 ##############################################################################
3 #
4 #    OpenERP, Open Source Management Solution
5 #    Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>). All Rights Reserved
6 #    $Id$
7 #
8 #    This program is free software: you can redistribute it and/or modify
9 #    it under the terms of the GNU General Public License as published by
10 #    the Free Software Foundation, either version 3 of the License, or
11 #    (at your option) any later version.
12 #
13 #    This program is distributed in the hope that it will be useful,
14 #    but WITHOUT ANY WARRANTY; without even the implied warranty of
15 #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 #    GNU General Public License for more details.
17 #
18 #    You should have received a copy of the GNU General Public License
19 #    along with this program.  If not, see <http://www.gnu.org/licenses/>.
20 #
21 ##############################################################################
22
23 import time
24 from osv import fields,osv
25 import netsvc
26 import mx.DateTime
27 from mx.DateTime import RelativeDateTime, today, DateTime, localtime
28 from tools import config
29 from tools.translate import _
30
31 class mrp_repair(osv.osv):
32     _name = 'mrp.repair'
33     _description = 'Repairs Order'
34
35     def _amount_untaxed(self, cr, uid, ids, field_name, arg, context):
36         res = {}
37         cur_obj=self.pool.get('res.currency')
38         for repair in self.browse(cr, uid, ids):
39             res[repair.id] = 0.0
40             for line in repair.operations:
41                 res[repair.id] += line.price_subtotal
42             for line in repair.fees_lines:
43                 res[repair.id] += line.price_subtotal
44             cur = repair.pricelist_id.currency_id
45             res[repair.id] = cur_obj.round(cr, uid, cur, res[repair.id])
46         return res
47
48     def _amount_tax(self, cr, uid, ids, field_name, arg, context):
49         res = {}
50         cur_obj=self.pool.get('res.currency')
51         for repair in self.browse(cr, uid, ids):
52             val = 0.0
53             cur=repair.pricelist_id.currency_id
54             for line in repair.operations:
55                 if line.to_invoice:
56                     for c in self.pool.get('account.tax').compute(cr, uid, line.tax_id, line.price_unit, line.product_uom_qty, repair.partner_invoice_id.id, line.product_id, repair.partner_id):
57                         val+= c['amount']
58             for line in repair.fees_lines:
59                 if line.to_invoice:
60                     for c in self.pool.get('account.tax').compute(cr, uid, line.tax_id, line.price_unit, line.product_uom_qty, repair.partner_invoice_id.id, line.product_id, repair.partner_id):
61                         val+= c['amount']
62             res[repair.id]=cur_obj.round(cr, uid, cur, val)
63         return res
64
65     def _amount_total(self, cr, uid, ids, field_name, arg, context):
66         res = {}
67         untax = self._amount_untaxed(cr, uid, ids, field_name, arg, context)
68         tax = self._amount_tax(cr, uid, ids, field_name, arg, context)
69         cur_obj=self.pool.get('res.currency')
70         for id in ids:
71             repair=self.browse(cr, uid, [id])[0]
72             cur=repair.pricelist_id.currency_id
73             res[id] = cur_obj.round(cr, uid, cur, untax.get(id, 0.0) + tax.get(id, 0.0))
74         return res
75
76     _columns = {
77         'name' : fields.char('Repair Ref',size=24, required=True),
78         'product_id': fields.many2one('product.product', string='Product to Repair', required=True, readonly=True, states={'draft':[('readonly',False)]}),
79         'partner_id' : fields.many2one('res.partner', 'Partner', select=True, help='This field allow you to choose the parner that will be invoiced and delivered'),
80         'address_id': fields.many2one('res.partner.address', 'Delivery Address', domain="[('partner_id','=',partner_id)]"),
81         'prodlot_id': fields.many2one('stock.production.lot', 'Lot Number', select=True, domain="[('product_id','=',product_id)]"),
82         'state': fields.selection([
83             ('draft','Quotation'),
84             ('confirmed','Confirmed'),
85             ('ready','Ready to Repair'),
86             ('under_repair','Under Repair'),
87             ('2binvoiced','To be Invoiced'),
88             ('invoice_except','Invoice Exception'),
89             ('done','Done'),
90             ('cancel','Cancel')
91             ], 'Repair State', readonly=True, help="Gives the state of the Repair Order"),
92         'location_id': fields.many2one('stock.location', 'Current Location', required=True, select=True, readonly=True, states={'draft':[('readonly',False)]}),
93         'location_dest_id': fields.many2one('stock.location', 'Delivery Location', readonly=True, states={'draft':[('readonly',False)]}),
94         'move_id': fields.many2one('stock.move', 'Move',required=True, domain="[('product_id','=',product_id)]", readonly=True, states={'draft':[('readonly',False)]}),
95         'guarantee_limit': fields.date('Guarantee limit', help="The garantee limit is computed as: last move date + warranty defined on selected product. If the current date is below the garantee limit, each operation and fee you will add will be set as 'not to invoiced' by default. Note that you can change manually afterwards."),
96         'operations' : fields.one2many('mrp.repair.line', 'repair_id', 'Operation Lines', readonly=True, states={'draft':[('readonly',False)]}),
97         'pricelist_id': fields.many2one('product.pricelist', 'Pricelist', help='The pricelist comes from the selected partner, by default.'),
98         'partner_invoice_id':fields.many2one('res.partner.address', 'Invoicing Address',  domain="[('partner_id','=',partner_id)]"),
99         'invoice_method':fields.selection([
100             ("none","No Invoice"),
101             ("b4repair","Before Repair"),
102             ("after_repair","After Repair")
103            ], "Invoice Method",
104             select=True, required=True, states={'draft':[('readonly',False)]}, readonly=True, help='This field allow you to change the workflow of the repair order. If value selected is different from \'No Invoice\', it also allow you to select the pricelist and invoicing address.'),
105         'invoice_id': fields.many2one('account.invoice', 'Invoice', readonly=True),
106         'picking_id': fields.many2one('stock.picking', 'Packing',readonly=True),
107         'fees_lines' : fields.one2many('mrp.repair.fee', 'repair_id', 'Fees Lines', readonly=True, states={'draft':[('readonly',False)]}),
108         'internal_notes' : fields.text('Internal Notes'),
109         'quotation_notes' : fields.text('Quotation Notes'),
110         'deliver_bool': fields.boolean('Deliver', help="Check this box if you want to manage the delivery once the product is repaired. If cheked, it will create a packing with selected product. Note that you can select the locations in the Info tab, if you have the extended view."),
111         'invoiced': fields.boolean('Invoiced', readonly=True),
112         'repaired' : fields.boolean('Repaired', readonly=True),
113         'amount_untaxed': fields.function(_amount_untaxed, method=True, string='Untaxed Amount'),
114         'amount_tax': fields.function(_amount_tax, method=True, string='Taxes'),
115         'amount_total': fields.function(_amount_total, method=True, string='Total'),
116     }
117
118     _defaults = {
119         'state': lambda *a: 'draft',
120         'deliver_bool': lambda *a: True,
121         'name': lambda obj, cr, uid, context: obj.pool.get('ir.sequence').get(cr, uid, 'mrp.repair'),
122         'invoice_method': lambda *a: 'none',
123         'pricelist_id': lambda self, cr, uid,context : self.pool.get('product.pricelist').search(cr,uid,[('type','=','sale')])[0]
124     }
125
126     def copy(self, cr, uid, id, default=None, context=None):
127         if not default:
128             default = {}
129         default.update({
130             'state':'draft',
131             'repaired':False,
132             'invoiced':False,
133             'invoice_id': False,
134             'picking_id': False,
135             'name': self.pool.get('ir.sequence').get(cr, uid, 'mrp.repair'),
136         })
137         return super(mrp_repair, self).copy(cr, uid, id, default, context)
138
139
140     def onchange_product_id(self, cr, uid, ids, product_id=None):
141         return {'value': {
142                     'prodlot_id': False,
143                     'move_id': False,
144                     'guarantee_limit' :False,
145                     'location_id':  False,
146                     'location_dest_id': False,
147                 }
148         }
149
150     def onchange_move_id(self, cr, uid, ids, prod_id=False, move_id=False):
151         data = {}
152         data['value'] = {}
153         if not prod_id:
154             return data
155         if move_id:
156             move =  self.pool.get('stock.move').browse(cr, uid, move_id)
157             product = self.pool.get('product.product').browse(cr, uid, prod_id)
158             date = move.date_planned
159             limit = mx.DateTime.strptime(date, '%Y-%m-%d %H:%M:%S') + RelativeDateTime(months=product.warranty)
160             data['value']['guarantee_limit'] = limit.strftime('%Y-%m-%d')
161             data['value']['location_id'] = move.location_dest_id.id
162             data['value']['location_dest_id'] = move.location_dest_id.id
163             if move.address_id:
164                 data['value']['partner_id'] = move.address_id.partner_id and move.address_id.partner_id.id
165             else:
166                 data['value']['partner_id'] = False
167             data['value']['address_id'] = move.address_id and move.address_id.id
168             d = self.onchange_partner_id(cr, uid, ids, data['value']['partner_id'], data['value']['address_id'])
169             data['value'].update(d['value'])
170         return data
171
172     def button_dummy(self, cr, uid, ids, context=None):
173         return True
174
175     def onchange_partner_id(self, cr, uid, ids, part, address_id):
176         if not part:
177             return {'value': {
178                         'address_id': False,
179                         'partner_invoice_id': False,
180                         'pricelist_id': self.pool.get('product.pricelist').search(cr,uid,[('type','=','sale')])[0]
181                     }
182             }
183         addr = self.pool.get('res.partner').address_get(cr, uid, [part], ['delivery', 'invoice', 'default'])
184         partner = self.pool.get('res.partner').browse(cr, uid, part)
185         pricelist = partner.property_product_pricelist and partner.property_product_pricelist.id or False
186         return {'value': {
187                     'address_id': address_id or addr['delivery'],
188                     'partner_invoice_id': addr['invoice'],
189                     'pricelist_id': pricelist
190                 }
191         }
192
193     def onchange_lot_id(self, cr, uid, ids, lot, product_id):
194         data = {}
195         data['value'] = {
196             'location_id': False,
197             'location_dest_id': False,
198             'move_id': False,
199             'guarantee_limit': False
200         }
201
202         if not lot:
203             return data
204         lot_info = self.pool.get('stock.production.lot').browse(cr, uid, lot)
205         move_ids = self.pool.get('stock.move').search(cr, uid, [('prodlot_id', '=', lot)])
206
207         if not len(move_ids):
208             return data
209
210         def get_last_move(lst_move):
211             while lst_move.move_dest_id and lst_move.move_dest_id.state == 'done':
212                 lst_move = lst_move.move_dest_id
213             return lst_move
214
215         move_id = move_ids[0]
216         move = get_last_move(self.pool.get('stock.move').browse(cr, uid, move_id))
217         data['value']['move_id'] = move.id
218         d = self.onchange_move_id(cr, uid, ids, product_id, move.id)
219         data['value'].update(d['value'])
220         return data
221
222     def action_cancel_draft(self, cr, uid, ids, *args):
223         if not len(ids):
224             return False
225         mrp_line_obj = self.pool.get('mrp.repair.line')
226         for repair in self.browse(cr, uid, ids):
227             mrp_line_obj.write(cr, uid, [l.id for l in repair.operations], {'state': 'draft'})
228         self.write(cr, uid, ids, {'state':'draft'})
229         wf_service = netsvc.LocalService("workflow")
230         for id in ids:
231             wf_service.trg_create(uid, 'mrp.repair', id, cr)
232         return True
233
234     def action_confirm(self, cr, uid, ids, *args):
235         mrp_line_obj = self.pool.get('mrp.repair.line')
236         for o in self.browse(cr, uid, ids):
237             if (o.invoice_method == 'b4repair'):
238                 self.write(cr, uid, [o.id], {'state': '2binvoiced'})
239             else:
240                 self.write(cr, uid, [o.id], {'state': 'confirmed'})
241                 mrp_line_obj.write(cr, uid, [l.id for l in o.operations], {'state': 'confirmed'})
242         return True
243
244     def action_cancel(self, cr, uid, ids, context=None):
245         ok=True
246         mrp_line_obj = self.pool.get('mrp.repair.line')
247         for repair in self.browse(cr, uid, ids):
248             mrp_line_obj.write(cr, uid, [l.id for l in repair.operations], {'state': 'cancel'})
249         self.write(cr,uid,ids,{'state':'cancel'})
250         return True
251
252     def wkf_invoice_create(self, cr, uid, ids, *args):
253         return self.action_invoice_create(cr, uid, ids)
254
255     def action_invoice_create(self, cr, uid, ids, group=False, context=None):
256         res={}
257         invoices_group = {}
258         inv_obj = self.pool.get('account.invoice')
259         invoice_line_obj = self.pool.get('account.invoice.line')
260         repair_fee_obj = self.pool.get('mrp.repair.fee')
261         repair_line_obj = self.pool.get('mrp.repair.line')
262         for repair in self.browse(cr, uid, ids, context=context):
263             res[repair.id]=False
264             if repair.state in ('draft','cancel') or repair.invoice_id:
265                 continue
266             if not (repair.partner_id.id and repair.partner_invoice_id.id):
267                 raise osv.except_osv(_('No partner !'),_('You have to select a partner and invoicing address in the repair form !'))
268             comment=repair.quotation_notes
269             if (repair.invoice_method != 'none'):
270                 if group and repair.partner_invoice_id.id in invoices_group:
271                     inv_id = invoices_group[repair.partner_invoice_id.id]
272                     invoice = inv_obj.browse(cr, uid,inv_id)
273                     invoice_vals = {
274                         'name': invoice.name +', '+repair.name,
275                         'origin': invoice.origin+', '+repair.name,
276                         'comment':(comment and (invoice.comment and invoice.comment+"\n"+comment or comment)) or (invoice.comment and invoice.comment or ''),
277                     }
278                     inv_obj.write(cr, uid, [inv_id],invoice_vals,context=context)
279                 else:
280                     if not repair.partner_id.property_account_receivable:
281                         raise osv.except_osv(_('Error !'), _('No account defined for partner "%s".') % repair.partner_id.name )
282                     account_id = repair.partner_id.property_account_receivable.id
283                     inv = {
284                         'name': repair.name,
285                         'origin':repair.name,
286                         'type': 'out_invoice',
287                         'account_id': account_id,
288                         'partner_id': repair.partner_id.id,
289                         'address_invoice_id': repair.address_id.id,
290                         'currency_id': repair.pricelist_id.currency_id.id,
291                         'comment': repair.quotation_notes,
292                         'fiscal_position': repair.partner_id.property_account_position.id
293                     }
294                     inv_id = inv_obj.create(cr, uid, inv)
295                     invoices_group[repair.partner_invoice_id.id] = inv_id
296                 self.write(cr, uid, repair.id , {'invoiced':True,'invoice_id' : inv_id})
297
298                 for operation in repair.operations:
299                     if operation.to_invoice == True:
300                         if group:
301                             name = repair.name + '-' + operation.name
302                         else:
303                             name = operation.name
304                         
305                         if operation.product_id.property_account_income:
306                             account_id = operation.product_id.property_account_income.id
307                         elif operation.product_id.categ_id.property_account_income_categ:
308                             account_id = operation.product_id.categ_id.property_account_income_categ.id
309                         else:
310                             raise osv.except_osv(_('Error !'), _('No account defined for product "%s".') % operation.product_id.name )
311
312                         invoice_line_id = invoice_line_obj.create(cr, uid, {
313                             'invoice_id': inv_id,
314                             'name': name,
315                             'origin':repair.name,
316                             'account_id': account_id, 
317                             'quantity' : operation.product_uom_qty,
318                             'invoice_line_tax_id': [(6,0,[x.id for x in operation.tax_id])],
319                             'uos_id' : operation.product_uom.id,
320                             'price_unit' : operation.price_unit,
321                             'price_subtotal' : operation.product_uom_qty*operation.price_unit,
322                             'product_id' : operation.product_id and operation.product_id.id or False
323                             })
324                         repair_line_obj.write(cr, uid, [operation.id], {'invoiced':True,'invoice_line_id':invoice_line_id})
325                 for fee in repair.fees_lines:
326                     if fee.to_invoice == True:
327                         if group:
328                             name = repair.name + '-' + fee.name
329                         else:
330                             name = fee.name
331                         if not fee.product_id:
332                             raise osv.except_osv(_('Warning !'), _('No product defined on Fees!'))
333                         if fee.product_id.property_account_income:
334                             account_id = fee.product_id.property_account_income.id
335                         elif fee.product_id.categ_id.property_account_income_categ:
336                             account_id = fee.product_id.categ_id.property_account_income_categ.id
337                         else:
338                             raise osv.except_osv(_('Error !'), _('No account defined for product "%s".') % fee.product_id.name)
339                         invoice_fee_id = invoice_line_obj.create(cr, uid, {
340                             'invoice_id': inv_id,
341                             'name': name,
342                             'origin':repair.name,
343                             'account_id': account_id,
344                             'quantity': fee.product_uom_qty,
345                             'invoice_line_tax_id': [(6,0,[x.id for x in fee.tax_id])],
346                             'uos_id': fee.product_uom.id,
347                             'product_id': fee.product_id and fee.product_id.id or False,
348                             'price_unit': fee.price_unit,
349                             'price_subtotal': fee.product_uom_qty*fee.price_unit
350                             })
351                         repair_fee_obj.write(cr, uid, [fee.id], {'invoiced':True,'invoice_line_id':invoice_fee_id})
352                 res[repair.id]=inv_id
353         #self.action_invoice_end(cr, uid, ids)
354         return res
355
356     def action_repair_ready(self, cr, uid, ids, context=None):
357         self.write(cr, uid, ids, {'state':'ready'})
358         return True
359
360     def action_invoice_cancel(self, cr, uid, ids, context=None):
361         self.write(cr, uid, ids, {'state':'invoice_except'})
362         return True
363
364     def action_repair_start(self, cr, uid, ids, context=None):
365         self.write(cr, uid, ids, {'state':'under_repair'})
366         return True
367
368     def action_invoice_end(self, cr, uid, ids, context=None):
369         for order in self.browse(cr, uid, ids):
370             val = {}
371             if (order.invoice_method=='b4repair'):
372                 val['state'] = 'ready'
373             else:
374                 #val['state'] = 'done'
375                 pass
376             self.write(cr, uid, [order.id], val)
377         return True
378
379     def action_repair_end(self, cr, uid, ids, context=None):
380         for order in self.browse(cr, uid, ids):
381             val = {}
382             val['repaired']=True
383             if (not order.invoiced and order.invoice_method=='after_repair'):
384                 val['state'] = '2binvoiced'
385             elif (not order.invoiced and order.invoice_method=='b4repair'):
386                 val['state'] = 'ready'
387             else:
388                 #val['state'] = 'done'
389                 pass
390             self.write(cr, uid, [order.id], val)
391         return True
392
393     def wkf_repair_done(self, cr, uid, ids, *args):
394         res=self.action_repair_done(cr,uid,ids)
395         return True
396
397     def action_repair_done(self, cr, uid, ids, context=None):
398         res = {}
399         company = self.pool.get('res.users').browse(cr, uid, uid).company_id
400         for repair in self.browse(cr, uid, ids, context=context):
401             for move in repair.operations:
402                 move_id = self.pool.get('stock.move').create(cr, uid, {
403                     'name': move.name,
404                     'product_id': move.product_id.id,
405                     'product_qty': move.product_uom_qty,
406                     'product_uom': move.product_uom.id,
407                     'address_id': repair.address_id and repair.address_id.id or False,
408                     'location_id': move.location_id.id,
409                     'location_dest_id': move.location_dest_id.id,
410                     'tracking_id': False,
411                     'state': 'done',
412                 })
413                 self.pool.get('mrp.repair.line').write(cr, uid, [move.id], {'move_id': move_id})
414
415             if repair.deliver_bool:
416                 picking = self.pool.get('stock.picking').create(cr, uid, {
417                     'origin': repair.name,
418                     'state': 'draft',
419                     'move_type': 'one',
420                     'address_id': repair.address_id and repair.address_id.id or False,
421                     'note': repair.internal_notes,
422                     'invoice_state': 'none',
423                     'type': 'out',
424                 })
425                 wf_service = netsvc.LocalService("workflow")
426                 wf_service.trg_validate(uid, 'stock.picking', picking, 'button_confirm', cr)
427
428                 move_id = self.pool.get('stock.move').create(cr, uid, {
429                     'name': repair.name,
430                     'picking_id': picking,
431                     'product_id': repair.product_id.id,
432                     'product_qty': 1.0,
433                     'product_uom': repair.product_id.uom_id.id,
434                     #'product_uos_qty': line.product_uom_qty,
435                     #'product_uos': line.product_uom.id,
436                     'prodlot_id': repair.prodlot_id and repair.prodlot_id.id or False,
437                     'address_id': repair.address_id and repair.address_id.id or False,
438                     'location_id': repair.location_id.id,
439                     'location_dest_id': repair.location_dest_id.id,
440                     'tracking_id': False,
441                     'state': 'assigned',    # FIXME done ?
442                 })
443                 self.write(cr, uid, [repair.id], {'state':'done', 'picking_id':picking})
444                 res[repair.id] = picking
445             else:
446                 self.write(cr, uid, [repair.id], {'state':'done'})
447         return res
448
449
450 mrp_repair()
451
452
453 class ProductChangeMixin(object):
454     def product_id_change(self, cr, uid, ids, pricelist, product, uom=False, product_uom_qty=0, partner_id=False, guarantee_limit=False):
455         result = {}
456         warning = {}
457
458         if not product_uom_qty:
459             product_uom_qty = 1
460         result['product_uom_qty'] = product_uom_qty
461
462         if product:
463             product_obj =  self.pool.get('product.product').browse(cr, uid, product)
464             if partner_id:
465                 partner = self.pool.get('res.partner').browse(cr, uid, partner_id)
466                 result['tax_id'] = self.pool.get('account.fiscal.position').map_tax(cr, uid, partner.property_account_position, product_obj.taxes_id)
467
468             result['name'] = product_obj.partner_ref
469             result['product_uom'] = product_obj.uom_id and product_obj.uom_id.id or False
470             if not pricelist:
471                 warning={
472                     'title':'No Pricelist !',
473                     'message':
474                         'You have to select a pricelist in the Repair form !\n'
475                         'Please set one before choosing a product.'
476                 }
477             else:
478                 price = self.pool.get('product.pricelist').price_get(cr, uid, [pricelist],
479                             product, product_uom_qty, partner_id, {'uom': uom,})[pricelist]
480
481                 if price is False:
482                      warning={
483                         'title':'No valid pricelist line found !',
484                         'message':
485                             "Couldn't find a pricelist line matching this product and quantity.\n"
486                             "You have to change either the product, the quantity or the pricelist."
487                     }
488                 else:
489                     result.update({'price_unit': price, 'price_subtotal' :price*product_uom_qty})
490
491         return {'value': result, 'warning': warning}
492
493
494 class mrp_repair_line(osv.osv, ProductChangeMixin):
495     _name = 'mrp.repair.line'
496     _description = 'Repair Operations Lines'
497
498     def copy_data(self, cr, uid, id, default=None, context=None):
499         if not default: default = {}
500         default.update( {'invoice_line_id':False,'move_id':False,'invoiced':False,'state':'draft'})
501         return super(mrp_repair_line, self).copy_data(cr, uid, id, default, context)
502
503     def _amount_line(self, cr, uid, ids, field_name, arg, context):
504         res = {}
505         cur_obj=self.pool.get('res.currency')
506         for line in self.browse(cr, uid, ids):
507             res[line.id] = line.to_invoice and line.price_unit * line.product_uom_qty or 0
508             cur = line.repair_id.pricelist_id.currency_id
509             res[line.id] = cur_obj.round(cr, uid, cur, res[line.id])
510         return res
511
512     _columns = {
513         'name' : fields.char('Description',size=64,required=True),
514         'repair_id': fields.many2one('mrp.repair', 'Repair Order Ref',ondelete='cascade', select=True),
515         'type': fields.selection([('add','Add'),('remove','Remove')],'Type', required=True),
516         'to_invoice': fields.boolean('To Invoice'),
517         'product_id': fields.many2one('product.product', 'Product', domain=[('sale_ok','=',True)], required=True),
518         'invoiced': fields.boolean('Invoiced',readonly=True),
519         'price_unit': fields.float('Unit Price', required=True, digits=(16, int(config['price_accuracy']))),
520         'price_subtotal': fields.function(_amount_line, method=True, string='Subtotal',digits=(16, int(config['price_accuracy']))),
521         'tax_id': fields.many2many('account.tax', 'repair_operation_line_tax', 'repair_operation_line_id', 'tax_id', 'Taxes'),
522         'product_uom_qty': fields.float('Quantity (UoM)', digits=(16,2), required=True),
523         'product_uom': fields.many2one('product.uom', 'Product UoM', required=True),
524         'invoice_line_id': fields.many2one('account.invoice.line', 'Invoice Line', readonly=True),
525         'location_id': fields.many2one('stock.location', 'Source Location', required=True, select=True),
526         'location_dest_id': fields.many2one('stock.location', 'Dest. Location', required=True, select=True),
527         'move_id': fields.many2one('stock.move', 'Inventory Move', readonly=True),
528         'state': fields.selection([('draft','Draft'),('confirmed','Confirmed'),('done','Done'),('cancel','Canceled')], 'Status', required=True, readonly=True),
529     }
530     _defaults = {
531      'state': lambda *a: 'draft',
532      'product_uom_qty':lambda *a:1,
533     }
534
535     def onchange_operation_type(self, cr, uid, ids, type, guarantee_limit):
536         if not type:
537             return {'value': {
538                         'location_id': False,
539                         'location_dest_id': False
540                     }
541             }
542         produc_id = self.pool.get('stock.location').search(cr, uid, [('name','=','Production')])[0]
543         if type == 'add':
544             stock_id = self.pool.get('stock.location').search(cr, uid, [('name','=','Stock')])[0]
545             to_invoice=False
546             if guarantee_limit and today() > mx.DateTime.strptime(guarantee_limit, '%Y-%m-%d'):
547                 to_invoice=True
548             return {'value': {
549                         'to_invoice': to_invoice,
550                         'location_id': stock_id,
551                         'location_dest_id' : produc_id
552                     }
553             }
554         return {'value': {
555                 'to_invoice': False,
556                 'location_id': produc_id,
557                 'location_dest_id':False
558             }
559         }
560
561 mrp_repair_line()
562
563 class mrp_repair_fee(osv.osv, ProductChangeMixin):
564     _name = 'mrp.repair.fee'
565     _description = 'Repair Fees line'
566     def copy_data(self, cr, uid, id, default=None, context=None):
567         if not default: default = {}
568         default.update( {'invoice_line_id':False,'invoiced':False})
569         return super(mrp_repair_fee, self).copy_data(cr, uid, id, default, context)
570     def _amount_line(self, cr, uid, ids, field_name, arg, context):
571         res = {}
572         cur_obj=self.pool.get('res.currency')
573         for line in self.browse(cr, uid, ids):
574             res[line.id] = line.to_invoice and line.price_unit * line.product_uom_qty or 0
575             cur = line.repair_id.pricelist_id.currency_id
576             res[line.id] = cur_obj.round(cr, uid, cur, res[line.id])
577         return res
578
579     _columns = {
580         'repair_id': fields.many2one('mrp.repair', 'Repair Order Ref', required=True, ondelete='cascade', select=True),
581         'name': fields.char('Description', size=64, select=True,required=True),
582         'product_id': fields.many2one('product.product', 'Product'),
583         'product_uom_qty': fields.float('Quantity', digits=(16,2), required=True),
584         'price_unit': fields.float('Unit Price', required=True),
585         'product_uom': fields.many2one('product.uom', 'Product UoM', required=True),
586         'price_subtotal': fields.function(_amount_line, method=True, string='Subtotal',digits=(16, int(config['price_accuracy']))),
587         'tax_id': fields.many2many('account.tax', 'repair_fee_line_tax', 'repair_fee_line_id', 'tax_id', 'Taxes'),
588         'invoice_line_id': fields.many2one('account.invoice.line', 'Invoice Line', readonly=True),
589         'to_invoice': fields.boolean('To Invoice'),
590         'invoiced': fields.boolean('Invoiced',readonly=True),
591     }
592     _defaults = {
593         'to_invoice': lambda *a: True,
594     }
595
596 mrp_repair_fee()
597 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: