From d8f2b1b87e8e3b8fc6ac63ec2d43e7bef0049ada Mon Sep 17 00:00:00 2001 From: jem-odoo Date: Tue, 8 May 2018 13:07:58 +0200 Subject: [PATCH] [IMP] sale_timesheet: wizard to create SO from project This commit provides a wizard to create a SO from a non billable project. It sets sale_line_id on the project, and the project on the SO line to simulate the project was created by the SOL. The created SO contains one line with a selected service product, with ordered qty set as the total of planned hours of all task in projects. The wizard also links all non billable active tasks of the project to the SO line (and its timesheet entries). --- addons/sale_timesheet/__manifest__.py | 1 + addons/sale_timesheet/wizard/__init__.py | 1 + .../wizard/project_create_sale_order.py | 91 +++++++++++++++++++ .../project_create_sale_order_views.xml | 39 ++++++++ 4 files changed, 132 insertions(+) create mode 100644 addons/sale_timesheet/wizard/project_create_sale_order.py create mode 100644 addons/sale_timesheet/wizard/project_create_sale_order_views.xml diff --git a/addons/sale_timesheet/__manifest__.py b/addons/sale_timesheet/__manifest__.py index ba22b0818f2..fae72deab02 100644 --- a/addons/sale_timesheet/__manifest__.py +++ b/addons/sale_timesheet/__manifest__.py @@ -28,6 +28,7 @@ have real delivered quantities in sales orders. 'report/project_profitability_report_analysis_views.xml', 'data/sale_timesheet_filters.xml', 'wizard/project_task_assign_so_line_views.xml', + 'wizard/project_create_sale_order_views.xml', ], 'demo': [ 'data/sale_service_demo.xml', diff --git a/addons/sale_timesheet/wizard/__init__.py b/addons/sale_timesheet/wizard/__init__.py index dc6b755ec0a..08132642389 100644 --- a/addons/sale_timesheet/wizard/__init__.py +++ b/addons/sale_timesheet/wizard/__init__.py @@ -2,3 +2,4 @@ # Part of Odoo. See LICENSE file for full copyright and licensing details. from . import project_task_assign_so_line +from . import project_create_sale_order diff --git a/addons/sale_timesheet/wizard/project_create_sale_order.py b/addons/sale_timesheet/wizard/project_create_sale_order.py new file mode 100644 index 00000000000..0c964bf4560 --- /dev/null +++ b/addons/sale_timesheet/wizard/project_create_sale_order.py @@ -0,0 +1,91 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from odoo import api, fields, models, _ +from odoo.exceptions import UserError + + +class ProjectCreateSalesOrder(models.TransientModel): + _name = 'project.create.sale.order' + _description = "Create a SO from project" + + @api.model + def default_get(self, fields): + result = super(ProjectCreateSalesOrder, self).default_get(fields) + + active_model = self._context.get('active_model') + if active_model != 'project.project': + raise UserError(_('You can only apply this action from a project.')) + + active_id = self._context.get('active_id') + if 'project_id' in fields and active_id: + result['project_id'] = active_id + result['partner_id'] = self.env['project.project'].browse(active_id).partner_id.id + return result + + project_id = fields.Many2one('project.project', "Project", domain=[('sale_line_id', '=', False)], help="Project to make billable", required=True) + product_id = fields.Many2one('product.product', domain=[('type', '=', 'service'), ('invoice_policy', '=', 'delivery')], required=True) + partner_id = fields.Many2one('res.partner', string="Customer", domain=[('customer', '=', True)], required=True) + price_unit = fields.Float("Price", help="Price unit of the selected product for the generated Sales Order.") + currency_id = fields.Many2one('res.currency', string="Currency", related='product_id.currency_id') + + @api.onchange('product_id') + def _onchange_product_id(self): + if self.product_id: + self.price_unit = self.product_id.lst_price + + @api.multi + def action_create_sale_order(self): + # if project linked to SO line or at least on tasks with SO line, then we consider project as billable. + if self.project_id.sale_line_id or self.project_id.tasks.mapped('sale_line_id'): + raise UserError(_("The project is already linked to a sales order item.")) + + # create SO + sale_order = self.env['sale.order'].create({ + 'partner_id': self.partner_id.id, + 'analytic_account_id': self.project_id.analytic_account_id.id, + 'client_order_ref': self.project_id.name, + }) + sale_order.onchange_partner_id() + sale_order.onchange_partner_shipping_id() + + planned_hours = sum(self.project_id.tasks.mapped('planned_hours')) + + # trying to simulate the SO line created a task, according to the product configuration + # To avoid, generating a task when confirming the SO + task_id = False + if self.product_id.service_tracking in ['task_new_project', 'task_global_project']: + task_id = self.env['project.task'].search([('project_id', '=', self.project_id.id)], order='create_date DESC', limit=1).id + + # create SO line + sale_order_line = self.env['sale.order.line'].create({ + 'order_id': sale_order.id, + 'product_id': self.product_id.id, + 'price_unit': self.price_unit, + 'project_id': self.project_id.id, # prevent to re-create a project on confirmation + 'task_id': task_id, + 'product_uom_qty': sale_order.company_id.project_time_mode_id._compute_quantity(planned_hours, self.product_id.uom_id, raise_if_failure=False), + }) + + # confirm SO + sale_order.action_confirm() + + # link project and tasks to SO line + self.project_id.write({ + 'sale_line_id': sale_order_line.id, + }) + self.project_id.tasks.filtered(lambda t: not t.sale_line_id).write({ + 'sale_line_id': sale_order_line.id, + 'partner_id': sale_order.partner_id.id, + 'email_from': sale_order.partner_id.email, + }) + + view_form_id = self.env.ref('sale.view_order_form').id + action = self.env.ref('sale.action_orders').read()[0] + action.update({ + 'views': [(view_form_id, 'form')], + 'view_mode': 'form', + 'name': sale_order.name, + 'res_id': sale_order.id, + }) + return action diff --git a/addons/sale_timesheet/wizard/project_create_sale_order_views.xml b/addons/sale_timesheet/wizard/project_create_sale_order_views.xml new file mode 100644 index 00000000000..306f9e335f2 --- /dev/null +++ b/addons/sale_timesheet/wizard/project_create_sale_order_views.xml @@ -0,0 +1,39 @@ + + + + + project.create.sale.order.wizard.form + project.create.sale.order + +
+ + + + + + + + + +
+
+
+
+
+ + + Create a Sales Order + ir.actions.act_window + project.create.sale.order + form + form + + new + + + + + +