Merge lp:~camptocamp/stock-logistic-flows/7.0-picking_dispatch_wave-according-defined-number-of-sales_rde into lp:stock-logistic-flows

Proposed by Romain Deheele - Camptocamp
Status: Merged
Merged at revision: 62
Proposed branch: lp:~camptocamp/stock-logistic-flows/7.0-picking_dispatch_wave-according-defined-number-of-sales_rde
Merge into: lp:stock-logistic-flows
Diff against target: 469 lines (+432/-0)
7 files modified
picking_dispatch_wave/__init__.py (+22/-0)
picking_dispatch_wave/__openerp__.py (+46/-0)
picking_dispatch_wave/dispatch_wave.py (+119/-0)
picking_dispatch_wave/dispatch_wave_view.xml (+46/-0)
picking_dispatch_wave/i18n/fr.po (+82/-0)
picking_dispatch_wave/i18n/picking_dispatch_wave.pot (+70/-0)
picking_dispatch_wave/test/test_dispatch_wave.yml (+47/-0)
To merge this branch: bzr merge lp:~camptocamp/stock-logistic-flows/7.0-picking_dispatch_wave-according-defined-number-of-sales_rde
Reviewer Review Type Date Requested Status
Nicolas Bessi - Camptocamp (community) Approve
Yannick Vaucher @ Camptocamp tests Approve
Alexandre Fayolle - camptocamp code review, test Approve
Review via email: mp+214568@code.launchpad.net

Description of the change

Hello,

This MP adds an addon named picking_dispatch_wave, the principle :
 - Allows to set a 'sale order wave' to pick according a number n of sales that you set.
    1.The picker sets a number n of sale orders.
    2.The wizard will select moves from n oldest sales that are linked to ready pickings.
    3.A picking dispatch is created with found moves

Regards,

Romain

To post a comment you must log in.
Revision history for this message
Alexandre Fayolle - camptocamp (alexandre-fayolle-c2c) wrote :

Needs fixing:

* convert the description in __openerp__.py to Restructured Text
* please add a test
* I'm concerned the SQL query may bypass the multicompany security rules
* NEVER DO SQL PARAMETER SUBSTITUTION WITH THE % OPERATOR!!!!

review: Needs Fixing (code review, no test)
Revision history for this message
Romain Deheele - Camptocamp (romaindeheele) wrote :

Hi,

Tests are added.
SQL is removed and replaced by ORM searchs.
Description has been updated.

Romain

Revision history for this message
Alexandre Fayolle - camptocamp (alexandre-fayolle-c2c) wrote :

LGTM

review: Approve (code review, test)
65. By Yannick Vaucher @ Camptocamp

[PEP8]

Revision history for this message
Yannick Vaucher @ Camptocamp (yvaucher-c2c) wrote :

Shouldn't *_to_done private method be *_to_do or _to_be_done ?

Here are few remarks about translations:

* After installatation on fresh install with last code Wizard fields and label aren't translated. (But this may not be related to this MP)

* In translation, in french there should be a space before punctuation.

* "selon le nombre de commandes de vente voulu." I would replace 'voulu' by 'défini'.

* l.291 +msgstr "Prépare un bon de préparation" -> Préparer

* picker_id help message is not translated nor picking_id field name

review: Needs Fixing (code review, no test)
66. By Romain Deheele - Camptocamp

[UPD] terms review and translation changes

Revision history for this message
Romain Deheele - Camptocamp (romaindeheele) wrote :

Thanks Yannick,

I've reworked terms and translation.

Romain

Revision history for this message
Yannick Vaucher @ Camptocamp (yvaucher-c2c) wrote :

Ok translations works now

Thanks for the changes.

review: Approve (tests)
Revision history for this message
Yannick Vaucher @ Camptocamp (yvaucher-c2c) wrote :

(my tests)

- Installed the module
- tried to create a picking dispatch

Revision history for this message
Nicolas Bessi - Camptocamp (nbessi-c2c-deactivatedaccount) wrote :

Need fixing.
Set has a better performance than list and it should avoid duplicate.
Please see inline comment

review: Needs Fixing
Revision history for this message
Nicolas Bessi - Camptocamp (nbessi-c2c-deactivatedaccount) wrote :
67. By Romain Deheele - Camptocamp

use set() instead of [], rename nb variable, moves selected only if picking state is assigned

Revision history for this message
Romain Deheele - Camptocamp (romaindeheele) wrote :

Thanks Nicolas,

I made changes accordingly.
I add a condition : only assigned pickings are selected.

Revision history for this message
Nicolas Bessi - Camptocamp (nbessi-c2c-deactivatedaccount) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added directory 'picking_dispatch_wave'
2=== added file 'picking_dispatch_wave/__init__.py'
3--- picking_dispatch_wave/__init__.py 1970-01-01 00:00:00 +0000
4+++ picking_dispatch_wave/__init__.py 2014-06-13 15:52:45 +0000
5@@ -0,0 +1,22 @@
6+# -*- coding: utf-8 -*-
7+##############################################################################
8+#
9+# Author: Alexandre Fayolle, Romain Deheele
10+# Copyright 2014 Camptocamp SA
11+#
12+# This program is free software: you can redistribute it and/or modify
13+# it under the terms of the GNU Affero General Public License as
14+# published by the Free Software Foundation, either version 3 of the
15+# License, or (at your option) any later version.
16+#
17+# This program is distributed in the hope that it will be useful,
18+# but WITHOUT ANY WARRANTY; without even the implied warranty of
19+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20+# GNU Affero General Public License for more details.
21+#
22+# You should have received a copy of the GNU Affero General Public License
23+# along with this program. If not, see <http://www.gnu.org/licenses/>.
24+#
25+##############################################################################
26+
27+from . import dispatch_wave # noqa
28
29=== added file 'picking_dispatch_wave/__openerp__.py'
30--- picking_dispatch_wave/__openerp__.py 1970-01-01 00:00:00 +0000
31+++ picking_dispatch_wave/__openerp__.py 2014-06-13 15:52:45 +0000
32@@ -0,0 +1,46 @@
33+# -*- coding: utf-8 -*-
34+##############################################################################
35+#
36+# Author: Alexandre Fayolle, Romain Deheele
37+# Copyright 2014 Camptocamp SA
38+#
39+# This program is free software: you can redistribute it and/or modify
40+# it under the terms of the GNU Affero General Public License as
41+# published by the Free Software Foundation, either version 3 of the
42+# License, or (at your option) any later version.
43+#
44+# This program is distributed in the hope that it will be useful,
45+# but WITHOUT ANY WARRANTY; without even the implied warranty of
46+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
47+# GNU Affero General Public License for more details.
48+#
49+# You should have received a copy of the GNU Affero General Public License
50+# along with this program. If not, see <http://www.gnu.org/licenses/>.
51+#
52+##############################################################################
53+
54+
55+{
56+ "name": "Picking Dispatch Wave",
57+ "version": "0.1",
58+ "depends": ['picking_dispatch', 'sale_stock'],
59+ "author": "Camptocamp",
60+ 'license': 'AGPL-3',
61+ "description": """Allows to set a picking dispatch
62+including the number maximum of pickings that you want to pick:
63+
64+* The picker sets a number n of pickings to do.
65+
66+* The wizard will select moves from n pickings with oldest min_date.
67+
68+* A picking dispatch is created with found moves
69+
70+It's sort of basic wave picking.
71+""",
72+ "website": "http://www.camptocamp.com",
73+ "category": "Warehouse Management",
74+ "demo": [],
75+ "data": ['dispatch_wave_view.xml'],
76+ "test": ['test/test_dispatch_wave.yml'],
77+ "installable": True,
78+}
79
80=== added file 'picking_dispatch_wave/dispatch_wave.py'
81--- picking_dispatch_wave/dispatch_wave.py 1970-01-01 00:00:00 +0000
82+++ picking_dispatch_wave/dispatch_wave.py 2014-06-13 15:52:45 +0000
83@@ -0,0 +1,119 @@
84+# -*- coding: utf-8 -*-
85+##############################################################################
86+#
87+# Author: Alexandre Fayolle, Romain Deheele
88+# Copyright 2014 Camptocamp SA
89+#
90+# This program is free software: you can redistribute it and/or modify
91+# it under the terms of the GNU Affero General Public License as
92+# published by the Free Software Foundation, either version 3 of the
93+# License, or (at your option) any later version.
94+#
95+# This program is distributed in the hope that it will be useful,
96+# but WITHOUT ANY WARRANTY; without even the implied warranty of
97+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
98+# GNU Affero General Public License for more details.
99+#
100+# You should have received a copy of the GNU Affero General Public License
101+# along with this program. If not, see <http://www.gnu.org/licenses/>.
102+#
103+#############################################################################
104+import logging
105+
106+from openerp.osv import orm, fields
107+from openerp.tools.translate import _
108+_logger = logging.getLogger(__name__)
109+
110+
111+class StockPickingDispatchWave(orm.TransientModel):
112+ _name = "stock.picking.dispatch.wave"
113+
114+ def _get_pickings_to_do(self, cr, uid, max_nb, context=None):
115+ context = context or {}
116+ move_obj = self.pool['stock.move']
117+ move_ids = []
118+ picking_ids = set()
119+ move_ids = move_obj.search(cr, uid,
120+ [('dispatch_id', '=', False),
121+ ('state', '=', 'assigned'),
122+ ('type', '=', 'out')],
123+ order='date_expected DESC',
124+ context=context)
125+ for move in move_obj.browse(cr, uid, move_ids, context=context):
126+ if len(picking_ids) == max_nb:
127+ break
128+ if move.picking_id.state == 'assigned' and \
129+ move.picking_id.id not in picking_ids:
130+ picking_ids.add(move.picking_id.id)
131+ return picking_ids
132+
133+ def _get_moves_from_picking_list(self, cr, uid, picking_ids, context=None):
134+ context = context or {}
135+ move_obj = self.pool['stock.move']
136+ move_ids = move_obj.search(cr, uid,
137+ [('picking_id', 'in', picking_ids)],
138+ context=context)
139+ return move_ids
140+
141+ def _get_moves_from_pickings_to_do(self, cr, uid, max_nb, context=None):
142+ context = context or {}
143+ move_ids = []
144+ picking_ids = self.\
145+ _get_pickings_to_do(cr, uid, max_nb, context=context)
146+ if picking_ids:
147+ move_ids = self._get_moves_from_picking_list(cr, uid, picking_ids,
148+ context=context)
149+ return move_ids
150+
151+ _columns = {
152+ 'max_pickings_to_do': fields.integer('Number maximum of pickings '
153+ 'to prepare',
154+ help='number maximum of pickings '
155+ 'that we want to prepare'),
156+ 'picker_id': fields.many2one('res.users', 'User', required=True,
157+ help='the user to which the pickings '
158+ 'are assigned'),
159+ }
160+
161+ _defaults = {
162+ 'max_pickings_to_do': 0,
163+ 'picker_id': lambda self, cr, uid, ctx: uid,
164+ }
165+
166+ def action_create_picking_dispatch(self, cr, uid, ids, context=None):
167+ context = context or {}
168+ wave = self.browse(cr, uid, ids, context=context)[0]
169+ if wave.max_pickings_to_do:
170+ move_ids = self.\
171+ _get_moves_from_pickings_to_do(cr, uid,
172+ wave.max_pickings_to_do,
173+ context=context)
174+ if move_ids:
175+ # create picking_dispatch
176+ dispatch_obj = self.pool['picking.dispatch']
177+ dispatch_vals = {
178+ 'picker_id': wave.picker_id.id
179+ }
180+ dispatch_id = dispatch_obj.create(cr, uid,
181+ dispatch_vals,
182+ context=context)
183+ # affect move_ids on the new dispatch
184+ self.pool['stock.move'].write(cr, uid, move_ids,
185+ {'dispatch_id': dispatch_id},
186+ context=context)
187+ context['active_id'] = dispatch_id
188+ return {
189+ 'domain': str([('id', '=', dispatch_id)]),
190+ 'view_type': 'form',
191+ 'view_mode': 'form',
192+ 'res_model': 'picking.dispatch',
193+ 'type': 'ir.actions.act_window',
194+ 'context': context,
195+ 'res_id': dispatch_id,
196+ }
197+ else:
198+ raise orm.except_orm(_('Information'),
199+ _('No ready pickings to deliver!'))
200+ else:
201+ raise orm.except_orm(_('Error'),
202+ _('You need to set at least one unit to do.'))
203
204=== added file 'picking_dispatch_wave/dispatch_wave_view.xml'
205--- picking_dispatch_wave/dispatch_wave_view.xml 1970-01-01 00:00:00 +0000
206+++ picking_dispatch_wave/dispatch_wave_view.xml 2014-06-13 15:52:45 +0000
207@@ -0,0 +1,46 @@
208+<?xml version="1.0" encoding="utf-8"?>
209+<openerp>
210+ <data>
211+
212+ <act_window id="action_prepare_picking_dispatch"
213+ name="Picking Wave"
214+ res_model="stock.picking.dispatch.wave"
215+ view_type="form"
216+ view_mode="form"
217+ target="new"
218+ />
219+
220+ <menuitem id="menu_prepare_picking_dispatch"
221+ parent="stock.menu_stock_warehouse_mgmt"
222+ sequence="40"
223+ action="action_prepare_picking_dispatch"
224+ groups="stock.group_stock_user,stock.group_stock_manager"
225+ />
226+
227+ <record model="ir.ui.view" id="picking_dispatch_prepare_form">
228+ <field name="name">stock.picking.dispatch.wave form</field>
229+ <field name="model">stock.picking.dispatch.wave</field>
230+ <field name="arch" type="xml">
231+ <form string="Picking Wave" version="7.0">
232+ <group>
233+ <p class="oe_grey">
234+ This action will prepare you a picking dispatch
235+ according the number of pickings that you set.
236+ </p>
237+ </group>
238+ <group>
239+ <field name="max_pickings_to_do"/>
240+ <field name="picker_id"/>
241+ </group>
242+ <footer>
243+ <button name="action_create_picking_dispatch" string="Create Picking Dispatch" type="object"
244+ class="oe_highlight"/>
245+ or
246+ <button string="Cancel" class="oe_link" special="cancel" />
247+ </footer>
248+ </form>
249+ </field>
250+ </record>
251+
252+ </data>
253+</openerp>
254
255=== added directory 'picking_dispatch_wave/i18n'
256=== added file 'picking_dispatch_wave/i18n/fr.po'
257--- picking_dispatch_wave/i18n/fr.po 1970-01-01 00:00:00 +0000
258+++ picking_dispatch_wave/i18n/fr.po 2014-06-13 15:52:45 +0000
259@@ -0,0 +1,82 @@
260+# Translation of OpenERP Server.
261+# This file contains the translation of the following modules:
262+# * picking_dispatch_wave
263+#
264+msgid ""
265+msgstr ""
266+"Project-Id-Version: OpenERP Server 7.0\n"
267+"Report-Msgid-Bugs-To: \n"
268+"POT-Creation-Date: 2014-06-13 15:12+0000\n"
269+"PO-Revision-Date: 2014-06-13 15:12+0000\n"
270+"Last-Translator: <>\n"
271+"Language-Team: \n"
272+"MIME-Version: 1.0\n"
273+"Content-Type: text/plain; charset=UTF-8\n"
274+"Content-Transfer-Encoding: \n"
275+"Plural-Forms: \n"
276+
277+#. module: picking_dispatch_wave
278+#: view:stock.picking.dispatch.wave:0
279+msgid "This action will prepare you a picking dispatch\n"
280+" according the number of pickings that you set."
281+msgstr "Cette action va vous préparer un bon de préparation\n"
282+" selon le nombre de livraisons défini."
283+
284+#. module: picking_dispatch_wave
285+#: model:ir.actions.act_window,name:picking_dispatch_wave.action_prepare_picking_dispatch
286+#: model:ir.ui.menu,name:picking_dispatch_wave.menu_prepare_picking_dispatch
287+#: view:stock.picking.dispatch.wave:0
288+msgid "Picking Wave"
289+msgstr "Préparer n livraisons"
290+
291+#. module: picking_dispatch_wave
292+#: help:stock.picking.dispatch.wave,max_pickings_to_do:0
293+msgid "number maximum of pickings that we want to prepare"
294+msgstr "Nombre maximum de livraisons que vous voulez préparez"
295+
296+#. module: picking_dispatch_wave
297+#: field:stock.picking.dispatch.wave,max_pickings_to_do:0
298+msgid "Number maximum of pickings to prepare"
299+msgstr "Nombre maximum de livraisons à préparer"
300+
301+#. module: picking_dispatch_wave
302+#: field:stock.picking.dispatch.wave,picker_id:0
303+msgid "User"
304+msgstr "Utilisateur"
305+
306+#. module: picking_dispatch_wave
307+#: view:stock.picking.dispatch.wave:0
308+msgid "Cancel"
309+msgstr "Annuler"
310+
311+#. module: picking_dispatch_wave
312+#: view:stock.picking.dispatch.wave:0
313+msgid "Create Picking Dispatch"
314+msgstr "Créer un bon de préparation"
315+
316+#. module: picking_dispatch_wave
317+#: view:stock.picking.dispatch.wave:0
318+msgid "or"
319+msgstr "ou"
320+
321+#. module: picking_dispatch_wave
322+#: help:stock.picking.dispatch.wave,picker_id:0
323+msgid "the user to which the pickings are assigned"
324+msgstr "l'utilisateur à qui les livraisons sont attribuées"
325+
326+#. module: picking_dispatch_wave
327+#: model:ir.model,name:picking_dispatch_wave.model_stock_picking_dispatch_wave
328+msgid "stock.picking.dispatch.wave"
329+msgstr "stock.picking.dispatch.wave"
330+
331+#. module: picking_dispatch_wave
332+#: code:addons/picking_dispatch_wave/dispatch_wave.py:116
333+#, python-format
334+msgid "You need to set at least one unit to do."
335+msgstr "Sélectionner au moins une livraison à préparer"
336+
337+#. module: picking_dispatch_wave
338+#: code:addons/picking_dispatch_wave/dispatch_wave.py:113
339+#, python-format
340+msgid "No ready pickings to deliver!"
341+msgstr "Aucune livraison à préparer !"
342
343=== added file 'picking_dispatch_wave/i18n/picking_dispatch_wave.pot'
344--- picking_dispatch_wave/i18n/picking_dispatch_wave.pot 1970-01-01 00:00:00 +0000
345+++ picking_dispatch_wave/i18n/picking_dispatch_wave.pot 2014-06-13 15:52:45 +0000
346@@ -0,0 +1,70 @@
347+# Translation of OpenERP Server.
348+# This file contains the translation of the following modules:
349+# * picking_dispatch_wave
350+#
351+msgid ""
352+msgstr ""
353+"Project-Id-Version: OpenERP Server 7.0\n"
354+"Report-Msgid-Bugs-To: \n"
355+"POT-Creation-Date: 2014-06-13 15:12+0000\n"
356+"PO-Revision-Date: 2014-06-13 15:12+0000\n"
357+"Last-Translator: <>\n"
358+"Language-Team: \n"
359+"MIME-Version: 1.0\n"
360+"Content-Type: text/plain; charset=UTF-8\n"
361+"Content-Transfer-Encoding: \n"
362+"Plural-Forms: \n"
363+
364+#. module: picking_dispatch_wave
365+#: view:stock.picking.dispatch.wave:0
366+msgid "This action will prepare you a picking dispatch\n"
367+" according the number of pickings that you set."
368+msgstr ""
369+
370+#. module: picking_dispatch_wave
371+#: model:ir.actions.act_window,name:picking_dispatch_wave.action_prepare_picking_dispatch
372+#: model:ir.ui.menu,name:picking_dispatch_wave.menu_prepare_picking_dispatch
373+#: view:stock.picking.dispatch.wave:0
374+msgid "Picking Wave"
375+msgstr ""
376+
377+#. module: picking_dispatch_wave
378+#: help:stock.picking.dispatch.wave,max_pickings_to_do:0
379+msgid "number maximum of pickings that we want to prepare"
380+msgstr ""
381+
382+#. module: picking_dispatch_wave
383+#: field:stock.picking.dispatch.wave,max_pickings_to_do:0
384+msgid "Number maximum of pickings to prepare"
385+msgstr ""
386+
387+#. module: picking_dispatch_wave
388+#: field:stock.picking.dispatch.wave,picker_id:0
389+msgid "User"
390+msgstr ""
391+
392+#. module: picking_dispatch_wave
393+#: view:stock.picking.dispatch.wave:0
394+msgid "Cancel"
395+msgstr ""
396+
397+#. module: picking_dispatch_wave
398+#: view:stock.picking.dispatch.wave:0
399+msgid "Create Picking Dispatch"
400+msgstr ""
401+
402+#. module: picking_dispatch_wave
403+#: view:stock.picking.dispatch.wave:0
404+msgid "or"
405+msgstr ""
406+
407+#. module: picking_dispatch_wave
408+#: help:stock.picking.dispatch.wave,picker_id:0
409+msgid "the user to which the pickings are assigned"
410+msgstr ""
411+
412+#. module: picking_dispatch_wave
413+#: model:ir.model,name:picking_dispatch_wave.model_stock_picking_dispatch_wave
414+msgid "stock.picking.dispatch.wave"
415+msgstr ""
416+
417
418=== added directory 'picking_dispatch_wave/test'
419=== added file 'picking_dispatch_wave/test/test_dispatch_wave.yml'
420--- picking_dispatch_wave/test/test_dispatch_wave.yml 1970-01-01 00:00:00 +0000
421+++ picking_dispatch_wave/test/test_dispatch_wave.yml 2014-06-13 15:52:45 +0000
422@@ -0,0 +1,47 @@
423+-
424+ In order to test stock picking wave
425+ I have to ensure when a picking dispatch wave is set, a picking dispatch is well created.
426+-
427+ I create a product
428+-
429+ !record {model: product.product, id: prod_wave}:
430+ name: product_wave
431+ procure_method: make_to_stock
432+ supply_method: buy
433+-
434+ I create a SO
435+-
436+ !record {model: sale.order, id: so_wave}:
437+ partner_id: base.res_partner_2
438+ order_policy: 'manual'
439+ picking_policy: 'direct'
440+ order_line:
441+ - product_id: prod_wave
442+ product_uom_qty: 3
443+-
444+ Then I confirm the order
445+-
446+ !workflow {model: sale.order, action: order_confirm, ref: so_wave}
447+-
448+ Then (for test needs) I force the picking as ready to deliver
449+-
450+ !python {model: stock.picking}: |
451+ picking = self.search(cr, uid, [('sale_id', '=', ref("so_wave"))])
452+ self.force_assign(cr, uid, picking)
453+-
454+ Then I ask a picking dispatch wave with 1 unit.
455+-
456+ !record {model: stock.picking.dispatch.wave, id: wiz_wave}:
457+ max_pickings_to_do: 1
458+ picker_id: base.user_demo
459+-
460+ Then I confirm the demand
461+-
462+ !python {model: stock.picking.dispatch.wave}:
463+ self.action_create_picking_dispatch(cr, uid, [ref("wiz_wave")], context=context)
464+-
465+ Then I check if the picking dispatch is created
466+-
467+ !python {model: picking.dispatch}: |
468+ dispatchs = self.search(cr, uid, [('state','=','draft'),('picker_id','=',ref("base.user_demo"))])
469+ assert len(dispatchs) == 1, "The dispatch is ready"

Subscribers

People subscribed via source and target branches