merged
[odoo/odoo.git] / addons / purchase / purchase.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 from osv import fields
24 from osv import osv
25 import time
26 import netsvc
27
28 import ir
29 from mx import DateTime
30 import pooler
31 from tools import config
32 from tools.translate import _
33
34 #
35 # Model definition
36 #
37 class purchase_order(osv.osv):
38     def _calc_amount(self, cr, uid, ids, prop, unknow_none, unknow_dict):
39         res = {}
40         for order in self.browse(cr, uid, ids):
41             res[order.id] = 0
42             for oline in order.order_line:
43                 res[order.id] += oline.price_unit * oline.product_qty
44         return res
45
46     def _amount_all(self, cr, uid, ids, field_name, arg, context):
47         res = {}
48         cur_obj=self.pool.get('res.currency')
49         for order in self.browse(cr, uid, ids):
50             res[order.id] = {
51                 'amount_untaxed': 0.0,
52                 'amount_tax': 0.0,
53                 'amount_total': 0.0,
54             }
55             val = val1 = 0.0
56             cur=order.pricelist_id.currency_id
57             for line in order.order_line:
58                 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):
59                     val+= c['amount']
60                 val1 += line.price_subtotal
61             res[order.id]['amount_tax']=cur_obj.round(cr, uid, cur, val)
62             res[order.id]['amount_untaxed']=cur_obj.round(cr, uid, cur, val1)
63             res[order.id]['amount_total']=res[order.id]['amount_untaxed'] + res[order.id]['amount_tax']
64         return res
65
66     def _set_minimum_planned_date(self, cr, uid, ids, name, value, arg, context):
67         if not value: return False
68         if type(ids)!=type([]):
69             ids=[ids]
70         for po in self.browse(cr, uid, ids, context):
71             cr.execute("""update purchase_order_line set
72                     date_planned=%s
73                 where
74                     order_id=%s and
75                     (date_planned=%s or date_planned<%s)""", (value,po.id,po.minimum_planned_date,value))
76         return True
77
78     def _minimum_planned_date(self, cr, uid, ids, field_name, arg, context):
79         res={}
80         purchase_obj=self.browse(cr, uid, ids, context=context)
81         for purchase in purchase_obj:
82             res[purchase.id] = False
83             if purchase.order_line:
84                 min_date=purchase.order_line[0].date_planned
85                 for line in purchase.order_line:
86                     if line.date_planned < min_date:
87                         min_date=line.date_planned
88                 res[purchase.id]=min_date
89         return res
90
91     def _invoiced_rate(self, cursor, user, ids, name, arg, context=None):
92         res = {}
93         for purchase in self.browse(cursor, user, ids, context=context):
94             tot = 0.0
95             if purchase.invoice_id and purchase.invoice_id.state not in ('draft','cancel'):
96                 tot += purchase.invoice_id.amount_untaxed
97             if purchase.amount_untaxed:
98                 res[purchase.id] = tot * 100.0 / purchase.amount_untaxed
99             else:
100                 res[purchase.id] = 0.0
101         return res
102
103     def _shipped_rate(self, cr, uid, ids, name, arg, context=None):
104         if not ids: return {}
105         res = {}
106         for id in ids:
107             res[id] = [0.0,0.0]
108         cr.execute('''SELECT
109                 p.purchase_id,sum(m.product_qty), m.state
110             FROM
111                 stock_move m
112             LEFT JOIN
113                 stock_picking p on (p.id=m.picking_id)
114             WHERE
115                 p.purchase_id in ('''+','.join(map(str,ids))+''')
116             GROUP BY m.state, p.purchase_id''')
117         for oid,nbr,state in cr.fetchall():
118             if state=='cancel':
119                 continue
120             if state=='done':
121                 res[oid][0] += nbr or 0.0
122                 res[oid][1] += nbr or 0.0
123             else:
124                 res[oid][1] += nbr or 0.0
125         for r in res:
126             if not res[r][1]:
127                 res[r] = 0.0
128             else:
129                 res[r] = 100.0 * res[r][0] / res[r][1]
130         return res
131
132     def _get_order(self, cr, uid, ids, context={}):
133         result = {}
134         for line in self.pool.get('purchase.order.line').browse(cr, uid, ids, context=context):
135             result[line.order_id.id] = True
136         return result.keys()
137
138     def _invoiced(self, cursor, user, ids, name, arg, context=None):
139         res = {}
140         for purchase in self.browse(cursor, user, ids, context=context):
141             if purchase.invoice_id.reconciled:
142                 res[purchase.id] = purchase.invoice_id.reconciled
143             else:
144                 res[purchase.id] = False
145         return res
146
147     _columns = {
148         'name': fields.char('Order Reference', size=64, required=True, select=True),
149         'origin': fields.char('Origin', size=64,
150             help="Reference of the document that generated this purchase order request."
151         ),
152         'partner_ref': fields.char('Supplier Ref.', size=64),
153         'date_order':fields.date('Date Ordered', required=True, states={'confirmed':[('readonly',True)], 'approved':[('readonly',True)]}, help="Date on which this document has been created."),
154         'date_approve':fields.date('Date Approved', readonly=1),
155         'partner_id':fields.many2one('res.partner', 'Supplier', required=True, states={'confirmed':[('readonly',True)], 'approved':[('readonly',True)]}, change_default=True),
156         'partner_address_id':fields.many2one('res.partner.address', 'Supplier Address', required=True, states={'posted':[('readonly',True)]}),
157
158         'dest_address_id':fields.many2one('res.partner.address', 'Destination Address', states={'posted':[('readonly',True)]},
159             help="Put an address if you want to deliver directly from the supplier to the customer." \
160                 "In this case, it will remove the warehouse link and set the customer location."
161         ),
162         'warehouse_id': fields.many2one('stock.warehouse', 'Warehouse', states={'posted':[('readonly',True)]}),
163         'location_id': fields.many2one('stock.location', 'Destination', required=True),
164
165         '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."),
166
167         'state': fields.selection([('draft', 'Request for Quotation'), ('wait', 'Waiting'), ('confirmed', 'Waiting Supplier Ack'), ('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),
168         'order_line': fields.one2many('purchase.order.line', 'order_id', 'Order Lines', states={'approved':[('readonly',True)]}),
169         'validator' : fields.many2one('res.users', 'Validated by', readonly=True),
170         'notes': fields.text('Notes'),
171         'invoice_id': fields.many2one('account.invoice', 'Invoice', readonly=True),
172         '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"),
173         'shipped':fields.boolean('Received', readonly=True, select=True),
174         'shipped_rate': fields.function(_shipped_rate, method=True, string='Received', type='float'),
175         'invoiced': fields.function(_invoiced, method=True, string='Invoiced & Paid', type='boolean'),
176         'invoiced_rate': fields.function(_invoiced_rate, method=True, string='Invoiced', type='float'),
177         'invoice_method': fields.selection([('manual','Manual'),('order','From Order'),('picking','From Picking')], 'Invoicing Control', required=True,
178             help="From Order: a draft invoice will be pre-generated based on the purchase order. The accountant " \
179                 "will just have to validate this invoice for control.\n" \
180                 "From Picking: a draft invoice will be pre-genearted based on validated receptions.\n" \
181                 "Manual: no invoice will be pre-generated. The accountant will have to encode manually."
182         ),
183         '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."),
184         'amount_untaxed': fields.function(_amount_all, method=True, string='Untaxed Amount',
185             store={
186                 'purchase.order.line': (_get_order, None, 10),
187             }, multi="sums"),
188         'amount_tax': fields.function(_amount_all, method=True, string='Taxes',
189             store={
190                 'purchase.order.line': (_get_order, None, 10),
191             }, multi="sums"),
192         'amount_total': fields.function(_amount_all, method=True, string='Total',
193             store={
194                 'purchase.order.line': (_get_order, None, 10),
195             }, multi="sums"),
196         'fiscal_position': fields.many2one('account.fiscal.position', 'Fiscal Position')
197     }
198     _defaults = {
199         'date_order': lambda *a: time.strftime('%Y-%m-%d'),
200         'state': lambda *a: 'draft',
201         'name': lambda obj, cr, uid, context: obj.pool.get('ir.sequence').get(cr, uid, 'purchase.order'),
202         'shipped': lambda *a: 0,
203         'invoice_method': lambda *a: 'order',
204         'invoiced': lambda *a: 0,
205         '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'],
206         '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,
207     }
208     _name = "purchase.order"
209     _description = "Purchase order"
210     _order = "name desc"
211
212     def unlink(self, cr, uid, ids, context=None):
213         purchase_orders = self.read(cr, uid, ids, ['state'])
214         unlink_ids = []
215         for s in purchase_orders:
216             if s['state'] in ['draft','cancel']:
217                 unlink_ids.append(s['id'])
218             else:
219                 raise osv.except_osv(_('Invalid action !'), _('Cannot delete Purchase Order(s) which are in %s State!' % s['state']))
220         return super(purchase_order, self).unlink(cr, uid, unlink_ids, context=context)
221
222     def button_dummy(self, cr, uid, ids, context={}):
223         return True
224
225     def onchange_dest_address_id(self, cr, uid, ids, adr_id):
226         if not adr_id:
227             return {}
228         part_id = self.pool.get('res.partner.address').read(cr, uid, [adr_id], ['partner_id'])[0]['partner_id'][0]
229         loc_id = self.pool.get('res.partner').browse(cr, uid, part_id).property_stock_customer.id
230         return {'value':{'location_id': loc_id, 'warehouse_id': False}}
231
232     def onchange_warehouse_id(self, cr, uid, ids, warehouse_id):
233         if not warehouse_id:
234             return {}
235         res = self.pool.get('stock.warehouse').read(cr, uid, [warehouse_id], ['lot_input_id'])[0]['lot_input_id'][0]
236         return {'value':{'location_id': res, 'dest_address_id': False}}
237
238     def onchange_partner_id(self, cr, uid, ids, part):
239         if not part:
240             return {'value':{'partner_address_id': False, 'fiscal_position': False}}
241         addr = self.pool.get('res.partner').address_get(cr, uid, [part], ['default'])
242         part = self.pool.get('res.partner').browse(cr, uid, part)
243         pricelist = part.property_product_pricelist_purchase.id
244         fiscal_position = part.property_account_position and part.property_account_position.id or False
245         return {'value':{'partner_address_id': addr['default'], 'pricelist_id': pricelist, 'fiscal_position': fiscal_position}}
246
247     def wkf_approve_order(self, cr, uid, ids, context={}):
248         self.write(cr, uid, ids, {'state': 'approved', 'date_approve': time.strftime('%Y-%m-%d')})
249         return True
250
251     def wkf_confirm_order(self, cr, uid, ids, context={}):
252         for po in self.browse(cr, uid, ids):
253             if self.pool.get('res.partner.event.type').check(cr, uid, 'purchase_open'):
254                 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})
255         current_name = self.name_get(cr, uid, ids)[0][1]
256         for id in ids:
257             self.write(cr, uid, [id], {'state' : 'confirmed', 'validator' : uid})
258         return True
259
260     def wkf_warn_buyer(self, cr, uid, ids):
261         self.write(cr, uid, ids, {'state' : 'wait', 'validator' : uid})
262         request = pooler.get_pool(cr.dbname).get('res.request')
263         for po in self.browse(cr, uid, ids):
264             managers = []
265             for oline in po.order_line:
266                 manager = oline.product_id.product_manager
267                 if manager and not (manager.id in managers):
268                     managers.append(manager.id)
269             for manager_id in managers:
270                 request.create(cr, uid,
271                       {'name' : "Purchase amount over the limit",
272                        'act_from' : uid,
273                        'act_to' : manager_id,
274                        'body': 'Somebody has just confirmed a purchase with an amount over the defined limit',
275                        'ref_partner_id': po.partner_id.id,
276                        'ref_doc1': 'purchase.order,%d' % (po.id,),
277                        })
278     def inv_line_create(self, cr, uid, a, ol):
279         return (0, False, {
280             'name': ol.name,
281             'account_id': a,
282             'price_unit': ol.price_unit or 0.0,
283             'quantity': ol.product_qty,
284             'product_id': ol.product_id.id or False,
285             'uos_id': ol.product_uom.id or False,
286             'invoice_line_tax_id': [(6, 0, [x.id for x in ol.taxes_id])],
287             'account_analytic_id': ol.account_analytic_id.id,
288         })
289
290     def action_cancel_draft(self, cr, uid, ids, *args):
291         if not len(ids):
292             return False
293         self.write(cr, uid, ids, {'state':'draft','shipped':0})
294         wf_service = netsvc.LocalService("workflow")
295         for p_id in ids:
296             wf_service.trg_create(uid, 'purchase.order', p_id, cr)
297         return True
298
299     def action_invoice_create(self, cr, uid, ids, *args):
300         res = False
301         journal_obj = self.pool.get('account.journal')
302         for o in self.browse(cr, uid, ids):
303             il = []
304             for ol in o.order_line:
305
306                 if ol.product_id:
307                     a = ol.product_id.product_tmpl_id.property_account_expense.id
308                     if not a:
309                         a = ol.product_id.categ_id.property_account_expense_categ.id
310                     if not a:
311                         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,))
312                 else:
313                     a = self.pool.get('ir.property').get(cr, uid, 'property_account_expense_categ', 'product.category')
314                 fpos = o.fiscal_position or False
315                 a = self.pool.get('account.fiscal.position').map_account(cr, uid, fpos, a)
316                 il.append(self.inv_line_create(cr, uid, a, ol))
317
318             a = o.partner_id.property_account_payable.id
319             journal_ids = journal_obj.search(cr, uid, [('type', '=','purchase')], limit=1)
320             inv = {
321                 'name': o.partner_ref or o.name,
322                 'reference': "P%dPO%d" % (o.partner_id.id, o.id),
323                 'account_id': a,
324                 'type': 'in_invoice',
325                 'partner_id': o.partner_id.id,
326                 'currency_id': o.pricelist_id.currency_id.id,
327                 'address_invoice_id': o.partner_address_id.id,
328                 'address_contact_id': o.partner_address_id.id,
329                 'journal_id': len(journal_ids) and journal_ids[0] or False,
330                 'origin': o.name,
331                 'invoice_line': il,
332                 'fiscal_position': o.partner_id.property_account_position.id,
333                 'payment_term':o.partner_id.property_payment_term and o.partner_id.property_payment_term.id or False,
334             }
335             inv_id = self.pool.get('account.invoice').create(cr, uid, inv, {'type':'in_invoice'})
336             self.pool.get('account.invoice').button_compute(cr, uid, [inv_id], {'type':'in_invoice'}, set_total=True)
337
338             self.write(cr, uid, [o.id], {'invoice_id': inv_id})
339             res = inv_id
340         return res
341
342     def has_stockable_product(self,cr, uid, ids, *args):
343         for order in self.browse(cr, uid, ids):
344             for order_line in order.order_line:
345                 if order_line.product_id and order_line.product_id.product_tmpl_id.type in ('product', 'consu'):
346                     return True
347         return False
348
349     def action_cancel(self, cr, uid, ids, context={}):
350         ok = True
351         purchase_order_line_obj = self.pool.get('purchase.order.line')
352         for purchase in self.browse(cr, uid, ids):
353             for pick in purchase.picking_ids:
354                 if pick.state not in ('draft','cancel'):
355                     raise osv.except_osv(
356                         _('Could not cancel purchase order !'),
357                         _('You must first cancel all packing attached to this purchase order.'))
358             for pick in purchase.picking_ids:
359                 wf_service = netsvc.LocalService("workflow")
360                 wf_service.trg_validate(uid, 'stock.picking', pick.id, 'button_cancel', cr)
361             inv = purchase.invoice_id
362             if inv and inv.state not in ('cancel','draft'):
363                 raise osv.except_osv(
364                     _('Could not cancel this purchase order !'),
365                     _('You must first cancel all invoices attached to this purchase order.'))
366             if inv:
367                 wf_service = netsvc.LocalService("workflow")
368                 wf_service.trg_validate(uid, 'account.invoice', inv.id, 'invoice_cancel', cr)
369         self.write(cr,uid,ids,{'state':'cancel'})
370         return True
371
372     def action_picking_create(self,cr, uid, ids, *args):
373         picking_id = False
374         for order in self.browse(cr, uid, ids):
375             loc_id = order.partner_id.property_stock_supplier.id
376             istate = 'none'
377             if order.invoice_method=='picking':
378                 istate = '2binvoiced'
379             picking_id = self.pool.get('stock.picking').create(cr, uid, {
380                 'origin': order.name+((order.origin and (':'+order.origin)) or ''),
381                 'type': 'in',
382                 'address_id': order.dest_address_id.id or order.partner_address_id.id,
383                 'invoice_state': istate,
384                 'purchase_id': order.id,
385             })
386             for order_line in order.order_line:
387                 if not order_line.product_id:
388                     continue
389                 if order_line.product_id.product_tmpl_id.type in ('product', 'consu'):
390                     dest = order.location_id.id
391                     self.pool.get('stock.move').create(cr, uid, {
392                         'name': 'PO:'+order_line.name,
393                         'product_id': order_line.product_id.id,
394                         'product_qty': order_line.product_qty,
395                         'product_uos_qty': order_line.product_qty,
396                         'product_uom': order_line.product_uom.id,
397                         'product_uos': order_line.product_uom.id,
398                         'date_planned': order_line.date_planned,
399                         'location_id': loc_id,
400                         'location_dest_id': dest,
401                         'picking_id': picking_id,
402                         'move_dest_id': order_line.move_dest_id.id,
403                         'state': 'assigned',
404                         'purchase_line_id': order_line.id,
405                     })
406                     if order_line.move_dest_id:
407                         self.pool.get('stock.move').write(cr, uid, [order_line.move_dest_id.id], {'location_id':order.location_id.id})
408             wf_service = netsvc.LocalService("workflow")
409             wf_service.trg_validate(uid, 'stock.picking', picking_id, 'button_confirm', cr)
410         return picking_id
411     def copy(self, cr, uid, id, default=None,context={}):
412         if not default:
413             default = {}
414         default.update({
415             'state':'draft',
416             'shipped':False,
417             'invoiced':False,
418             'invoice_id':False,
419             'picking_ids':[],
420             'name': self.pool.get('ir.sequence').get(cr, uid, 'purchase.order'),
421         })
422         return super(purchase_order, self).copy(cr, uid, id, default, context)
423
424 purchase_order()
425
426 class purchase_order_line(osv.osv):
427     def _amount_line(self, cr, uid, ids, prop, unknow_none,unknow_dict):
428         res = {}
429         cur_obj=self.pool.get('res.currency')
430         for line in self.browse(cr, uid, ids):
431             cur = line.order_id.pricelist_id.currency_id
432             res[line.id] = cur_obj.round(cr, uid, cur, line.price_unit * line.product_qty)
433         return res
434
435     _columns = {
436         'name': fields.char('Description', size=256, required=True),
437         'product_qty': fields.float('Quantity', required=True, digits=(16,2)),
438         'date_planned': fields.datetime('Scheduled date', required=True),
439         'taxes_id': fields.many2many('account.tax', 'purchase_order_taxe', 'ord_id', 'tax_id', 'Taxes'),
440         'product_uom': fields.many2one('product.uom', 'Product UOM', required=True),
441         'product_id': fields.many2one('product.product', 'Product', domain=[('purchase_ok','=',True)], change_default=True),
442         'move_ids': fields.one2many('stock.move', 'purchase_line_id', 'Reservation', readonly=True, ondelete='set null'),
443         'move_dest_id': fields.many2one('stock.move', 'Reservation Destination', ondelete='set null'),
444         'price_unit': fields.float('Unit Price', required=True, digits=(16, int(config['price_accuracy']))),
445         'price_subtotal': fields.function(_amount_line, method=True, string='Subtotal'),
446         'notes': fields.text('Notes'),
447         'order_id': fields.many2one('purchase.order', 'Order Ref', select=True, required=True, ondelete='cascade'),
448         'account_analytic_id':fields.many2one('account.analytic.account', 'Analytic Account',),
449     }
450     _defaults = {
451         'product_qty': lambda *a: 1.0
452     }
453     _table = 'purchase_order_line'
454     _name = 'purchase.order.line'
455     _description = 'Purchase Order lines'
456     def copy_data(self, cr, uid, id, default=None,context={}):
457         if not default:
458             default = {}
459         default.update({'state':'draft', 'move_ids':[]})
460         return super(purchase_order_line, self).copy_data(cr, uid, id, default, context)
461
462     def product_id_change(self, cr, uid, ids, pricelist, product, qty, uom,
463             partner_id, date_order=False, fiscal_position=False):
464         if not pricelist:
465             raise osv.except_osv(_('No Pricelist !'), _('You have to select a pricelist in the purchase form !\nPlease set one before choosing a product.'))
466         if not  partner_id:
467             raise osv.except_osv(_('No Partner!'), _('You have to select a partner in the purchase form !\nPlease set one partner before choosing a product.'))
468         if not product:
469             return {'value': {'price_unit': 0.0, 'name':'','notes':'', 'product_uom' : False}, 'domain':{'product_uom':[]}}
470         prod= self.pool.get('product.product').browse(cr, uid,product)
471         lang=False
472         if partner_id:
473             lang=self.pool.get('res.partner').read(cr, uid, partner_id)['lang']
474         context={'lang':lang}
475         context['partner_id'] = partner_id
476
477         prod = self.pool.get('product.product').browse(cr, uid, product, context=context)
478         prod_uom_po = prod.uom_po_id.id
479         if not uom:
480             uom = prod_uom_po
481         if not date_order:
482             date_order = time.strftime('%Y-%m-%d')
483         price = self.pool.get('product.pricelist').price_get(cr,uid,[pricelist],
484                 product, qty or 1.0, partner_id, {
485                     'uom': uom,
486                     'date': date_order,
487                     })[pricelist]
488
489         qty = qty or 1.0
490         seller_delay = 0
491         for s in prod.seller_ids:
492             if s.name.id == partner_id:
493                 seller_delay = s.delay
494                 temp_qty = s.qty # supplier _qty assigned to temp
495                 if qty < temp_qty: # If the supplier quantity is greater than entered from user, set minimal.
496                     qty = temp_qty
497
498         dt = (DateTime.now() + DateTime.RelativeDateTime(days=seller_delay or 0.0)).strftime('%Y-%m-%d %H:%M:%S')
499         prod_name = self.pool.get('product.product').name_get(cr, uid, [prod.id])[0][1]
500
501
502         res = {'value': {'price_unit': price, 'name':prod_name, 'taxes_id':map(lambda x: x.id, prod.supplier_taxes_id),
503             'date_planned': dt,'notes':prod.description_purchase,
504             'product_qty': qty,
505             'product_uom': uom}}
506         domain = {}
507
508         partner = self.pool.get('res.partner').browse(cr, uid, partner_id)
509         taxes = self.pool.get('account.tax').browse(cr, uid,map(lambda x: x.id, prod.supplier_taxes_id))
510         fpos = fiscal_position and self.pool.get('account.fiscal.position').browse(cr, uid, fiscal_position) or False
511         res['value']['taxes_id'] = self.pool.get('account.fiscal.position').map_tax(cr, uid, fpos, taxes)
512
513         res2 = self.pool.get('product.uom').read(cr, uid, [uom], ['category_id'])
514         res3 = prod.uom_id.category_id.id
515         domain = {'product_uom':[('category_id','=',res2[0]['category_id'][0])]}
516         if res2[0]['category_id'][0] != res3:
517             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'))
518
519         res['domain'] = domain
520         return res
521
522     def product_uom_change(self, cr, uid, ids, pricelist, product, qty, uom,
523             partner_id, date_order=False):
524         res = self.product_id_change(cr, uid, ids, pricelist, product, qty, uom,
525                 partner_id, date_order=date_order)
526         if 'product_uom' in res['value']:
527             del res['value']['product_uom']
528         if not uom:
529             res['value']['price_unit'] = 0.0
530         return res
531 purchase_order_line()
532
533 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
534