From f4685cdc6f6b34884a62eb51b545cf8a83647d4d Mon Sep 17 00:00:00 2001 From: florian-dacosta <> Date: Mon, 3 Oct 2011 18:27:47 -0300 Subject: [PATCH 1/3] [REF] purchase: modular picking creation bzr revid: rvalyi@gmail.com-20111003212747-3pnxz25eohzfvq5i --- addons/purchase/purchase.py | 117 +++++++++++++++++++++--------------- 1 file changed, 69 insertions(+), 48 deletions(-) diff --git a/addons/purchase/purchase.py b/addons/purchase/purchase.py index db494add04b..cfcaa5bbed5 100644 --- a/addons/purchase/purchase.py +++ b/addons/purchase/purchase.py @@ -425,56 +425,78 @@ class purchase_order(osv.osv): self.log(cr, uid, id, message) return True + def _prepare_order_picking(self, cr, uid, order, *args): + pick_name = self.pool.get('ir.sequence').get(cr, uid, 'stock.picking.in') + istate = 'none' + if order.invoice_method=='picking': + istate = '2binvoiced' + return { + 'name': pick_name, + 'origin': order.name+((order.origin and (':'+order.origin)) or ''), + 'type': 'in', + 'address_id': order.dest_address_id.id or order.partner_address_id.id, + 'invoice_state': istate, + 'purchase_id': order.id, + 'company_id': order.company_id.id, + 'move_lines' : [], + } + + def _prepare_order_line_move(self, cr, uid, order_line, picking_id, *args): + loc_id = order_line.order_id.partner_id.property_stock_supplier.id + dest = order_line.order_id.location_id.id + return { + 'name': order_line.order_id.name + ': ' +(order_line.name or ''), + 'product_id': order_line.product_id.id, + 'product_qty': order_line.product_qty, + 'product_uos_qty': order_line.product_qty, + 'product_uom': order_line.product_uom.id, + 'product_uos': order_line.product_uom.id, + 'date': order_line.date_planned, + 'date_expected': order_line.date_planned, + 'location_id': loc_id, + 'location_dest_id': dest, + 'picking_id': picking_id, + 'address_id': order_line.order_id.dest_address_id.id or order_line.order_id.partner_address_id.id, + 'move_dest_id': order_line.move_dest_id.id, + 'state': 'draft', + 'purchase_line_id': order_line.id, + 'company_id': order_line.order_id.company_id.id, + 'price_unit': order_line.price_unit + } + + def _create_pickings(self, cr, uid, order_lines, picking_id=False, *args): + """ + Create pickings for given order lines. Filtering the order lines allows to partition the delivery + over several pickings. + + :param cr: database cursor + :param uid: current user id + :param order: sale order object + :param order_lines: sale order line objects + :param picking_id: id of picking to use evenually + :return: True + """ + picking_id = self.pool.get('stock.picking').create(cr, uid, self._prepare_order_picking(cr, uid, order, args)) + todo_moves = [] + for order_line in order_lines: + if not order_line.product_id: + continue + if order_line.product_id.product_tmpl_id.type in ('product', 'consu'): + move = self.pool.get('stock.move').create(cr, uid, self._prepare_order_line_move(cr, uid, order_line, picking_id, args)) + if order_line.move_dest_id: + self.pool.get('stock.move').write(cr, uid, [order_line.move_dest_id.id], {'location_id': order_line.order_id.location_id.id}) + todo_moves.append(move) + self.pool.get('stock.move').action_confirm(cr, uid, todo_moves) + self.pool.get('stock.move').force_assign(cr, uid, todo_moves) + wf_service = netsvc.LocalService("workflow") + wf_service.trg_validate(uid, 'stock.picking', picking_id, 'button_confirm', cr) + + return picking_id + def action_picking_create(self,cr, uid, ids, *args): picking_id = False for order in self.browse(cr, uid, ids): - loc_id = order.partner_id.property_stock_supplier.id - istate = 'none' - if order.invoice_method=='picking': - istate = '2binvoiced' - pick_name = self.pool.get('ir.sequence').get(cr, uid, 'stock.picking.in') - picking_id = self.pool.get('stock.picking').create(cr, uid, { - 'name': pick_name, - 'origin': order.name+((order.origin and (':'+order.origin)) or ''), - 'type': 'in', - 'address_id': order.dest_address_id.id or order.partner_address_id.id, - 'invoice_state': istate, - 'purchase_id': order.id, - 'company_id': order.company_id.id, - 'move_lines' : [], - }) - todo_moves = [] - for order_line in order.order_line: - if not order_line.product_id: - continue - if order_line.product_id.product_tmpl_id.type in ('product', 'consu'): - dest = order.location_id.id - move = self.pool.get('stock.move').create(cr, uid, { - 'name': order.name + ': ' +(order_line.name or ''), - 'product_id': order_line.product_id.id, - 'product_qty': order_line.product_qty, - 'product_uos_qty': order_line.product_qty, - 'product_uom': order_line.product_uom.id, - 'product_uos': order_line.product_uom.id, - 'date': order_line.date_planned, - 'date_expected': order_line.date_planned, - 'location_id': loc_id, - 'location_dest_id': dest, - 'picking_id': picking_id, - 'address_id': order.dest_address_id.id or order.partner_address_id.id, - 'move_dest_id': order_line.move_dest_id.id, - 'state': 'draft', - 'purchase_line_id': order_line.id, - 'company_id': order.company_id.id, - 'price_unit': order_line.price_unit - }) - if order_line.move_dest_id: - self.pool.get('stock.move').write(cr, uid, [order_line.move_dest_id.id], {'location_id':order.location_id.id}) - todo_moves.append(move) - self.pool.get('stock.move').action_confirm(cr, uid, todo_moves) - self.pool.get('stock.move').force_assign(cr, uid, todo_moves) - wf_service = netsvc.LocalService("workflow") - wf_service.trg_validate(uid, 'stock.picking', picking_id, 'button_confirm', cr) + self._create_pickings(cr, uid, [order_line for order_line in order.order_line], False, args) return picking_id def copy(self, cr, uid, id, default=None, context=None): @@ -490,7 +512,6 @@ class purchase_order(osv.osv): }) return super(purchase_order, self).copy(cr, uid, id, default, context) - def do_merge(self, cr, uid, ids, context=None): """ To merge similar type of purchase orders. From e08e6d973a6d2fa17703c816184461331c1ccbe1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Valyi?= Date: Sat, 15 Oct 2011 22:31:25 -0300 Subject: [PATCH 2/3] [REF] purchase: more work for modular picking generation bzr revid: rvalyi@gmail.com-20111016013125-0blsz0g2gl5z1plv --- addons/purchase/purchase.py | 45 ++++++++++++++++++++----------------- 1 file changed, 24 insertions(+), 21 deletions(-) diff --git a/addons/purchase/purchase.py b/addons/purchase/purchase.py index cfcaa5bbed5..b29daf19731 100644 --- a/addons/purchase/purchase.py +++ b/addons/purchase/purchase.py @@ -441,11 +441,9 @@ class purchase_order(osv.osv): 'move_lines' : [], } - def _prepare_order_line_move(self, cr, uid, order_line, picking_id, *args): - loc_id = order_line.order_id.partner_id.property_stock_supplier.id - dest = order_line.order_id.location_id.id + def _prepare_order_line_move(self, cr, uid, order, order_line, picking_id, *args): return { - 'name': order_line.order_id.name + ': ' +(order_line.name or ''), + 'name': order.name + ': ' +(order_line.name or ''), 'product_id': order_line.product_id.id, 'product_qty': order_line.product_qty, 'product_uos_qty': order_line.product_qty, @@ -453,27 +451,33 @@ class purchase_order(osv.osv): 'product_uos': order_line.product_uom.id, 'date': order_line.date_planned, 'date_expected': order_line.date_planned, - 'location_id': loc_id, - 'location_dest_id': dest, + 'location_id': order.partner_id.property_stock_supplier.id, + 'location_dest_id': order.location_id.id, 'picking_id': picking_id, - 'address_id': order_line.order_id.dest_address_id.id or order_line.order_id.partner_address_id.id, + 'address_id': order.dest_address_id.id or order.partner_address_id.id, 'move_dest_id': order_line.move_dest_id.id, 'state': 'draft', 'purchase_line_id': order_line.id, - 'company_id': order_line.order_id.company_id.id, + 'company_id': order.company_id.id, 'price_unit': order_line.price_unit } - def _create_pickings(self, cr, uid, order_lines, picking_id=False, *args): + def _create_pickings(self, cr, uid, order, order_lines, picking_id=False, *args): """ - Create pickings for given order lines. Filtering the order lines allows to partition the delivery - over several pickings. + Creates pickings and appropriate stock moves for given order lines. - :param cr: database cursor - :param uid: current user id - :param order: sale order object - :param order_lines: sale order line objects - :param picking_id: id of picking to use evenually + If ``picking_id`` is provided, the stock moves will be added to it, otherwise + a standard outgoing picking will be created to wrap the stock moves, as returned + by :meth:`~._prepare_order_picking`. + + Modules that wish to customize the procurements or partition the stock moves over + multiple stock pickings may override this method and call ``super()`` with + different subsets of ``order_lines`` and/or preset ``picking_id`` values. + + :param browse_record order: purchase order to which the order lines belong + :param list(browse_record) order_lines: sale order line records to procure + :param int picking_id: optional ID of a stock picking to which the created stock moves + will be added. A new picking will be created if ommitted. :return: True """ picking_id = self.pool.get('stock.picking').create(cr, uid, self._prepare_order_picking(cr, uid, order, args)) @@ -482,9 +486,9 @@ class purchase_order(osv.osv): if not order_line.product_id: continue if order_line.product_id.product_tmpl_id.type in ('product', 'consu'): - move = self.pool.get('stock.move').create(cr, uid, self._prepare_order_line_move(cr, uid, order_line, picking_id, args)) + move = self.pool.get('stock.move').create(cr, uid, self._prepare_order_line_move(cr, uid, order, order_line, picking_id, args)) if order_line.move_dest_id: - self.pool.get('stock.move').write(cr, uid, [order_line.move_dest_id.id], {'location_id': order_line.order_id.location_id.id}) + self.pool.get('stock.move').write(cr, uid, [order_line.move_dest_id.id], {'location_id': order.location_id.id}) todo_moves.append(move) self.pool.get('stock.move').action_confirm(cr, uid, todo_moves) self.pool.get('stock.move').force_assign(cr, uid, todo_moves) @@ -494,10 +498,9 @@ class purchase_order(osv.osv): return picking_id def action_picking_create(self,cr, uid, ids, *args): - picking_id = False for order in self.browse(cr, uid, ids): - self._create_pickings(cr, uid, [order_line for order_line in order.order_line], False, args) - return picking_id + picking_id = self._create_pickings(cr, uid, order, [order_line for order_line in order.order_line], False, args) + return picking_id #FIXME this is brittle to assume there is only 1 picking_id, but has been kept for API compatibility def copy(self, cr, uid, id, default=None, context=None): if not default: From fea2c90d3576ece26b88cc1e958ee770c351b364 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Valyi?= Date: Thu, 27 Oct 2011 19:14:59 -0200 Subject: [PATCH 3/3] [FIX] purchase: create picking in _create_pickings only if picking_id not already provided; thanks to Cloves Almeida for reporting this! bzr revid: rvalyi@gmail.com-20111027211459-hk8n2milqo2c37vk --- addons/purchase/purchase.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/addons/purchase/purchase.py b/addons/purchase/purchase.py index 99aa6f377ee..dc364197c6e 100644 --- a/addons/purchase/purchase.py +++ b/addons/purchase/purchase.py @@ -480,7 +480,8 @@ class purchase_order(osv.osv): will be added. A new picking will be created if ommitted. :return: True """ - picking_id = self.pool.get('stock.picking').create(cr, uid, self._prepare_order_picking(cr, uid, order, args)) + if not picking_id: + picking_id = self.pool.get('stock.picking').create(cr, uid, self._prepare_order_picking(cr, uid, order, args)) todo_moves = [] for order_line in order_lines: if not order_line.product_id: @@ -499,7 +500,7 @@ class purchase_order(osv.osv): def action_picking_create(self,cr, uid, ids, *args): for order in self.browse(cr, uid, ids): - picking_id = self._create_pickings(cr, uid, order, [order_line for order_line in order.order_line], False, args) + picking_id = self._create_pickings(cr, uid, order, [order_line for order_line in order.order_line], None, args) return picking_id #FIXME this is brittle to assume there is only 1 picking_id, but has been kept for API compatibility def copy(self, cr, uid, id, default=None, context=None):