Override Unlink function to delete only Draft Procurement,Stock Moves,Stock Picking...
[odoo/odoo.git] / addons / purchase / purchase.py
1 # -*- encoding: utf-8 -*-
2 ##############################################################################
3 #
4 # Copyright (c) 2004-2008 TINY SPRL. (http://tiny.be) All Rights Reserved.
5 #
6 # $Id$
7 #
8 # WARNING: This program as such is intended to be used by professional
9 # programmers who take the whole responsability of assessing all potential
10 # consequences resulting from its eventual inadequacies and bugs
11 # End users who are looking for a ready-to-use solution with commercial
12 # garantees and support are strongly adviced to contract a Free Software
13 # Service Company
14 #
15 # This program is Free Software; you can redistribute it and/or
16 # modify it under the terms of the GNU General Public License
17 # as published by the Free Software Foundation; either version 2
18 # of the License, or (at your option) any later version.
19 #
20 # This program is distributed in the hope that it will be useful,
21 # but WITHOUT ANY WARRANTY; without even the implied warranty of
22 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23 # GNU General Public License for more details.
24 #
25 # You should have received a copy of the GNU General Public License
26 # along with this program; if not, write to the Free Software
27 # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
28 #
29 ##############################################################################
30
31 from osv import fields
32 from osv import osv
33 import time
34 import netsvc
35
36 import ir
37 from mx import DateTime
38 import pooler
39 from tools import config
40 from tools.translate import _
41
42 #
43 # Model definition
44 #
45 class purchase_order(osv.osv):
46     def _calc_amount(self, cr, uid, ids, prop, unknow_none, unknow_dict):
47         res = {}
48         for order in self.browse(cr, uid, ids):
49             res[order.id] = 0
50             for oline in order.order_line:
51                 res[order.id] += oline.price_unit * oline.product_qty
52         return res
53
54     def _amount_untaxed(self, cr, uid, ids, field_name, arg, context):
55         res = {}
56         cur_obj=self.pool.get('res.currency')
57         for purchase in self.browse(cr, uid, ids):
58             res[purchase.id] = 0.0
59             for line in purchase.order_line:
60                 res[purchase.id] += line.price_subtotal
61             cur = purchase.pricelist_id.currency_id
62             res[purchase.id] = cur_obj.round(cr, uid, cur, res[purchase.id])
63
64         return res
65
66     def _amount_tax(self, cr, uid, ids, field_name, arg, context):
67         res = {}
68         cur_obj=self.pool.get('res.currency')
69         for order in self.browse(cr, uid, ids):
70             val = 0.0
71             cur=order.pricelist_id.currency_id
72             for line in order.order_line:
73                 for c in self.pool.get('account.tax').compute(cr, uid, line.taxes_id, line.price_unit, line.product_qty, order.partner_address_id.id, line.product_id, order.partner_id):
74                     val+= cur_obj.round(cr, uid, cur, c['amount'])
75             res[order.id]=cur_obj.round(cr, uid, cur, val)
76         return res
77
78     def _amount_total(self, cr, uid, ids, field_name, arg, context):
79         res = {}
80         untax = self._amount_untaxed(cr, uid, ids, field_name, arg, context)
81         tax = self._amount_tax(cr, uid, ids, field_name, arg, context)
82         cur_obj=self.pool.get('res.currency')
83         for id in ids:
84             order=self.browse(cr, uid, [id])[0]
85             cur=order.pricelist_id.currency_id
86             res[id] = cur_obj.round(cr, uid, cur, untax.get(id, 0.0) + tax.get(id, 0.0))
87         return res
88
89     def _set_minimum_planned_date(self, cr, uid, ids, name, value, arg, context):
90         if not value: return False
91         if type(ids)!=type([]):
92             ids=[ids]
93         for po in self.browse(cr, uid, ids, context):
94             cr.execute("""update purchase_order_line set
95                     date_planned=%s
96                 where
97                     order_id=%d and
98                     (date_planned=%s or date_planned<%s)""", (value,po.id,po.minimum_planned_date,value))
99         return True
100
101     def _minimum_planned_date(self, cr, uid, ids, field_name, arg, context):
102         res={}
103         purchase_obj=self.browse(cr, uid, ids, context=context)
104         for purchase in purchase_obj:
105             res[purchase.id] = False
106             if purchase.order_line:
107                 min_date=purchase.order_line[0].date_planned
108                 for line in purchase.order_line:
109                     if line.date_planned < min_date:
110                         min_date=line.date_planned
111                 res[purchase.id]=min_date
112         return res
113
114     def _invoiced_rate(self, cursor, user, ids, name, arg, context=None):
115         res = {}
116         for purchase in self.browse(cursor, user, ids, context=context):
117             tot = 0.0
118             if purchase.invoice_id and purchase.invoice_id.state not in ('draft','cancel'):
119                 tot += purchase.invoice_id.amount_untaxed
120             if purchase.amount_untaxed:
121                 res[purchase.id] = tot * 100.0 / purchase.amount_untaxed
122             else:
123                 res[purchase.id] = 0.0
124         return res
125
126     def _shipped_rate(self, cr, uid, ids, name, arg, context=None):
127         if not ids: return {}
128         res = {}
129         for id in ids:
130             res[id] = [0.0,0.0]
131         cr.execute('''SELECT
132                 p.purchase_id,sum(m.product_qty), m.state
133             FROM
134                 stock_move m
135             LEFT JOIN
136                 stock_picking p on (p.id=m.picking_id)
137             WHERE
138                 p.purchase_id in ('''+','.join(map(str,ids))+''')
139             GROUP BY m.state, p.purchase_id''')
140         for oid,nbr,state in cr.fetchall():
141             if state=='cancel':
142                 continue
143             if state=='done':
144                 res[oid][0] += nbr or 0.0
145                 res[oid][1] += nbr or 0.0
146             else:
147                 res[oid][1] += nbr or 0.0
148         for r in res:
149             if not res[r][1]:
150                 res[r] = 0.0
151             else:
152                 res[r] = 100.0 * res[r][0] / res[r][1]
153         return res
154
155     _columns = {
156         'name': fields.char('Order Reference', size=64, required=True, select=True),
157         'origin': fields.char('Origin', size=64, 
158             help="Reference of the document that generated this purchase order request."
159         ),
160         'partner_ref': fields.char('Partner Ref.', size=64),
161         'date_order':fields.date('Date Ordered', required=True, states={'confirmed':[('readonly',True)], 'approved':[('readonly',True)]}),
162         'date_approve':fields.date('Date Approved', readonly=1),
163         'partner_id':fields.many2one('res.partner', 'Supplier', required=True, states={'confirmed':[('readonly',True)], 'approved':[('readonly',True)]}, change_default=True),
164         'partner_address_id':fields.many2one('res.partner.address', 'Address', required=True, states={'posted':[('readonly',True)]}),
165
166         'dest_address_id':fields.many2one('res.partner.address', 'Destination Address', states={'posted':[('readonly',True)]},
167             help="Put an address if you want to deliver directly from the supplier to the customer." \
168                 "In this case, it will remove the warehouse link and set the customer location."
169         ),
170         'warehouse_id': fields.many2one('stock.warehouse', 'Warehouse', states={'posted':[('readonly',True)]}),
171         'location_id': fields.many2one('stock.location', 'Destination', required=True),
172
173         'pricelist_id':fields.many2one('product.pricelist', 'Pricelist', required=True, states={'confirmed':[('readonly',True)], 'approved':[('readonly',True)]}, help="The pricelist sets the currency used for this purchase order. It also computes the supplier price for the selected products/quantities."),
174
175         'state': fields.selection([('draft', 'Request for Quotation'), ('wait', 'Waiting'), ('confirmed', 'Confirmed'), ('approved', 'Approved'),('except_picking', 'Shipping Exception'), ('except_invoice', 'Invoice Exception'), ('done', 'Done'), ('cancel', 'Cancelled')], 'Order Status', readonly=True, help="The state of the purchase order or the quotation request. A quotation is a purchase order in a 'Draft' state. Then the order has to be confirmed by the user, the state switch to 'Confirmed'. Then the supplier must confirm the order to change the state to 'Approved'. When the purchase order is paid and received, the state becomes 'Done'. If a cancel action occurs in the invoice or in the reception of goods, the state becomes in exception.", select=True),
176         'order_line': fields.one2many('purchase.order.line', 'order_id', 'Order Lines', states={'confirmed':[('readonly',True)], 'approved':[('readonly',True)]}),
177         'validator' : fields.many2one('res.users', 'Validated by', readonly=True),
178         'notes': fields.text('Notes'),
179         'invoice_id': fields.many2one('account.invoice', 'Invoice', readonly=True),
180         'picking_ids': fields.one2many('stock.picking', 'purchase_id', 'Picking List', readonly=True, help="This is the list of picking list that have been generated for this purchase"),
181         'shipped':fields.boolean('Received', readonly=True, select=True),
182         'shipped_rate': fields.function(_shipped_rate, method=True, string='Received', type='float'),
183         'invoiced':fields.boolean('Invoiced & Paid', readonly=True, select=True),
184         'invoiced_rate': fields.function(_invoiced_rate, method=True, string='Invoiced', type='float'),
185         'invoice_method': fields.selection([('manual','Manual'),('order','From Order'),('picking','From Picking')], 'Invoicing Control', required=True,
186             help="From Order: a draft invoice will be pre-generated based on the purchase order. The accountant " \
187                 "will just have to validate this invoice for control.\n" \
188                 "From Picking: a draft invoice will be pre-genearted based on validated receptions.\n" \
189                 "Manual: no invoice will be pre-generated. The accountant will have to encode manually."
190         ),
191         'minimum_planned_date':fields.function(_minimum_planned_date, fnct_inv=_set_minimum_planned_date, method=True,store=True, string='Planned Date', type='datetime', help="This is computed as the minimum scheduled date of all purchase order lines' products."),
192         'amount_untaxed': fields.function(_amount_untaxed, method=True, string='Untaxed Amount'),
193         'amount_tax': fields.function(_amount_tax, method=True, string='Taxes'),
194         'amount_total': fields.function(_amount_total, method=True, string='Total'),
195     }
196     _defaults = {
197         'date_order': lambda *a: time.strftime('%Y-%m-%d'),
198         'state': lambda *a: 'draft',
199         'name': lambda obj, cr, uid, context: obj.pool.get('ir.sequence').get(cr, uid, 'purchase.order'),
200         'shipped': lambda *a: 0,
201         'invoice_method': lambda *a: 'order',
202         'invoiced': lambda *a: 0,
203         'partner_address_id': lambda self, cr, uid, context: context.get('partner_id', False) and self.pool.get('res.partner').address_get(cr, uid, [context['partner_id']], ['default'])['default'],
204         'pricelist_id': lambda self, cr, uid, context: context.get('partner_id', False) and self.pool.get('res.partner').browse(cr, uid, context['partner_id']).property_product_pricelist_purchase.id,
205     }
206     _name = "purchase.order"
207     _description = "Purchase order"
208     _order = "name desc"
209     
210     def unlink(self, cr, uid, ids):
211         purchase_orders = self.read(cr, uid, ids, ['state'])
212         unlink_ids = []
213         for s in purchase_orders:
214             if s['state'] in ['draft','cancel']:
215                 unlink_ids.append(s['id'])
216             else:
217                 raise osv.except_osv(_('Invalid action !'), _('Cannot delete Purchase Order(s) which are in %s State!' % s['state']))
218         osv.osv.unlink(self, cr, uid, unlink_ids)
219         return True
220     
221     def button_dummy(self, cr, uid, ids, context={}):
222         return True
223
224     def onchange_dest_address_id(self, cr, uid, ids, adr_id):
225         if not adr_id:
226             return {}
227         part_id = self.pool.get('res.partner.address').read(cr, uid, [adr_id], ['partner_id'])[0]['partner_id'][0]
228         loc_id = self.pool.get('res.partner').browse(cr, uid, part_id).property_stock_customer.id
229         return {'value':{'location_id': loc_id, 'warehouse_id': False}}
230
231     def onchange_warehouse_id(self, cr, uid, ids, warehouse_id):
232         if not warehouse_id:
233             return {}
234         res = self.pool.get('stock.warehouse').read(cr, uid, [warehouse_id], ['lot_input_id'])[0]['lot_input_id'][0]
235         return {'value':{'location_id': res, 'dest_address_id': False}}
236
237     def onchange_partner_id(self, cr, uid, ids, part):
238         if not part:
239             return {'value':{'partner_address_id': False}}
240         addr = self.pool.get('res.partner').address_get(cr, uid, [part], ['default'])
241         pricelist = self.pool.get('res.partner').property_get(cr, uid,
242                                         part,property_pref=['property_product_pricelist_purchase']).get('property_product_pricelist_purchase',False)
243         return {'value':{'partner_address_id': addr['default'], 'pricelist_id': pricelist}}
244
245     def wkf_approve_order(self, cr, uid, ids):
246         self.write(cr, uid, ids, {'state': 'approved', 'date_approve': time.strftime('%Y-%m-%d')})
247         return True
248
249     def wkf_confirm_order(self, cr, uid, ids, context={}):
250         for po in self.browse(cr, uid, ids):
251             if self.pool.get('res.partner.event.type').check(cr, uid, 'purchase_open'):
252                 self.pool.get('res.partner.event').create(cr, uid, {'name':'Purchase Order: '+po.name, 'partner_id':po.partner_id.id, 'date':time.strftime('%Y-%m-%d %H:%M:%S'), 'user_id':uid, 'partner_type':'retailer', 'probability': 1.0, 'planned_cost':po.amount_untaxed})
253         current_name = self.name_get(cr, uid, ids)[0][1]
254         for id in ids:
255             self.write(cr, uid, [id], {'state' : 'confirmed', 'validator' : uid})
256         return True
257
258     def wkf_warn_buyer(self, cr, uid, ids):
259         self.write(cr, uid, ids, {'state' : 'wait', 'validator' : uid})
260         request = pooler.get_pool(cr.dbname).get('res.request')
261         for po in self.browse(cr, uid, ids):
262             managers = []
263             for oline in po.order_line:
264                 manager = oline.product_id.product_manager
265                 if manager and not (manager.id in managers):
266                     managers.append(manager.id)
267             for manager_id in managers:
268                 request.create(cr, uid,
269                       {'name' : "Purchase amount over the limit",
270                        'act_from' : uid,
271                        'act_to' : manager_id,
272                        'body': 'Somebody has just confirmed a purchase with an amount over the defined limit',
273                        'ref_partner_id': po.partner_id.id,
274                        'ref_doc1': 'purchase.order,%d' % (po.id,),
275                        })
276     def inv_line_create(self,a,ol):
277         return (0, False, {
278                     'name': ol.name,
279                     'account_id': a,
280                     'price_unit': ol.price_unit or 0.0,
281                     'quantity': ol.product_qty,
282                     'product_id': ol.product_id.id or False,
283                     'uos_id': ol.product_uom.id or False,
284                     'invoice_line_tax_id': [(6, 0, [x.id for x in ol.taxes_id])],
285                     'account_analytic_id': ol.account_analytic_id.id,
286                 })
287
288     def action_invoice_create(self, cr, uid, ids, *args):
289         res = False
290         for o in self.browse(cr, uid, ids):
291             il = []
292             for ol in o.order_line:
293
294                 if ol.product_id:
295                     a = ol.product_id.product_tmpl_id.property_account_expense.id
296                     if not a:
297                         a = ol.product_id.categ_id.property_account_expense_categ.id
298                     if not a:
299                         raise osv.except_osv(_('Error !'), _('There is no expense account defined for this product: "%s" (id:%d)') % (ol.product_id.name, ol.product_id.id,))
300                 else:
301                     a = self.pool.get('ir.property').get(cr, uid, 'property_account_expense_categ', 'product.category')
302                 il.append(self.inv_line_create(a,ol))
303 #               il.append((0, False, {
304 #                   'name': ol.name,
305 #                   'account_id': a,
306 #                   'price_unit': ol.price_unit or 0.0,
307 #                   'quantity': ol.product_qty,
308 #                   'product_id': ol.product_id.id or False,
309 #                   'uos_id': ol.product_uom.id or False,
310 #                   'invoice_line_tax_id': [(6, 0, [x.id for x in ol.taxes_id])],
311 #                   'account_analytic_id': ol.account_analytic_id.id,
312 #               }))
313
314             a = o.partner_id.property_account_payable.id
315             inv = {
316                 'name': o.partner_ref or o.name,
317                 'reference': "P%dPO%d" % (o.partner_id.id, o.id),
318                 'account_id': a,
319                 'type': 'in_invoice',
320                 'partner_id': o.partner_id.id,
321                 'currency_id': o.pricelist_id.currency_id.id,
322                 'address_invoice_id': o.partner_address_id.id,
323                 'address_contact_id': o.partner_address_id.id,
324                 'origin': o.name,
325                 'invoice_line': il,
326             }
327             inv_id = self.pool.get('account.invoice').create(cr, uid, inv, {'type':'in_invoice'})
328             self.pool.get('account.invoice').button_compute(cr, uid, [inv_id], {'type':'in_invoice'}, set_total=True)
329
330             self.write(cr, uid, [o.id], {'invoice_id': inv_id})
331             res = inv_id
332         return res
333
334     def has_stockable_product(self,cr, uid, ids, *args):
335         for order in self.browse(cr, uid, ids):
336             for order_line in order.order_line:
337                 if order_line.product_id and order_line.product_id.product_tmpl_id.type in ('product', 'consu'):
338                     return True
339         return False
340
341     def action_picking_create(self,cr, uid, ids, *args):
342         picking_id = False
343         for order in self.browse(cr, uid, ids):
344             loc_id = order.partner_id.property_stock_supplier.id
345             istate = 'none'
346             if order.invoice_method=='picking':
347                 istate = '2binvoiced'
348             picking_id = self.pool.get('stock.picking').create(cr, uid, {
349                 'origin': order.name+((order.origin and (':'+order.origin)) or ''),
350                 'type': 'in',
351                 'address_id': order.dest_address_id.id or order.partner_address_id.id,
352                 'invoice_state': istate,
353                 'purchase_id': order.id,
354             })
355             for order_line in order.order_line:
356                 if not order_line.product_id:
357                     continue
358                 if order_line.product_id.product_tmpl_id.type in ('product', 'consu'):
359                     dest = order.location_id.id
360                     self.pool.get('stock.move').create(cr, uid, {
361                         'name': 'PO:'+order_line.name,
362                         'product_id': order_line.product_id.id,
363                         'product_qty': order_line.product_qty,
364                         'product_uos_qty': order_line.product_qty,
365                         'product_uom': order_line.product_uom.id,
366                         'product_uos': order_line.product_uom.id,
367                         'date_planned': order_line.date_planned,
368                         'location_id': loc_id,
369                         'location_dest_id': dest,
370                         'picking_id': picking_id,
371                         'move_dest_id': order_line.move_dest_id.id,
372                         'state': 'assigned',
373                         'purchase_line_id': order_line.id,
374                     })
375                     if order_line.move_dest_id:
376                         self.pool.get('stock.move').write(cr, uid, [order_line.move_dest_id.id], {'location_id':order.location_id.id})
377             wf_service = netsvc.LocalService("workflow")
378             wf_service.trg_validate(uid, 'stock.picking', picking_id, 'button_confirm', cr)
379         return picking_id
380     def copy(self, cr, uid, id, default=None,context={}):
381         if not default:
382             default = {}
383         default.update({
384             'state':'draft',
385             'shipped':False,
386             'invoiced':False,
387             'invoice_id':False,
388             'picking_ids':[],
389             'name': self.pool.get('ir.sequence').get(cr, uid, 'purchase.order'),
390         })
391         return super(purchase_order, self).copy(cr, uid, id, default, context)
392
393 purchase_order()
394
395 class purchase_order_line(osv.osv):
396     def _amount_line(self, cr, uid, ids, prop, unknow_none,unknow_dict):
397         res = {}
398         cur_obj=self.pool.get('res.currency')
399         for line in self.browse(cr, uid, ids):
400             cur = line.order_id.pricelist_id.currency_id
401             res[line.id] = cur_obj.round(cr, uid, cur, line.price_unit * line.product_qty)
402         return res
403
404     _columns = {
405         'name': fields.char('Description', size=64, required=True),
406         'product_qty': fields.float('Quantity', required=True, digits=(16,2)),
407         'date_planned': fields.datetime('Scheduled date', required=True),
408         'taxes_id': fields.many2many('account.tax', 'purchase_order_taxe', 'ord_id', 'tax_id', 'Taxes'),
409         'product_uom': fields.many2one('product.uom', 'Product UOM', required=True),
410         'product_id': fields.many2one('product.product', 'Product', domain=[('purchase_ok','=',True)], change_default=True),
411         'move_id': fields.many2one('stock.move', 'Reservation', ondelete='set null'),
412         'move_dest_id': fields.many2one('stock.move', 'Reservation Destination', ondelete='set null'),
413         'price_unit': fields.float('Unit Price', required=True, digits=(16, int(config['price_accuracy']))),
414         'price_subtotal': fields.function(_amount_line, method=True, string='Subtotal'),
415         'notes': fields.text('Notes'),
416         'order_id': fields.many2one('purchase.order', 'Order Ref', select=True, required=True, ondelete='cascade'),
417         'account_analytic_id':fields.many2one('account.analytic.account', 'Analytic Account',),
418     }
419     _defaults = {
420         'product_qty': lambda *a: 1.0
421     }
422     _table = 'purchase_order_line'
423     _name = 'purchase.order.line'
424     _description = 'Purchase Order lines'
425     def copy(self, cr, uid, id, default=None,context={}):
426         if not default:
427             default = {}
428         default.update({'state':'draft', 'move_id':False})
429         return super(purchase_order_line, self).copy(cr, uid, id, default, context)
430
431     def product_id_change(self, cr, uid, ids, pricelist, product, qty, uom,
432             partner_id, date_order=False):
433         prod= self.pool.get('product.product').browse(cr, uid,product)
434         if not pricelist:
435             raise osv.except_osv(_('No Pricelist !'), _('You have to select a pricelist in the purchase form !\nPlease set one before choosing a product.'))
436         if not product:
437             return {'value': {'price_unit': 0.0, 'name':'','notes':'', 'product_uom' : False}, 'domain':{'product_uom':[]}}
438         lang=False
439         if partner_id:
440             lang=self.pool.get('res.partner').read(cr, uid, partner_id)['lang']
441         context={'lang':lang}
442
443         prod = self.pool.get('product.product').read(cr, uid, product, ['supplier_taxes_id','name','seller_delay','uom_po_id','description_purchase'],context=context)
444         prod_uom_po = prod['uom_po_id'][0]
445         if not uom:
446             uom = prod_uom_po
447         if not date_order:
448             date_order = time.strftime('%Y-%m-%d')
449         price = self.pool.get('product.pricelist').price_get(cr,uid,[pricelist],
450                 product, qty or 1.0, partner_id, {
451                     'uom': uom,
452                     'date': date_order,
453                     })[pricelist]
454         dt = (DateTime.now() + DateTime.RelativeDateTime(days=prod['seller_delay'] or 0.0)).strftime('%Y-%m-%d %H:%M:%S')
455         prod_name = self.pool.get('product.product').name_get(cr, uid, [product], context=context)[0][1]
456
457         res = {'value': {'price_unit': price, 'name':prod_name, 'taxes_id':prod['supplier_taxes_id'], 'date_planned': dt,'notes':prod['description_purchase'], 'product_uom': uom}}
458         domain = {}
459
460         
461         taxes = self.pool.get('account.tax').browse(cr, uid,prod['supplier_taxes_id'])
462         taxep = None
463         if partner_id:
464             taxep_id = self.pool.get('res.partner').property_get(cr, uid,partner_id,property_pref=['property_account_supplier_tax']).get('property_account_supplier_tax',False)
465             if taxep_id:
466                                 taxep=self.pool.get('account.tax').browse(cr, uid,taxep_id)                
467         if not taxep or not taxep.id:
468             res['value']['taxes_id'] = [x.id for x in product.taxes_id]
469         else:
470             res5 = [taxep.id]
471             for t in taxes:
472                 if not t.tax_group==taxep.tax_group:
473                     res5.append(t.id)
474             res['value']['taxes_id'] = res5
475
476         res2 = self.pool.get('product.uom').read(cr, uid, [uom], ['category_id'])
477         res3 = self.pool.get('product.uom').read(cr, uid, [prod_uom_po], ['category_id'])
478         domain = {'product_uom':[('category_id','=',res2[0]['category_id'][0])]}
479         if res2[0]['category_id'] != res3[0]['category_id']:
480             raise osv.except_osv(_('Wrong Product UOM !'), _('You have to select a product UOM in the same category than the purchase UOM of the product'))
481
482         res['domain'] = domain
483         return res
484
485     def product_uom_change(self, cr, uid, ids, pricelist, product, qty, uom,
486             partner_id, date_order=False):
487         res = self.product_id_change(cr, uid, ids, pricelist, product, qty, uom,
488                 partner_id, date_order=date_order)
489         if 'product_uom' in res['value']:
490             del res['value']['product_uom']
491         if not uom:
492             res['value']['price_unit'] = 0.0
493         return res
494 purchase_order_line()
495
496 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
497