Merge lp:~akretion-team/openobject-addons/addons-purchase-extensible-action-picking-create into lp:openobject-addons

Proposed by Raphaël Valyi - http://www.akretion.com
Status: Merged
Approved by: Olivier Dony (Odoo)
Approved revision: no longer in the source branch.
Merged at revision: 5576
Proposed branch: lp:~akretion-team/openobject-addons/addons-purchase-extensible-action-picking-create
Merge into: lp:openobject-addons
Diff against target: 144 lines (+75/-50)
1 file modified
purchase/purchase.py (+75/-50)
To merge this branch: bzr merge lp:~akretion-team/openobject-addons/addons-purchase-extensible-action-picking-create
Reviewer Review Type Date Requested Status
Olivier Dony (Odoo) Approve
Review via email: mp+79485@code.launchpad.net

Description of the change

Hello,

This refactoring is the equivalent of the one you just merged in the sale module:
https://code.launchpad.net/~akretion-team/openobject-addons/addons-sale-extensible-action-ship-create/+merge/76609

But this time it's in the purchase module.
It breaks down the monolothic generation of the purchase picking and receptions moves and make sure those objects can be customized or localized before they are created into the database.
This is very useful in localizations such as for Brazil where some fiscal code must be propagated from the purchase to the supplier invoice, passing through the picking.

This refactoring also permit to have several pickings for one purchase order, something useful for blanket orders or lines with drop shipping for instance.

To post a comment you must log in.
Revision history for this message
Cloves Almeida (cjalmeida) wrote :

It seems to me that, in line 63, the code would always create a new picking regardless a "picking_id" is provided.

Revision history for this message
Raphaël Valyi - http://www.akretion.com (rvalyi) wrote :

Hello Colves,

Thank you for reviewing, you were absolutely right, we where creating the picking no matter what so our promise to be able to split the receptions was broken.

I fixed it in commit #5468

BTW, it buzzed me how this code would no create a picking when you buy service. But it's works because the purchase module checks that in the workflow. This is a different system that what is implemented in the sale module, I'm not sure if it's on purpose. Anyway it all works, I was just mentioning it's strange the design is different from the sale module.

Cloves, can you approve he review if you agree with the code now?
Dear OpenERP SA, thanks for merging this...

Revision history for this message
Olivier Dony (Odoo) (odo-openerp) wrote :

@Cloves: thanks for reviewing!

@Raphael:
This looks good, I will merge it after fixing some remaining glitches, such as:

 - remnants of copy/pasted `sale`-related stuff in the docstring
 - wrong indentation for move/picking confirmation code, causing useless repeated calls
 - passing down of *args without unpacking them, creating undesired args nesting (will correct this in `sale` too)
 - [order_line for order_line in order.order_line] -> order.order_line

And while doing this, we can also take the opportunity to:
 - change the return values to lists, as you suggested, which it should already have done
   (few cases where this return value was used can be fixed when porting modules to 6.1)
 - do some cleanup in existing code, e.g.:
   + order_line.product_id.product_tmpl_id.type -> order_line.product_id.type
   + self.pool.get('stock.move').write(cr, uid, [order_line.move_dest_id.id], {'location_id': order.location_id.id})
     should just be
     order_line.move_dest_id.write({'location_id': order.location_id.id})
   + etc.

BTW, great work on the docstring! :-)

Thank you this nice MP, and for keeping on improving OpenERP's business API and code extensibility!

review: Approve
Revision history for this message
Olivier Dony (Odoo) (odo-openerp) wrote :

Note: I forgot to mention that in order to bind the PO's workflow to a single picking subflow, the action_picking_create() method must only return one picking ID. By default we consider the first (and usually only) picking as the critical one for the PO workflow, but this can now be easily overridden if needed. I changed that and added a comment about it in the code.

Thanks!

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'purchase/purchase.py'
2--- purchase/purchase.py 2011-10-18 08:42:34 +0000
3+++ purchase/purchase.py 2011-10-27 21:32:28 +0000
4@@ -425,57 +425,83 @@
5 self.log(cr, uid, id, message)
6 return True
7
8+ def _prepare_order_picking(self, cr, uid, order, *args):
9+ pick_name = self.pool.get('ir.sequence').get(cr, uid, 'stock.picking.in')
10+ istate = 'none'
11+ if order.invoice_method=='picking':
12+ istate = '2binvoiced'
13+ return {
14+ 'name': pick_name,
15+ 'origin': order.name+((order.origin and (':'+order.origin)) or ''),
16+ 'type': 'in',
17+ 'address_id': order.dest_address_id.id or order.partner_address_id.id,
18+ 'invoice_state': istate,
19+ 'purchase_id': order.id,
20+ 'company_id': order.company_id.id,
21+ 'move_lines' : [],
22+ }
23+
24+ def _prepare_order_line_move(self, cr, uid, order, order_line, picking_id, *args):
25+ return {
26+ 'name': order.name + ': ' +(order_line.name or ''),
27+ 'product_id': order_line.product_id.id,
28+ 'product_qty': order_line.product_qty,
29+ 'product_uos_qty': order_line.product_qty,
30+ 'product_uom': order_line.product_uom.id,
31+ 'product_uos': order_line.product_uom.id,
32+ 'date': order_line.date_planned,
33+ 'date_expected': order_line.date_planned,
34+ 'location_id': order.partner_id.property_stock_supplier.id,
35+ 'location_dest_id': order.location_id.id,
36+ 'picking_id': picking_id,
37+ 'address_id': order.dest_address_id.id or order.partner_address_id.id,
38+ 'move_dest_id': order_line.move_dest_id.id,
39+ 'state': 'draft',
40+ 'purchase_line_id': order_line.id,
41+ 'company_id': order.company_id.id,
42+ 'price_unit': order_line.price_unit
43+ }
44+
45+ def _create_pickings(self, cr, uid, order, order_lines, picking_id=False, *args):
46+ """
47+ Creates pickings and appropriate stock moves for given order lines.
48+
49+ If ``picking_id`` is provided, the stock moves will be added to it, otherwise
50+ a standard outgoing picking will be created to wrap the stock moves, as returned
51+ by :meth:`~._prepare_order_picking`.
52+
53+ Modules that wish to customize the procurements or partition the stock moves over
54+ multiple stock pickings may override this method and call ``super()`` with
55+ different subsets of ``order_lines`` and/or preset ``picking_id`` values.
56+
57+ :param browse_record order: purchase order to which the order lines belong
58+ :param list(browse_record) order_lines: sale order line records to procure
59+ :param int picking_id: optional ID of a stock picking to which the created stock moves
60+ will be added. A new picking will be created if ommitted.
61+ :return: True
62+ """
63+ if not picking_id:
64+ picking_id = self.pool.get('stock.picking').create(cr, uid, self._prepare_order_picking(cr, uid, order, args))
65+ todo_moves = []
66+ for order_line in order_lines:
67+ if not order_line.product_id:
68+ continue
69+ if order_line.product_id.product_tmpl_id.type in ('product', 'consu'):
70+ move = self.pool.get('stock.move').create(cr, uid, self._prepare_order_line_move(cr, uid, order, order_line, picking_id, args))
71+ if order_line.move_dest_id:
72+ self.pool.get('stock.move').write(cr, uid, [order_line.move_dest_id.id], {'location_id': order.location_id.id})
73+ todo_moves.append(move)
74+ self.pool.get('stock.move').action_confirm(cr, uid, todo_moves)
75+ self.pool.get('stock.move').force_assign(cr, uid, todo_moves)
76+ wf_service = netsvc.LocalService("workflow")
77+ wf_service.trg_validate(uid, 'stock.picking', picking_id, 'button_confirm', cr)
78+
79+ return picking_id
80+
81 def action_picking_create(self,cr, uid, ids, *args):
82- picking_id = False
83 for order in self.browse(cr, uid, ids):
84- loc_id = order.partner_id.property_stock_supplier.id
85- istate = 'none'
86- if order.invoice_method=='picking':
87- istate = '2binvoiced'
88- pick_name = self.pool.get('ir.sequence').get(cr, uid, 'stock.picking.in')
89- picking_id = self.pool.get('stock.picking').create(cr, uid, {
90- 'name': pick_name,
91- 'origin': order.name+((order.origin and (':'+order.origin)) or ''),
92- 'type': 'in',
93- 'address_id': order.dest_address_id.id or order.partner_address_id.id,
94- 'invoice_state': istate,
95- 'purchase_id': order.id,
96- 'company_id': order.company_id.id,
97- 'move_lines' : [],
98- })
99- todo_moves = []
100- for order_line in order.order_line:
101- if not order_line.product_id:
102- continue
103- if order_line.product_id.product_tmpl_id.type in ('product', 'consu'):
104- dest = order.location_id.id
105- move = self.pool.get('stock.move').create(cr, uid, {
106- 'name': order.name + ': ' +(order_line.name or ''),
107- 'product_id': order_line.product_id.id,
108- 'product_qty': order_line.product_qty,
109- 'product_uos_qty': order_line.product_qty,
110- 'product_uom': order_line.product_uom.id,
111- 'product_uos': order_line.product_uom.id,
112- 'date': order_line.date_planned,
113- 'date_expected': order_line.date_planned,
114- 'location_id': loc_id,
115- 'location_dest_id': dest,
116- 'picking_id': picking_id,
117- 'address_id': order.dest_address_id.id or order.partner_address_id.id,
118- 'move_dest_id': order_line.move_dest_id.id,
119- 'state': 'draft',
120- 'purchase_line_id': order_line.id,
121- 'company_id': order.company_id.id,
122- 'price_unit': order_line.price_unit
123- })
124- if order_line.move_dest_id:
125- self.pool.get('stock.move').write(cr, uid, [order_line.move_dest_id.id], {'location_id':order.location_id.id})
126- todo_moves.append(move)
127- self.pool.get('stock.move').action_confirm(cr, uid, todo_moves)
128- self.pool.get('stock.move').force_assign(cr, uid, todo_moves)
129- wf_service = netsvc.LocalService("workflow")
130- wf_service.trg_validate(uid, 'stock.picking', picking_id, 'button_confirm', cr)
131- return picking_id
132+ picking_id = self._create_pickings(cr, uid, order, [order_line for order_line in order.order_line], None, args)
133+ return picking_id #FIXME this is brittle to assume there is only 1 picking_id, but has been kept for API compatibility
134
135 def copy(self, cr, uid, id, default=None, context=None):
136 if not default:
137@@ -490,7 +516,6 @@
138 })
139 return super(purchase_order, self).copy(cr, uid, id, default, context)
140
141-
142 def do_merge(self, cr, uid, ids, context=None):
143 """
144 To merge similar type of purchase orders.

Subscribers

People subscribed via source and target branches

to all changes: