← Back to team overview

openerp-community-reviewer team mailing list archive

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

 

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

Requested reviews:
  Stock and Logistic Core Editors (stock-logistic-core-editors)

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

[ADD] stock_pricking_priority

Add a priority attribute to pickings.

This priority can be changed after the picking is confirmed and a
wizard can be run to recompute the availability of pickings
depending on the new priorities.
-- 
https://code.launchpad.net/~camptocamp/stock-logistic-flows/7.0-add_stock_picking_priority-afe/+merge/197206
Your team Stock and Logistic Core Editors is requested to review the proposed merge of lp:~camptocamp/stock-logistic-flows/7.0-add_stock_picking_priority-afe into lp:stock-logistic-flows/7.0.
=== added directory 'stock_picking_priority'
=== added file 'stock_picking_priority/__init__.py'
--- stock_picking_priority/__init__.py	1970-01-01 00:00:00 +0000
+++ stock_picking_priority/__init__.py	2013-11-29 14:21:37 +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 picking_priority

=== added file 'stock_picking_priority/__openerp__.py'
--- stock_picking_priority/__openerp__.py	1970-01-01 00:00:00 +0000
+++ stock_picking_priority/__openerp__.py	2013-11-29 14:21:37 +0000
@@ -0,0 +1,44 @@
+# -*- 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" : "Picking priority",
+    "version" : "0.1",
+    "depends" : ['stock'],
+    "author" : "Camptocamp",
+    'license': 'AGPL-3',
+    "description": """Add a priority attribute to pickings.
+
+    This priority can be changed after the picking is confirmed and a
+    wizard can be run to recompute the availability of pickings
+    depending on the new priorities.""",
+    "website" : "http://www.camptocamp.com";,
+    "category" : "Warehouse management",
+    "demo" : [
+        'picking_priority_demo.yml',
+        ],
+    "data" : ['picking_priority_view.xml',
+              ],
+    "test": ['tests/test_picking_priority.yml',
+             ],
+    "installable": True,
+}

=== added file 'stock_picking_priority/picking_priority.py'
--- stock_picking_priority/picking_priority.py	1970-01-01 00:00:00 +0000
+++ stock_picking_priority/picking_priority.py	2013-11-29 14:21:37 +0000
@@ -0,0 +1,123 @@
+# -*- 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, osv, fields
+from openerp.tools.translate import _
+_logger = logging.getLogger(__name__)
+
+
+class StockPicking(orm.Model):
+    _inherit = "stock.picking"
+    _columns = {
+        'priority': fields.selection([('0', 'Normal'),
+                                      ('1', 'Urgent'),
+                                      ('2', 'Very Urgent')],
+                                     'Priority',
+                                     required=True,
+                                     help='The priority of the picking'),
+        }
+    _defaults = {
+        'priority': '0',
+        }
+
+    def retry_assign_all(self, cr, uid, ids, context=None):
+        domain = [('type', '!=', 'in'),
+                  ('move_lines', '!=', []),
+                  ('state', 'in', ('confirmed', 'assigned'))]
+        if ids:
+            domain += [('ids', 'in', ids)]
+        picking_ids = self.search(cr, uid, domain,
+                                  order='priority desc, min_date',
+                                  context=context)
+        _logger.info('cancelling pickings')
+        cancelled_ids = self.cancel_assign(cr, uid, picking_ids, context)
+        assigned_ids = []
+        errors = []
+        _logger.info('reassigning pickings')
+        for picking_id in picking_ids:
+            try:
+                assigned_id = self.action_assign(cr, uid, [picking_id],
+                                                 context)
+                assigned_ids.append(assigned_id)
+            except osv.except_osv, exc:
+                name = self.read(cr, uid, picking_id, ['name'],
+                                 context=context)['name']
+                errors.append(u'%s: %s' % (name, exc.args[-1]))
+                _logger.info('error in action_assign for picking %s: %s'
+                             % (name, exc))
+        if errors:
+            message = '\n'.join(errors)
+            raise orm.except_orm(_(u'Warning'),
+                                 _(u'No operations validated due '
+                                   'to the following errors:\n%s') % message)
+        return cancelled_ids, assigned_ids
+
+
+class StockPickingOut(orm.Model):
+    _inherit = 'stock.picking.out'
+    _columns = {
+        'priority': fields.selection([('0', 'Normal'),
+                                      ('1', 'Urgent'),
+                                      ('2', 'Very Urgent')],
+                                     'Priority',
+                                     required=True,
+                                     help='The priority of the picking'),
+        }
+    _defaults = {
+        'priority': '0',
+        }
+
+    def retry_assign_all(self, cr, uid, ids, context=None):
+        return self.pool.get('stock.picking').retry_assign_all(cr, uid, ids,
+                                                               context=context)
+
+
+class StockPickingIn(orm.Model):
+    _inherit = 'stock.picking.in'
+    _columns = {
+        'priority': fields.selection([('0', 'Normal'),
+                                      ('1', 'Urgent'),
+                                      ('2', 'Very Urgent')],
+                                     'Priority',
+                                     required=True,
+                                     help='The priority of the picking'),
+        }
+    _defaults = {
+        'priority': '0',
+        }
+
+    def retry_assign_all(self, cr, uid, ids, context=None):
+        return self.pool.get('stock.picking').retry_assign_all(cr, uid, ids,
+                                                               context=context)
+
+
+class StockPickingRetryAvailability(orm.TransientModel):
+    _name = "stock.picking.retry.availability"
+    _columns = {
+        }
+
+    def action_retry_assign(self, cr, uid, ids, context=None):
+        pick_obj = self.pool['stock.picking']
+
+        pick_obj.retry_assign_all(cr, uid, [], context=context)
+
+        return {'type': 'ir.actions.act_window_close'}

=== added file 'stock_picking_priority/picking_priority_demo.yml'
--- stock_picking_priority/picking_priority_demo.yml	1970-01-01 00:00:00 +0000
+++ stock_picking_priority/picking_priority_demo.yml	2013-11-29 14:21:37 +0000
@@ -0,0 +1,81 @@
+-
+ Create 3 pickings and procurements for 3 M-Opt on shelf 1 due Dec 10, 1st and 20
+ M-Opt (product.product_product_10)
+ initial stock is 8 units in shelf 1 (stock.stock_location_components)
+-
+ !record {model: stock.picking.out, id: ship_mopt_1}:
+   name: mopt1_1
+   move_lines:
+     - product_id: product.product_product_10
+       product_qty: 3
+       product_uom: product.product_uom_unit
+       location_id: stock.stock_location_components
+       location_dest_id: stock.stock_location_output
+       date: 2013-12-10
+       date_expected: 2013-12-10
+       procurements:
+         - name: mopt1_1
+           origin: test
+           date_planned: 2013-12-10
+           product_id: product.product_product_10
+           product_qty: 3
+           product_uom: product.product_uom_unit
+           location_id: stock.stock_location_components
+           procure_method: make_to_stock
+-
+ !record {model: stock.picking.out, id: ship_mopt_2}:
+   name: mopt1_2
+   move_lines:
+     - product_id: product.product_product_10
+       product_qty: 3
+       product_uom: product.product_uom_unit
+       location_id: stock.stock_location_components
+       location_dest_id: stock.stock_location_output
+       date: 2013-12-01
+       date_expected: 2013-12-01
+       procurements:
+         - name: mopt1_2
+           origin: test
+           date_planned: 2013-12-01
+           product_id: product.product_product_10
+           product_qty: 3
+           product_uom: product.product_uom_unit
+           location_id: stock.stock_location_components
+           procure_method: make_to_stock
+-
+ !record {model: stock.picking.out, id: ship_mopt_3}:
+   name: mopt1_3
+   move_lines:
+     - product_id: product.product_product_10
+       product_qty: 3
+       product_uom: product.product_uom_unit
+       location_id: stock.stock_location_components
+       location_dest_id: stock.stock_location_output
+       date: 2013-12-20
+       date_expected: 2013-12-20
+       procurements:
+         - name: mopt1_3
+           origin: test
+           date_planned: 2013-12-20
+           product_id: product.product_product_10
+           product_qty: 3
+           product_uom: product.product_uom_unit
+           location_id: stock.stock_location_components
+           procure_method: make_to_stock
+-
+ Confirm the 3 pickings
+-
+  !workflow {model: stock.picking, action: button_confirm, ref: ship_mopt_1}
+-
+  !workflow {model: stock.picking, action: button_confirm, ref: ship_mopt_2}
+-
+  !workflow {model: stock.picking, action: button_confirm, ref: ship_mopt_3}
+-
+ Confirm the procurements
+-
+ !python {model: procurement.order}: |
+   procurement_ids = self.search(cr, uid, [('name', 'in', ('mopt1_1', 'mopt1_2', 'mopt1_3'))], context=context)
+   from openerp import netsvc
+   wf_service = netsvc.LocalService("workflow")
+   for id in procurement_ids:
+     wf_service.trg_validate(uid, 'procurement.order', id, 'button_confirm', cr)

=== added file 'stock_picking_priority/picking_priority_view.xml'
--- stock_picking_priority/picking_priority_view.xml	1970-01-01 00:00:00 +0000
+++ stock_picking_priority/picking_priority_view.xml	2013-11-29 14:21:37 +0000
@@ -0,0 +1,64 @@
+<?xml version="1.0" encoding="utf-8"?>
+<openerp>
+  <data>
+    <record model="ir.ui.view" id="stock_picking_form">
+      <field name="name">stock.picking.form priority</field>
+      <field name="model">stock.picking</field>
+      <field name="inherit_id" ref="stock.view_picking_form"/>
+      <field name="arch" type="xml">
+        <field name="date_done" position="after">
+          <field name="priority"/>
+        </field>
+      </field>
+    </record>
+    <record model="ir.ui.view" id="stock_picking_out_form">
+      <field name="name">stock.picking.out.form priority</field>
+      <field name="model">stock.picking.out</field>
+      <field name="inherit_id" ref="stock.view_picking_form"/>
+      <field name="arch" type="xml">
+        <field name="date_done" position="after">
+          <field name="priority"/>
+        </field>
+      </field>
+    </record>
+
+    <act_window id="action_retry_assign"
+                name="Retry picking assignment"
+                res_model="stock.picking.retry.availability"
+                view_type="form"
+                view_mode="form"
+                target="new"
+                />
+
+    <menuitem id="menu_retry_availability"
+              name="Recompute picking availability"
+              parent="procurement.menu_stock_sched"
+              sequence="40"
+              action="action_retry_assign"
+              groups="stock.group_stock_user,stock.group_stock_manager"
+              />
+
+    <record model="ir.ui.view" id="picking_retry_availability_form">
+      <field name="name">stock.picking.retry.availability form</field>
+      <field name="model">stock.picking.retry.availability</field>
+      <field name="arch" type="xml">
+        <form string="Recompute picking availability" version="7.0">
+          <sheet>
+            <p class="oe_grey">
+              This action will recompute the picking availability based on their
+              priority. Before running it, make sure that you have changed the
+              priority of at least one picking.
+            </p>
+          </sheet>
+          <footer>
+            <button name="action_retry_assign" string="Recompute availability" type="object"
+                    class="oe_highlight"/>
+            or
+            <button string="Cancel" class="oe_link" special="cancel" />
+          </footer>
+        </form>
+      </field>
+    </record>
+
+  </data>
+</openerp>

=== added directory 'stock_picking_priority/tests'
=== added file 'stock_picking_priority/tests/test_picking_priority.yml'
--- stock_picking_priority/tests/test_picking_priority.yml	1970-01-01 00:00:00 +0000
+++ stock_picking_priority/tests/test_picking_priority.yml	2013-11-29 14:21:37 +0000
@@ -0,0 +1,136 @@
+-
+ Create 3 pickings and procurements for 10 M-Las on shelf 1 due Dec 10, 1st and 20
+ M-Las (product.product_product_11)
+ initial stock is 26 units in shelf 1 (stock.stock_location_components)
+-
+ !record {model: stock.picking.out, id: ship_mlas_1}:
+   name: mlas1_1
+   move_lines:
+     - product_id: product.product_product_11
+       product_qty: 10
+       product_uom: product.product_uom_unit
+       location_id: stock.stock_location_components
+       location_dest_id: stock.stock_location_output
+       date: 2013-12-20
+       date_expected: 2013-12-20
+       procurements:
+         - name: mlas1_1
+           origin: test
+           date_planned: 2013-12-20
+           product_id: product.product_product_11
+           product_qty: 10
+           product_uom: product.product_uom_unit
+           location_id: stock.stock_location_components
+           procure_method: make_to_stock
+-
+ !record {model: stock.picking.out, id: ship_mlas_2}:
+   name: mlas1_2
+   move_lines:
+     - product_id: product.product_product_11
+       product_qty: 10
+       product_uom: product.product_uom_unit
+       location_id: stock.stock_location_components
+       location_dest_id: stock.stock_location_output
+       date: 2013-12-01
+       date_expected: 2013-12-01
+       procurements:
+         - name: mlas1_2
+           origin: test
+           date_planned: 2013-12-01
+           product_id: product.product_product_11
+           product_qty: 10
+           product_uom: product.product_uom_unit
+           location_id: stock.stock_location_components
+           procure_method: make_to_stock
+-
+ !record {model: stock.picking.out, id: ship_mlas_3}:
+   name: mlas1_3
+   move_lines:
+     - product_id: product.product_product_11
+       product_qty: 10
+       product_uom: product.product_uom_unit
+       location_id: stock.stock_location_components
+       location_dest_id: stock.stock_location_output
+       date: 2013-12-10
+       date_expected: 2013-12-10
+       procurements:
+         - name: mlas1_3
+           origin: test
+           date_planned: 2013-12-10
+           product_id: product.product_product_11
+           product_qty: 10
+           product_uom: product.product_uom_unit
+           location_id: stock.stock_location_components
+           procure_method: make_to_stock
+-
+ Confirm the 3 pickings
+-
+  !workflow {model: stock.picking, action: button_confirm, ref: ship_mlas_1}
+-
+  !workflow {model: stock.picking, action: button_confirm, ref: ship_mlas_2}
+-
+  !workflow {model: stock.picking, action: button_confirm, ref: ship_mlas_3}
+-
+ Confirm the procurements
+-
+ !python {model: procurement.order}: |
+   procurement_ids = self.search(cr, uid, [('name', 'in', ('mlas1_1', 'mlas1_2', 'mlas1_3'))], context=context)
+   from openerp import netsvc
+   wf_service = netsvc.LocalService("workflow")
+   for id in procurement_ids:
+     wf_service.trg_validate(uid, 'procurement.order', id, 'button_confirm', cr)
+-
+  I run the scheduler.
+-
+  !python {model: procurement.order}: |
+    self.run_scheduler(cr, uid)
+-
+ Check the procurement states
+-
+ !python {model: procurement.order}: |
+   procurement_ids = self.search(cr, uid, [('name', 'in', ('mlas1_1', 'mlas1_2', 'mlas1_3'))], context=context)
+   states = {}
+   for proc in self.browse(cr, uid, procurement_ids, context=context):
+       states[proc.name] = proc.state
+   expected = {'mlas1_1': 'exception', 'mlas1_2': 'ready', 'mlas1_3': 'ready'}
+   assert states == expected, 'wrong procurement states: %s (expected %s)' % (states, expected)
+-
+ Check the picking states
+-
+ !python {model: stock.picking}: |
+   picking_ids = [ref("ship_mlas_1"), ref("ship_mlas_2"), ref("ship_mlas_3")]
+   states = {}
+   for pick in self.browse(cr, uid, picking_ids, context=context):
+       states[pick.id] = pick.state
+   expected = {ref("ship_mlas_1"): 'confirmed', ref("ship_mlas_2"): 'assigned', ref("ship_mlas_3"): 'assigned'}
+   assert states == expected, 'wrong picking states %s (expected: %s)' % (states, expected)
+-
+ I set the priority of picking ship_mlas_1 to Urgent
+-
+ !python {model: stock.picking.out}: |
+   self.write(cr, uid, ref("ship_mlas_1"), {'priority': '1'}, context=context)
+-
+ I retry the assignments
+-
+ !python {model: stock.picking.out}: |
+   self.retry_assign_all(cr, uid, [], context=context)
+-
+ Check the procurement states
+-
+ !python {model: procurement.order}: |
+   procurement_ids = self.search(cr, uid, [('name', 'in', ('mlas1_1', 'mlas1_2', 'mlas1_3'))], context=context)
+   states = {}
+   for proc in self.browse(cr, uid, procurement_ids, context=context):
+       states[proc.name] = proc.state
+   expected = {'mlas1_1': 'exception', 'mlas1_2': 'ready', 'mlas1_3': 'ready'}
+   assert states == expected, 'wrong procurement states: %s (expected %s)' % (states, expected)
+-
+ Check the picking states
+-
+ !python {model: stock.picking}: |
+   picking_ids = [ref("ship_mlas_1"), ref("ship_mlas_2"), ref("ship_mlas_3")]
+   states = {}
+   for pick in self.browse(cr, uid, picking_ids, context=context):
+       states[pick.id] = pick.state
+   expected = {ref("ship_mlas_1"): 'assigned', ref("ship_mlas_2"): 'assigned', ref("ship_mlas_3"): 'confirmed'}
+   assert states == expected, 'wrong picking states %s (expected: %s)' % (states, expected)


Follow ups