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