bc5c608426b70b906eab09867bb2fefd10ec80c5
[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         for repair in self.browse(cr, uid, ids, context=context):
259             res[repair.id]=False
260             if repair.state in ('draft','cancel') or repair.invoice_id:
261                 continue
262             if not (repair.partner_id.id and repair.partner_invoice_id.id):
263                 raise osv.except_osv(_('No partner !'),_('You have to select a partner and invoicing address in the repair form !'))
264             comment=repair.quotation_notes
265             if (repair.invoice_method != 'none'):
266                 if group and repair.partner_invoice_id.id in invoices_group:
267                     inv_id= invoices_group[repair.partner_invoice_id.id]
268                     invoice=invoice_obj.browse(cr, uid,inv_id)
269                     invoice_vals = {
270                         'name': invoice.name +', '+repair.name,
271                         'origin': invoice.origin+', '+repair.name,
272                         'comment':(comment and (invoice.comment and invoice.comment+"\n"+comment or comment)) or (invoice.comment and invoice.comment or ''),
273                     }
274                     invoice_obj.write(cr, uid, [inv_id],invoice_vals,context=context)
275                 else:
276                     if not repair.partner_id.property_account_receivable:
277                         raise osv.except_osv(_('Error !'), _('No account defined for partner "%s".') % repair.partner_id.name )
278                     account_id = repair.partner_id.property_account_receivable.id
279                     inv = {
280                         'name': repair.name,
281                         'origin':repair.name,
282                         'type': 'out_invoice',
283                         'account_id': account_id,
284                         'partner_id': repair.partner_id.id,
285                         'address_invoice_id': repair.address_id.id,
286                         'currency_id': repair.pricelist_id.currency_id.id,
287                         'comment': repair.quotation_notes,
288                         'fiscal_position': repair.partner_id.property_account_position.id
289                     }
290                     inv_obj = self.pool.get('account.invoice')
291                     inv_id = inv_obj.create(cr, uid, inv)
292                     invoices_group[repair.partner_invoice_id.id] = inv_id
293                 self.write(cr, uid, repair.id , {'invoiced':True,'invoice_id' : inv_id})
294
295                 for operation in repair.operations:
296                     if operation.to_invoice == True:
297                         if group:
298                             name = repair.name + '-' + operation.name
299                         else:
300                             name = operation.name
301                         
302                         if operation.product_id.property_account_income:
303                             account_id = operation.product_id.property_account_income.id
304                         elif operation.product_id.categ_id.property_account_income_categ:
305                             account_id = operation.product_id.categ_id.property_account_income_categ.id
306                         else:
307                             raise osv.except_osv(_('Error !'), _('No account defined for product "%s".') % operation.product_id.name )
308
309                         invoice_line_id = self.pool.get('account.invoice.line').create(cr, uid, {
310                             'invoice_id': inv_id,
311                             'name': name,
312                             'origin':repair.name,
313                             'account_id': account_id, 
314                             'quantity' : operation.product_uom_qty,
315                             'invoice_line_tax_id': [(6,0,[x.id for x in operation.tax_id])],
316                             'uos_id' : operation.product_uom.id,
317                             'price_unit' : operation.price_unit,
318                             'price_subtotal' : operation.product_uom_qty*operation.price_unit,
319                             'product_id' : operation.product_id and operation.product_id.id or False
320                             })
321                         self.pool.get('mrp.repair.line').write(cr, uid, [operation.id], {'invoiced':True,'invoice_line_id':invoice_line_id})
322                 for fee in repair.fees_lines:
323                     if fee.to_invoice == True:
324                         if group:
325                             name = repair.name + '-' + fee.name
326                         else:
327                             name = fee.name
328                         if not fee.product_id:
329                             raise osv.except_osv(_('Warning !'), _('No product defined on Fees!'))
330                         if fee.product_id.property_account_income:
331                             account_id = fee.product_id.property_account_income.id
332                         elif fee.product_id.categ_id.property_account_income_categ:
333                             account_id = fee.product_id.categ_id.property_account_income_categ.id
334                         else:
335                             raise osv.except_osv(_('Error !'), _('No account defined for product "%s".') % fee.product_id.name)
336                         invoice_fee_id = self.pool.get('account.invoice.line').create(cr, uid, {
337                             'invoice_id': inv_id,
338                             'name': name,
339                             'origin':repair.name,
340                             'account_id': account_id,
341                             'quantity': fee.product_uom_qty,
342                             'invoice_line_tax_id': [(6,0,[x.id for x in fee.tax_id])],
343                             'uos_id': fee.product_uom.id,
344                             'product_id': fee.product_id and fee.product_id.id or False,
345                             'price_unit': fee.price_unit,
346                             'price_subtotal': fee.product_uom_qty*fee.price_unit
347                             })
348                         self.pool.get('mrp.repair.fee').write(cr, uid, [fee.id], {'invoiced':True,'invoice_line_id':invoice_fee_id})
349                 res[repair.id]=inv_id
350         #self.action_invoice_end(cr, uid, ids)
351         return res
352
353     def action_repair_ready(self, cr, uid, ids, context=None):
354         self.write(cr, uid, ids, {'state':'ready'})
355         return True
356
357     def action_invoice_cancel(self, cr, uid, ids, context=None):
358         self.write(cr, uid, ids, {'state':'invoice_except'})
359         return True
360
361     def action_repair_start(self, cr, uid, ids, context=None):
362         self.write(cr, uid, ids, {'state':'under_repair'})
363         return True
364
365     def action_invoice_end(self, cr, uid, ids, context=None):
366         for order in self.browse(cr, uid, ids):
367             val = {}
368             if (order.invoice_method=='b4repair'):
369                 val['state'] = 'ready'
370             else:
371                 #val['state'] = 'done'
372                 pass
373             self.write(cr, uid, [order.id], val)
374         return True
375
376     def action_repair_end(self, cr, uid, ids, context=None):
377         for order in self.browse(cr, uid, ids):
378             val = {}
379             val['repaired']=True
380             if (not order.invoiced and order.invoice_method=='after_repair'):
381                 val['state'] = '2binvoiced'
382             elif (not order.invoiced and order.invoice_method=='b4repair'):
383                 val['state'] = 'ready'
384             else:
385                 #val['state'] = 'done'
386                 pass
387             self.write(cr, uid, [order.id], val)
388         return True
389
390     def wkf_repair_done(self, cr, uid, ids, *args):
391         res=self.action_repair_done(cr,uid,ids)
392         return True
393
394     def action_repair_done(self, cr, uid, ids, context=None):
395         res = {}
396         company = self.pool.get('res.users').browse(cr, uid, uid).company_id
397         for repair in self.browse(cr, uid, ids, context=context):
398             for move in repair.operations:
399                 move_id = self.pool.get('stock.move').create(cr, uid, {
400                     'name': move.name,
401                     'product_id': move.product_id.id,
402                     'product_qty': move.product_uom_qty,
403                     'product_uom': move.product_uom.id,
404                     'address_id': repair.address_id and repair.address_id.id or False,
405                     'location_id': move.location_id.id,
406                     'location_dest_id': move.location_dest_id.id,
407                     'tracking_id': False,
408                     'state': 'done',
409                 })
410                 self.pool.get('mrp.repair.line').write(cr, uid, [move.id], {'move_id': move_id})
411
412             if repair.deliver_bool:
413                 picking = self.pool.get('stock.picking').create(cr, uid, {
414                     'origin': repair.name,
415                     'state': 'draft',
416                     'move_type': 'one',
417                     'address_id': repair.address_id and repair.address_id.id or False,
418                     'note': repair.internal_notes,
419                     'invoice_state': 'none',
420                     'type': 'out',
421                 })
422                 wf_service = netsvc.LocalService("workflow")
423                 wf_service.trg_validate(uid, 'stock.picking', picking, 'button_confirm', cr)
424
425                 move_id = self.pool.get('stock.move').create(cr, uid, {
426                     'name': repair.name,
427                     'picking_id': picking,
428                     'product_id': repair.product_id.id,
429                     'product_qty': 1.0,
430                     'product_uom': repair.product_id.uom_id.id,
431                     #'product_uos_qty': line.product_uom_qty,
432                     #'product_uos': line.product_uom.id,
433                     'prodlot_id': repair.prodlot_id and repair.prodlot_id.id or False,
434                     'address_id': repair.address_id and repair.address_id.id or False,
435                     'location_id': repair.location_id.id,
436                     'location_dest_id': repair.location_dest_id.id,
437                     'tracking_id': False,
438                     'state': 'assigned',    # FIXME done ?
439                 })
440                 self.write(cr, uid, [repair.id], {'state':'done', 'picking_id':picking})
441                 res[repair.id] = picking
442             else:
443                 self.write(cr, uid, [repair.id], {'state':'done'})
444         return res
445
446
447 mrp_repair()
448
449
450 class ProductChangeMixin(object):
451     def product_id_change(self, cr, uid, ids, pricelist, product, uom=False, product_uom_qty=0, partner_id=False, guarantee_limit=False):
452         result = {}
453         warning = {}
454
455         if not product_uom_qty:
456             product_uom_qty = 1
457         result['product_uom_qty'] = product_uom_qty
458
459         if product:
460             product_obj =  self.pool.get('product.product').browse(cr, uid, product)
461             if partner_id:
462                 partner = self.pool.get('res.partner').browse(cr, uid, partner_id)
463                 result['tax_id'] = self.pool.get('account.fiscal.position').map_tax(cr, uid, partner.property_account_position, product_obj.taxes_id)
464
465             result['name'] = product_obj.partner_ref
466             result['product_uom'] = product_obj.uom_id and product_obj.uom_id.id or False
467             if not pricelist:
468                 warning={
469                     'title':'No Pricelist !',
470                     'message':
471                         'You have to select a pricelist in the Repair form !\n'
472                         'Please set one before choosing a product.'
473                 }
474             else:
475                 price = self.pool.get('product.pricelist').price_get(cr, uid, [pricelist],
476                             product, product_uom_qty, partner_id, {'uom': uom,})[pricelist]
477
478                 if price is False:
479                      warning={
480                         'title':'No valid pricelist line found !',
481                         'message':
482                             "Couldn't find a pricelist line matching this product and quantity.\n"
483                             "You have to change either the product, the quantity or the pricelist."
484                     }
485                 else:
486                     result.update({'price_unit': price, 'price_subtotal' :price*product_uom_qty})
487
488         return {'value': result, 'warning': warning}
489
490
491 class mrp_repair_line(osv.osv, ProductChangeMixin):
492     _name = 'mrp.repair.line'
493     _description = 'Repair Operations Lines'
494
495     def copy_data(self, cr, uid, id, default=None, context=None):
496         if not default: default = {}
497         default.update( {'invoice_line_id':False,'move_id':False,'invoiced':False,'state':'draft'})
498         return super(mrp_repair_line, self).copy_data(cr, uid, id, default, context)
499
500     def _amount_line(self, cr, uid, ids, field_name, arg, context):
501         res = {}
502         cur_obj=self.pool.get('res.currency')
503         for line in self.browse(cr, uid, ids):
504             res[line.id] = line.to_invoice and line.price_unit * line.product_uom_qty or 0
505             cur = line.repair_id.pricelist_id.currency_id
506             res[line.id] = cur_obj.round(cr, uid, cur, res[line.id])
507         return res
508
509     _columns = {
510         'name' : fields.char('Description',size=64,required=True),
511         'repair_id': fields.many2one('mrp.repair', 'Repair Order Ref',ondelete='cascade', select=True),
512         'type': fields.selection([('add','Add'),('remove','Remove')],'Type', required=True),
513         'to_invoice': fields.boolean('To Invoice'),
514         'product_id': fields.many2one('product.product', 'Product', domain=[('sale_ok','=',True)], required=True),
515         'invoiced': fields.boolean('Invoiced',readonly=True),
516         'price_unit': fields.float('Unit Price', required=True, digits=(16, int(config['price_accuracy']))),
517         'price_subtotal': fields.function(_amount_line, method=True, string='Subtotal',digits=(16, int(config['price_accuracy']))),
518         'tax_id': fields.many2many('account.tax', 'repair_operation_line_tax', 'repair_operation_line_id', 'tax_id', 'Taxes'),
519         'product_uom_qty': fields.float('Quantity (UoM)', digits=(16,2), required=True),
520         'product_uom': fields.many2one('product.uom', 'Product UoM', required=True),
521         'invoice_line_id': fields.many2one('account.invoice.line', 'Invoice Line', readonly=True),
522         'location_id': fields.many2one('stock.location', 'Source Location', required=True, select=True),
523         'location_dest_id': fields.many2one('stock.location', 'Dest. Location', required=True, select=True),
524         'move_id': fields.many2one('stock.move', 'Inventory Move', readonly=True),
525         'state': fields.selection([('draft','Draft'),('confirmed','Confirmed'),('done','Done'),('cancel','Canceled')], 'Status', required=True, readonly=True),
526     }
527     _defaults = {
528      'state': lambda *a: 'draft',
529      'product_uom_qty':lambda *a:1,
530     }
531
532     def onchange_operation_type(self, cr, uid, ids, type, guarantee_limit):
533         if not type:
534             return {'value': {
535                         'location_id': False,
536                         'location_dest_id': False
537                     }
538             }
539         produc_id = self.pool.get('stock.location').search(cr, uid, [('name','=','Production')])[0]
540         if type == 'add':
541             stock_id = self.pool.get('stock.location').search(cr, uid, [('name','=','Stock')])[0]
542             to_invoice=False
543             if guarantee_limit and today() > mx.DateTime.strptime(guarantee_limit, '%Y-%m-%d'):
544                 to_invoice=True
545             return {'value': {
546                         'to_invoice': to_invoice,
547                         'location_id': stock_id,
548                         'location_dest_id' : produc_id
549                     }
550             }
551         return {'value': {
552                 'to_invoice': False,
553                 'location_id': produc_id,
554                 'location_dest_id':False
555             }
556         }
557
558 mrp_repair_line()
559
560 class mrp_repair_fee(osv.osv, ProductChangeMixin):
561     _name = 'mrp.repair.fee'
562     _description = 'Repair Fees line'
563     def copy_data(self, cr, uid, id, default=None, context=None):
564         if not default: default = {}
565         default.update( {'invoice_line_id':False,'invoiced':False})
566         return super(mrp_repair_fee, self).copy_data(cr, uid, id, default, context)
567     def _amount_line(self, cr, uid, ids, field_name, arg, context):
568         res = {}
569         cur_obj=self.pool.get('res.currency')
570         for line in self.browse(cr, uid, ids):
571             res[line.id] = line.to_invoice and line.price_unit * line.product_uom_qty or 0
572             cur = line.repair_id.pricelist_id.currency_id
573             res[line.id] = cur_obj.round(cr, uid, cur, res[line.id])
574         return res
575
576     _columns = {
577         'repair_id': fields.many2one('mrp.repair', 'Repair Order Ref', required=True, ondelete='cascade', select=True),
578         'name': fields.char('Description', size=64, select=True,required=True),
579         'product_id': fields.many2one('product.product', 'Product'),
580         'product_uom_qty': fields.float('Quantity', digits=(16,2), required=True),
581         'price_unit': fields.float('Unit Price', required=True),
582         'product_uom': fields.many2one('product.uom', 'Product UoM', required=True),
583         'price_subtotal': fields.function(_amount_line, method=True, string='Subtotal',digits=(16, int(config['price_accuracy']))),
584         'tax_id': fields.many2many('account.tax', 'repair_fee_line_tax', 'repair_fee_line_id', 'tax_id', 'Taxes'),
585         'invoice_line_id': fields.many2one('account.invoice.line', 'Invoice Line', readonly=True),
586         'to_invoice': fields.boolean('To Invoice'),
587         'invoiced': fields.boolean('Invoiced',readonly=True),
588     }
589     _defaults = {
590         'to_invoice': lambda *a: True,
591     }
592
593 mrp_repair_fee()
594 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: