← Back to team overview

openerp-community-reviewer team mailing list archive

lp:~camptocamp/stock-logistic-flows/7.0-add-stock_recompute_availability_on_force-afe into lp:stock-logistic-flows/7.0

 

Alexandre Fayolle - camptocamp has proposed merging lp:~camptocamp/stock-logistic-flows/7.0-add-stock_recompute_availability_on_force-afe into lp:stock-logistic-flows/7.0.

Requested reviews:
  Nicolas Bessi - Camptocamp (nbessi-c2c)

For more details, see:
https://code.launchpad.net/~camptocamp/stock-logistic-flows/7.0-add-stock_recompute_availability_on_force-afe/+merge/187772

[ADD] stock_recompute_availability_on_force
 
when forcing a picking availability, recompute availability of other pickings which could be affected by the forcing

-- 
https://code.launchpad.net/~camptocamp/stock-logistic-flows/7.0-add-stock_recompute_availability_on_force-afe/+merge/187772
Your team Stock and Logistic Core Editors is subscribed to branch lp:stock-logistic-flows/7.0.
=== added directory 'stock_recompute_availability_on_force'
=== added file 'stock_recompute_availability_on_force/__init__.py'
--- stock_recompute_availability_on_force/__init__.py	1970-01-01 00:00:00 +0000
+++ stock_recompute_availability_on_force/__init__.py	2013-09-26 13:14:02 +0000
@@ -0,0 +1,22 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+#    Author: Alexandre Fayolle
+#    Copyright 2013 Camptocamp SA
+#
+#    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 . import stock

=== added file 'stock_recompute_availability_on_force/__openerp__.py'
--- stock_recompute_availability_on_force/__openerp__.py	1970-01-01 00:00:00 +0000
+++ stock_recompute_availability_on_force/__openerp__.py	2013-09-26 13:14:02 +0000
@@ -0,0 +1,40 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+#    Author: Alexandre Fayolle
+#    Copyright 2013 Camptocamp SA
+#
+#    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/>.
+#
+##############################################################################
+{
+    'name' : 'Recompute stock move state on Force Availability',
+    'version' : '0.1',
+    'category' : 'Warehouse Management',
+    'description': '''
+    When the user forced availability of a stock.picking, recompute the availability of other pickings which could be affected by this forcing.
+    ''',
+    'author' : 'Camptocamp',
+    'website' : 'http://www.camptocamp.com',
+    'depends' : ['stock', 'procurement'],
+    'data' : ['procurement_workflow.xml',
+              ],
+    'demo' : [],
+    'test' : [],
+    'installable': True,
+    'auto_install' : False,
+    'application' : False,
+}
+
+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

=== added file 'stock_recompute_availability_on_force/procurement_workflow.xml'
--- stock_recompute_availability_on_force/procurement_workflow.xml	1970-01-01 00:00:00 +0000
+++ stock_recompute_availability_on_force/procurement_workflow.xml	2013-09-26 13:14:02 +0000
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<openerp>
+  <data>
+    <record id="trans_ready_confirm" model="workflow.transition">
+      <field name="act_from" ref="procurement.act_make_done"/>
+      <field name="act_to" ref="procurement.act_confirm"/>
+      <field name="condition">check_cancel_assigned_moves()</field>
+      <field name="signal">button_recompute_availability</field>
+    </record>
+
+  </data>
+</openerp>

=== added file 'stock_recompute_availability_on_force/stock.py'
--- stock_recompute_availability_on_force/stock.py	1970-01-01 00:00:00 +0000
+++ stock_recompute_availability_on_force/stock.py	2013-09-26 13:14:02 +0000
@@ -0,0 +1,85 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+#    Author: Alexandre Fayolle
+#    Copyright 2013 Camptocamp SA
+#
+#    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 logging
+from openerp.osv import orm
+from openerp import netsvc
+
+_logger = logging.getLogger(__name__)
+
+class stock_picking(orm.Model):
+    _inherit = 'stock.picking'
+
+    def force_assign(self, cr, uid, ids, *args):
+        """
+        when forcing availability on a picking, get the procurements
+        for all the assigned moves for the same product on the same
+        stock location, and send the recompute_availability signal to
+        reset them in confirmed state. The scheduler can then be run
+        to recompute availability. 
+        """
+        _logger.debug('stock.picking force_assign %s', ids)
+        move_obj = self.pool.get('stock.move')
+        procurement_obj = self.pool.get('procurement.order')
+        product_locations = set()
+        for pick in self.browse(cr, uid, ids):
+            for move in pick.move_lines:
+                product_locations.add((move.product_id.id, move.location_id.id))
+        res = super(stock_picking, self).force_assign(cr, uid, ids, *args)
+        other_move_ids = []
+        for product_id, location_id in product_locations:
+            other_move_ids += move_obj.search(cr, uid,
+                                        [('product_id', '=', product_id),
+                                         ('location_id', '=', location_id),
+                                         ('state', '=', 'assigned'),
+                                         ('picking_id', 'not in', ids),
+                                         ])
+        procurement_ids = procurement_obj.search(cr, uid,
+                                                 [('move_id', 'in', other_move_ids),
+                                                  ('procure_method', '=', 'make_to_stock'),
+                                                  ])
+        move_ids = [proc.move_id.id for proc in procurement_obj.browse(cr, uid, procurement_ids)]
+        move_obj.cancel_assign(cr, uid, move_ids)
+        wf_service = netsvc.LocalService("workflow")
+        for proc_id in procurement_ids:
+            _logger.debug('button_recompute_availability on procurement.order %d', proc_id)
+            wf_service.trg_validate(uid, 'procurement.order', proc_id, 'button_recompute_availability', cr)
+        our_procurement_ids = procurement_obj.search(cr, uid, [('move_id.picking_id', 'in', ids)])
+        for proc_id in our_procurement_ids:
+            _logger.debug('button_check on procurement.order %d', proc_id)
+            wf_service.trg_validate(uid, 'procurement.order', proc_id, 'button_check', cr)
+        return res
+
+class stock_picking_in(orm.Model):
+    _inherit = 'stock.picking.in'
+    def force_assign(self, cr, uid, ids, *args):
+        return self.pool.get('stock.picking').force_assign(cr, uid, ids, *args)
+
+class stock_picking_out(orm.Model):
+    _inherit = 'stock.picking.out'
+    def force_assign(self, cr, uid, ids, *args):
+        return self.pool.get('stock.picking').force_assign(cr, uid, ids, *args)
+
+
+class procurement_order(orm.Model):
+    _inherit = 'procurement.order'
+
+    def check_cancel_assigned_moves(self, cr, uid, ids, context=None):
+        return all(procurement.move_id.state == 'confirmed' for procurement in self.browse(cr, uid, ids, context=context))

=== added file 'stock_recompute_availability_on_force/stock_view.xml'
--- stock_recompute_availability_on_force/stock_view.xml	1970-01-01 00:00:00 +0000
+++ stock_recompute_availability_on_force/stock_view.xml	2013-09-26 13:14:02 +0000
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<openerp>
+    <data>
+
+
+    </data>
+</openerp>

=== added directory 'stock_recompute_availability_on_force/test'
=== added directory 'stock_recompute_availability_on_force/tests'
=== added file 'stock_recompute_availability_on_force/tests/__init__.py'
--- stock_recompute_availability_on_force/tests/__init__.py	1970-01-01 00:00:00 +0000
+++ stock_recompute_availability_on_force/tests/__init__.py	2013-09-26 13:14:02 +0000
@@ -0,0 +1,5 @@
+from . import test_force_recompute
+
+checks = [
+    test_force_recompute,
+]

=== added file 'stock_recompute_availability_on_force/tests/test_force_recompute.py'
--- stock_recompute_availability_on_force/tests/test_force_recompute.py	1970-01-01 00:00:00 +0000
+++ stock_recompute_availability_on_force/tests/test_force_recompute.py	2013-09-26 13:14:02 +0000
@@ -0,0 +1,111 @@
+# -*- coding: utf-8 -*-
+
+import logging
+
+from openerp import tools
+from openerp.tests import common
+from openerp import netsvc
+
+_logger = logging.getLogger('openerp.test.force_recompute')
+
+class TestForceRecompute(common.TransactionCase):
+
+    def setUp(self):
+        super(TestForceRecompute, self).setUp()
+        cr, uid = self.cr, self.uid
+        self.procurement = self.registry('procurement.order')
+        self.stock_move = self.registry('stock.move')
+        self.stock_picking = self.registry('stock.picking.out')
+        self.stock_location = self.registry('stock.location')
+        self.product_product = self.registry('product.product')
+        self.res_partner = self.registry('res.partner')
+        self.wf_service = netsvc.LocalService("workflow")
+
+        #
+        # create picking and procurement for CARD
+        #
+        
+        prod_id = self.product_product.search(cr, uid, [('default_code', '=', 'CARD')])[0]
+        location_data = self.stock_location.default_get(cr, uid,['active', 'usage',
+                                                                 'chained_location_type',
+                                                                 'chainged_auto_packing',
+                                                                 'company_id',
+                                                                 'posx', 'posy', 'posz', 'icon',
+                                                                 'scrap_location'])
+        location_data.update({'name': 'test', 'usage': 'internal'})
+        location_id = self.stock_location.create(cr, uid, location_data)
+        location_dest_id = self.stock_location.search(cr, uid, [('name', '=', 'Customers')])[0]
+        location_supplier_id = self.stock_location.search(cr, uid, [('name', '=', 'Suppliers')])[0]
+        move_data = self.stock_move.default_get(cr, uid,
+                                                ['state', 'company_id', 'priority', 'scrapped', 'date', 'date_expected'])
+        move_data.update({'product_id': prod_id,
+                          'product_qty': 15,
+                          'product_uom': 1,
+                          'location_dest_id': location_id,
+                          'location_id': location_supplier_id,
+                          'name': 'initial inventory'})
+        move_id = self.stock_move.create(cr, uid, move_data)
+        self.stock_move.action_done(cr, uid, [move_id])
+        self.picking_ids = []
+        self.procurement_ids = []
+        for i in range(2):
+            picking_data = self.stock_picking.default_get(cr, uid,
+                                                          ['company_id', 'name', 'state', 'move_type', 'type', 'invoice_state', 'date'])
+            move_data = self.stock_move.default_get(cr, uid,
+                                                    ['state', 'company_id', 'priority', 'scrapped', 'date', 'date_expected'])
+            proc_data = self.procurement.default_get(cr, uid, ['state', 'priority', 'date_planned', 'close_move', 'company_id'])
+            picking_id = self.stock_picking.create(cr, uid,
+                                                picking_data)
+            self.picking_ids.append(picking_id)
+            move_data.update({'location_id': location_id,
+                              'location_dest_id': location_dest_id,
+                              'product_id': prod_id,
+                              'product_qty': 10,
+                              'product_uom': 1,
+                              'partner_id': self.res_partner.search(cr, uid, [('name', '=', 'Camptocamp')])[0],
+                              'picking_id': picking_id,
+                              'name': 'test move card',
+                              })
+            move_id = self.stock_move.create(cr, uid, move_data)
+            proc_data.update({'name': 'test procurement',
+                              'product_id': prod_id,
+                              'product_qty': 10,
+                              'product_uom': 1,
+                              'procure_method': 'make_to_stock',
+                              'location_id': location_id,
+                              'move_id': move_id
+                              })
+            procurement_id = self.procurement.create(cr, uid, proc_data)
+            self.procurement_ids.append(procurement_id)
+            self.wf_service.trg_validate(uid, 'stock.picking', picking_id, 'button_confirm', cr)
+            self.wf_service.trg_validate(uid, 'procurement.order', procurement_id, 'button_confirm', cr)
+            self.wf_service.trg_validate(uid, 'procurement.order', procurement_id, 'button_check', cr)
+
+    def test_setup(self):
+        cr, uid = self.cr, self.uid
+        picking1 = self.stock_picking.browse(cr, uid, self.picking_ids[0])
+        self.assertEqual(picking1.state, 'assigned')
+        procurement1 = self.procurement.browse(cr, uid, self.procurement_ids[0])
+        self.assertEqual(procurement1.state, 'ready')
+        picking2 = self.stock_picking.browse(cr, uid, self.picking_ids[1])
+        self.assertEqual(picking2.state, 'confirmed')
+        procurement2 = self.procurement.browse(cr, uid, self.procurement_ids[1])
+        self.assertEqual(procurement2.state, 'exception')
+
+    def test_force_assign(self):
+        cr, uid = self.cr, self.uid
+        self.stock_picking.force_assign(cr, uid, [self.picking_ids[1]])
+        # tests
+        picking2 = self.stock_picking.browse(cr, uid, self.picking_ids[1])
+        for move in picking2.move_lines:
+            self.assertEqual(move.state, 'assigned')
+        self.assertEqual(picking2.state, 'assigned')
+        procurement2 = self.procurement.browse(cr, uid, self.procurement_ids[1])
+        self.assertEqual(procurement2.state, 'ready')
+        picking1 = self.stock_picking.browse(cr, uid, self.picking_ids[0])
+        for move in picking1.move_lines:
+            self.assertEqual(move.state, 'confirmed')
+        self.assertEqual(picking1.state, 'confirmed')
+        procurement1 = self.procurement.browse(cr, uid, self.procurement_ids[0])
+        self.assertEqual(procurement1.state, 'confirmed')
+        


Follow ups