[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).
This commit is contained in:
@@ -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',
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -0,0 +1,39 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<odoo>
|
||||
|
||||
<record id="project_create_sale_order_view_form" model="ir.ui.view">
|
||||
<field name="name">project.create.sale.order.wizard.form</field>
|
||||
<field name="model">project.create.sale.order</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Create Sales Order from Project">
|
||||
<group>
|
||||
<group>
|
||||
<field name="project_id" readonly="1"/>
|
||||
<field name="product_id" options="{'no_create_edit': True, 'no_create': True}"/>
|
||||
<field name="price_unit" widget='monetary' options="{'currency_field': 'currency_id', 'field_digits': True}"/>
|
||||
<field name="partner_id"/>
|
||||
<field name="currency_id" invisible="1"/>
|
||||
</group>
|
||||
</group>
|
||||
<footer>
|
||||
<button string="Create Sales Order" type="object" name="action_create_sale_order" class="oe_highlight"/>
|
||||
<button string="Cancel" special="cancel" type="object" class="btn btn-default oe_inline"/>
|
||||
</footer>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="project_project_action_multi_create_sale_order" model="ir.actions.act_window">
|
||||
<field name="name">Create a Sales Order</field>
|
||||
<field name="type">ir.actions.act_window</field>
|
||||
<field name="res_model">project.create.sale.order</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_mode">form</field>
|
||||
<field name="view_id" ref="project_create_sale_order_view_form"/>
|
||||
<field name="target">new</field>
|
||||
<field name="multi" eval="False"/>
|
||||
<field name="binding_model_id" ref="project.model_project_project"/>
|
||||
<field name="groups_id" eval="[(4, ref('sales_team.group_sale_salesman'))]"/>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
Reference in New Issue
Block a user