1 # -*- coding: utf-8 -*-
2 ##############################################################################
4 # OpenERP, Open Source Management Solution
5 # Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>).
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.
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.
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/>.
20 ##############################################################################
22 from osv import osv, fields
26 from _common import rounding
28 from tools import config
29 from tools.translate import _
34 #----------------------------------------------------------
36 #----------------------------------------------------------
38 class product_uom_categ(osv.osv):
39 _name = 'product.uom.categ'
40 _description = 'Product uom categ'
42 'name': fields.char('Name', size=64, required=True, translate=True),
46 class product_uom(osv.osv):
48 _description = 'Product Unit of Measure'
50 def _factor(self, cursor, user, ids, name, arg, context):
52 for uom in self.browse(cursor, user, ids, context=context):
54 if uom.factor_inv_data:
55 res[uom.id] = uom.factor_inv_data
57 res[uom.id] = round(1 / uom.factor, 6)
62 def _factor_inv(self, cursor, user, id, name, value, arg, context):
64 if 'read_delta' in ctx:
68 if round(1 / round(1/value, 6), 6) != value:
70 self.write(cursor, user, id, {
71 'factor': round(1/value, 6),
72 'factor_inv_data': data,
75 self.write(cursor, user, id, {
77 'factor_inv_data': 0.0,
81 'name': fields.char('Name', size=64, required=True, translate=True),
82 'category_id': fields.many2one('product.uom.categ', 'UoM Category', required=True, ondelete='cascade',
83 help="Unit of Measure of a category can be converted between each others in the same category."),
84 'factor': fields.float('Rate', digits=(12, 6), required=True,
85 help='The coefficient for the formula:\n' \
86 '1 (base unit) = coeff (this unit). Rate = 1 / Factor.'),
87 'factor_inv': fields.function(_factor, digits=(12, 6),
88 method=True, string='Factor',
89 help='The coefficient for the formula:\n' \
90 'coeff (base unit) = 1 (this unit). Factor = 1 / Rate.'),
91 'factor_inv_data': fields.float('Factor', digits=(12, 6)),
92 'rounding': fields.float('Rounding Precision', digits=(16, 3), required=True,
93 help="The computed quantity will be a multiple of this value. Use 1.0 for products that can not be split."),
94 'active': fields.boolean('Active'),
98 'factor': lambda *a: 1.0,
99 'factor_inv': lambda *a: 1.0,
100 'active': lambda *a: 1,
101 'rounding': lambda *a: 0.01,
105 ('factor_gt_zero', 'CHECK (factor!=0)', 'Value of the factor can never be 0 !'),
106 ('factor_inv_data_gt_zero', 'CHECK (factor_inv_data!=0)', 'Value of the factor_inv_data can never be 0 !'),
109 def _compute_qty(self, cr, uid, from_uom_id, qty, to_uom_id=False):
110 if not from_uom_id or not qty or not to_uom_id:
112 uoms = self.browse(cr, uid, [from_uom_id, to_uom_id])
113 if uoms[0].id == from_uom_id:
114 from_unit, to_unit = uoms[0], uoms[-1]
116 from_unit, to_unit = uoms[-1], uoms[0]
117 return self._compute_qty_obj(cr, uid, from_unit, qty, to_unit)
119 def _compute_qty_obj(self, cr, uid, from_unit, qty, to_unit, context={}):
120 if from_unit.category_id.id <> to_unit.category_id.id:
122 if from_unit.factor_inv_data:
123 amount = qty * from_unit.factor_inv_data
125 amount = qty / from_unit.factor
127 if to_unit.factor_inv_data:
128 amount = rounding(amount / to_unit.factor_inv_data, to_unit.rounding)
130 amount = rounding(amount * to_unit.factor, to_unit.rounding)
133 def _compute_price(self, cr, uid, from_uom_id, price, to_uom_id=False):
134 if not from_uom_id or not price or not to_uom_id:
136 uoms = self.browse(cr, uid, [from_uom_id, to_uom_id])
137 if uoms[0].id == from_uom_id:
138 from_unit, to_unit = uoms[0], uoms[-1]
140 from_unit, to_unit = uoms[-1], uoms[0]
141 if from_unit.category_id.id <> to_unit.category_id.id:
143 if from_unit.factor_inv_data:
144 amount = price / from_unit.factor_inv_data
146 amount = price * from_unit.factor
148 if to_unit.factor_inv_data:
149 amount = amount * to_unit.factor_inv_data
151 amount = amount / to_unit.factor
154 def onchange_factor_inv(self, cursor, user, ids, value):
156 return {'value': {'factor': 0}}
157 return {'value': {'factor': round(1/value, 6)}}
159 def onchange_factor(self, cursor, user, ids, value):
161 return {'value': {'factor_inv': 0}}
162 return {'value': {'factor_inv': round(1/value, 6)}}
167 class product_ul(osv.osv):
169 _description = "Shipping Unit"
171 'name' : fields.char('Name', size=64,select=True, required=True, translate=True),
172 'type' : fields.selection([('unit','Unit'),('pack','Pack'),('box', 'Box'), ('palet', 'Pallet')], 'Type', required=True),
177 #----------------------------------------------------------
179 #----------------------------------------------------------
180 class product_category(osv.osv):
182 def name_get(self, cr, uid, ids, context=None):
185 reads = self.read(cr, uid, ids, ['name','parent_id'], context)
188 name = record['name']
189 if record['parent_id']:
190 name = record['parent_id'][1]+' / '+name
191 res.append((record['id'], name))
194 def _name_get_fnc(self, cr, uid, ids, prop, unknow_none, context):
195 res = self.name_get(cr, uid, ids, context)
198 _name = "product.category"
199 _description = "Product Category"
201 'name': fields.char('Name', size=64, required=True, translate=True),
202 'complete_name': fields.function(_name_get_fnc, method=True, type="char", string='Name'),
203 'parent_id': fields.many2one('product.category','Parent Category', select=True),
204 'child_id': fields.one2many('product.category', 'parent_id', string='Child Categories'),
205 'sequence': fields.integer('Sequence'),
208 def _check_recursion(self, cr, uid, ids):
211 cr.execute('select distinct parent_id from product_category where id in ('+','.join(map(str, ids))+')')
212 ids = filter(None, map(lambda x:x[0], cr.fetchall()))
219 (_check_recursion, 'Error ! You can not create recursive categories.', ['parent_id'])
221 def child_get(self, cr, uid, ids):
227 #----------------------------------------------------------
229 #----------------------------------------------------------
230 class product_template(osv.osv):
231 _name = "product.template"
232 _description = "Product Template"
233 def _calc_seller_delay(self, cr, uid, ids, name, arg, context={}):
235 for product in self.browse(cr, uid, ids, context):
236 if product.seller_ids:
237 result[product.id] = product.seller_ids[0].delay
239 result[product.id] = 1
243 'name': fields.char('Name', size=128, required=True, translate=True, select=True),
244 'product_manager': fields.many2one('res.users','Product Manager'),
245 'description': fields.text('Description',translate=True),
246 'description_purchase': fields.text('Purchase Description',translate=True),
247 'description_sale': fields.text('Sale Description',translate=True),
248 'type': fields.selection([('product','Stockable Product'),('consu', 'Consumable'),('service','Service')], 'Product Type', required=True, help="Will change the way requisitions are processed. Consumables are stockable products with infinite stock, or for use when you have no inventory management in the system."),
249 'supply_method': fields.selection([('produce','Produce'),('buy','Buy')], 'Supply method', required=True, help="Produce will generate production order or tasks, according to the product type. Purchase will trigger purchase orders when requested."),
250 'sale_delay': fields.float('Customer Lead Time', help="This is the average time between the confirmation of the customer order and the delivery of the finished products. It's the time you promise to your customers."),
251 'produce_delay': fields.float('Manufacturing Lead Time', help="Average time to produce this product. This is only for the production order and, if it is a multi-level bill of material, it's only for the level of this product. Different delays will be summed for all levels and purchase orders."),
252 'procure_method': fields.selection([('make_to_stock','Make to Stock'),('make_to_order','Make to Order')], 'Requisition Method', required=True, help="'Make to Stock': When needed, take from the stock or wait until re-supplying. 'Make to Order': When needed, purchase or produce for the requisition request."),
253 'rental': fields.boolean('Can be Rent'),
254 'categ_id': fields.many2one('product.category','Category', required=True, change_default=True),
255 'list_price': fields.float('Sale Price', digits=(16, int(config['price_accuracy'])), help="Base price for computing the customer price. Sometimes called the catalog price."),
256 'standard_price': fields.float('Cost Price', required=True, digits=(16, int(config['price_accuracy'])), help="The cost of the product for accounting stock valuation. It can serves as a base price for supplier price."),
257 'volume': fields.float('Volume', help="The volume in m3."),
258 'weight': fields.float('Gross weight', help="The gross weight in Kg."),
259 'weight_net': fields.float('Net weight', help="The net weight in Kg."),
260 'cost_method': fields.selection([('standard','Standard Price'), ('average','Average Price')], 'Costing Method', required=True,
261 help="Standard Price: the cost price is fixed and recomputed periodically (usually at the end of the year), Average Price: the cost price is recomputed at each reception of products."),
262 'warranty': fields.float('Warranty (months)'),
263 'sale_ok': fields.boolean('Can be sold', help="Determine if the product can be visible in the list of product within a selection from a sale order line."),
264 'purchase_ok': fields.boolean('Can be Purchased', help="Determine if the product is visible in the list of products within a selection from a purchase order line."),
265 'state': fields.selection([('',''),('draft', 'In Development'),('sellable','In Production'),('end','End of Lifecycle'),('obsolete','Obsolete')], 'State', help="Tells the user if he can use the product or not."),
266 'uom_id': fields.many2one('product.uom', 'Default UoM', required=True, help="Default Unit of Measure used for all stock operation."),
267 'uom_po_id': fields.many2one('product.uom', 'Purchase UoM', required=True, help="Default Unit of Measure used for purchase orders. It must in the same category than the default unit of measure."),
268 'uos_id' : fields.many2one('product.uom', 'Unit of Sale',
269 help='Used by companies that manages two unit of measure: invoicing and inventory management. For example, in food industries, you will manage a stock of ham but invoice in Kg. Keep empty to use the default UOM.'),
270 'uos_coeff': fields.float('UOM -> UOS Coeff', digits=(16,4),
271 help='Coefficient to convert UOM to UOS\n'
272 ' uom = uos * coeff'),
273 'mes_type': fields.selection((('fixed', 'Fixed'), ('variable', 'Variable')), 'Measure Type', required=True),
274 'seller_delay': fields.function(_calc_seller_delay, method=True, type='integer', string='Supplier Lead Time', help="This is the average delay in days between the purchase order confirmation and the reception of goods for this product and for the default supplier. It is used by the scheduler to order requests based on reordering delays."),
275 'seller_ids': fields.one2many('product.supplierinfo', 'product_id', 'Partners'),
276 'loc_rack': fields.char('Rack', size=16),
277 'loc_row': fields.char('Row', size=16),
278 'loc_case': fields.char('Case', size=16),
279 'company_id': fields.many2one('res.company', 'Company'),
282 def _get_uom_id(self, cr, uid, *args):
283 cr.execute('select id from product_uom order by id limit 1')
285 return res and res[0] or False
287 def _default_category(self, cr, uid, context={}):
288 if 'categ_id' in context and context['categ_id']:
289 return context['categ_id']
292 def onchange_uom(self, cursor, user, ids, uom_id,uom_po_id):
293 if uom_id and uom_po_id:
294 uom_obj=self.pool.get('product.uom')
295 uom=uom_obj.browse(cursor,user,[uom_id])[0]
296 uom_po=uom_obj.browse(cursor,user,[uom_po_id])[0]
297 if uom.category_id.id != uom_po.category_id.id:
298 return {'value': {'uom_po_id': uom_id}}
302 'company_id': lambda s,cr,uid,c: s.pool.get('res.company')._company_default_get(cr, uid, 'product.template', c),
303 # 'company_id': lambda self, cr, uid, context: False, # Visible by all
304 'type': lambda *a: 'product',
305 'list_price': lambda *a: 1,
306 'cost_method': lambda *a: 'standard',
307 'supply_method': lambda *a: 'buy',
308 'standard_price': lambda *a: 1,
309 'sale_ok': lambda *a: 1,
310 'sale_delay': lambda *a: 7,
311 'produce_delay': lambda *a: 1,
312 'purchase_ok': lambda *a: 1,
313 'procure_method': lambda *a: 'make_to_stock',
314 'uom_id': _get_uom_id,
315 'uom_po_id': _get_uom_id,
316 'uos_coeff' : lambda *a: 1.0,
317 'mes_type' : lambda *a: 'fixed',
318 'categ_id' : _default_category,
321 def _check_uom(self, cursor, user, ids):
322 for product in self.browse(cursor, user, ids):
323 if product.uom_id.category_id.id <> product.uom_po_id.category_id.id:
327 def _check_uos(self, cursor, user, ids):
328 for product in self.browse(cursor, user, ids):
330 and product.uos_id.category_id.id \
331 == product.uom_id.category_id.id:
336 (_check_uos, 'Error: UOS must be in a different category than the UOM', ['uos_id']),
337 (_check_uom, 'Error: The default UOM and the purchase UOM must be in the same category.', ['uom_id']),
340 def name_get(self, cr, user, ids, context={}):
341 if 'partner_id' in context:
343 return super(product_template, self).name_get(cr, user, ids, context)
347 class product_product(osv.osv):
348 def view_header_get(self, cr, uid, view_id, view_type, context):
349 res = super(product_product, self).view_header_get(cr, uid, view_id, view_type, context)
350 if (context.get('categ_id', False)):
351 return _('Products: ')+self.pool.get('product.category').browse(cr, uid, context['categ_id'], context).name
354 def _product_price(self, cr, uid, ids, name, arg, context={}):
356 quantity = context.get('quantity', 1)
357 pricelist = context.get('pricelist', False)
361 price = self.pool.get('product.pricelist').price_get(cr,uid,[pricelist], id, quantity, context=context)[pricelist]
366 res.setdefault(id, 0.0)
369 def _get_product_available_func(states, what):
370 def _product_available(self, cr, uid, ids, name, arg, context={}):
371 return {}.fromkeys(ids, 0.0)
372 return _product_available
374 _product_qty_available = _get_product_available_func(('done',), ('in', 'out'))
375 _product_virtual_available = _get_product_available_func(('confirmed','waiting','assigned','done'), ('in', 'out'))
376 _product_outgoing_qty = _get_product_available_func(('confirmed','waiting','assigned'), ('out',))
377 _product_incoming_qty = _get_product_available_func(('confirmed','waiting','assigned'), ('in',))
379 def _product_lst_price(self, cr, uid, ids, name, arg, context=None):
381 product_uom_obj = self.pool.get('product.uom')
383 res.setdefault(id, 0.0)
384 for product in self.browse(cr, uid, ids, context=context):
386 uom = product.uos_id or product.uom_id
387 res[product.id] = product_uom_obj._compute_price(cr, uid,
388 uom.id, product.list_price, context['uom'])
390 res[product.id] = product.list_price
391 res[product.id] = (res[product.id] or 0.0) * (product.price_margin or 1.0) + product.price_extra
394 def _get_partner_code_name(self, cr, uid, ids, product_id, partner_id, context={}):
395 product = self.browse(cr, uid, [product_id], context)[0]
396 for supinfo in product.seller_ids:
397 if supinfo.name.id == partner_id:
398 return {'code': supinfo.product_code, 'name': supinfo.product_name, 'variants': ''}
399 return {'code' : product.default_code, 'name' : product.name, 'variants': product.variants}
401 def _product_code(self, cr, uid, ids, name, arg, context={}):
403 for p in self.browse(cr, uid, ids, context):
404 res[p.id] = self._get_partner_code_name(cr, uid, [], p.id, context.get('partner_id', None), context)['code']
407 def _product_partner_ref(self, cr, uid, ids, name, arg, context={}):
409 for p in self.browse(cr, uid, ids, context):
410 data = self._get_partner_code_name(cr, uid, [], p.id, context.get('partner_id', None), context)
411 if not data['variants']:
412 data['variants'] = p.variants
414 data['code'] = p.code
416 data['name'] = p.name
417 res[p.id] = (data['code'] and ('['+data['code']+'] ') or '') + \
418 (data['name'] or '') + (data['variants'] and (' - '+data['variants']) or '')
422 'active': lambda *a: 1,
423 'price_extra': lambda *a: 0.0,
424 'price_margin': lambda *a: 1.0,
427 _name = "product.product"
428 _description = "Product"
429 _table = "product_product"
430 _inherits = {'product.template': 'product_tmpl_id'}
432 'qty_available': fields.function(_product_qty_available, method=True, type='float', string='Real Stock'),
433 'virtual_available': fields.function(_product_virtual_available, method=True, type='float', string='Virtual Stock'),
434 'incoming_qty': fields.function(_product_incoming_qty, method=True, type='float', string='Incoming'),
435 'outgoing_qty': fields.function(_product_outgoing_qty, method=True, type='float', string='Outgoing'),
436 'price': fields.function(_product_price, method=True, type='float', string='Customer Price', digits=(16, int(config['price_accuracy']))),
437 'lst_price' : fields.function(_product_lst_price, method=True, type='float', string='List Price', digits=(16, int(config['price_accuracy']))),
438 'code': fields.function(_product_code, method=True, type='char', string='Code'),
439 'partner_ref' : fields.function(_product_partner_ref, method=True, type='char', string='Customer ref'),
440 'default_code' : fields.char('Code', size=64),
441 'active': fields.boolean('Active'),
442 'variants': fields.char('Variants', size=64),
443 'product_tmpl_id': fields.many2one('product.template', 'Product Template', required=True),
444 'ean13': fields.char('EAN13', size=13),
445 'packaging' : fields.one2many('product.packaging', 'product_id', 'Logistical Units', help="Gives the different ways to package the same product. This has no impact on the picking order and is mainly used if you use the EDI module."),
446 'price_extra': fields.float('Variant Price Extra', digits=(16, int(config['price_accuracy']))),
447 'price_margin': fields.float('Variant Price Margin', digits=(16, int(config['price_accuracy']))),
448 'pricelist_id': fields.dummy(string='Pricelist',relation='product.pricelist', type='many2one'),
451 def onchange_uom(self, cursor, user, ids, uom_id,uom_po_id):
452 if uom_id and uom_po_id:
453 uom_obj=self.pool.get('product.uom')
454 uom=uom_obj.browse(cursor,user,[uom_id])[0]
455 uom_po=uom_obj.browse(cursor,user,[uom_po_id])[0]
456 if uom.category_id.id != uom_po.category_id.id:
457 return {'value': {'uom_po_id': uom_id}}
460 def _check_ean_key(self, cr, uid, ids):
461 for partner in self.browse(cr, uid, ids):
462 if not partner.ean13:
464 if len(partner.ean13) <> 13:
473 sum += int(partner.ean13[i])
475 sum += 3 * int(partner.ean13[i])
476 check = int(math.ceil(sum / 10.0) * 10 - sum)
477 if check != int(partner.ean13[12]):
481 _constraints = [(_check_ean_key, 'Error: Invalid ean code', ['ean13'])]
483 def on_order(self, cr, uid, ids, orderline, quantity):
486 def name_get(self, cr, user, ids, context={}):
490 #name = self._product_partner_ref(cr, user, [d['id']], '', '', context)[d['id']]
491 #code = self._product_code(cr, user, [d['id']], '', '', context)[d['id']]
492 name = d.get('name','')
493 code = d.get('default_code',False)
495 name = '[%s] %s' % (code,name)
497 name = name + ' - %s' % (d['variants'],)
498 return (d['id'], name)
499 result = map(_name_get, self.read(cr, user, ids, ['variants','name','default_code'], context))
502 def name_search(self, cr, user, name='', args=None, operator='ilike', context=None, limit=100):
508 ids = self.search(cr, user, [('default_code','=',name)]+ args, limit=limit, context=context)
510 ids = self.search(cr, user, [('ean13','=',name)]+ args, limit=limit, context=context)
512 ids = self.search(cr, user, [('default_code',operator,name)]+ args, limit=limit, context=context)
513 ids += self.search(cr, user, [('name',operator,name)]+ args, limit=limit, context=context)
515 ids = self.search(cr, user, args, limit=limit, context=context)
516 result = self.name_get(cr, user, ids, context)
520 # Could be overrided for variants matrices prices
522 def price_get(self, cr, uid, ids, ptype='list_price', context={}):
524 product_uom_obj = self.pool.get('product.uom')
526 for product in self.browse(cr, uid, ids, context=context):
527 res[product.id] = product[ptype] or 0.0
528 if ptype == 'list_price':
529 res[product.id] = (res[product.id] * (product.price_margin or 1.0)) + \
532 uom = product.uos_id or product.uom_id
533 res[product.id] = product_uom_obj._compute_price(cr, uid,
534 uom.id, res[product.id], context['uom'])
537 def copy(self, cr, uid, id, default=None, context=None):
541 product = self.read(cr, uid, id, ['name'], context=context)
544 default = default.copy()
545 default['name'] = product['name'] + _(' (copy)')
547 if context.get('variant',False):
548 fields = ['product_tmpl_id', 'active', 'variants', 'default_code',
549 'price_margin', 'price_extra']
550 data = self.read(cr, uid, id, fields=fields, context=context)
554 data['product_tmpl_id'] = data.get('product_tmpl_id', False) \
555 and data['product_tmpl_id'][0]
557 return self.create(cr, uid, data)
559 return super(product_product, self).copy(cr, uid, id, default=default,
563 class product_packaging(osv.osv):
564 _name = "product.packaging"
565 _description = "Packaging"
568 'sequence': fields.integer('Sequence'),
569 'name' : fields.char('Description', size=64),
570 'qty' : fields.float('Quantity by Package',
571 help="The total number of products you can put by palet or box."),
572 'ul' : fields.many2one('product.ul', 'Type of Package', required=True),
573 'ul_qty' : fields.integer('Package by layer'),
574 'rows' : fields.integer('Number of Layer', required=True,
575 help='The number of layer on a palet or box'),
576 'product_id' : fields.many2one('product.product', 'Product', select=1, ondelete='cascade', required=True),
577 'ean' : fields.char('EAN', size=14,
578 help="The EAN code of the package unit."),
579 'code' : fields.char('Code', size=14,
580 help="The code of the transport unit."),
581 'weight': fields.float('Total Package Weight',
582 help='The weight of a full of products palet or box.'),
583 'weight_ul': fields.float('Empty Package Weight',
584 help='The weight of the empty UL'),
585 'height': fields.float('Height', help='The height of the package'),
586 'width': fields.float('Width', help='The width of the package'),
587 'length': fields.float('Length', help='The length of the package'),
592 def name_get(self, cr, uid, ids, context={}):
596 for pckg in self.browse(cr, uid, ids,context=context):
597 p_name = pckg.ean and '[' + pckg.ean + '] ' or ''
598 p_name += pckg.ul.name
599 res.append((pckg.id,p_name))
602 def _get_1st_ul(self, cr, uid, context={}):
603 cr.execute('select id from product_ul order by id asc limit 1')
605 return (res and res[0]) or False
608 'rows' : lambda *a : 3,
609 'sequence' : lambda *a : 1,
614 salt = '31' * 6 + '3'
616 for ean_part, salt_part in zip(ean, salt):
617 sum += int(ean_part) * int(salt_part)
618 return (10 - (sum % 10)) % 10
619 checksum = staticmethod(checksum)
624 class product_supplierinfo(osv.osv):
625 _name = "product.supplierinfo"
626 _description = "Information about a product supplier"
628 'name' : fields.many2one('res.partner', 'Partner', required=True, ondelete='cascade', help="Supplier of this product"),
629 'product_name': fields.char('Partner Product Name', size=128, help="Name of the product for this partner, will be used when printing a request for quotation. Keep empty to use the internal one."),
630 'product_code': fields.char('Partner Product Code', size=64, help="Code of the product for this partner, will be used when printing a request for quotation. Keep empty to use the internal one."),
631 'sequence' : fields.integer('Priority'),
632 'qty' : fields.float('Minimal Quantity', required=True, help="The minimal quantity to purchase for this supplier, expressed in the purchase unit of measure."),
633 'product_id' : fields.many2one('product.template', 'Product', required=True, ondelete='cascade', select=True),
634 'delay' : fields.integer('Delivery Delay', required=True, help="Delay in days between the confirmation of the purchase order and the reception of the products in your warehouse. Used by the scheduler for automatic computation of the purchase order planning."),
635 'pricelist_ids': fields.one2many('pricelist.partnerinfo', 'suppinfo_id', 'Supplier Pricelist'),
636 'company_id':fields.many2one('res.company','Company'),
639 'qty': lambda *a: 0.0,
640 'sequence': lambda *a: 1,
641 'delay': lambda *a: 1,
642 'company_id': lambda self,cr,uid,c: self.pool.get('res.company')._company_default_get(cr, uid, 'product.supplierinfo', c)
645 product_supplierinfo()
648 class pricelist_partnerinfo(osv.osv):
649 _name = 'pricelist.partnerinfo'
651 'name': fields.char('Description', size=64),
652 'suppinfo_id': fields.many2one('product.supplierinfo', 'Partner Information', required=True, ondelete='cascade'),
653 'min_quantity': fields.float('Quantity', required=True),
654 'price': fields.float('Unit Price', required=True, digits=(16, int(config['price_accuracy']))),
656 _order = 'min_quantity asc'
657 pricelist_partnerinfo()
661 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: