[FIX] Schedule jobs even if their next time has passed.
[odoo/odoo.git] / addons / mrp / mrp.py
index c796391..f500d4f 100644 (file)
@@ -2,7 +2,7 @@
 ##############################################################################
 #
 #    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
@@ -27,6 +27,7 @@ import ir
 import netsvc
 import time
 from mx import DateTime
+from tools.translate import _
 
 #----------------------------------------------------------
 # Workcenters
@@ -108,8 +109,8 @@ class mrp_routing(osv.osv):
         '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."
         ),
     }
@@ -128,7 +129,7 @@ class mrp_routing_workcenter(osv.osv):
         'cycle_nbr': fields.float('Number of Cycle', required=True,
             help="A cycle is defined in the workcenter definition."),
         'hour_nbr': fields.float('Number of Hours', required=True),
-        'routing_id': fields.many2one('mrp.routing', 'Parent Routing', select=True),
+        'routing_id': fields.many2one('mrp.routing', 'Parent Routing', select=True, ondelete='cascade'),
         'note': fields.text('Description')
     }
     _defaults = {
@@ -144,6 +145,8 @@ class mrp_bom(osv.osv):
         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)])
@@ -190,7 +193,7 @@ class mrp_bom(osv.osv):
         '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'),
@@ -213,7 +216,7 @@ class mrp_bom(osv.osv):
     def _check_recursion(self, cr, uid, ids):
         level = 500
         while len(ids):
-            cr.execute('select distinct bom_id from mrp_bom where id in ('+','.join(map(str,ids))+')')
+            cr.execute('select distinct bom_id from mrp_bom where id in %s', (tuple(ids),))
             ids = filter(None, map(lambda x:x[0], cr.fetchall()))
             if not level:
                 return False
@@ -233,10 +236,10 @@ class mrp_bom(osv.osv):
             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
@@ -247,24 +250,27 @@ class mrp_bom(osv.osv):
                     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(
                 {
@@ -279,13 +285,14 @@ class mrp_bom(osv.osv):
                 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': float(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)
@@ -293,21 +300,6 @@ class mrp_bom(osv.osv):
                 result2 = result2 + res[1]
         return result, result2
 
-    def set_indices(self, cr, uid, ids, context = {}):
-        if not ids or (ids and not ids[0]):
-            return True
-        res = self.read(cr, uid, ids, ['revision_ids', 'revision_type'])
-        rev_ids = res[0]['revision_ids']
-        idx = 1
-        new_idx = []
-        for rev_id in rev_ids:
-            if res[0]['revision_type'] == 'numeric':
-                self.pool.get('mrp.bom.revision').write(cr, uid, [rev_id], {'indice' : idx})
-            else:
-                self.pool.get('mrp.bom.revision').write(cr, uid, [rev_id], {'indice' : "%c"%(idx+96,)})
-            idx+=1
-        return True
-
 mrp_bom()
 
 class mrp_bom_revision(osv.osv):
@@ -340,27 +332,6 @@ class mrp_production(osv.osv):
     _description = 'Production'
     _date_name  = 'date_planned'
 
-    def _get_sale_order(self,cr,uid,ids,field_name=False):
-        move_obj=self.pool.get('stock.move')
-        def get_parent_move(move_id):
-            move = move_obj.browse(cr,uid,move_id)
-            if move.move_dest_id:
-                return get_parent_move(move.move_dest_id.id)
-            return move_id
-        productions=self.read(cr,uid,ids,['id','move_prod_id'])
-        res={}
-        for production in productions:
-            res[production['id']]=False
-            if production.get('move_prod_id',False):
-                parent_move_line=get_parent_move(production['move_prod_id'][0])
-                if parent_move_line:
-                    move = move_obj.browse(cr,uid,parent_move_line)
-                    if field_name=='name':
-                        res[production['id']]=move.sale_line_id and move.sale_line_id.order_id.name or False
-                    if field_name=='client_order_ref':
-                        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):
@@ -373,35 +344,36 @@ class mrp_production(osv.osv):
                 result[prod.id]['cycle_total'] += wc.cycle
         return result
 
+    def _production_date_end(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
+        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')
-
-    def _sale_ref_calc(self, cr, uid, ids, prop, unknow_none, unknow_dict):
-        return self._get_sale_order(cr,uid,ids,field_name='client_order_ref')
-
     _columns = {
         'name': fields.char('Reference', size=64, required=True),
         'origin': fields.char('Origin', size=64),
         '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_end': fields.function(_production_date_end, method=True, type='date', string='Scheduled End'),
+        'date_planned_date': fields.function(_production_date, method=True, type='date', string='Scheduled Date'),
         'date_planned': fields.datetime('Scheduled date', required=True, select=1),
         'date_start': fields.datetime('Start Date'),
         'date_finnished': fields.datetime('End Date'),
@@ -422,8 +394,6 @@ class mrp_production(osv.osv):
         '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'),
     }
     _defaults = {
         'priority': lambda *a: '1',
@@ -433,7 +403,7 @@ class mrp_production(osv.osv):
         '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:
@@ -441,7 +411,18 @@ class mrp_production(osv.osv):
                 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': [],
+            'state': 'draft'
+        })
+        return super(mrp_production, self).copy(cr, uid, id, default, context)
 
     def location_id_change(self, cr, uid, ids, src, dest, context={}):
         if dest:
@@ -458,6 +439,14 @@ class mrp_production(osv.osv):
         result = {'product_uom':uom}
         return {'value':result}
 
+    def bom_id_change(self, cr, uid, ids, product):
+        if not product:
+            return {}
+        res = self.pool.get('mrp.bom').read(cr, uid, [product], ['routing_id'])[0]
+        routing_id = res['routing_id'] and res['routing_id'][0]
+        result = {'routing_id':routing_id}
+        return {'value':result}
+
     def action_picking_except(self, cr, uid, ids):
         self.write(cr, uid, ids, {'state':'picking_except'})
         return True
@@ -465,8 +454,8 @@ class mrp_production(osv.osv):
     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:
@@ -477,7 +466,7 @@ class mrp_production(osv.osv):
                     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})
@@ -514,15 +503,15 @@ class mrp_production(osv.osv):
 
     #TODO Review materials in function in_prod and prod_end.
     def action_production_end(self, cr, uid, ids):
-        move_ids = []
+#        move_ids = []
         for production in self.browse(cr, uid, ids):
             for res in production.move_lines:
                 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)
+#                move_ids.append(res.id)
             vals= {'state':'confirmed'}
             new_moves = [x.id for x in production.move_created_ids]
             self.pool.get('stock.move').write(cr, uid, new_moves, vals)
@@ -532,7 +521,7 @@ class mrp_production(osv.osv):
             self.pool.get('stock.move').check_assign(cr, uid, new_moves)
             self.pool.get('stock.move').action_done(cr, uid, new_moves)
             self._costs_generate(cr, uid, production)
-        self.pool.get('stock.move').action_done(cr, uid, move_ids)
+#        self.pool.get('stock.move').action_done(cr, uid, move_ids)
         self.write(cr,  uid, ids, {'state': 'done'})
         return True
 
@@ -632,7 +621,7 @@ class mrp_production(osv.osv):
             }
             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
@@ -705,14 +694,15 @@ stock_move()
 
 class mrp_production_workcenter_line(osv.osv):
     _name = 'mrp.production.workcenter.line'
-    _description = 'Production workcenters used'
+    _description = 'Work Orders'
+    _order = 'sequence'
     _columns = {
-        'name': fields.char('Name', size=64, required=True),
+        'name': fields.char('Work Order', size=64, required=True),
         'workcenter_id': fields.many2one('mrp.workcenter', 'Workcenter', required=True),
         '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),
+        'production_id': fields.many2one('mrp.production', 'Production Order', select=True, ondelete='cascade'),
     }
     _defaults = {
         'sequence': lambda *a: 1,
@@ -754,10 +744,10 @@ class mrp_procurement(osv.osv):
         '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),
@@ -769,12 +759,21 @@ class mrp_procurement(osv.osv):
             " 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',
@@ -783,8 +782,8 @@ class mrp_procurement(osv.osv):
         '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:
@@ -792,8 +791,8 @@ class mrp_procurement(osv.osv):
                 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)
@@ -810,13 +809,26 @@ class mrp_procurement(osv.osv):
                 return True
         return False
 
+    def get_phantom_bom_id(self, cr, uid, ids, context=None):
+        for procurement in self.browse(cr, uid, ids, context=context):
+            if procurement.move_id and procurement.move_id.product_id.supply_method=='produce' \
+                 and procurement.move_id.product_id.procure_method=='make_to_order':
+                    phantom_bom_id = self.pool.get('mrp.bom').search(cr, uid, [
+                        ('product_id', '=', procurement.move_id.product_id.id),
+                        ('bom_id', '=', False),
+                        ('type', '=', 'phantom')]) 
+                    return phantom_bom_id 
+        return False
+
     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
@@ -832,7 +844,8 @@ class mrp_procurement(osv.osv):
     #
     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={}):
@@ -872,7 +885,7 @@ class mrp_procurement(osv.osv):
         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
 
@@ -889,7 +902,7 @@ class mrp_procurement(osv.osv):
         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:
@@ -907,10 +920,10 @@ class mrp_procurement(osv.osv):
     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:
@@ -918,7 +931,7 @@ class mrp_procurement(osv.osv):
                     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
 
@@ -930,6 +943,8 @@ class mrp_procurement(osv.osv):
 
     def action_confirm(self, cr, uid, ids, context={}):
         for procurement in self.browse(cr, uid, ids):
+            if procurement.product_qty <= 0.00:
+                raise osv.except_osv(_('Data Insufficient !'), _('Please check the Quantity of Procurement Order(s), it should not be less than 1!'))
             if procurement.product_id.type in ('product', 'consu'):
                 if not procurement.move_id:
                     source = procurement.location_id.id
@@ -947,14 +962,14 @@ class mrp_procurement(osv.osv):
                     })
                     self.write(cr, uid, [procurement.id], {'move_id': id, 'close_move':1})
                 else:
-                    # TODO: check this
-                    if procurement.procure_method=='make_to_stock' and procurement.move_id.state in ('waiting',):
-                        id = self.pool.get('stock.move').write(cr, uid, [procurement.move_id.id], {'state':'confirmed'})
+                    if procurement.procure_method=='make_to_stock' and procurement.move_id.state in ('draft','waiting',):
+                        # properly call action_confirm() on stock.move to abide by the location chaining etc.
+                        id = self.pool.get('stock.move').action_confirm(cr, uid, [procurement.move_id.id], context=context)
         self.write(cr, uid, ids, {'state':'confirmed','message':''})
         return True
 
-    def action_move_assigned(self, cr, uid, ids):
-        self.write(cr, uid, ids, {'state':'running','message':'from stock: products assigned.'})
+    def action_move_assigned(self, cr, uid, ids, context={}):
+        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={}):
@@ -966,9 +981,9 @@ class mrp_procurement(osv.osv):
             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={}):
@@ -1002,6 +1017,8 @@ class mrp_procurement(osv.osv):
                     [produce_id], properties=[x.id for x in procurement.property_ids])
             wf_service = netsvc.LocalService("workflow")
             wf_service.trg_validate(uid, 'mrp.production', produce_id, 'button_confirm', cr)
+            self.pool.get('stock.move').write(cr, uid, [res_id],
+                    {'location_id':procurement.location_id.id})
         return produce_id
 
     def action_po_assign(self, cr, uid, ids, context={}):
@@ -1022,13 +1039,17 @@ class mrp_procurement(osv.osv):
 
             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})
+            newdate = newdate - procurement.product_id.seller_ids[0].delay
+
+            #Passing partner_id to context for purchase order line integrity of Line name
+            context.update({'lang':partner.lang, 'partner_id':partner_id})
+            
             product=self.pool.get('product.product').browse(cr,uid,procurement.product_id.id,context=context)
 
             line = {
-                'name': product.name,
+                'name': product.partner_ref,
                 'product_qty': qty,
                 'product_id': procurement.product_id.id,
                 'product_uom': uom_id,
@@ -1039,9 +1060,9 @@ class mrp_procurement(osv.osv):
             }
 
             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,
@@ -1049,28 +1070,34 @@ class mrp_procurement(osv.osv):
                 '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 and proc.move_id:
+                if proc.move_id.state not in ('done','cancel'):
+                    todo2.append(proc.move_id.id)
+            else:
+                if proc.move_id and proc.move_id.state=='waiting':
+                    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
@@ -1080,17 +1107,21 @@ class mrp_procurement(osv.osv):
                 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
@@ -1110,10 +1141,10 @@ class stock_warehouse_orderpoint(osv.osv):
         'name': fields.char('Name', size=32, required=True),
         'active': fields.boolean('Active'),
         'logic': fields.selection([('max','Order to Max'),('price','Best price (not yet active!)')], 'Reordering Mode', required=True),
-        'warehouse_id': fields.many2one('stock.warehouse', 'Warehouse', required=True),
-        'location_id': fields.many2one('stock.location', 'Location', required=True),
-        'product_id': fields.many2one('product.product', 'Product', required=True, domain=[('type','=','product')]),
-        'product_uom': fields.many2one('product.uom', 'Product UOM', required=True ),
+        'warehouse_id': fields.many2one('stock.warehouse', 'Warehouse', required=True, ondelete="cascade"),
+        'location_id': fields.many2one('stock.location', 'Location', required=True, ondelete="cascade"),
+        'product_id': fields.many2one('product.product', 'Product', required=True, domain=[('type','=','product')], ondelete="cascade"),
+        'product_uom': fields.many2one('product.uom', 'Product UOM', required=True),
         'product_min_qty': fields.float('Min Quantity', required=True,
             help="When the virtual stock goes belong the Min Quantity, Open ERP generates "\
             "a procurement to bring the virtual stock to the Max Quantity."),
@@ -1122,7 +1153,7 @@ class stock_warehouse_orderpoint(osv.osv):
             "a procurement to bring the virtual stock to the Max Quantity."),
         'qty_multiple': fields.integer('Qty Multiple', required=True,
             help="The procurement quantity will by rounded up to this multiple."),
-        'procurement_id': fields.many2one('mrp.procurement', 'Purchase Order')
+        'procurement_id': fields.many2one('mrp.procurement', 'Purchase Order', ondelete="set null")
     }
     _defaults = {
         'active': lambda *a: 1,
@@ -1131,6 +1162,11 @@ class stock_warehouse_orderpoint(osv.osv):
         'name': lambda x,y,z,c: x.pool.get('ir.sequence').get(y,z,'mrp.warehouse.orderpoint') or '',
         'product_uom': lambda sel, cr, uid, context: context.get('product_uom', False),
     }
+    
+    _sql_constraints = [
+        ( 'qty_multiple_check', 'CHECK( qty_multiple > 0 )', _('Qty Multiple must be greater than zero.')),
+    ]
+    
     def onchange_warehouse_id(self, cr, uid, ids, warehouse_id, context={}):
         if warehouse_id:
             w=self.pool.get('stock.warehouse').browse(cr,uid,warehouse_id, context)
@@ -1143,6 +1179,13 @@ class stock_warehouse_orderpoint(osv.osv):
             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()
 
 
@@ -1166,7 +1209,6 @@ class StockMove(osv.osv):
                 factor = move.product_qty
                 bom_point = self.pool.get('mrp.bom').browse(cr, uid, bis[0])
                 res = self.pool.get('mrp.bom')._bom_explode(cr, uid, bom_point, factor, [])
-                dest = move.product_id.product_tmpl_id.property_stock_production.id
                 state = 'confirmed'
                 if move.state=='assigned':
                     state='assigned'
@@ -1178,9 +1220,8 @@ class StockMove(osv.osv):
                         'product_qty': line['product_qty'],
                         'product_uos': line['product_uos'],
                         'product_uos_qty': line['product_uos_qty'],
-                        'move_dest_id': move.id,
                         'state': state,
-                        'location_dest_id': dest,
+                        'name': line['name'],
                         'move_history_ids': [(6,0,[move.id])],
                         'move_history_ids2': [(6,0,[])],
                         'procurements': []
@@ -1203,10 +1244,9 @@ class StockMove(osv.osv):
                     wf_service = netsvc.LocalService("workflow")
                     wf_service.trg_validate(uid, 'mrp.procurement', proc_id, 'button_confirm', cr)
                 self.pool.get('stock.move').write(cr, uid, [move.id], {
-                    'location_id': move.location_dest_id.id,
+                    'location_id': move.location_dest_id.id, # src and dest locations identical to have correct inventory, dummy move
                     'auto_validate': True,
                     'picking_id': False,
-                    'location_id': dest,
                     'state': 'waiting'
                 })
                 for m in self.pool.get('mrp.procurement').search(cr, uid, [('move_id','=',move.id)], context):