##############################################################################
#
# OpenERP, Open Source Management Solution
-# Copyright (C) 2004-2008 Tiny SPRL (<http://tiny.be>). All Rights Reserved
+# Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>). All Rights Reserved
# $Id$
#
# This program is free software: you can redistribute it and/or modify
import netsvc
import time
from mx import DateTime
+from tools.translate import _
#----------------------------------------------------------
# Workcenters
'workcenter_lines': fields.one2many('mrp.routing.workcenter', 'routing_id', 'Workcenters'),
'location_id': fields.many2one('stock.location', 'Production Location',
- help="Keep empty if you produce at the location where the finnished products are needed." \
- "Put a location if you produce at a fixed location. This can be a partner location " \
+ help="Keep empty if you produce at the location where the finished products are needed." \
+ "Set a location if you produce at a fixed location. This can be a partner location " \
"if you subcontract the manufacturing operations."
),
}
result = {}
for bom in self.browse(cr, uid, ids, context=context):
result[bom.id] = map(lambda x: x.id, bom.bom_lines)
+ if bom.bom_lines:
+ continue
ok = ((name=='child_complete_ids') and (bom.product_id.supply_method=='produce'))
if bom.type=='phantom' or ok:
sids = self.pool.get('mrp.bom').search(cr, uid, [('bom_id','=',False),('product_id','=',bom.product_id.id)])
'product_efficiency': fields.float('Product Efficiency', required=True, help="Efficiency on the production. A factor of 0.9 means a loss of 10% in the production."),
'bom_lines': fields.one2many('mrp.bom', 'bom_id', 'BoM Lines'),
'bom_id': fields.many2one('mrp.bom', 'Parent BoM', ondelete='cascade', select=True),
- 'routing_id': fields.many2one('mrp.routing', 'Routing', help="The list of operations (list of workcenters) to produce the finnished product. The routing is mainly used to compute workcenter costs during operations and to plan futur loads on workcenters based on production plannification."),
+ 'routing_id': fields.many2one('mrp.routing', 'Routing', help="The list of operations (list of workcenters) to produce the finished product. The routing is mainly used to compute workcenter costs during operations and to plan futur loads on workcenters based on production plannification."),
'property_ids': fields.many2many('mrp.property', 'mrp_bom_property_rel', 'bom_id','property_id', 'Properties'),
'revision_ids': fields.one2many('mrp.bom.revision', 'bom_id', 'BoM Revisions'),
'revision_type': fields.selection([('numeric','numeric indices'),('alpha','alphabetical indices')], 'indice type'),
return {'value': v}
return {}
- def _bom_find(self, cr, uid, product_id, product_uom, properties = []):
+ def _bom_find(self, cr, uid, product_id, product_uom, properties=[]):
bom_result = False
# Why searching on BoM without parent ?
- cr.execute('select id from mrp_bom where product_id=%d and bom_id is null order by sequence', (product_id,))
+ cr.execute('select id from mrp_bom where product_id=%s and bom_id is null order by sequence', (product_id,))
ids = map(lambda x: x[0], cr.fetchall())
max_prop = 0
result = False
prop+=1
if (prop>max_prop) or ((max_prop==0) and not result):
result = bom.id
+ max_prop = prop
return result
- def _bom_explode(self, cr, uid, bom, factor, properties, addthis=False, level=10):
+ def _bom_explode(self, cr, uid, bom, factor, properties, addthis=False, level=0):
factor = factor / (bom.product_efficiency or 1.0)
factor = rounding(factor, bom.product_rounding)
if factor<bom.product_rounding:
factor = bom.product_rounding
result = []
result2 = []
+ phantom=False
if bom.type=='phantom' and not bom.bom_lines:
newbom = self._bom_find(cr, uid, bom.product_id.id, bom.product_uom.id, properties)
if newbom:
res = self._bom_explode(cr, uid, self.browse(cr, uid, [newbom])[0], factor*bom.product_qty, properties, addthis=True, level=level+10)
result = result + res[0]
result2 = result2 + res[1]
+ phantom=True
else:
- return [],[]
- else:
+ phantom=False
+ if not phantom:
if addthis and not bom.bom_lines:
result.append(
{
for wc_use in bom.routing_id.workcenter_lines:
wc = wc_use.workcenter_id
d, m = divmod(factor, wc_use.workcenter_id.capacity_per_cycle)
- cycle = (d + (m and 1.0 or 0.0)) * wc_use.cycle_nbr
+ mult = (d + (m and 1.0 or 0.0))
+ cycle = mult * wc_use.cycle_nbr
result2.append({
'name': bom.routing_id.name,
'workcenter_id': wc.id,
- 'sequence': level,
+ 'sequence': level+(wc_use.sequence or 0),
'cycle': cycle,
- 'hour': wc_use.hour_nbr + (wc.time_start+wc.time_stop+cycle*wc.time_cycle) * (wc.time_efficiency or 1.0),
+ 'hour': float(wc_use.hour_nbr*mult + (wc.time_start+wc.time_stop+cycle*wc.time_cycle) * (wc.time_efficiency or 1.0)),
})
for bom2 in bom.bom_lines:
res = self._bom_explode(cr, uid, bom2, factor, properties, addthis=True, level=level+10)
res[production['id']]=move.sale_line_id and move.sale_line_id.order_id.client_order_ref or False
return res
+ def _production_calc(self, cr, uid, ids, prop, unknow_none, context={}):
+ result = {}
+ for prod in self.browse(cr, uid, ids, context=context):
+ result[prod.id] = {
+ 'hour_total': 0.0,
+ 'cycle_total': 0.0,
+ }
+ for wc in prod.workcenter_lines:
+ result[prod.id]['hour_total'] += wc.hour
+ result[prod.id]['cycle_total'] += wc.cycle
+ return result
+
+ def _production_date(self, cr, uid, ids, prop, unknow_none, context={}):
+ result = {}
+ for prod in self.browse(cr, uid, ids, context=context):
+ result[prod.id] = prod.date_planned[:10]
+ return result
+
def _sale_name_calc(self, cr, uid, ids, prop, unknow_none, unknow_dict):
return self._get_sale_order(cr,uid,ids,field_name='name')
'priority': fields.selection([('0','Not urgent'),('1','Normal'),('2','Urgent'),('3','Very Urgent')], 'Priority'),
'product_id': fields.many2one('product.product', 'Product', required=True, domain=[('type','<>','service')]),
- 'product_qty': fields.float('Product Qty', required=True),
- 'product_uom': fields.many2one('product.uom', 'Product UOM', required=True),
- 'product_uos_qty': fields.float('Product Qty'),
- 'product_uos': fields.many2one('product.uom', 'Product UOM'),
+ 'product_qty': fields.float('Product Qty', required=True, states={'draft':[('readonly',False)]}, readonly=True),
+ 'product_uom': fields.many2one('product.uom', 'Product UOM', required=True, states={'draft':[('readonly',False)]}, readonly=True),
+ 'product_uos_qty': fields.float('Product UoS Qty', states={'draft':[('readonly',False)]}, readonly=True),
+ 'product_uos': fields.many2one('product.uom', 'Product UoS', states={'draft':[('readonly',False)]}, readonly=True),
- 'location_src_id': fields.many2one('stock.location', 'Raw Products Location', required=True,
+ 'location_src_id': fields.many2one('stock.location', 'Raw Materials Location', required=True,
help="Location where the system will look for products used in raw materials."),
- 'location_dest_id': fields.many2one('stock.location', 'Finnished Products Location', required=True,
- help="Location where the system will stock the finnished products."),
+ 'location_dest_id': fields.many2one('stock.location', 'Finished Products Location', required=True,
+ help="Location where the system will stock the finished products."),
+ 'date_planned_date': fields.function(_production_date, method=True, type='date', string='Planned Date'),
'date_planned': fields.datetime('Scheduled date', required=True, select=1),
'date_start': fields.datetime('Start Date'),
'date_finnished': fields.datetime('End Date'),
'bom_id': fields.many2one('mrp.bom', 'Bill of Material', domain=[('bom_id','=',False)]),
+ 'routing_id': fields.many2one('mrp.routing', string='Routing', on_delete='set null'),
'picking_id': fields.many2one('stock.picking', 'Packing list', readonly=True,
help="This is the internal picking list take bring the raw materials to the production plan."),
'workcenter_lines': fields.one2many('mrp.production.workcenter.line', 'production_id', 'Workcenters Utilisation'),
'state': fields.selection([('draft','Draft'),('picking_except', 'Packing Exception'),('confirmed','Waiting Goods'),('ready','Ready to Produce'),('in_production','In Production'),('cancel','Canceled'),('done','Done')],'Status', readonly=True),
+ 'hour_total': fields.function(_production_calc, method=True, type='float', string='Total Hours', multi='workorder'),
+ 'cycle_total': fields.function(_production_calc, method=True, type='float', string='Total Cycles', multi='workorder'),
+
'sale_name': fields.function(_sale_name_calc, method=True, type='char', string='Sale Name'),
'sale_ref': fields.function(_sale_ref_calc, method=True, type='char', string='Sale Ref'),
}
'name': lambda x,y,z,c: x.pool.get('ir.sequence').get(y,z,'mrp.production') or '/',
}
_order = 'date_planned asc, priority desc';
- def unlink(self, cr, uid, ids):
+ def unlink(self, cr, uid, ids, context=None):
productions = self.read(cr, uid, ids, ['state'])
unlink_ids = []
for s in productions:
unlink_ids.append(s['id'])
else:
raise osv.except_osv(_('Invalid action !'), _('Cannot delete Production Order(s) which are in %s State!' % s['state']))
- return osv.osv.unlink(self, cr, uid, unlink_ids)
+ return osv.osv.unlink(self, cr, uid, unlink_ids, context=context)
+
+ def copy(self, cr, uid, id, default=None,context=None):
+ if not default:
+ default = {}
+ default.update({
+ 'name': self.pool.get('ir.sequence').get(cr, uid, 'mrp.production'),
+ 'move_lines' : [],
+ 'move_created_ids': []
+ })
+ return super(mrp_production, self).copy(cr, uid, id, default, context)
def location_id_change(self, cr, uid, ids, src, dest, context={}):
if dest:
def action_compute(self, cr, uid, ids, properties=[]):
results = []
for production in self.browse(cr, uid, ids):
- cr.execute('delete from mrp_production_product_line where production_id=%d', (production.id,))
- cr.execute('delete from mrp_production_workcenter_line where production_id=%d', (production.id,))
+ cr.execute('delete from mrp_production_product_line where production_id=%s', (production.id,))
+ cr.execute('delete from mrp_production_workcenter_line where production_id=%s', (production.id,))
bom_point = production.bom_id
bom_id = production.bom_id.id
if not bom_point:
bom_id = self.pool.get('mrp.bom')._bom_find(cr, uid, production.product_id.id, production.product_uom.id, properties)
if bom_id:
- self.write(cr, uid, [production.id], {'bom_id': bom_id})
- bom_point = self.pool.get('mrp.bom').browse(cr, uid, [bom_id])[0]
+ bom_point = self.pool.get('mrp.bom').browse(cr, uid, bom_id)
+ routing_id = bom_point.routing_id.id or False
+ self.write(cr, uid, [production.id], {'bom_id': bom_id, 'routing_id': routing_id})
if not bom_id:
- raise osv.except_osv('Error', "Couldn't find bill of material for product")
+ raise osv.except_osv(_('Error'), _("Couldn't find bill of material for product"))
#if bom_point.routing_id and bom_point.routing_id.location_id:
# self.write(cr, uid, [production.id], {'location_src_id': bom_point.routing_id.location_id.id})
for move in production.move_created_ids:
#XXX must use the orm
cr.execute('INSERT INTO stock_move_history_ids \
- (parent_id, child_id) VALUES (%d,%d)',
+ (parent_id, child_id) VALUES (%s,%s)',
(res.id, move.id))
move_ids.append(res.id)
vals= {'state':'confirmed'}
}
res_final_id = self.pool.get('stock.move').create(cr, uid, data)
- self.write(cr, uid, [production.id], {'move_created_ids': [(6, 'WTF', [res_final_id])]})
+ self.write(cr, uid, [production.id], {'move_created_ids': [(6, 0, [res_final_id])]})
moves = []
for line in production.product_lines:
move_id=False
_columns = {
'name': fields.char('Name', size=64, required=True),
'workcenter_id': fields.many2one('mrp.workcenter', 'Workcenter', required=True),
- 'cycle': fields.float('Nbr of cycle'),
- 'hour': fields.float('Nbr of hour'),
+ 'cycle': fields.float('Nbr of cycle', digits=(16,2)),
+ 'hour': fields.float('Nbr of hour', digits=(16,2)),
'sequence': fields.integer('Sequence', required=True),
'production_id': fields.many2one('mrp.production', 'Production Order', select=True),
}
'date_planned': fields.datetime('Scheduled date', required=True),
'date_close': fields.datetime('Date Closed'),
'product_id': fields.many2one('product.product', 'Product', required=True),
- 'product_qty': fields.float('Quantity', required=True),
- 'product_uom': fields.many2one('product.uom', 'Product UoM', required=True),
- 'product_uos_qty': fields.float('UoS Quantity'),
- 'product_uos': fields.many2one('product.uom', 'Product UoS'),
+ 'product_qty': fields.float('Quantity', required=True, states={'draft':[('readonly',False)]}, readonly=True),
+ 'product_uom': fields.many2one('product.uom', 'Product UoM', required=True, states={'draft':[('readonly',False)]}, readonly=True),
+ 'product_uos_qty': fields.float('UoS Quantity', states={'draft':[('readonly',False)]}, readonly=True),
+ 'product_uos': fields.many2one('product.uom', 'Product UoS', states={'draft':[('readonly',False)]}, readonly=True),
'move_id': fields.many2one('stock.move', 'Reservation', ondelete='set null'),
'bom_id': fields.many2one('mrp.bom', 'BoM', ondelete='cascade', select=True),
" a make to order method."),
'purchase_id': fields.many2one('purchase.order', 'Purchase Order'),
- 'purchase_line_id': fields.many2one('purchase.order.line', 'Purchase Order Line'),
+ 'note': fields.text('Note'),
'property_ids': fields.many2many('mrp.property', 'mrp_procurement_property_rel', 'procurement_id','property_id', 'Properties'),
'message': fields.char('Latest error', size=64),
- 'state': fields.selection([('draft','Draft'),('confirmed','Confirmed'),('exception','Exception'),('running','Running'),('cancel','Cancel'),('done','Done'),('waiting','Waiting')], 'Status')
+ 'state': fields.selection([
+ ('draft','Draft'),
+ ('confirmed','Confirmed'),
+ ('exception','Exception'),
+ ('running','Running'),
+ ('cancel','Cancel'),
+ ('ready','Ready'),
+ ('done','Done'),
+ ('waiting','Waiting')], 'Status', required=True),
+ 'note' : fields.text('Note'),
}
_defaults = {
'state': lambda *a: 'draft',
'close_move': lambda *a: 0,
'procure_method': lambda *a: 'make_to_order',
}
-
- def unlink(self, cr, uid, ids):
+
+ def unlink(self, cr, uid, ids, context=None):
procurements = self.read(cr, uid, ids, ['state'])
unlink_ids = []
for s in procurements:
unlink_ids.append(s['id'])
else:
raise osv.except_osv(_('Invalid action !'), _('Cannot delete Procurement Order(s) which are in %s State!' % s['state']))
- return osv.osv.unlink(self, cr, uid, unlink_ids)
-
+ return osv.osv.unlink(self, cr, uid, unlink_ids, context=context)
+
def onchange_product_id(self, cr, uid, ids, product_id, context={}):
if product_id:
w=self.pool.get('product.product').browse(cr,uid,product_id, context)
def check_move_cancel(self, cr, uid, ids, context={}):
res = True
+ ok = False
for procurement in self.browse(cr, uid, ids, context):
if procurement.move_id:
+ ok = True
if not procurement.move_id.state=='cancel':
res = False
- return res
+ return res and ok
def check_move_done(self, cr, uid, ids, context={}):
res = True
#
def _quantity_compute_get(self, cr, uid, proc, context={}):
if proc.product_id.type=='product':
- return proc.move_id.product_uos_qty
+ if proc.move_id.product_uos:
+ return proc.move_id.product_uos_qty
return False
def _uom_compute_get(self, cr, uid, proc, context={}):
properties = [x.id for x in procurement.property_ids]
bom_id = self.pool.get('mrp.bom')._bom_find(cr, uid, procurement.product_id.id, procurement.product_uom.id, properties)
if not bom_id:
- cr.execute('update mrp_procurement set message=%s where id=%d', ('No BoM defined for this product !', procurement.id))
+ cr.execute('update mrp_procurement set message=%s where id=%s', (_('No BoM defined for this product !'), procurement.id))
return False
return True
res = True
user = self.pool.get('res.users').browse(cr, uid, uid)
for procurement in self.browse(cr, uid, ids):
- if procurement.product_id.product_tmpl_id.supply_method=='buy':
+ if procurement.product_id.product_tmpl_id.supply_method<>'produce':
if procurement.product_id.seller_ids:
partner = procurement.product_id.seller_ids[0].name
if user.company_id and user.company_id.partner_id:
def check_buy(self, cr, uid, ids):
user = self.pool.get('res.users').browse(cr, uid, uid)
for procurement in self.browse(cr, uid, ids):
- if procurement.product_id.product_tmpl_id.supply_method=='produce':
+ if procurement.product_id.product_tmpl_id.supply_method<>'buy':
return False
if not procurement.product_id.seller_ids:
- cr.execute('update mrp_procurement set message=%s where id=%d', ('No supplier defined for this product !', procurement.id))
+ cr.execute('update mrp_procurement set message=%s where id=%s', (_('No supplier defined for this product !'), procurement.id))
return False
partner = procurement.product_id.seller_ids[0].name
if user.company_id and user.company_id.partner_id:
return False
address_id = self.pool.get('res.partner').address_get(cr, uid, [partner.id], ['delivery'])['delivery']
if not address_id:
- cr.execute('update mrp_procurement set message=%s where id=%d', ('No address defined for the supplier', procurement.id))
+ cr.execute('update mrp_procurement set message=%s where id=%s', (_('No address defined for the supplier'), procurement.id))
return False
return True
return True
def action_move_assigned(self, cr, uid, ids):
- self.write(cr, uid, ids, {'state':'running','message':'from stock: products assigned.'})
+ self.write(cr, uid, ids, {'state':'running','message':_('from stock: products assigned.')})
return True
def _check_make_to_stock_service(self, cr, uid, procurement, context={}):
id = procurement.move_id.id
if not (procurement.move_id.state in ('done','assigned','cancel')):
ok = ok and self.pool.get('stock.move').action_assign(cr, uid, [id])
- cr.execute('select count(id) from stock_warehouse_orderpoint where product_id=%d', (procurement.product_id.id,))
+ cr.execute('select count(id) from stock_warehouse_orderpoint where product_id=%s', (procurement.product_id.id,))
if not cr.fetchone()[0]:
- cr.execute('update mrp_procurement set message=%s where id=%d', ('from stock and no minimum orderpoint rule defined', procurement.id))
+ cr.execute('update mrp_procurement set message=%s where id=%s', (_('from stock and no minimum orderpoint rule defined'), procurement.id))
return ok
def action_produce_assign_service(self, cr, uid, ids, context={}):
price = self.pool.get('product.pricelist').price_get(cr, uid, [pricelist_id], procurement.product_id.id, qty, False, {'uom': uom_id})[pricelist_id]
- newdate = DateTime.strptime(procurement.date_planned, '%Y-%m-%d %H:%M:%S') - DateTime.RelativeDateTime(days=procurement.product_id.product_tmpl_id.seller_delay or 0.0)
+ newdate = DateTime.strptime(procurement.date_planned, '%Y-%m-%d %H:%M:%S')
newdate = newdate - DateTime.RelativeDateTime(days=company.po_lead)
context.update({'lang':partner.lang})
product=self.pool.get('product.product').browse(cr,uid,procurement.product_id.id,context=context)
}
taxes_ids = procurement.product_id.product_tmpl_id.supplier_taxes_id
- self.pool.get('account.fiscal.position').map_tax(cr, uid, partner, taxes_ids)
+ taxes = self.pool.get('account.fiscal.position').map_tax(cr, uid, partner.property_account_position, taxes_ids)
line.update({
- 'taxes_id':[(6,0,taxes_ids)]
+ 'taxes_id':[(6,0,taxes)]
})
purchase_id = self.pool.get('purchase.order').create(cr, uid, {
'origin': procurement.origin,
'partner_address_id': address_id,
'location_id': procurement.location_id.id,
'pricelist_id': pricelist_id,
- 'order_line': [(0,0,line)]
+ 'order_line': [(0,0,line)],
+ 'fiscal_position': partner.property_account_position and partner.property_account_position.id or False
})
self.write(cr, uid, [procurement.id], {'state':'running', 'purchase_id':purchase_id})
return purchase_id
def action_cancel(self, cr, uid, ids):
todo = []
+ todo2 = []
for proc in self.browse(cr, uid, ids):
if proc.move_id and proc.move_id.state=='waiting':
- todo.append(proc.move_id.id)
+ if proc.close_move:
+ todo2.append(proc.move_id.id)
+ else:
+ todo.append(proc.move_id.id)
+ if len(todo2):
+ self.pool.get('stock.move').action_cancel(cr, uid, todo2)
if len(todo):
self.pool.get('stock.move').write(cr, uid, todo, {'state':'assigned'})
self.write(cr, uid, ids, {'state':'cancel'})
-
wf_service = netsvc.LocalService("workflow")
for id in ids:
wf_service.trg_trigger(uid, 'mrp.procurement', id, cr)
-
return True
def action_check_finnished(self, cr, uid, ids):
- return True
+ return self.check_move_done(cr, uid, ids)
def action_check(self, cr, uid, ids):
ok = False
ok = True
return ok
+ def action_ready(self, cr, uid, ids):
+ res = self.write(cr, uid, ids, {'state':'ready'})
+ return res
+
def action_done(self, cr, uid, ids):
for procurement in self.browse(cr, uid, ids):
if procurement.move_id:
if procurement.close_move and (procurement.move_id.state <> 'done'):
self.pool.get('stock.move').action_done(cr, uid, [procurement.move_id.id])
res = self.write(cr, uid, ids, {'state':'done', 'date_close':time.strftime('%Y-%m-%d')})
-
wf_service = netsvc.LocalService("workflow")
for id in ids:
wf_service.trg_trigger(uid, 'mrp.procurement', id, cr)
return res
+
def run_scheduler(self, cr, uid, automatic=False, use_new_cursor=False, context=None):
'''
use_new_cursor: False or the dbname
v = {'product_uom':prod.uom_id.id}
return {'value': v}
return {}
+ def copy(self, cr, uid, id, default=None,context={}):
+ if not default:
+ default = {}
+ default.update({
+ 'name': self.pool.get('ir.sequence').get(cr, uid, 'mrp.warehouse.orderpoint') or '',
+ })
+ return super(stock_warehouse_orderpoint, self).copy(cr, uid, id, default, context)
stock_warehouse_orderpoint()
'product_uos_qty': line['product_uos_qty'],
'move_dest_id': move.id,
'state': state,
+ 'name': line['name'],
'location_dest_id': dest,
'move_history_ids': [(6,0,[move.id])],
'move_history_ids2': [(6,0,[])],