From f14b1bd81b581fd39bb8f0ca3392e61a2c84841a Mon Sep 17 00:00:00 2001 From: Denis Leemann Date: Tue, 30 May 2017 14:25:37 +0200 Subject: [PATCH] Add sale_project_fixed_price_task_completed_invoicing --- .../__init__.py | 4 ++ .../__manifest__.py | 21 +++++++ .../models/__init__.py | 8 +++ .../models/procurement.py | 19 +++++++ .../models/product.py | 25 +++++++++ .../models/project_task.py | 55 +++++++++++++++++++ .../models/sale_order.py | 25 +++++++++ .../models/sale_order_line.py | 35 ++++++++++++ .../views/project_views.xml | 24 ++++++++ 9 files changed, 216 insertions(+) create mode 100644 sale_project_fixed_price_task_completed_invoicing/__init__.py create mode 100644 sale_project_fixed_price_task_completed_invoicing/__manifest__.py create mode 100644 sale_project_fixed_price_task_completed_invoicing/models/__init__.py create mode 100644 sale_project_fixed_price_task_completed_invoicing/models/procurement.py create mode 100644 sale_project_fixed_price_task_completed_invoicing/models/product.py create mode 100644 sale_project_fixed_price_task_completed_invoicing/models/project_task.py create mode 100644 sale_project_fixed_price_task_completed_invoicing/models/sale_order.py create mode 100644 sale_project_fixed_price_task_completed_invoicing/models/sale_order_line.py create mode 100644 sale_project_fixed_price_task_completed_invoicing/views/project_views.xml diff --git a/sale_project_fixed_price_task_completed_invoicing/__init__.py b/sale_project_fixed_price_task_completed_invoicing/__init__.py new file mode 100644 index 000000000000..a77a6fcbc5d3 --- /dev/null +++ b/sale_project_fixed_price_task_completed_invoicing/__init__.py @@ -0,0 +1,4 @@ +# -*- coding: utf-8 -*- +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from . import models diff --git a/sale_project_fixed_price_task_completed_invoicing/__manifest__.py b/sale_project_fixed_price_task_completed_invoicing/__manifest__.py new file mode 100644 index 000000000000..42f806f5cad6 --- /dev/null +++ b/sale_project_fixed_price_task_completed_invoicing/__manifest__.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- +# Copyright 2017 Camptocamp SA +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). +{ + "name": "Sale project fixed price task completed invoicing", + "version": "10.0.1.0.0", + "depends": [ + 'product', + 'project', + 'sale', + 'sale_timesheet', + ], + "author": "Camptocamp,Odoo Community Association (OCA)", + "website": "http://www.camptocamp.com", + "license": "AGPL-3", + "category": "Sale", + "data": [ + 'views/project_views.xml', + ], + 'installable': True, +} diff --git a/sale_project_fixed_price_task_completed_invoicing/models/__init__.py b/sale_project_fixed_price_task_completed_invoicing/models/__init__.py new file mode 100644 index 000000000000..00edd76c6dbb --- /dev/null +++ b/sale_project_fixed_price_task_completed_invoicing/models/__init__.py @@ -0,0 +1,8 @@ +# -*- coding: utf-8 -*- +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from . import product +from . import project_task +from . import sale_order +from . import sale_order_line +from . import procurement diff --git a/sale_project_fixed_price_task_completed_invoicing/models/procurement.py b/sale_project_fixed_price_task_completed_invoicing/models/procurement.py new file mode 100644 index 000000000000..e83ad6e02b6d --- /dev/null +++ b/sale_project_fixed_price_task_completed_invoicing/models/procurement.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- +# Copyright 2017 Camptocamp SA +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from odoo import models + + +class ProcurementOrder(models.Model): + _inherit = 'procurement.order' + + def _is_procurement_task(self): + return (self.product_id.type == 'service' and + self.product_id.track_service in ('task', 'completed_task')) + + def _create_service_task(self): + task = super(ProcurementOrder, self)._create_service_task() + if self.product_id.track_service == 'completed_task': + task.fixed_price = True + return task diff --git a/sale_project_fixed_price_task_completed_invoicing/models/product.py b/sale_project_fixed_price_task_completed_invoicing/models/product.py new file mode 100644 index 000000000000..37d2ececc0de --- /dev/null +++ b/sale_project_fixed_price_task_completed_invoicing/models/product.py @@ -0,0 +1,25 @@ +# -*- coding: utf-8 -*- +# Copyright 2017 Camptocamp SA +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from odoo import api, fields, models + + +class ProductTemplate(models.Model): + _inherit = 'product.template' + + track_service = fields.Selection(selection_add=[ + ('completed_task', 'Completed Task')] + ) + + +class ProductProduct(models.Model): + _inherit = 'product.product' + + @api.multi + def _need_procurement(self): + for product in self: + if (product.type == 'service' and + product.track_service == 'completed_task'): + return True + return super(ProductProduct, self)._need_procurement() diff --git a/sale_project_fixed_price_task_completed_invoicing/models/project_task.py b/sale_project_fixed_price_task_completed_invoicing/models/project_task.py new file mode 100644 index 000000000000..e7524e243cc1 --- /dev/null +++ b/sale_project_fixed_price_task_completed_invoicing/models/project_task.py @@ -0,0 +1,55 @@ +# -*- coding: utf-8 -*- +# Copyright 2017 Camptocamp SA +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from odoo import api, fields, models, _ +from odoo.exceptions import ValidationError, UserError + + +class ProjectTask(models.Model): + _inherit = 'project.task' + + invoiceable = fields.Boolean( + string='Invoiceable', + ) + fixed_price = fields.Boolean( + string='Fixed Price', + ) + + @api.multi + def toggle_invoiceable(self): + for task in self: + # We dont' want to modify when the related SOLine is invoiced + if (not task.sale_line_id or + task.sale_line_id.state in ('done', 'cancel')): + raise UserError(_("You cannot modify the status if there is " + "no Sale Order Line or if it has been " + "invoiced.")) + task.invoiceable = not task.invoiceable + task.sale_line_id._check_delivered_qty() + + @api.multi + def write(self, vals): + for task in self: + if (vals.get('sale_line_id') and + task.sale_line_id.state in ('done', 'cancel')): + raise ValidationError(_('You cannot modify the Sale Order ' + 'Line of the task once it is invoiced') + ) + return super(ProjectTask, self).write(vals) + + def create(self, vals): + SOLine = self.env['sale.order.line'] + so_line = SOLine.browse(vals.get('sale_line_id')) + # We don't want to add a project.task to an already invoiced line + if so_line and so_line.state in ('done', 'cancel'): + raise ValidationError(_('You cannot add a task to and invoiced ' + 'Sale Order Line')) + return super(ProjectTask, self).create(vals) + + @api.onchange('invoiceable') + def _onchange_invoiceable(self): + for task in self: + if not task.invoiceable: + continue + task.sale_line_id._check_delivered_qty() diff --git a/sale_project_fixed_price_task_completed_invoicing/models/sale_order.py b/sale_project_fixed_price_task_completed_invoicing/models/sale_order.py new file mode 100644 index 000000000000..f1fa88ce18b7 --- /dev/null +++ b/sale_project_fixed_price_task_completed_invoicing/models/sale_order.py @@ -0,0 +1,25 @@ +# -*- coding: utf-8 -*- +# Copyright 2017 Camptocamp SA +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from odoo import api, models + + +class SaleOrder(models.Model): + _inherit = 'sale.order' + + @api.multi + def action_confirm(self): + res = super(SaleOrder, self).action_confirm() + for order in self: + if not order.project_project_id: + for line in order.order_line: + if (line.product_id.track_service in ('completed_task', + 'timesheet')): + if not order.project_id: + order._create_analytic_account( + prefix=line.product_id.default_code or None) + order.project_id.project_create( + {'name': order.project_id.name, + 'use_tasks': True}) + return res diff --git a/sale_project_fixed_price_task_completed_invoicing/models/sale_order_line.py b/sale_project_fixed_price_task_completed_invoicing/models/sale_order_line.py new file mode 100644 index 000000000000..ad58d6608bec --- /dev/null +++ b/sale_project_fixed_price_task_completed_invoicing/models/sale_order_line.py @@ -0,0 +1,35 @@ +# -*- coding: utf-8 -*- +# Copyright 2017 Camptocamp SA +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from odoo import api, models, _ +from odoo.exceptions import ValidationError + + +class SaleOrderLine(models.Model): + _inherit = 'sale.order.line' + + @api.model + def create(self, vals): + line = super(SaleOrderLine, self).create(vals) + if (line.state == 'sale' and not line.order_id.project_id and + line.product_id.track_service in ('completed_task', )): + line.order_id._create_analytic_account() + + @api.constrains('product_id') + def _onchange_product_id(self): + for line in self: + if ('completed_task' == line.product_id.track_service and + line.product_uom_qty != 1.0): + raise ValidationError(_("The quantity for 'Complete Task' " + "products must be exactly one")) + + @api.multi + def _check_delivered_qty(self): + for line in self: + tasks_count = self.env['project.task'].search_count( + [('sale_line_id', '=', line.id)]) + task_invoiced_count = self.env['project.task'].search_count( + [('sale_line_id', '=', line.id), ('invoiceable', '=', True)]) + if tasks_count == task_invoiced_count: + line.qty_delivered = 1.0 diff --git a/sale_project_fixed_price_task_completed_invoicing/views/project_views.xml b/sale_project_fixed_price_task_completed_invoicing/views/project_views.xml new file mode 100644 index 000000000000..155d4ca84262 --- /dev/null +++ b/sale_project_fixed_price_task_completed_invoicing/views/project_views.xml @@ -0,0 +1,24 @@ + + + + + project.task.form.track + project.task + + + + + + + + +
+ +
+ +
+
+ +