[WIP] pack operations + do_partial refactored again
authorQuentin (OpenERP) <qdp-launchpad@openerp.com>
Tue, 23 Jul 2013 15:34:24 +0000 (17:34 +0200)
committerQuentin (OpenERP) <qdp-launchpad@openerp.com>
Tue, 23 Jul 2013 15:34:24 +0000 (17:34 +0200)
bzr revid: qdp-launchpad@openerp.com-20130723153424-9179x8burkch0ap0

addons/stock/__openerp__.py
addons/stock/report/report_stock_move.py
addons/stock/static/src/js/widgets.js
addons/stock/stock.py
addons/stock/stock_view.xml
addons/stock/wizard/__init__.py
addons/stock/wizard/stock_partial_move.py [deleted file]
addons/stock/wizard/stock_partial_move_view.xml [deleted file]
addons/stock/wizard/stock_partial_picking.py [deleted file]
addons/stock/wizard/stock_partial_picking_view.xml [deleted file]

index 60e1742..644a0da 100644 (file)
@@ -69,8 +69,6 @@ Dashboard / Reports for Warehouse Management will include:
         'stock_data.xml',
         'wizard/stock_move_view.xml',
         'wizard/stock_change_product_qty_view.xml',
-        'wizard/stock_partial_picking_view.xml',
-        'wizard/stock_partial_move_view.xml',
         'wizard/stock_fill_inventory_view.xml',
         'wizard/stock_inventory_merge_view.xml',
         'wizard/stock_location_product_view.xml',
index 5b03fb6..2f991c6 100644 (file)
@@ -23,10 +23,9 @@ from openerp import tools
 from openerp.osv import fields,osv
 from openerp.addons.decimal_precision import decimal_precision as dp
 
-# FP Note: TODO: drop this table and use the stock.move table instead
+# FP Note: TODO: move in stock.py
 class stock_quant(osv.osv):
     _inherit = "stock.quant"
-    _description = "Stock Statistics"
     def read_group(self, cr, uid, domain, fields, groupby, offset=0, limit=None, context=None, orderby=False):
         res = super(stock_quant, self).read_group(cr, uid, domain, fields, groupby, offset=offset, limit=limit, context=context, orderby=orderby)
         product_obj = self.pool.get("product.product")
index 1c5de1d..550acac 100644 (file)
@@ -49,7 +49,7 @@ function openerp_picking_widgets(instance){
                     cols: {
                         product: op.product_id[1],
                         uom: op.product_uom ? product_uom[1] : '',
-                        qty: op.product_uom_qty,
+                        qty: op.product_qty,
                     }
                 });
             });
@@ -247,7 +247,7 @@ function openerp_picking_widgets(instance){
             var self = this;
             console.log('Copy Package:',package_id);
             new instance.web.Model('stock.quant.package')
-                .call('action_copy',[[package_id]])
+                .call('copy',[[package_id]])
                 .then(function(){
                     return self.refresh_ui(self.picking.id);
                 });
index 9ef9612..5b3b17f 100644 (file)
@@ -220,8 +220,8 @@ class stock_quant(osv.osv):
         Use the removal strategies of product to search for the correct quants
         If you inherit, put the super at the end of your method.
 
-        :location_id: child_of this location_id
-        :product_id: id of product
+        :location: browse record of the parent location in which the quants have to be found
+        :product: browse record of the product to find
         :qty in UoM of product
         :lot_id NOT USED YET !
         """
@@ -234,7 +234,6 @@ class stock_quant(osv.osv):
             elif removal_strategy=='lifo':
                 result += self._quants_get_lifo(cr, uid, location, product, qty, domain, prefered_order=prefered_order, context=context)
             else:
-                print removal_strategy
                 raise osv.except_osv(_('Error!'), _('Removal strategy %s not implemented.' % (removal_strategy,)))
         return result
 
@@ -303,7 +302,7 @@ class stock_quant(osv.osv):
             if not quant_neg:
                 continue
             result = True
-            to_solve_quant = self.search(cr, uid, [('propagated_from_id', '=', quant_neg.id)], context=context)
+            to_solve_quant = self.search(cr, uid, [('propagated_from_id', '=', quant_neg.id), ('id', '!=', quant.id)], context=context)
             if not to_solve_quant:
                 continue
             to_solve_quant = self.browse(cr, uid, to_solve_quant[0], context=context)
@@ -372,7 +371,6 @@ class stock_quant(osv.osv):
     def _location_owner(self, cr, uid, quant, location, context=None):
         return location and (location.usage == 'internal') and location.company_id or False
 
-
 #----------------------------------------------------------
 # Stock Picking
 #----------------------------------------------------------
@@ -493,24 +491,6 @@ class stock_picking(osv.osv):
     _sql_constraints = [
         ('name_uniq', 'unique(name, company_id)', 'Reference must be unique per Company!'),
     ]
-    def action_process(self, cr, uid, ids, context=None):
-        if context is None:
-            context = {}
-        """Open the partial picking wizard"""
-        context.update({
-            'active_model': self._name,
-            'active_ids': ids,
-            'active_id': len(ids) and ids[0] or False
-        })
-        return {
-            'view_type': 'form',
-            'view_mode': 'form',
-            'res_model': 'stock.partial.picking',
-            'type': 'ir.actions.act_window',
-            'target': 'new',
-            'context': context,
-            'nodestroy': True,
-        }
 
     def copy(self, cr, uid, id, default=None, context=None):
         if default is None:
@@ -587,7 +567,7 @@ class stock_picking(osv.osv):
         @return: True
         """
         self.draft_force_assign(cr, uid, ids)
-        return self.action_process(cr, uid, ids, context=context)
+        return self.do_partial(cr, uid, ids, context=context)
 
     def cancel_assign(self, cr, uid, ids, *args):
         """ Cancels picking and moves.
@@ -664,223 +644,121 @@ class stock_picking(osv.osv):
 
     # FP Note: review all methods aboce this line for stock.picking
 
-    #TODO move this in another class?
-    def get_done_reserved_quants(self, cr, uid, picking_id, move, context=None):
-        stock_operation_obj = self.pool.get('stock.pack.operation')
-        quant_obj = self.pool.get('stock.quant')
-        operation_ids = stock_operation_obj.find_packaging_op_from_product(cr, uid, move.product_id, picking_id, context=context)
-        todo_later = []
-        possible_quants = [quant.id for quant in move.reserved_quant_ids]
-        done_reserved_quants = set()
-        for op in stock_operation_obj.browse(cr, uid, operation_ids, context=context):
-            if op.product_id:
-                #TODO: document me
-                todo_later += [op.id]
-            elif op.quant_id:
-                #split for partial and take care of reserved quants
-                quant_tuples = quant_obj._get_quant_tuples(cr, uid, [op.quant_id.id], op.product_qty, context=context)
-                quant_obj.real_split_quants(cr, uid, quant_tuples, context=context)
-                done_reserved_quants = done_reserved_quants.union(set([qt[0] for qt in quant_tuples]))
-            elif op.package_id:
-                #moving a package never splits quants but we need to take care of the reserved_quant_ids
-                all_children_quants = self.pool.get('stock.quant.package').quants_get(cr, uid, op.package_id, context=context)
-                done_reserved_quants = done_reserved_quants.union(set(all_children_quants))
-
-
-        #finish the partial split by operation that leaves the choice of quant to move
-        for op in stock_operation_obj.browse(cr, uid, todo_later, context=context):
-            quant_tuples = quant_obj._get_quant_tuples(cr, uid, possible_quants, op.product_qty, context=context)
-            quant_obj.real_split_quants(cr, uid, quant_tuples, context=context)
-            done_reserved_quants = done_reserved_quants.union(set([qt[0] for qt in quant_tuples]))
-
-        return done_reserved_quants
-
-    #TODO move this in another class?
     def make_packaging(self, cr, uid, picking_id, todo_move_ids, context=None):
         stock_operation_obj = self.pool.get('stock.pack.operation')
         move_obj = self.pool.get('stock.move')
         quant_obj = self.pool.get('stock.quant')
-        for move in move_obj.browse(cr, uid, todo_move_ids, context=context):
-            operation_ids = stock_operation_obj.find_packaging_op_from_product(cr, uid, move.product_id, picking_id, context=context)
-            possible_quants = [q.id for q in move.quant_ids]
-            for op in stock_operation_obj.browse(cr, uid, operation_ids, context=context):
-                if not op.result_package_id:
-                    continue
-                if op.product_id:
-                    quant_tuples = quant_obj._get_quant_tuples(cr, uid, possible_quants, op.product_qty, context=context)
-                    quant_obj.real_split_quants(cr, uid, quant_tuples, context=context)
-                    quant_obj.write(cr, uid, [qt[0] for qt in quant_tuples], {'package_id': op.result_package_id.id}, context=context)
-                elif op.quant_id:
-                    quant_tuples = quant_obj._get_quant_tuples(cr, uid, [op.quant_id.id], op.product_qty, context=context)
-                    quant_obj.real_split_quants(cr, uid, quant_tuples, context=context)
-                    quant_obj.write(cr, uid, [qt[0] for qt in quant_tuples], {'package_id': op.result_package_id.id}, context=context)
-                elif op.package_id:
-                    #pack existing packs
-                    self.pool.get('stock.quant.package').write(cr, uid, op.package_id.id, {'parent_id': op.result_package_id.id}, context=context)
-
-    def do_partial(self, cr, uid, picking_id, partial_datas, only_split_lines=False, context=None):
+        package_obj = self.pool.get('stock.quant.package')
+        picking = self.browse(cr, uid, picking_id, context=context)
+        op_todo = []
+        for op in picking.pack_operation_ids:
+            if not op.result_package_id:
+                continue
+            if op.package_id:
+                #pack existing packs
+                package_obj.write(cr, uid, op.package_id.id, {'parent_id': op.result_package_id.id}, context=context)
+            if op.quant_id:
+                #if the quant was split, work on the new quant because the original quant may be referenced in another operation
+                quant = quant_obj._quant_split(cr, uid, op.quant_id, op.quant_id.qty - op.product_qty, context=context) or op.quant_id
+                #TODO raise an error if the quant already had a package_id not null ?
+                quant_obj.write(cr, uid, quant.id, {'package_id': op.result_package_id.id}, context=context)
+            if op.product_id:
+                #this kind of opeiration has the lowest priority so we delay its resolution untill all others are done
+                op_todo.append(op)
+        for op in op_todo:
+            all_picking_quants = []
+            #make the list of all quants involved in this picking
+            for move in picking.move_lines:
+                all_picking_quants += [q.id for q in move.quant_ids]
+            quants = quant_obj.quants_get(cr, uid, picking.location_dest_id, op.product_id, op.product_qty, domain=[('id', 'in', all_picking_quants), ('package_id', '=', False)], context=context)
+            quant_ids = []
+            for quant, qty in quants:
+                quant_obj._quant_split(cr, uid, quant, qty, context=context)
+                quant_ids.append(quant.id)
+            quant_obj.write(cr, uid, quant_ids, {'package_id': op.result_package_id.id}, context=context)
+
+    def _find_move_from_product(self, cr, uid, picking_id, product_id, context=None):
         stock_move_obj = self.pool.get('stock.move')
+        move_id = stock_move_obj.search(cr, uid, [('picking_id', '=', picking_id), ('product_id', '=', product_id), ('state', 'not in', ['cancel', 'done'])], limit=1, context=context)
+        return move_id and move_id[0] or False
+
+    def _create_unexpected_move(self, cr, uid, picking, product, qty, cost, context=None):
+        stock_move_obj = self.pool.get('stock.move')
+        seq_obj_name = 'stock.picking.' + picking.type
+        return stock_move_obj.create(cr, uid, {'name': self.pool.get('ir.sequence').get(cr, uid, seq_obj_name),
+                                               'product_id': product.id,
+                                               'product_uom_qty': qty,
+                                               'product_uom': product.product_uom.id,
+                                                            #'lot_id': wizard_line.lot_id.id,
+                                               'location_id': picking.location_id.id,
+                                               'location_dest_id': picking.location_dest_id.id,
+                                               'picking_id': False,
+                                               'price_unit': cost}, context=context)
+
+    def do_partial(self, cr, uid, picking_ids, context=None):
+        """ Makes partial picking based on pack_operation_ids field.
+        :param only_split_line: boolen that depicts if the moves should not be set to done.
+        """
+        #TODO: this variable should be in argument
+        only_split_lines = False
+        stock_move_obj = self.pool.get('stock.move')
+        quant_obj = self.pool.get('stock.quant')
+        quant_package_obj = self.pool.get('stock.quant.package')
         proc_group = self.pool.get("procurement.group")
         group_id = proc_group.create(cr, uid, {}, context=context)
         todo_move_ids = []
-        for partial_data in partial_datas:
-            todo_move_ids += [stock_move_obj.do_partial(cr, uid, partial_data.get('move_id'), partial_data, group_id, context=context)]
-        ctx = context.copy()
-        ctx.update({'backorder_of': picking_id})
-        stock_move_obj.action_confirm(cr, uid, todo_move_ids, context=ctx)
+        assert len(picking_ids) == 1, 'Partial picking is only supported fir one record at a time'
+        if context is None:
+            context = {}
+        picking = self.browse(cr, uid, picking_ids[0], context=context)
+        need_backorder = False
+        #first check that a backorder needs to be created or not
+        for move in picking.move_lines:
+            if move.remaining_qty != 0:
+                need_backorder = True
+                break
+        if need_backorder:
+            for op in picking.pack_operation_ids:
+                if op.package_id:
+                    #moving a package that includes several quants
+                    included_package_ids = quant_package_obj.search(cr, uid, [('parent_id', 'child_of', [op.package_id.id])], context=context)
+                    included_quant_ids = quant_obj.search(cr, uid, [('package_id', 'in', included_package_ids)], context=context)
+                    for quant in quant_obj.browse(cr, uid, included_quant_ids, context=context):
+                        move_id = self._find_move_from_product(cr, uid, picking.id, quant.product_id.id, context=context)
+                        partial_datas = {
+                            'product_qty': quant.qty * op.product_qty,
+                            'product_uom_id': quant.product_id.product_uom_id.id,
+                            'cost': quant.cost,
+                        }
+                        if not move_id:
+                            move_id = self._create_unexpected_move(cr, uid, picking, quant.product_id, op.product_qty * quant.qty, quant.cost, context=context)
+                        todo_move_ids += [stock_move_obj.do_partial(cr, uid, move_id, partial_datas, group_id, context=context)]
+                else:
+                    move_id = op.move_id.id
+                    partial_datas = {
+                        'product_qty': op.product_qty,
+                        'product_uom_id': op.product_uom_id.id,
+                        'cost': op.cost,
+                    }
+                    if not move_id:
+                        if op.product_id:
+                            #product not expected
+                            move_id = self._create_unexpected_move(cr, uid, picking, op.product_id, op.product_qty, op.product_id.standard_price, context=context)
+                        elif op.quant_id:
+                            #quant not expected
+                            move_id = self._create_unexpected_move(cr, uid, picking, op.quant_id.product_id, op.product_qty, op.quant_id.cost, context=context)
+
+                    todo_move_ids += [stock_move_obj.do_partial(cr, uid, move_id, partial_datas, group_id, context=context)]
+        else:
+            todo_move_ids = [m.id for m in picking.move_lines]
+        if need_backorder:
+            ctx = context.copy()
+            ctx.update({'backorder_of': picking.id})
+            stock_move_obj.action_confirm(cr, uid, todo_move_ids, context=ctx)
         if not only_split_lines:
             stock_move_obj.action_done(cr, uid, todo_move_ids, context=context)
-            self.make_packaging(cr, uid, picking_id, todo_move_ids, context=context)
-        
-#        """ Makes partial picking and moves done.
-#        @param partial_datas : Dictionary containing details of partial picking
-#                          like partner_id, partner_id, delivery_date,
-#                          delivery moves with product_id, product_qty, uom
-#        @return: Dictionary of values
-#        """
-#        if context is None:
-#            context = {}
-#        else:
-#            context = dict(context)
-#        res = {}
-#        move_obj = self.pool.get('stock.move')
-#        product_obj = self.pool.get('product.product')
-#        currency_obj = self.pool.get('res.currency')
-#        uom_obj = self.pool.get('product.uom')
-#        sequence_obj = self.pool.get('ir.sequence')
-#        for pick in self.browse(cr, uid, ids, context=context):
-#            new_picking = None
-#            complete, too_many, too_few = [], [], []
-#            move_product_qty, lot_ids, partial_qty, product_uoms = {}, {}, {}, {}
-#
-#            
-#            for move in pick.move_lines:
-#                if move.state in ('done', 'cancel'):
-#                    continue
-#                partial_data = partial_datas.get('move%s'%(move.id), {})
-#                product_qty = partial_data.get('product_qty',0.0)
-#                move_product_qty[move.id] = product_qty
-#                product_uom = partial_data.get('product_uom',False)
-#                product_price = partial_data.get('product_price',0.0)
-#                lot_id = partial_data.get('lot_id')
-#                lot_ids[move.id] = lot_id
-#                product_uoms[move.id] = product_uom
-#                partial_qty[move.id] = uom_obj._compute_qty(cr, uid, product_uoms[move.id], product_qty, move.product_uom.id)
-#                if move.product_qty == partial_qty[move.id]:
-#                    complete.append(move)
-#                elif move.product_qty > partial_qty[move.id]:
-#                    too_few.append(move)
-#                else:
-#                    too_many.append(move)
-#
-#
-#            for move in too_few:
-#                #create a backorder stock move with the remaining quantity
-#                product_qty = move_product_qty[move.id]
-#                if not new_picking:
-#                    #a backorder picking doesn't exist yet, create a new one
-#                    new_picking_name = pick.name
-#                    self.write(cr, uid, [pick.id], 
-#                               {'name': sequence_obj.get(cr, uid,
-#                                            'stock.picking.%s'%(pick.type)),
-#                               })
-#                    new_picking = self.copy(cr, uid, pick.id,
-#                            {
-#                                'name': new_picking_name,
-#                                'move_lines' : [],
-#                                'state':'draft',
-#                            })
-#                    #modify the existing picking (this trick is needed to keep the eventual workflows pointing on the first picking)
-#                    unlink_operation_order = [(2, op.id) for op in pick.pack_operation_ids]
-#                    self.write(cr, uid, [pick.id], 
-#                               {
-#                                'pack_operation_ids': unlink_operation_order
-#                               })
-#                done_reserved_quants = set()
-#                if product_qty != 0:
-#                    #take care of partial picking in reserved quants
-#                    done_reserved_quants = self.get_done_reserved_quants(cr, uid, pick.id, move, context=context)
-#                    #copy the stock move
-#                    new_picking_record = self.browse(cr, uid, new_picking, context=context)
-#
-#                    defaults = {
-#                            'product_qty' : product_qty,
-#                            'product_uos_qty': product_qty, #TODO: put correct uos_qty
-#                            'picking_id' : new_picking,
-#                            'state': 'assigned',
-#                            'move_dest_id': False,
-#                            'price_unit': product_price,
-#                            'product_uom': product_uoms[move.id],
-#                            'reserved_quant_ids': [(4,x) for x in list(done_reserved_quants)]
-#                    }
-#                    lot_id = lot_ids[move.id]
-#                    if lot_id:
-#                        defaults.update(lot_id=lot_id)
-#                    backorder_move_id = move_obj.copy(cr, uid, move.id, defaults)
-#                    self.make_packaging(cr, uid, pick.id, move_obj.browse(cr, uid, backorder_move_id, context=context), list(done_reserved_quants), context=context)
-#                #modify the existing stock move    
-#                possible_quants = [x.id for x in move.reserved_quant_ids]
-#                move_obj.write(cr, uid, [move.id],
-#                        {
-#                            'product_qty': move.product_qty - partial_qty[move.id],
-#                            'product_uos_qty': move.product_qty - partial_qty[move.id], #TODO: put correct uos_qty
-#                            'lot_id': False,
-#                            'tracking_id': False,
-#                            'reserved_quant_ids': [(4,x) for x in list(set(possible_quants) - done_reserved_quants)],
-#                        })
-#
-#            if new_picking:
-#                move_obj.write(cr, uid, [c.id for c in complete], {'picking_id': new_picking})
-#            for move in complete:
-#                defaults = {'product_uom': product_uoms[move.id], 'product_qty': move_product_qty[move.id]}
-#                if lot_ids.get(move.id):
-#                    defaults.update({'lot_id': lot_ids[move.id]})
-#                move_obj.write(cr, uid, [move.id], defaults)
-#
-#
-#                #take care of packaging for completed moves
-#                possible_quants = [x.id for x in move.reserved_quant_ids]
-#
-#                self.make_packaging(cr, uid, new_picking, move, possible_quants, context=context)
-#
-#
-#
-#            for move in too_many:
-#                product_qty = move_product_qty[move.id]
-#                defaults = {
-#                    'product_qty' : product_qty,
-#                    'product_uos_qty': product_qty, #TODO: put correct uos_qty
-#                    'product_uom': product_uoms[move.id]
-#                }
-#                lot_id = lot_ids.get(move.id)
-#                if lot_ids.get(move.id):
-#                    defaults.update(lot_id=lot_id)
-#                if new_picking:
-#                    defaults.update(picking_id=new_picking)
-#                move_obj.write(cr, uid, [move.id], defaults)
-#
-#                possible_quants = [x.id for x in move.reserved_quant_ids]
-#                self.make_packaging(cr, uid, new_picking, move, possible_quants, context=context)
-#            # At first we confirm the new picking (if necessary)
-#            if new_picking:
-#                self.signal_button_confirm(cr, uid, [new_picking])
-#                # Then we finish the good picking
-#                self.write(cr, uid, [pick.id], {'backorder_id': new_picking})
-#                self.action_move(cr, uid, [new_picking], context=context)
-#                self.signal_button_done(cr, uid, [new_picking])
-#                delivered_pack_id = new_picking
-#                back_order_name = self.browse(cr, uid, delivered_pack_id, context=context).name
-#                self.message_post(cr, uid, ids, body=_("Back order <em>%s</em> has been <b>created</b>.") % (back_order_name), context=context)
-#            else:
-#                self.action_move(cr, uid, [pick.id], context=context)
-#                self.signal_button_done(cr, uid, [pick.id])
-#                delivered_pack_id = pick.id
-#
-#            delivered_pack = self.browse(cr, uid, delivered_pack_id, context=context)
-#            res[pick.id] = {'delivered_picking': delivered_pack.id}
-#
-#        return res
-    
+            self.make_packaging(cr, uid, picking.id, todo_move_ids, context=context)
+
     # views associated to each picking type
     _VIEW_LIST = {
         'out': 'view_picking_out_form',
@@ -889,90 +767,24 @@ class stock_picking(osv.osv):
     }
     def _get_view_id(self, cr, uid, type):
         """Get the view id suiting the given type
-        
+
         @param type: the picking type as a string
         @return: view i, or False if no view found
         """
-        res = self.pool.get('ir.model.data').get_object_reference(cr, uid, 
-            'stock', self._VIEW_LIST.get(type, 'view_picking_form'))            
+        res = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'stock', self._VIEW_LIST.get(type, 'view_picking_form'))      
         return res and res[1] or False
 
     def _get_picking_for_packing_ui(self, cr, uid, context=None):
         res = self.search(cr, uid, [('state', '=', 'assigned')], limit=1, context=context)
         return res and res[0] or False  # TODO: what to do if nothing is left to do?
 
-    def action_done_from_packing_ui(self, cr, uid, picking_id, context=None):
-        if context is None:
-            context = {}
-        #create partial picking wizard that handles the split of stock moves and the backorder if needed
-        ctx = context.copy()
-        ctx['active_ids'] = [picking_id]
-        ctx['active_model'] = 'stock.picking'
-        partial_picking_obj = self.pool.get('stock.partial.picking')
-        partial_wizard_id = partial_picking_obj.create(cr, uid, {}, context=ctx)
-        partial_wizard_result = partial_picking_obj.do_partial(cr, uid, [partial_wizard_id], context=context)
-
-        #todo_picking_id = picking_id if picking was total, it's the backorder if the picking was partial
-        #todo_picking_id = partial_wizard_result[picking_id]['delivered_picking']
-
-
-
-
-
-#all stuff below should be removed except the parent packaging /!\
-#        all_done_quants = []
-#        for move in self.browse(cr, uid, todo_picking_id, context=context).move_lines:
-#            all_done_quants += [quant.id for quant in move.reserved_quant_ids]
-#
-#        for operation in self.browse(cr, uid, todo_picking_id, context=context).pack_operation_ids:
-#            if operation.result_package_id:
-#                if operation.package_id:
-#                    if operation.package_id.parent_id:
-#                        # decide what to do ?
-#                        pass
-#                    #pack existing packs
-#                    self.pool.get('stock.quant.package').write(cr, uid, operation.package_id.id, {'parent_id': operation.result_package_id.id}, context=context)
-#                elif operation.product_id:
-#                    #self.split_and_assign_quants(
-#                    pass
-#
-#
-#
-#
-#                if self.pool.get('stock.pack.operation').search(cr, uid, [('picking_id', '=', todo_picking_id), ('result_package_id', '!=', False)]
-#                    pass
-#        #def split_and_assign_quants(self, cr, uid, quant_tuples, move, context=None):
-#        #fill all the packages with assigned operations
-#        for operation in self.browse(cr, uid, todo_picking_id, context=context).pack_operation_ids:
-#            if operation.result_package_id:
-#                if operation.package_id:
-#                    #pack existing packs
-#                    self.pool.get('stock.quant.package').write(cr, uid, operation.package_id.id, {'parent_id': operation.result_package_id.id}, context=context)
-#                elif operation.quant_id:
-#                    if operation.quant_id.parent_id:
-#                        # decide what to do
-#                        pass
-#                    #assign_pack may split the quant and write the package on it (above test should be in that method instead)
-#                    self.pool.get('stock.quant').assign_pack(cr, uid, operation.quant_id.id, operation.product_qty, operation.result_package_id.id, context=context)
-#                elif operation.product_id:
-#                    pass 
-#        #don't call action_done of picking because it will make all moves don, but make a partial delivery
-#        line_ids = []
-#        for move in self.browse(cr, uid, picking_id, context=context).move_lines:
-#            line += [{
-#                'product_id': move.product_id.id,
-#                'quantity': move.product_qty - move.remaining_qty,
-#                'product_uom': move.product_uom_id.id,
-#
-#            }]
-#
-#        #self.action_done(cr, uid, picking_id, context=context)
-
+    def action_done_from_packing_ui(self, cr, uid, picking_id, only_split_lines=False, context=None):
+        self.do_partial(cr, uid, picking_id, only_split_lines, context=context)
         #return id of next picking to work on
         return self._get_picking_for_packing_ui(cr, uid, context=context)
 
     def action_pack(self, cr, uid, picking_id, context=None):
-        #put all the operations of the picking that aren't yet assigned to a package to this new one
+        #put all the operations of the picking that aren't yet assigned to a package to a new one
         stock_operation_obj = self.pool.get('stock.pack.operation')
         package_obj = self.pool.get('stock.quant.package')
         #create a new empty stock.quant.package
@@ -1003,9 +815,8 @@ class stock_picking(osv.osv):
         error_msg = ''
         todo_on_moves = []
         todo_on_operations = []
-
         #check if the barcode correspond to a product
-        matching_product_ids = product_obj.search(cr, uid, ['|', ('code','=',barcode_str), ('ean13', '=', barcode_str)], context=context)
+        matching_product_ids = product_obj.search(cr, uid, [('ean13', '=', barcode_str)], context=context)
         if matching_product_ids:
             todo_on_moves, todo_on_operations = stock_operation_obj._search_and_increment(cr, uid, picking_id, ('product_id', '=', matching_product_ids[0]), context=context)
 
@@ -1203,7 +1014,7 @@ class stock_move(osv.osv):
         # used for colors in tree views:
         'scrapped': fields.related('location_dest_id','scrap_location',type='boolean',relation='stock.location',string='Scrapped', readonly=True),
         'type': fields.related('picking_id', 'type', type='selection', selection=[('out', 'Sending Goods'), ('in', 'Getting Goods'), ('internal', 'Internal')], string='Shipping Type'),
-        'quant_ids': fields.many2many('stock.quant',  'stock_quant_move_rel', 'move_id', 'quant_id', 'Quants'),
+        'quant_ids': fields.many2many('stock.quant', 'stock_quant_move_rel', 'move_id', 'quant_id', 'Quants'),
         'reserved_quant_ids': fields.one2many('stock.quant', 'reservation_id', 'Reserved quants'),
         'remaining_qty': fields.function(_get_remaining_qty, type='float', string='Remaining Quantity', digits_compute=dp.get_precision('Product Unit of Measure'), states={'done': [('readonly', True)]}),
         'group_id': fields.many2one('procurement.group', 'Procurement Group'),
@@ -1524,11 +1335,13 @@ class stock_move(osv.osv):
                 #copy the original picking
                 original_picking = pick_obj.browse(cr, uid, context.get('backorder_of'), context=context)
                 new_picking_name = original_picking.name
+                #TODO back_order_name is False currently => find why
                 back_order_name = sequence_obj.get(cr, uid, 'stock.picking.%s' % (original_picking.type))
                 pick_obj.write(cr, uid, [original_picking.id], {'name': back_order_name})
                 pick = pick_obj.copy(cr, uid, original_picking.id, {'name': new_picking_name,
                                                     'move_lines': [],
                                                     'state': 'draft'})
+
                 pick_obj.message_post(cr, uid, original_picking.id, body=_("Back order <em>%s</em> has been <b>created</b>.") % (back_order_name), context=context)
                 unlink_operation_order = [(2, op.id) for op in original_picking.pack_operation_ids]
                 pick_obj.write(cr, uid, [original_picking.id], {'backorder_id': pick, 'pack_operation_ids': unlink_operation_order}, context=context)
@@ -1907,7 +1720,7 @@ class stock_move(osv.osv):
 #                product_avail[product.id] += product_uom_qty
 #        return res
 
-    def do_partial(self, cr, uid, move_id, partial_data, move_group_id, context=None):
+    def do_partial(self, cr, uid, move_id, partial_datas, move_group_id, context=None):
         """ Partially (or not) moves  a stock.move.
         @param partial_datas: Dictionary containing details of partial picking
                           like partner_id, delivery_date, delivery
@@ -1919,9 +1732,9 @@ class stock_move(osv.osv):
             context = {}
 
         move = self.browse(cr, uid, move_id, context=context)
-        product_uom_qty = partial_data.get('product_uom_qty', 0.0)
-        product_uom = partial_data.get('product_uom', False)
-        #lot_id = partial_data.get('lot_id')
+        product_uom_qty = partial_datas.get('product_qty', 0.0)
+        product_uom = partial_datas.get('product_uom_id')
+        #lot_id = partial_datas.get('lot_id')
         if move.state in ('done', 'cancel') or product_uom_qty == 0:
             return
         #TODO add back these constraint checks
@@ -1947,18 +1760,16 @@ class stock_move(osv.osv):
 
         #partial_qty is the quantity processed in the normalized product uom
         partial_qty = uom_obj._compute_qty(cr, uid, product_uom, product_uom_qty, move.product_id.uom_id.id)
-        if move.product_qty == partial_qty:
-            todo_move_id = move.id
-        elif move.product_qty > partial_qty:
+        if move.product_qty > partial_qty:
             defaults = {
-                        'product_uom_qty': product_uom_qty,
-                        'product_uos_qty': product_uom_qty,
-                        'picking_id': False,
-                        'group_id': move_group_id,
-                        'state': 'assigned',
-                        'move_dest_id': False,
-                        'price_unit': partial_data.get('price_unit', 0.0),
-                        }
+                'product_uom_qty': product_uom_qty,
+                'product_uos_qty': product_uom_qty,
+                'picking_id': False,
+                'group_id': move_group_id,
+                'state': 'assigned',
+                'move_dest_id': False,
+                'price_unit': partial_datas.get('cost', 0.0),
+            }
             new_move = self.copy(cr, uid, move.id, defaults)
             todo_move_id = new_move
             self.write(cr, uid, [move.id], {'product_uom_qty': move.product_qty - product_uom_qty,
@@ -1966,7 +1777,7 @@ class stock_move(osv.osv):
                                             'lot_id': False,
                                             'tracking_id': False})
         else:
-            self.write(cr, uid, [move.id], {'product_uom_qty': move.product_uom_qty, 'product_uos_qty': move.product_uom_qty})
+            self.write(cr, uid, [move.id], {'product_uom_qty': move.product_uom_qty, 'product_uos_qty': move.product_uom_qty, 'group_id': move_group_id, 'picking_id': False})
             todo_move_id = move.id
 
         #TODO LOT management
@@ -2298,7 +2109,8 @@ class stock_package(osv.osv):
         """Returns packages from quants for store"""
         res = set()
         for quant in self.browse(cr, uid, ids, context=context):
-            res.add(quant.package_id.id)
+            if quant.package_id:
+                res.add(quant.package_id.id)
         return list(res)
 
     _columns = {
@@ -2307,10 +2119,10 @@ class stock_package(osv.osv):
                                          store={'stock.quant.package': (_get_subpackages, ['name', 'parent_id'], 10)}),
         'parent_left': fields.integer('Left Parent', select=1),
         'parent_right': fields.integer('Right Parent', select=1),
-        'packaging_id': fields.many2one('product.packaging', 'Type of Packaging', type="many2one"),
+        'packaging_id': fields.many2one('product.packaging', 'Type of Packaging'),
+        'location_id': fields.related('quant_ids', 'location_id', type='many2one', relation='stock.location', string='Location',
+                                      store={'stock.quant': (_get_packages, ['location_id'], 10)}, readonly=True),
         'quant_ids': fields.one2many('stock.quant', 'package_id', 'Bulk Content'),
-        'location_id': fields.related('quant_ids', 'location_id', type='many2one', relation='stock.location', string='Location', 
-                                      store = {'stock.quant': (_get_packages, ['location_id'], 10)}, readonly=True),
         'parent_id': fields.many2one('stock.quant.package', 'Container Package', help="The package containing this item"),
         'children_ids': fields.one2many('stock.quant.package', 'parent_id', 'Contained Packages'),
 
@@ -2342,17 +2154,17 @@ class stock_package(osv.osv):
             'datas': datas
         }
 
-    # FP Note: why not just over ridding the copy method?
-    def action_copy(self, cr, uid, ids, context=None):
-        stock_operation_obj = self.pool.get('stock.pack.operation')
-        #search all the operations of given package
-        operation_ids = stock_operation_obj.search(cr, uid, [('result_package_id', 'in', ids)], context=context)
-        #create a new empty stock.quant.package
-        package_id = self.create(cr, uid, {}, context=context)
-        new_ops = []
-        #copy all operation and set the newly created package as result_package_id
-        for op in operation_ids:
-            new_ops += [stock_operation_obj.copy(cr, uid, op, {'result_package_id': package_id, 'quant_ids': []}, context=context)]
+    ## FP Note: why not just over ridding the copy method?
+    #def action_copy(self, cr, uid, ids, context=None):
+    #    stock_operation_obj = self.pool.get('stock.pack.operation')
+    #    #search all the operations of given package
+    #    operation_ids = stock_operation_obj.search(cr, uid, [('result_package_id', 'in', ids)], context=context)
+    #    #create a new empty stock.quant.package
+    #    package_id = self.create(cr, uid, {}, context=context)
+    #    new_ops = []
+    #    #copy all operation and set the newly created package as result_package_id
+    #    for op in operation_ids:
+    #        new_ops += [stock_operation_obj.copy(cr, uid, op, {'result_package_id': package_id, 'quant_ids': []}, context=context)]
 
     def quants_get(self, cr, uid, package_record, context=None):
         ''' find all the quants in the given package (browse record) recursively'''
@@ -2377,13 +2189,23 @@ class stock_pack_operation(osv.osv):
     _name = "stock.pack.operation"
     _description = "Packing Operation"
     _columns = {
-        'picking_id': fields.many2one('stock.picking', 'Stock Picking', help='The stock operation where the packing has been made'),
-        'product_id': fields.many2one('product.product', 'Product'),  # 1
-        'product_uom': fields.many2one('product.uom', 'Product Unit of Measure'),
+        'picking_id': fields.many2one('stock.picking', 'Stock Picking', help='The stock operation where the packing has been made', required=True),
+        'product_id': fields.many2one('product.product', 'Product', ondelete="CASCADE"),  # 1
+        'product_uom_id': fields.many2one('product.uom', 'Product Unit of Measure'),
         'product_qty': fields.float('Quantity', digits_compute=dp.get_precision('Product Unit of Measure'), required=True),
         'package_id': fields.many2one('stock.quant.package', 'Package'),  # 2
         'quant_id': fields.many2one('stock.quant', 'Quant'),  # 3
         'result_package_id': fields.many2one('stock.quant.package', 'Container Package', help="If set, the operations are packed into this package", required=False, ondelete='cascade'),
+        'date': fields.datetime('Date', required=True),
+        #'lot_id': fields.many2one('stock.production.lot', 'Serial Number', ondelete='CASCADE'),
+        'move_id': fields.many2one('stock.move', "Move"),
+        #'update_cost': fields.boolean('Need cost update'),
+        'cost': fields.float("Cost", help="Unit Cost for this product line"),
+        'currency': fields.many2one('res.currency', string="Currency", help="Currency in which Unit cost is expressed", ondelete='CASCADE'),
+    }
+
+    _defaults = {
+        'date': fields.date.context_today,
     }
 
     def _find_product_ids(self, cr, uid, operation_id, context=None):
@@ -2398,22 +2220,7 @@ class stock_pack_operation(osv.osv):
             included_quant_ids = quant_obj.search(cr, uid, [('package_id', 'in', included_package_ids)], context=context)
             return [quant.product_id.id for quant in quant_obj.browse(cr, uid, included_quant_ids, context=context)]
 
-    def find_packaging_op_from_product(self, cr, uid, product_id, picking_id, context=None):
-        #returns all ops that touches this product
-        #TOCHECK: don't we need to take only the ops with a result_package_id != False ?
-        res = []
-        op_ids = self.search(cr, uid, [('picking_id', '=', picking_id)], context=context)
-        for operation in self.browse(cr, uid, op_ids, context=context):
-            if operation.product_id and operation.product_id.id == product_id:
-                res += [operation.id]
-            if operation.quant_id and operation.quant_id.product_id.id == product_id:
-                res += [operation.id]
-            if operation.package_id:
-                all_quants = self.pool.get('stock.quant.package').search(cr, uid, [('parent_id', 'child_of', [operation.package_id.id])], context=context)
-                if any([self.pool.get('stock.quant').browse(cr, uid, quant, context=context).product_id.id == product_id for quant in all_quants]):
-                    res += [operation.id]
-        return res
-
+    #TODO: this function can be refactored
     def _search_and_increment(self, cr, uid, picking_id, key, context=None):
         '''Search for an operation on an existing key in a picking, if it exists increment the qty (+1) otherwise create it
 
@@ -2425,6 +2232,7 @@ class stock_pack_operation(osv.osv):
                  (2, ID)                remove and delete the linked record with id = ID (calls unlink on ID, that will delete the object completely, and the link to it as well)
         '''
         stock_move_obj = self.pool.get('stock.move')
+        quant_obj = self.pool.get('stock.quant')
         if context is None:
             context = {}
 
@@ -2440,12 +2248,22 @@ class stock_pack_operation(osv.osv):
         else:
             #no existing operation found for the given key and picking => create a new one
             var_name, dummy, value = key
-            qty = 1
+            uom_id = False
+            move_id = False
+            if var_name == 'product_id':
+                uom_id = self.pool.get('product.product').browse(cr, uid, value, context=context).uom_id.id
+                move_id = picking_obj._find_move_from_product(cr, uid, picking_id, value, context=context)
+            elif var_name == 'quant_id':
+                quant = quant_obj.browse(cr, uid, value, context=context)
+                uom_id = quant.product_id.uom_id.id
+                move_id = picking_obj._find_move_from_product(cr, uid, picking_id, quant.product_id.id, context=context)
+            move_id = move_id and move_id[0] or False
             values = {
                 'picking_id': picking_id,
                 var_name: value,
-                'product_qty': qty,
-                #'product_uom': 1,  # FIXME
+                'product_qty': 1,
+                'product_uom_id': uom_id,
+                'move_id': move_id
             }
             operation_id = self.create(cr, uid, values, context=context)
             values.update({'id': operation_id})
@@ -2458,11 +2276,10 @@ class stock_pack_operation(osv.osv):
                 corresponding_move = stock_move_obj.browse(cr, uid, corresponding_move_ids[0], context=context)
                 todo_on_moves += [(1, corresponding_move.id, {'remaining_qty': corresponding_move.remaining_qty - 1})]
             else:
-                #decide what to do
+                #TODO: no move found for the given operation => decide what to do.. raise an error?
                 pass
         return todo_on_moves, todo_on_operations
 
-
 class stock_warehouse_orderpoint(osv.osv):
     """
     Defines Minimum stock rules.
index b612a4b..9ef427e 100644 (file)
                     <button name="draft_validate" states="draft" string="Confirm &amp; Transfer" type="object" class="oe_highlight" groups="base.group_user"/>
                     <!-- <button name="check_assign" states="confirmed" string="Check Availability" type="object"/> -->
                     <button name="force_assign" states="confirmed" string="Force Availability" type="object" class="oe_highlight" groups="base.group_user"/>
-                    <button name="action_process" states="assigned" string="Confirm &amp; Transfer" groups="stock.group_stock_user" type="object" class="oe_highlight"/>
+                    <button name="do_partial" states="assigned" string="Confirm &amp; Transfer" groups="stock.group_stock_user" type="object" class="oe_highlight"/>
                     <button name="%(act_stock_return_picking)d" string="Reverse Transfer" states="done" type="action" groups="base.group_user"/>
                     <button name="button_cancel" states="assigned,confirmed,draft" string="Cancel Transfer" groups="base.group_user"/>
                     <field name="state" widget="statusbar" statusbar_visible="draft,assigned,done" statusbar_colors='{"shipping_except":"red","invoice_except":"red","waiting_date":"blue"}'/>
                     <notebook>
                         <page string="Products">
                             <field name="move_lines" context="{'address_in_id': partner_id, 'form_view_ref':'view_move_picking_form', 'tree_view_ref':'view_move_picking_tree', 'picking_type': 'internal'}"/>
+                            <field name="pack_operation_ids" domain="[('result_package_id', '!=', False)]">
+                                <tree editable="top">
+                                    <field name="product_id"/>
+                                    <field name="product_uom_id"/>
+                                    <field name="quant_id"/>
+                                    <field name="package_id"/>
+                                    <field name="move_id" domain="[('picking_id', '=', parent.id)]"/>
+                                    <field name="product_qty"/>
+                                </tree>
+                            </field>
                             <field name="note" placeholder="Add an internal note..." class="oe_inline"/>
                         </page>
+                        <page string="Packed">
+                        </page>
                         <page string="Additional Info">
                             <group>
                                 <group>
                         </page>
                     </notebook>
                 </sheet>
+                <div class="oe_chatter">
+                    <field name="message_follower_ids" widget="mail_followers"/>
+                    <field name="message_ids" widget="mail_thread"/>
+                </div>
                 </form>
             </field>
         </record>
                     <button name="draft_validate" states="draft" string="Confirm &amp; Deliver" type="object" class="oe_highlight"/>
                     <button name="action_assign" states="confirmed" string="Check Availability" type="object" class="oe_highlight"/>
                 </xpath>
-                <xpath expr="/form/header//button[@name='action_process']" position="replace">
-                    <button name="action_process" states="assigned" string="Deliver" type="object" class="oe_highlight"/>
+                <xpath expr="/form/header//button[@name='do_partial']" position="replace">
+                    <button name="do_partial" states="assigned" string="Deliver" type="object" class="oe_highlight"/>
                 </xpath>
                 <xpath expr="/form/header//field[@name='state']" position="replace">
                     <field name="state" nolabel="1" readonly="1" widget="statusbar" statusbar_visible="draft,confirmed,assigned,done" statusbar_colors='{"auto":"blue", "confirmed":"blue"}'/>
                 <xpath expr="//button[@name='draft_validate']" position="replace">
                     <button name="draft_validate" states="draft" string="Confirm &amp; Receive" type="object" class="oe_highlight"/>
                 </xpath>
-                <xpath expr="//button[@name='action_process']" position="replace">
-                    <button name="action_process" states="assigned" string="Receive" type="object" class="oe_highlight"/>
+                <xpath expr="//button[@name='do_partial']" position="replace">
+                    <button name="do_partial" states="assigned" string="Receive" type="object" class="oe_highlight"/>
                 </xpath>
                 <xpath expr="//field[@name='move_lines']" position="replace">
                     <field name="move_lines" context="{'address_in_id': partner_id, 'picking_type': 'in', 'form_view_ref':'view_move_picking_form', 'tree_view_ref':'view_move_picking_tree'}"/>
                 <form string="Stock Moves" version="7.0">
                 <header>
                     <button name="action_confirm" states="draft" string="Process Later" type="object" class="oe_highlight"/>
-                    <button name="%(action_partial_move_server)d" string="Process Partially" type="action" states="assigned" class="oe_highlight"/>
                     <button name="action_done" states="draft,assigned,confirmed" string="Process Entirely" type="object" class="oe_highlight"/>
                     <button name="force_assign" states="confirmed" string="Set Available" type="object" class="oe_highlight"/>
                     <button name="action_cancel" states="assigned,confirmed" string="Cancel Move" type="object"/>
                         icon="terp-gtk-jump-to-ltr" context="{'scrap': True}"
                         states="draft,waiting,confirmed,assigned"/>
                     <field name="state"/>
-                    <button name="%(action_partial_move_server)d"
-                        icon="terp-stock_effects-object-colorize" type="action"
-                        states="assigned" class="oe_highlight"/>
                     <button name="action_done" states="draft,assigned,confirmed"
                         icon="gtk-go-forward" type="object"
                         class="oe_highlight" help="Done"/>
index 236c103..cf1aa19 100644 (file)
@@ -22,8 +22,6 @@
 import stock_traceability
 import stock_move
 import stock_splitinto
-import stock_partial_picking
-import stock_partial_move
 import stock_inventory_merge
 import stock_fill_inventory
 import stock_inventory_line_split
diff --git a/addons/stock/wizard/stock_partial_move.py b/addons/stock/wizard/stock_partial_move.py
deleted file mode 100644 (file)
index 6e64d78..0000000
+++ /dev/null
@@ -1,87 +0,0 @@
-# -*- coding: utf-8 -*-
-##############################################################################
-#
-#    OpenERP, Open Source Management Solution
-#    Copyright (C) 2004-TODAY OpenERP SA (<http://openerp.com>).
-#
-#    This program is free software: you can redistribute it and/or modify
-#    it under the terms of the GNU Affero General Public License as
-#    published by the Free Software Foundation, either version 3 of the
-#    License, or (at your option) any later version.
-#
-#    This program is distributed in the hope that it will be useful,
-#    but WITHOUT ANY WARRANTY; without even the implied warranty of
-#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-#    GNU Affero General Public License for more details.
-#
-#    You should have received a copy of the GNU Affero General Public License
-#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
-#
-##############################################################################
-
-from openerp.osv import fields, osv
-from openerp.tools.misc import DEFAULT_SERVER_DATETIME_FORMAT
-import time
-from openerp.tools.translate import _
-
-class stock_partial_move_line(osv.osv_memory):
-    _inherit = "stock.partial.picking.line"
-    _name = "stock.partial.move.line"
-    _columns = {
-        'wizard_id' : fields.many2one('stock.partial.move', string="Wizard", ondelete='CASCADE'),
-    }
-
-class stock_partial_move(osv.osv_memory):
-    _name = "stock.partial.move"
-    _inherit = 'stock.partial.picking'
-    _description = "Partial Move Processing Wizard"
-    _columns = {
-        'date': fields.datetime('Date', required=True),
-        'move_ids' : fields.one2many('stock.partial.move.line', 'wizard_id', 'Moves'),
-
-        # picking_id is not used for move processing, so we remove the required attribute
-        # from the inherited column, and ignore it
-        'picking_id': fields.many2one('stock.picking', 'Picking'),
-     }
-
-    def default_get(self, cr, uid, fields, context=None):
-        if context is None: context = {}
-        # no call to super!
-        res = {}
-        move_ids = context.get('active_ids', [])
-        if not move_ids or not context.get('active_model') == 'stock.move':
-            return res
-        if 'move_ids' in fields:
-            move_ids = self.pool.get('stock.move').browse(cr, uid, move_ids, context=context)
-            moves = [self._partial_move_for(cr, uid, m) for m in move_ids if m.state not in ('done','cancel')]
-            res.update(move_ids=moves)
-        if 'date' in fields:
-            res.update(date=time.strftime(DEFAULT_SERVER_DATETIME_FORMAT))
-        return res
-
-    def do_partial(self, cr, uid, ids, context=None):
-        # no call to super!
-        assert len(ids) == 1, 'Partial move processing may only be done one form at a time.'
-        partial = self.browse(cr, uid, ids[0], context=context)
-        partial_data = {
-            'delivery_date' : partial.date
-        }
-        moves_ids = []
-        for move in partial.move_ids:
-            if not move.move_id:
-                raise osv.except_osv(_('Warning !'), _("You have manually created product lines, please delete them to proceed"))
-            move_id = move.move_id.id
-            partial_data['move%s' % (move_id)] = {
-                'product_id': move.product_id.id,
-                'product_qty': move.quantity,
-                'product_uom': move.product_uom.id,
-                'lot_id': move.lot_id.id,
-            }
-            moves_ids.append(move_id)
-            #if (move.move_id.picking_id.type == 'in') and (move.product_id.cost_method != 'standard'):
-            #    partial_data['move%s' % (move_id)].update(product_price=move.cost,
-            #                                              product_currency=move.currency.id)
-        self.pool.get('stock.move').do_partial(cr, uid, moves_ids, partial_data, context=context)
-        return {'type': 'ir.actions.act_window_close'}
-
-# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/addons/stock/wizard/stock_partial_move_view.xml b/addons/stock/wizard/stock_partial_move_view.xml
deleted file mode 100644 (file)
index d57eaf6..0000000
+++ /dev/null
@@ -1,68 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<openerp>
-    <data>
-        <record id="action_partial_move_server" model="ir.actions.server">
-            <field name="name">Deliver/Receive Products</field>
-            <field name="model_id" ref="model_stock_move"/>
-            <field name="state">code</field>
-            <field name="code">action = obj.action_partial_move(context=context)</field>
-        </record>
-
-        <record id="ir_open_partial_move_wizard" model="ir.values">
-            <field eval="'client_action_multi'" name="key2"/>
-            <field eval="'stock.move'" name="model"/>
-            <field name="name">Deliver/Receive Products</field>
-            <field eval="'ir.actions.server,%d'%action_partial_move_server" name="value"/>
-        </record>
-
-        <record id="stock_partial_move_form" model="ir.ui.view">
-            <field name="name">stock.partial.move.form</field>
-            <field name="model">stock.partial.move</field>
-            <field name="arch" type="xml">
-                <form string="Stock Move" version="7.0">
-                    <separator string="Products"/>
-                    <field name="move_ids"/>
-                    <footer>
-                        <button name="do_partial" string="_Validate" type="object" class="oe_highlight"/>
-                        or
-                        <button string="Cancel" class="oe_link" special="cancel" />
-                    </footer>
-                </form>
-            </field>
-        </record>
-
-        <record id="stock_partial_move_line_list" model="ir.ui.view">
-            <field name="name">stock.partial.move.line.list</field>
-            <field name="model">stock.partial.move.line</field>
-            <field name="arch" type="xml">
-                <tree editable="bottom" string="Product Moves">
-                    <field name="product_id" />
-                    <field name="quantity" />
-                    <field name="product_uom" groups="product.group_uom"/>
-                    <field name="lot_id" domain="[('product_id', '=', product_id)]" groups="stock.group_production_lot"/>
-                    <field name="update_cost" invisible="1"/>
-                    <field name="cost" attrs="{'invisible': [('update_cost','=', False)]}"/>
-                    <field name="currency" attrs="{'invisible': [('update_cost','=', False)]}" groups="base.group_multi_currency"/>
-                </tree>
-            </field>
-        </record>
-
-        <record id="stock_partial_move_line_form" model="ir.ui.view">
-            <field name="name">stock.partial.move.line.form</field>
-            <field name="model">stock.partial.move.line</field>
-            <field name="arch" type="xml">
-                <form string="Stock Partial Move Line" version="7.0">
-                    <group>
-                        <field name="product_id" />
-                        <field name="quantity" />
-                        <field name="product_uom" />
-                        <field name="lot_id" domain="[('product_id', '=', product_id)]"/>
-                        <field name="update_cost" invisible="1"/>
-                        <field name="cost" attrs="{'invisible': [('update_cost','=', False)]}"/>
-                        <field name="currency" attrs="{'invisible': [('update_cost','=', False)]}" groups="base.group_multi_currency"/>
-                    </group>
-                 </form>
-            </field>
-        </record>
-    </data>
-</openerp>
diff --git a/addons/stock/wizard/stock_partial_picking.py b/addons/stock/wizard/stock_partial_picking.py
deleted file mode 100644 (file)
index 5a94c0d..0000000
+++ /dev/null
@@ -1,182 +0,0 @@
-# -*- coding: utf-8 -*-
-##############################################################################
-#
-#    OpenERP, Open Source Management Solution
-#    Copyright (C) 2004-TODAY OpenERP SA (<http://openerp.com>).
-#
-#    This program is free software: you can redistribute it and/or modify
-#    it under the terms of the GNU Affero General Public License as
-#    published by the Free Software Foundation, either version 3 of the
-#    License, or (at your option) any later version.
-#
-#    This program is distributed in the hope that it will be useful,
-#    but WITHOUT ANY WARRANTY; without even the implied warranty of
-#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-#    GNU Affero General Public License for more details.
-#
-#    You should have received a copy of the GNU Affero General Public License
-#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
-#
-##############################################################################
-
-import time
-from lxml import etree
-from openerp.osv import fields, osv
-from openerp.tools.misc import DEFAULT_SERVER_DATETIME_FORMAT
-import openerp.addons.decimal_precision as dp
-from openerp.tools.translate import _
-
-class stock_partial_picking_line(osv.TransientModel):
-
-    def _tracking(self, cursor, user, ids, name, arg, context=None):
-        res = {}
-        for tracklot in self.browse(cursor, user, ids, context=context):
-            if not tracklot.move_id:
-                continue
-            tracking = False
-            if (tracklot.move_id.picking_id.type == 'in' and tracklot.product_id.track_incoming) or \
-               (tracklot.move_id.picking_id.type == 'out' and tracklot.product_id.track_outgoing):
-                tracking = True
-            res[tracklot.id] = tracking
-        return res
-
-    _name = "stock.partial.picking.line"
-    _rec_name = 'product_id'
-    _columns = {
-        'product_id': fields.many2one('product.product', string="Product", required=True, ondelete='CASCADE'),
-        'quantity': fields.float("Quantity", digits_compute=dp.get_precision('Product Unit of Measure'), required=True),
-        'product_uom': fields.many2one('product.uom', 'Unit of Measure', required=True, ondelete='CASCADE'),
-        'lot_id': fields.many2one('stock.production.lot', 'Serial Number', ondelete='CASCADE'),
-        'move_id': fields.many2one('stock.move', "Move", ondelete='CASCADE'),
-        'wizard_id': fields.many2one('stock.partial.picking', string="Wizard", ondelete='CASCADE'),
-        'update_cost': fields.boolean('Need cost update'),
-        'cost': fields.float("Cost", help="Unit Cost for this product line"),
-        'currency': fields.many2one('res.currency', string="Currency", help="Currency in which Unit cost is expressed", ondelete='CASCADE'),
-        'tracking': fields.function(_tracking, string='Tracking', type='boolean'),
-    }
-
-    def onchange_product_id(self, cr, uid, ids, product_id, context=None):
-        uom_id = False
-        if product_id:
-            product = self.pool.get('product.product').browse(cr, uid, product_id, context=context)
-            uom_id = product.uom_id.id
-        return {'value': {'product_uom': uom_id}}
-
-
-class stock_partial_picking(osv.osv_memory):
-    _name = "stock.partial.picking"
-    _rec_name = 'picking_id'
-    _description = "Partial Picking Processing Wizard"
-
-    def _hide_tracking(self, cursor, user, ids, name, arg, context=None):
-        res = {}
-        for wizard in self.browse(cursor, user, ids, context=context):
-            res[wizard.id] = any([not(x.tracking) for x in wizard.move_ids])
-        return res
-
-    _columns = {
-        'date': fields.datetime('Date', required=True),
-        'move_ids': fields.one2many('stock.partial.picking.line', 'wizard_id', 'Product Moves'),
-        'picking_id': fields.many2one('stock.picking', 'Picking', required=True, ondelete='CASCADE'),
-        'hide_tracking': fields.function(_hide_tracking, string='Tracking', type='boolean', help='This field is for internal purpose. It is used to decide if the column production lot has to be shown on the moves or not.'),
-        'only_split_lines': fields.boolean('Only Split', help="Check this field if you just want to split the picking and don't want to marke as done the lines in the backorder"),
-     }
-
-    def fields_view_get(self, cr, uid, view_id=None, view_type='form', context=None, toolbar=False, submenu=False):
-        #override of fields_view_get in order to change the label of the process button and the separator accordingly to the shipping type
-        if context is None:
-            context = {}
-        res = super(stock_partial_picking, self).fields_view_get(cr, uid, view_id=view_id, view_type=view_type, context=context, toolbar=toolbar, submenu=submenu)
-        type = context.get('default_type', False)
-        if type:
-            doc = etree.XML(res['arch'])
-            for node in doc.xpath("//button[@name='do_partial']"):
-                if type == 'in':
-                    node.set('string', _('_Receive'))
-                elif type == 'out':
-                    node.set('string', _('_Deliver'))
-            for node in doc.xpath("//separator[@name='product_separator']"):
-                if type == 'in':
-                    node.set('string', _('Receive Products'))
-                elif type == 'out':
-                    node.set('string', _('Deliver Products'))
-            res['arch'] = etree.tostring(doc)
-        return res
-
-    def default_get(self, cr, uid, fields, context=None):
-        if context is None:
-            context = {}
-        res = super(stock_partial_picking, self).default_get(cr, uid, fields, context=context)
-        picking_ids = context.get('active_ids', [])
-        active_model = context.get('active_model')
-
-        if not picking_ids or len(picking_ids) != 1:
-            # Partial Picking Processing may only be done for one picking at a time
-            return res
-        assert active_model in ('stock.picking', 'stock.picking.in', 'stock.picking.out'), 'Bad context propagation'
-        picking_id, = picking_ids
-        picking = self.pool.get('stock.picking').browse(cr, uid, picking_id, context=context)
-        if 'picking_id' in fields:
-            res.update(picking_id=picking_id)
-        if 'move_ids' in fields:
-            moves = [self._partial_move_for(cr, uid, m) for m in picking.move_lines if m.state not in ('done', 'cancel')]
-            res.update(move_ids=moves)
-        if 'date' in fields:
-            res.update(date=time.strftime(DEFAULT_SERVER_DATETIME_FORMAT))
-        return res
-
-    def _partial_move_for(self, cr, uid, move):
-        partial_move = {
-            'product_id': move.product_id.id,
-            'quantity': move.product_uom_qty,
-            'product_uom': move.product_uom.id,
-            'move_id': move.id,
-            'location_id': move.location_id.id,
-            'location_dest_id': move.location_dest_id.id,
-            'cost': move.product_id.standard_price,
-        }
-        return partial_move
-
-    def do_partial(self, cr, uid, ids, context=None):
-        assert len(ids) == 1, 'Partial picking processing may only be done one at a time.'
-        if context is None:
-            context = {}
-        stock_move_obj = self.pool.get('stock.move')
-        partial_wizard = self.browse(cr, uid, ids[0], context=context)
-        picking_id = partial_wizard.picking_id.id
-        assert picking_id, 'Picking not defined'
-        picking_type = partial_wizard.picking_id.type
-
-        partial_data = {
-            'delivery_date': partial_wizard.date
-        }
-
-        partial_datas = []
-        for wizard_line in partial_wizard.move_ids:
-            move_id = wizard_line.move_id.id
-            if not move_id:
-                seq_obj_name = 'stock.picking.' + picking_type
-                move_id = stock_move_obj.create(cr, uid, {'name': self.pool.get('ir.sequence').get(cr, uid, seq_obj_name),
-                                                    'product_id': wizard_line.product_id.id,
-                                                    'product_uom_qty': wizard_line.quantity,
-                                                    'product_uom': wizard_line.product_uom.id,
-                                                    'lot_id': wizard_line.lot_id.id,
-                                                    'location_id': partial_wizard.picking_id.location_id.id,
-                                                    'location_dest_id': partial_wizard.picking_id.location_dest_id.id,
-                                                    'picking_id': False,
-                                                    'price_unit': wizard_line.cost}, context=context)
-            partial_data.update({
-                'product_id': wizard_line.product_id.id,
-                'product_uom_qty': wizard_line.quantity,
-                'product_uom': wizard_line.product_uom.id,
-                'lot_id': wizard_line.lot_id.id,
-                'price_unit': wizard_line.cost,
-                'move_id': move_id,
-            })
-            tmp = partial_data.copy()
-            partial_datas += [tmp]
-        self.pool.get('stock.picking').do_partial(cr, uid, picking_id, partial_datas, partial_wizard.only_split_lines, context=context)
-        return {'type': 'ir.actions.act_window_close'}
-
-
-# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/addons/stock/wizard/stock_partial_picking_view.xml b/addons/stock/wizard/stock_partial_picking_view.xml
deleted file mode 100644 (file)
index a07fbb0..0000000
+++ /dev/null
@@ -1,78 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<openerp>
-    <data>
-        <record id="action_partial_picking" model="ir.actions.act_window">
-            <field name="name">Process Picking</field>
-            <field name="res_model">stock.partial.picking</field>
-            <field name="view_type">form</field>
-            <field name="view_mode">form</field>
-            <field name="target">new</field>
-        </record>
-
-        <!-- this view of stock.partial.picking wizard is dedicated to internal picking. The fields_view_get is ovveridden in order to change the label of the process button and the separator. -->
-        <record id="stock_partial_picking_form" model="ir.ui.view">
-            <field name="name">stock.partial.picking.form</field>
-            <field name="model">stock.partial.picking</field>
-            <field name="arch" type="xml">
-                <form string="Stock partial Picking" version="7.0">
-                    <field name="hide_tracking" invisible="1"/>
-                    <separator string="Transfer Products" name="product_separator"/>
-                    <field name="move_ids" context="{'hide_tracking': hide_tracking}">
-                        <tree editable="bottom" string="Product Moves">
-                            <field name="product_id" on_change="onchange_product_id(product_id)"/>
-                            <field name="quantity" />
-                            <field name="product_uom" groups="product.group_uom"/>
-                            <field name="tracking" invisible="1"/>
-                            <field name="lot_id" domain="[('product_id', '=', product_id)]" invisible="context.get('hide_tracking',False)" attrs="{'required':[('tracking','=',True), ('quantity', '!=', 0)]}" groups="stock.group_production_lot" context="{'default_product_id':product_id}"/>
-                            <!-- Removed as this feature is not logic: price must be updated upon reception of invoice -->
-                            <field name="update_cost" invisible="1"/>
-                            <field name="cost"  invisible="1"/>
-                            <field name="currency"  invisible="1"/>
-                        </tree>
-                    </field>
-                    <footer>
-                        <button name="do_partial" string="_Transfer" type="object" class="oe_highlight"/>
-                        or
-                        <button string="Cancel" class="oe_link" special="cancel" />
-                    </footer>
-                </form>
-            </field>
-        </record>
-
-        <record id="stock_partial_picking_line_list" model="ir.ui.view">
-            <field name="name">stock.partial.picking.line.list</field>
-            <field name="model">stock.partial.picking.line</field>
-            <field name="arch" type="xml">
-                <tree editable="bottom" string="Product Moves">
-                    <field name="product_id" />
-                    <field name="quantity" />
-                    <field name="product_uom" />
-                    <field name="tracking" invisible="1"/>
-                    <field name="lot_id" domain="[('product_id', '=', product_id)]" attrs="{'required':[('tracking','=',True)]}"/>
-                    <!-- Removed as this feature is not logic: price must be updated upon reception of invoice -->
-                    <field name="update_cost" invisible="1"/>
-                    <field name="cost" invisible="1"/>
-                    <field name="currency" invisible="1"/>
-                </tree>
-            </field>
-        </record>
-        <record id="stock_partial_picking_line_form" model="ir.ui.view">
-            <field name="name">stock.partial.picking.line.form</field>
-            <field name="model">stock.partial.picking.line</field>
-            <field name="arch" type="xml">
-                <form string="Stock Picking Line" version="7.0">
-                    <group col="4">
-                        <field name="product_id" />
-                        <field name="quantity" />
-                        <field name="product_uom" />
-                        <field name="tracking" invisible="1"/>
-                        <field name="lot_id" domain="[('product_id', '=', product_id)]" attrs="{'required':[('tracking','=',True)]}"/>
-                        <field name="update_cost" invisible="1"/>
-                        <field name="cost" attrs="{'invisible': [('update_cost','=', False)]}"/>
-                        <field name="currency" attrs="{'invisible': [('update_cost','=', False)]}" groups="base.group_multi_currency"/>
-                    </group>
-                 </form>
-            </field>
-        </record>
-    </data>
-</openerp>