diff --git a/addons/mrp/mrp_data.xml b/addons/mrp/mrp_data.xml index 42290edde38..42ad2b911af 100644 --- a/addons/mrp/mrp_data.xml +++ b/addons/mrp/mrp_data.xml @@ -32,7 +32,7 @@ This application supports complete integration and production scheduling for sto --> - Manufacture + Your Company Manufacture 5 diff --git a/addons/project_mrp/project_mrp.py b/addons/project_mrp/project_mrp.py index e00883ed764..1d72375e03c 100644 --- a/addons/project_mrp/project_mrp.py +++ b/addons/project_mrp/project_mrp.py @@ -49,7 +49,7 @@ 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.state == 'done': + if self._is_procurement_task(cr, uid, procurement, context=context) and procurement.task_id and procurement.task_id.stage_id.closed: return True return super(procurement_order, self)._check(cr, uid, procurement, context=context) @@ -70,6 +70,35 @@ class procurement_order(osv.osv): account = procurement.sale_line_id.order_id.project_id project_ids = project_project.search(cr, uid, [('analytic_account_id', '=', account.id)]) projects = project_project.browse(cr, uid, project_ids, context=context) + project = projects and projects[0] or False + return project + + def _create_service_task(self, cr, uid, procurement, context=None): + project_task = self.pool.get('project.task') + project = self._get_project(cr, uid, procurement, context=context) + planned_hours = self._convert_qty_company_hours(cr, uid, procurement, context=context) + task_id = project_task.create(cr, uid, { + 'name': '%s:%s' % (procurement.origin or '', procurement.product_id.name), + 'date_deadline': procurement.date_planned, + 'planned_hours': planned_hours, + 'remaining_hours': planned_hours, + 'partner_id': procurement.sale_line_id and procurement.sale_line_id.order_id.partner_id.id or False, + 'user_id': procurement.product_id.product_manager.id, + 'procurement_id': procurement.id, + 'description': procurement.name + '\n', + 'project_id': project and project.id or False, + 'company_id': procurement.company_id.id, + },context=context) + self.write(cr, uid, [procurement.id], {'task_id': task_id, 'message':_('Task created.')}, context=context) + self.project_task_create_note(cr, uid, [procurement.id], context=context) + return task_id + + def project_task_create_note(self, cr, uid, ids, context=None): + for procurement in self.browse(cr, uid, ids, context=context): + body = _("Task created") + self.message_post(cr, uid, [procurement.id], body=body, context=context) + if procurement.sale_line_id and procurement.sale_line_id.order_id: + procurement.sale_line_id.order_id.message_post(body=body) @@ -98,10 +127,10 @@ class project_task(osv.osv): } def _validate_subflows(self, cr, uid, ids, context=None): - wf_service = netsvc.LocalService("workflow") - for task in self.browse(cr, uid, ids): + proc_obj = self.pool.get("procurement.order") + for task in self.browse(cr, uid, ids, context=context): if task.procurement_id: - wf_service.trg_write(uid, 'procurement.order', task.procurement_id.id, cr) + proc_obj.check(cr, uid, [task.procurement_id.id], context=context) def write(self, cr, uid, ids, values, context=None): """ When closing tasks, validate subflows. """ @@ -124,9 +153,12 @@ class sale_order(osv.osv): _inherit = 'sale.order' 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, + if not(line.product_id.type== "service" and not line.product_id.auto_create_task): + proc_data = super(sale_order, self)._prepare_order_line_procurement(cr, uid, order, line, group_id = group_id, context=context) - proc_data['sale_line_id'] = line.id + proc_data['sale_line_id'] = line.id + else: + proc_data = False return proc_data def _picked_rate(self, cr, uid, ids, name, arg, context=None): diff --git a/addons/project_mrp/project_mrp_demo.xml b/addons/project_mrp/project_mrp_demo.xml index a840c0166e7..e4622866891 100644 --- a/addons/project_mrp/project_mrp_demo.xml +++ b/addons/project_mrp/project_mrp_demo.xml @@ -10,7 +10,6 @@ 150.0 5.0 - make_to_order diff --git a/addons/project_mrp/project_mrp_view.xml b/addons/project_mrp/project_mrp_view.xml index ebd212fbd48..4f7c2e59358 100644 --- a/addons/project_mrp/project_mrp_view.xml +++ b/addons/project_mrp/project_mrp_view.xml @@ -14,11 +14,14 @@ product.form.view.inherit product.product - + + + + diff --git a/addons/project_mrp/test/project_task_procurement.yml b/addons/project_mrp/test/project_task_procurement.yml index b16cce7590d..ed996465673 100644 --- a/addons/project_mrp/test/project_task_procurement.yml +++ b/addons/project_mrp/test/project_task_procurement.yml @@ -1,18 +1,43 @@ +- + Update product to automatically create tasks +- + !record {model: product.product, id: auto_task_service}: + auto_create_task: True + name: Advanced auto task Service + type: service + list_price: 150.0 + standard_price: 100.0 + uom_id: product.product_uom_day + uom_po_id: product.product_uom_day +- + Create a new sales order with a service product +- + !record {model: sale.order, id: sale_order_service}: + partner_id: base.res_partner_2 + pricelist_id: product.list0 +- + Associate a sale order line +- + !record {model: sale.order.line, id: service_line}: + product_id: auto_task_service + product_uom_qty: 50.0 + name: Fixing the bugs + order_id: sale_order_service - In order to test process to generate task automatic from procurement, I confirm sale order to sale service product. - - !workflow {model: sale.order, action: order_confirm, ref: sale.sale_order_3} + !workflow {model: sale.order, action: order_confirm, ref: sale_order_service} - I run the scheduler. - !python {model: procurement.order}: | self.run_scheduler(cr, uid) - - Now I check that task details after run procurement + Now I check the details of the generated task - !python {model: procurement.order}: | from datetime import datetime - procurement_ids = self.search(cr, uid, [('sale_line_id', '=', ref('line_services'))]) + procurement_ids = self.search(cr, uid, [('sale_line_id', '=', ref('service_line'))]) assert procurement_ids, "Procurement is not generated for Service Order Line." procurement = self.browse(cr, uid, procurement_ids[0], context=context) assert procurement.state != 'done' , "Procurement should not be closed." @@ -34,14 +59,14 @@ I close that task. - !python {model: project.task}: | - task_ids = self.search(cr, uid, [('sale_line_id', '=', ref('line_services'))]) + task_ids = self.search(cr, uid, [('sale_line_id', '=', ref('service_line'))]) assert task_ids, "Task is not generated for Service Order Line." self.write(cr, uid, task_ids, {'stage_id': ref('project.project_tt_deployment')}, context=context) - I check procurement of Service Order Line after closed task. - !python {model: procurement.order}: | - procurement_ids = self.search(cr, uid, [('sale_line_id', '=', ref('line_services'))]) + procurement_ids = self.search(cr, uid, [('sale_line_id', '=', ref('service_line'))]) assert procurement_ids, "Procurement is not generated for Service Order Line." procurement = self.browse(cr, uid, procurement_ids[0], context=context) assert procurement.state == 'done' , "Procurement should be closed." \ No newline at end of file diff --git a/addons/purchase/purchase.py b/addons/purchase/purchase.py index f9fa7e96964..e336e14f3c7 100644 --- a/addons/purchase/purchase.py +++ b/addons/purchase/purchase.py @@ -152,7 +152,7 @@ class purchase_order(osv.osv): def _get_picking_in(self, cr, uid, context=None): obj_data = self.pool.get('ir.model.data') - return obj_data.get_object_reference(cr, uid, 'stock','picking_type_in')[1] + return obj_data.get_object_reference(cr, uid, 'stock','picking_type_in') and obj_data.get_object_reference(cr, uid, 'stock','picking_type_in')[1] or False STATE_SELECTION = [ ('draft', 'Draft PO'), diff --git a/addons/purchase/purchase_data.xml b/addons/purchase/purchase_data.xml index 280f503a431..af53d5d9634 100644 --- a/addons/purchase/purchase_data.xml +++ b/addons/purchase/purchase_data.xml @@ -71,7 +71,7 @@ --> - Buy + Your Company Buy 5 diff --git a/addons/purchase_requisition/purchase_requisition.py b/addons/purchase_requisition/purchase_requisition.py index ff7a383480c..afb18dc0d2c 100644 --- a/addons/purchase_requisition/purchase_requisition.py +++ b/addons/purchase_requisition/purchase_requisition.py @@ -144,6 +144,7 @@ class purchase_requisition(osv.osv): def _prepare_purchase_order(self, cr, uid, requisition, supplier, context=None): supplier_pricelist = supplier.property_product_pricelist_purchase and supplier.property_product_pricelist_purchase.id or False + picking_type_in = self.pool.get("purchase.order")._get_picking_in(cr, uid, context=context) return { 'origin': requisition.name, 'date_order': requisition.date_end or fields.date.context_today(self, cr, uid, context=context), @@ -154,6 +155,7 @@ class purchase_requisition(osv.osv): 'fiscal_position': supplier.property_account_position and supplier.property_account_position.id or False, 'requisition_id': requisition.id, 'notes': requisition.description, + 'picking_type_id': picking_type_in, } def _prepare_purchase_order_line(self, cr, uid, requisition, requisition_line, purchase_id, supplier, context=None): diff --git a/addons/sale/sale.py b/addons/sale/sale.py index 9576c023830..a2f961848ea 100644 --- a/addons/sale/sale.py +++ b/addons/sale/sale.py @@ -169,10 +169,13 @@ class sale_order(osv.osv): ('waiting_date', 'Waiting Schedule'), ('progress', 'Sales Order'), ('manual', 'Sale to Invoice'), + ('shipping_except', 'Shipping Exception'), ('invoice_except', 'Invoice Exception'), ('done', 'Done'), - ], 'Status', readonly=True, track_visibility='onchange', - help="Gives the status of the quotation or sales order. \nThe exception status is automatically set when a cancel operation occurs in the processing of a document linked to the sales order. \nThe 'Waiting Schedule' status is set when the invoice is confirmed but waiting for the scheduler to run on the order date.", select=True), + ], 'Status', readonly=True, help="Gives the status of the quotation or sales order.\ + \nThe exception status is automatically set when a cancel operation occurs \ + in the invoice validation (Invoice Exception) or in the picking list process (Shipping Exception).\nThe 'Waiting Schedule' status is set when the invoice is confirmed\ + but waiting for the scheduler to run on the order date.", select=True), 'date_order': fields.datetime('Date', required=True, readonly=True, select=True, states={'draft': [('readonly', False)], 'sent': [('readonly', False)]}), 'create_date': fields.datetime('Creation Date', readonly=True, select=True, help="Date on which sales order is created."), 'date_confirm': fields.date('Confirmation Date', readonly=True, select=True, help="Date on which sales order is confirmed."), @@ -668,9 +671,11 @@ class sale_order(osv.osv): for line in order.order_line: if (line.state == 'done') or not line.product_id: continue - - proc_id = procurement_obj.create(cr, uid, self._prepare_order_line_procurement(cr, uid, order, line, group_id=group_id, context=context)) - proc_ids.append(proc_id) + + vals = self._prepare_order_line_procurement(cr, uid, order, line, group_id=group_id, context=context) + if vals: + 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) @@ -688,6 +693,61 @@ 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': + # returns True if there is at least one canceled line, False otherwise + def test_state(self, cr, uid, ids, mode, *args): + assert mode in ('finished', 'canceled'), _("invalid mode for test_state") + finished = True + canceled = False + write_done_ids = [] + write_cancel_ids = [] + 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]) + doneorcancel = all([x in ('done', 'cancel') for x in states]) + if cancel: + canceled = True + if line.state != 'exception': + write_cancel_ids.append(line.id) + if not doneorcancel: + finished = False + if doneorcancel and not cancel: + write_done_ids.append(line.id) + + if write_done_ids: + self.pool.get('sale.order.line').write(cr, uid, write_done_ids, {'state': 'done'}) + if write_cancel_ids: + self.pool.get('sale.order.line').write(cr, uid, write_cancel_ids, {'state': 'exception'}) + + if mode == 'finished': + return finished + elif mode == 'canceled': + return canceled + + + def procurement_lines_get(self, cr, uid, ids, *args): + res = [] + for order in self.browse(cr, uid, ids, context={}): + for line in order.order_line: + res += [x.id for x in line.procurement_ids] + return res + + + + + # TODO add a field price_unit_uos # - update it on change product and unit price # - use it in report if there is a uos @@ -743,8 +803,6 @@ class sale_order_line(osv.osv): 'sale.order.line': (lambda self,cr,uid,ids,ctx=None: ids, ['invoice_lines'], 10) }), 'price_unit': fields.float('Unit Price', required=True, digits_compute= dp.get_precision('Product Price'), readonly=True, states={'draft': [('readonly', False)]}), - 'type': fields.selection([('make_to_stock', 'from stock'), ('make_to_order', 'on order')], 'Procurement Method', required=True, readonly=True, states={'draft': [('readonly', False)]}, - help="From stock: When needed, the product is taken from the stock or we wait for replenishment.\nOn order: When needed, the product is purchased or produced."), 'price_subtotal': fields.function(_amount_line, string='Subtotal', digits_compute= dp.get_precision('Account')), 'tax_id': fields.many2many('account.tax', 'sale_order_tax', 'order_line_id', 'tax_id', 'Taxes', readonly=True, states={'draft': [('readonly', False)]}), 'address_allotment_id': fields.many2one('res.partner', 'Allotment Partner',help="A partner to whom the particular product needs to be allotted."), @@ -774,7 +832,6 @@ class sale_order_line(osv.osv): 'product_uos_qty': 1, 'sequence': 10, 'state': 'draft', - 'type': 'make_to_stock', 'price_unit': 0.0, 'delay': 0.0, } diff --git a/addons/sale/sale_view.xml b/addons/sale/sale_view.xml index ef6c19ad5a9..58a79106077 100644 --- a/addons/sale/sale_view.xml +++ b/addons/sale/sale_view.xml @@ -150,7 +150,6 @@ - diff --git a/addons/sale/sale_workflow.xml b/addons/sale/sale_workflow.xml index 53542c754bc..33f93380122 100644 --- a/addons/sale/sale_workflow.xml +++ b/addons/sale/sale_workflow.xml @@ -162,5 +162,124 @@ + + + + + + + wait_ship + + + + + cancel3 + True + stopall + action_cancel() + + + + + ship + function + action_ship_create() + + + + + ship_end + function + action_ship_end() + + + + + ship_cancel + True + stopall + action_cancel() + + + + + ship_except + function + write({'state':'shipping_except'}) + + + + + + + + + + + + + + + (order_policy=='picking') + + + + + + order_policy=='prepaid' + + + + + + cancel + + + + + + (order_policy!='prepaid') or invoiced + + + + + + + + + + + procurement.order + procurement_lines_get() + test_state('finished') + + + + + + test_state('canceled') + + + + + + ship_recreate + + + + + + ship_corrected + + + + + + ship_cancel + + + diff --git a/addons/sale_stock/sale_stock.py b/addons/sale_stock/sale_stock.py index b47410f2122..da134a3a520 100644 --- a/addons/sale_stock/sale_stock.py +++ b/addons/sale_stock/sale_stock.py @@ -91,20 +91,6 @@ class sale_order(osv.osv): return vals _columns = { - 'state': fields.selection([ - ('draft', 'Draft Quotation'), - ('sent', 'Quotation Sent'), - ('cancel', 'Cancelled'), - ('waiting_date', 'Waiting Schedule'), - ('progress', 'Sales Order'), - ('manual', 'Sale to Invoice'), - ('shipping_except', 'Shipping Exception'), - ('invoice_except', 'Invoice Exception'), - ('done', 'Done'), - ], 'Status', readonly=True, help="Gives the status of the quotation or sales order.\ - \nThe exception status is automatically set when a cancel operation occurs \ - in the invoice validation (Invoice Exception) or in the picking list process (Shipping Exception).\nThe 'Waiting Schedule' status is set when the invoice is confirmed\ - but waiting for the scheduler to run on the order date.", select=True), 'incoterm': fields.many2one('stock.incoterms', 'Incoterm', help="International Commercial Terms are a series of predefined commercial terms used in international transactions."), 'picking_policy': fields.selection([('direct', 'Deliver each product when available'), ('one', 'Deliver all products at once')], 'Shipping Policy', required=True, readonly=True, states={'draft': [('readonly', False)], 'sent': [('readonly', False)]}, @@ -212,41 +198,7 @@ class sale_order(osv.osv): self.write(cr, uid, [o.id], {'order_policy': 'manual'}, context=context) return res - # if mode == 'finished': - # returns True if all lines are done, False otherwise - # if mode == 'canceled': - # returns True if there is at least one canceled line, False otherwise - def test_state(self, cr, uid, ids, mode, *args): - assert mode in ('finished', 'canceled'), _("invalid mode for test_state") - finished = True - canceled = False - write_done_ids = [] - write_cancel_ids = [] - 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]) - doneorcancel = all([x in ('done', 'cancel') for x in states]) - if cancel: - canceled = True - if line.state != 'exception': - write_cancel_ids.append(line.id) - if not doneorcancel: - finished = False - if doneorcancel and not cancel: - write_done_ids.append(line.id) - if write_done_ids: - self.pool.get('sale.order.line').write(cr, uid, write_done_ids, {'state': 'done'}) - if write_cancel_ids: - self.pool.get('sale.order.line').write(cr, uid, write_cancel_ids, {'state': 'exception'}) - - if mode == 'finished': - return finished - elif mode == 'canceled': - return canceled - 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) @@ -258,8 +210,8 @@ class sale_order(osv.osv): res.update({'move_type': order.picking_policy}) return res - def action_ship_end(self, cr, uid, ids, context=None): + super(sale_order, self).action_ship_end(cr, uid, ids, context=context) for order in self.browse(cr, uid, ids, context=context): val = {'shipped': True} if order.state == 'shipping_except': @@ -269,15 +221,12 @@ class sale_order(osv.osv): if (not line.invoiced) and (line.state not in ('cancel', 'draft')): val['state'] = 'manual' break - for line in order.order_line: - towrite = [] - if line.state == 'exception': - towrite.append(line.id) - if towrite: - self.pool.get('sale.order.line').write(cr, uid, towrite, {'state': 'done'}, context=context) res = self.write(cr, uid, [order.id], val) return True + + + def has_stockable_products(self, cr, uid, ids, *args): for order in self.browse(cr, uid, ids): for order_line in order.order_line: @@ -285,12 +234,7 @@ class sale_order(osv.osv): return True return False - def procurement_lines_get(self, cr, uid, ids, *args): - res = [] - for order in self.browse(cr, uid, ids, context={}): - for line in order.order_line: - res += [x.id for x in line.procurement_ids] - return res + class sale_order_line(osv.osv): _inherit = 'sale.order.line' @@ -397,7 +341,6 @@ class sale_order_line(osv.osv): #update of result obtained in super function product_obj = product_obj.browse(cr, uid, product, context=context) res['value']['delay'] = (product_obj.sale_delay or 0.0) - #res['value']['type'] = product_obj.procure_method #check if product is available, and if not: raise an error uom2 = False diff --git a/addons/sale_stock/sale_stock_view.xml b/addons/sale_stock/sale_stock_view.xml index 47c42d202ab..a204263ef14 100644 --- a/addons/sale_stock/sale_stock_view.xml +++ b/addons/sale_stock/sale_stock_view.xml @@ -45,7 +45,7 @@ - +