From e12c9ab5ecc9ae46405ee1e78f1d0a64c3772851 Mon Sep 17 00:00:00 2001 From: "Quentin (OpenERP)" Date: Tue, 17 Sep 2013 08:43:59 +0200 Subject: [PATCH] [FIX] sale, project_mrp, sale_stock: fixed problems related to the procurement workflow bzr revid: qdp-launchpad@openerp.com-20130917064359-jwque36uwouac61p --- addons/project_mrp/project_mrp.py | 32 +++++------ addons/sale/sale.py | 56 +++++++++---------- addons/sale/sale_workflow.xml | 10 +++- addons/sale_stock/sale_stock.py | 19 ++++--- addons/stock_complex_routes/test/dropship.yml | 10 ++-- 5 files changed, 60 insertions(+), 67 deletions(-) diff --git a/addons/project_mrp/project_mrp.py b/addons/project_mrp/project_mrp.py index 7c478b63ad6..1e91c9da3b0 100644 --- a/addons/project_mrp/project_mrp.py +++ b/addons/project_mrp/project_mrp.py @@ -20,7 +20,6 @@ ############################################################################## from openerp.osv import fields, osv -from openerp import netsvc from openerp.tools.translate import _ class procurement_order(osv.osv): @@ -49,8 +48,8 @@ class procurement_order(osv.osv): return super(procurement_order, self)._run(cr, uid, procurement, context=context) def _check(self, cr, uid, procurement, context=None): - if self._is_procurement_task(cr, uid, procurement, context=context) and procurement.task_id and procurement.task_id.stage_id.closed: - return True + if self._is_procurement_task(cr, uid, procurement, context=context): + return procurement.task_id and procurement.task_id.stage_id.closed or False return super(procurement_order, self)._check(cr, uid, procurement, context=context) def _convert_qty_company_hours(self, cr, uid, procurement, context=None): @@ -149,25 +148,20 @@ class product_product(osv.osv): } +class sale_order_line(osv.osv): + _inherit = 'sale.order.line' + + def need_procurement(self, cr, uid, ids, context=None): + #when sale is installed alone, there is no need to create procurements, but with project_mrp + #we must create a procurement for each service that has the auto_create_task boolean set to True. + for line in self.browse(cr, uid, ids, context=context): + if line.product_id and line.product_id.type == 'service' and line.product_id.auto_create_task: + return True + return super(sale_order_line, self).need_procurement(cr, uid, ids, context=context) + class sale_order(osv.osv): _inherit = 'sale.order' - def _can_create_procurement(self, cr, uid, ids, context=None): - return True - - def _prepare_order_line_procurement(self, cr, uid, order, line, group_id=False, context=None): - proc_data = super(sale_order, self)._prepare_order_line_procurement(cr, - uid, order, line, group_id = group_id, context=context) - if not(line.product_id.type== "service" and not line.product_id.auto_create_task): - proc_data['sale_line_id'] = line.id - return proc_data - - def _check_create_procurement(self, cr, uid, order, line, context=None): - create = super(sale_order, self)._check_create_procurement(cr, uid, order, line, context=context) - if (line.product_id.type== "service" and not line.product_id.auto_create_task): - create = False - return create - def _picked_rate(self, cr, uid, ids, name, arg, context=None): if not ids: return {} diff --git a/addons/sale/sale.py b/addons/sale/sale.py index 133de124d3c..9df7792e3b2 100644 --- a/addons/sale/sale.py +++ b/addons/sale/sale.py @@ -622,6 +622,8 @@ class sale_order(osv.osv): } def action_done(self, cr, uid, ids, context=None): + for order in self.browse(cr, uid, ids, context=context): + self.pool.get('sale.order.line').write(cr, uid, [line.id for line in order.order_line], {'state': 'done'}, context=context) return self.write(cr, uid, ids, {'state': 'done'}, context=context) def _prepare_order_line_procurement(self, cr, uid, order, line, group_id=False, context=None): @@ -646,15 +648,16 @@ class sale_order(osv.osv): return date_planned def _prepare_procurement_group(self, cr, uid, order, context=None): - return { - 'name': order.name, 'partner_id': order.partner_shipping_id.id, - } + return {'name': order.name, 'partner_id': order.partner_shipping_id.id} - def _check_create_procurement(self, cr, uid, order, line, context=None): - return True - - def _can_create_procurement(self, cr, uid, ids, context=None): - return False + def procurement_needed(self, cr, uid, ids, context=None): + #when sale is installed only, there is no need to create procurements, that's only + #further installed modules (project_mrp, sale_stock) that will change this. + sale_line_obj = self.pool.get('sale.order.line') + res = [] + for order in self.browse(cr, uid, ids, context=context): + res.append(sale_line_obj.need_procurement(cr, uid, [line.id for line in order.order_line], context=context)) + return any(res) def action_ship_create(self, cr, uid, ids, context=None): """Create the required procurements to supply sales order lines, also connecting @@ -668,21 +671,23 @@ class sale_order(osv.osv): :return: True """ procurement_obj = self.pool.get('procurement.order') + sale_line_obj = self.pool.get('sale.order.line') for order in self.browse(cr, uid, ids, context=context): proc_ids = [] vals = self._prepare_procurement_group(cr, uid, order, context=context) - group_id = self.pool.get("procurement.group").create(cr, uid, vals, context=context) + group_id = self.pool.get("procurement.group").create(cr, uid, vals, context=context) order.write({'procurement_group_id': group_id}, context=context) for line in order.order_line: - if (line.state == 'done') or not line.product_id: - continue - if self._can_create_procurement(cr, uid, ids, context=context) and self._check_create_procurement(cr, uid, order, line, context=context): + if sale_line_obj.need_procurement(cr, uid, [line.id], context=context): + if (line.state == 'done') or not line.product_id: + continue vals = self._prepare_order_line_procurement(cr, uid, order, line, group_id=group_id, context=context) proc_id = procurement_obj.create(cr, uid, vals, context=context) proc_ids.append(proc_id) - #Confirm procurement order such that rules will be applied on it - procurement_obj.run(cr, uid, proc_ids, context=context) + #Confirm procurement order such that rules will be applied on it + #note that the workflow ensure proc_ids isn't an empty list + procurement_obj.run(cr, uid, proc_ids, context=context) # FP NOTE: do we need this? isn't it the workflow that should set this val = {} if order.state == 'shipping_except': @@ -697,14 +702,6 @@ class sale_order(osv.osv): order.write(val) return True - def action_ship_end(self, cr, uid, ids, context=None): - for order in self.browse(cr, uid, ids, context=context): - for line in order.order_line: - if line.state == 'exception': - self.pool.get('sale.order.line').write(cr, uid, [line.id], {'state': 'done'}, context=context) - - - # if mode == 'finished': # returns True if all lines are done, False otherwise # if mode == 'canceled': @@ -715,18 +712,12 @@ class sale_order(osv.osv): canceled = False write_done_ids = [] write_cancel_ids = [] - if not self._can_create_procurement(cr, uid, ids, context={}): - for order in self.browse(cr, uid, ids, context={}): - for line in order.order_line: - write_done_ids.append(line.id) - self.pool.get('sale.order.line').write(cr, uid, write_done_ids, {'state': 'done'}) - return True for order in self.browse(cr, uid, ids, context={}): #TODO: Need to rethink what happens when cancelling for line in order.order_line: states = [x.state for x in line.procurement_ids] - cancel = all([x == 'cancel' for x in states]) + cancel = states and all([x == 'cancel' for x in states]) doneorcancel = all([x in ('done', 'cancel') for x in states]) if cancel: canceled = True @@ -764,6 +755,11 @@ class sale_order(osv.osv): # - use it in report if there is a uos class sale_order_line(osv.osv): + def need_procurement(self, cr, uid, ids, context=None): + #when sale is installed only, there is no need to create procurements, that's only + #further installed modules (project_mrp, sale_stock) that will change this. + return False + def _amount_line(self, cr, uid, ids, field_name, arg, context=None): tax_obj = self.pool.get('account.tax') cur_obj = self.pool.get('res.currency') @@ -1145,4 +1141,4 @@ class procurement_order(osv.osv): _columns = { 'sale_line_id': fields.many2one('sale.order.line', string='Sale Order Line'), } - \ No newline at end of file + diff --git a/addons/sale/sale_workflow.xml b/addons/sale/sale_workflow.xml index 33f93380122..8699690fc77 100644 --- a/addons/sale/sale_workflow.xml +++ b/addons/sale/sale_workflow.xml @@ -191,8 +191,7 @@ ship_end - function - action_ship_end() + dummy @@ -240,7 +239,12 @@ - (order_policy!='prepaid') or invoiced + procurement_needed() and ((order_policy!='prepaid') or invoiced) + + + + + not procurement_needed() diff --git a/addons/sale_stock/sale_stock.py b/addons/sale_stock/sale_stock.py index 0942980aaf5..a9940be3b08 100644 --- a/addons/sale_stock/sale_stock.py +++ b/addons/sale_stock/sale_stock.py @@ -31,9 +31,6 @@ from openerp import SUPERUSER_ID class sale_order(osv.osv): _inherit = "sale.order" - def _can_create_procurement(self, cr, uid, ids, context=None): - return True - def copy(self, cr, uid, id, default=None, context=None): if not default: default = {} @@ -203,8 +200,6 @@ class sale_order(osv.osv): self.write(cr, uid, [o.id], {'order_policy': 'manual'}, context=context) return res - - def _get_date_planned(self, cr, uid, order, line, start_date, context=None): date_planned = super(sale_order, self)._get_date_planned(cr, uid, order, line, start_date, context=context) date_planned = (date_planned - timedelta(days=order.company_id.security_lead)).strftime(DEFAULT_SERVER_DATETIME_FORMAT) @@ -240,10 +235,17 @@ class sale_order(osv.osv): return False - class sale_order_line(osv.osv): _inherit = 'sale.order.line' - + + def need_procurement(self, cr, uid, ids, context=None): + #when sale is installed alone, there is no need to create procurements, but with sale_stock + #we must create a procurement for each product that is not a service. + for line in self.browse(cr, uid, ids, context=context): + if line.product_id and line.product_id.type != 'service': + return True + return super(sale_order_line, self).need_procurement(cr, uid, ids, context=context) + def _number_packages(self, cr, uid, ids, field_name, arg, context=None): res = {} for line in self.browse(cr, uid, ids, context=context): @@ -262,7 +264,6 @@ class sale_order_line(osv.osv): 'product_packaging': False, } - def button_cancel(self, cr, uid, ids, context=None): res = super(sale_order_line, self).button_cancel(cr, uid, ids, context=context) for line in self.browse(cr, uid, ids, context=context): @@ -407,4 +408,4 @@ class stock_move(osv.osv): res['account_analytic_id'] = sale_line.order_id.project_id and sale_line.order_id.project_id.id or False res['price_unit'] = sale_line.price_unit res['discount'] = sale_line.discount - return res \ No newline at end of file + return res diff --git a/addons/stock_complex_routes/test/dropship.yml b/addons/stock_complex_routes/test/dropship.yml index 53b65200aea..23d4ef7387a 100644 --- a/addons/stock_complex_routes/test/dropship.yml +++ b/addons/stock_complex_routes/test/dropship.yml @@ -64,9 +64,7 @@ Check one quant was created in Customers location with 200 pieces and one move in the history_ids - !python {model: stock.quant}: | - quant_id = self.search(cr, uid, [('product_id', '=', ref("drop_shop_product"))]) - print quant_id, [(x.qty, x.product_id.name) for x in self.browse(cr, uid, quant_id)] - quant_id = self.search(cr, uid, [('location_id', '=', ref('stock.stock_location_customers')),('qty', '=', 200), ('product_id', '=', ref("drop_shop_product"))]) - print quant_id, [(x.qty, x.product_id.name) for x in self.browse(cr, uid, quant_id)] - assert len(self.browse(cr, uid, quant_id)[0].history_ids) == 1 - assert len(quant_id) == 1 + quant_ids = self.search(cr, uid, [('location_id', '=', ref('stock.stock_location_customers')),('qty', '=', 200), ('product_id', '=', ref("drop_shop_product"))]) + assert quant_ids, 'No Quant found' + assert len(quant_ids) == 1 + assert len(self.browse(cr, uid, quant_ids)[0].history_ids) == 1