diff options
| author | stephanchrst <stephanchrst@gmail.com> | 2022-05-10 21:51:50 +0700 |
|---|---|---|
| committer | stephanchrst <stephanchrst@gmail.com> | 2022-05-10 21:51:50 +0700 |
| commit | 3751379f1e9a4c215fb6eb898b4ccc67659b9ace (patch) | |
| tree | a44932296ef4a9b71d5f010906253d8c53727726 /addons/sale_project/models/project.py | |
| parent | 0a15094050bfde69a06d6eff798e9a8ddf2b8c21 (diff) | |
initial commit 2
Diffstat (limited to 'addons/sale_project/models/project.py')
| -rw-r--r-- | addons/sale_project/models/project.py | 169 |
1 files changed, 169 insertions, 0 deletions
diff --git a/addons/sale_project/models/project.py b/addons/sale_project/models/project.py new file mode 100644 index 00000000..519b88e2 --- /dev/null +++ b/addons/sale_project/models/project.py @@ -0,0 +1,169 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from ast import literal_eval + +from odoo import api, fields, models, _ +from odoo.exceptions import ValidationError + + +class Project(models.Model): + _inherit = 'project.project' + + sale_line_id = fields.Many2one( + 'sale.order.line', 'Sales Order Item', copy=False, + domain="[('is_service', '=', True), ('is_expense', '=', False), ('order_id', '=', sale_order_id), ('state', 'in', ['sale', 'done']), '|', ('company_id', '=', False), ('company_id', '=', company_id)]", + help="Sales order item to which the project is linked. Link the timesheet entry to the sales order item defined on the project. " + "Only applies on tasks without sale order item defined, and if the employee is not in the 'Employee/Sales Order Item Mapping' of the project.") + sale_order_id = fields.Many2one('sale.order', 'Sales Order', + domain="[('order_line.product_id.type', '=', 'service'), ('partner_id', '=', partner_id), ('state', 'in', ['sale', 'done'])]", + copy=False, help="Sales order to which the project is linked.") + + _sql_constraints = [ + ('sale_order_required_if_sale_line', "CHECK((sale_line_id IS NOT NULL AND sale_order_id IS NOT NULL) OR (sale_line_id IS NULL))", 'The project should be linked to a sale order to select a sale order item.'), + ] + + @api.model + def _map_tasks_default_valeus(self, task, project): + defaults = super()._map_tasks_default_valeus(task, project) + defaults['sale_line_id'] = False + return defaults + + def action_view_so(self): + self.ensure_one() + action_window = { + "type": "ir.actions.act_window", + "res_model": "sale.order", + "name": "Sales Order", + "views": [[False, "form"]], + "context": {"create": False, "show_sale": True}, + "res_id": self.sale_order_id.id + } + return action_window + + +class ProjectTask(models.Model): + _inherit = "project.task" + + sale_order_id = fields.Many2one('sale.order', 'Sales Order', help="Sales order to which the task is linked.") + sale_line_id = fields.Many2one( + 'sale.order.line', 'Sales Order Item', domain="[('company_id', '=', company_id), ('is_service', '=', True), ('order_partner_id', 'child_of', commercial_partner_id), ('is_expense', '=', False), ('state', 'in', ['sale', 'done']), ('order_id', '=?', project_sale_order_id)]", + compute='_compute_sale_line', store=True, readonly=False, copy=False, + help="Sales order item to which the project is linked. Link the timesheet entry to the sales order item defined on the project. " + "Only applies on tasks without sale order item defined, and if the employee is not in the 'Employee/Sales Order Item Mapping' of the project.") + project_sale_order_id = fields.Many2one('sale.order', string="Project's sale order", related='project_id.sale_order_id') + invoice_count = fields.Integer("Number of invoices", related='sale_order_id.invoice_count') + task_to_invoice = fields.Boolean("To invoice", compute='_compute_task_to_invoice', search='_search_task_to_invoice', groups='sales_team.group_sale_salesman_all_leads') + + @api.depends('project_id.sale_line_id.order_partner_id') + def _compute_partner_id(self): + for task in self: + if not task.partner_id: + task.partner_id = task.project_id.sale_line_id.order_partner_id + super()._compute_partner_id() + + @api.depends('commercial_partner_id', 'sale_line_id.order_partner_id.commercial_partner_id', 'parent_id.sale_line_id', 'project_id.sale_line_id') + def _compute_sale_line(self): + for task in self: + if not task.sale_line_id: + task.sale_line_id = task.parent_id.sale_line_id or task.project_id.sale_line_id + # check sale_line_id and customer are coherent + if task.sale_line_id.order_partner_id.commercial_partner_id != task.partner_id.commercial_partner_id: + task.sale_line_id = False + + @api.constrains('sale_line_id') + def _check_sale_line_type(self): + for task in self.sudo(): + if task.sale_line_id: + if not task.sale_line_id.is_service or task.sale_line_id.is_expense: + raise ValidationError(_( + 'You cannot link the order item %(order_id)s - %(product_id)s to this task because it is a re-invoiced expense.', + order_id=task.sale_line_id.order_id.name, + product_id=task.sale_line_id.product_id.display_name, + )) + + def unlink(self): + if any(task.sale_line_id for task in self): + raise ValidationError(_('You have to unlink the task from the sale order item in order to delete it.')) + return super().unlink() + + # --------------------------------------------------- + # Actions + # --------------------------------------------------- + + def _get_action_view_so_ids(self): + return self.sale_order_id.ids + + def action_view_so(self): + self.ensure_one() + so_ids = self._get_action_view_so_ids() + action_window = { + "type": "ir.actions.act_window", + "res_model": "sale.order", + "name": "Sales Order", + "views": [[False, "tree"], [False, "form"]], + "context": {"create": False, "show_sale": True}, + "domain": [["id", "in", so_ids]], + } + if len(so_ids) == 1: + action_window["views"] = [[False, "form"]] + action_window["res_id"] = so_ids[0] + + return action_window + + def rating_get_partner_id(self): + partner = self.partner_id or self.sale_line_id.order_id.partner_id + if partner: + return partner + return super().rating_get_partner_id() + + @api.depends('sale_order_id.invoice_status', 'sale_order_id.order_line') + def _compute_task_to_invoice(self): + for task in self: + if task.sale_order_id: + task.task_to_invoice = bool(task.sale_order_id.invoice_status not in ('no', 'invoiced')) + else: + task.task_to_invoice = False + + @api.model + def _search_task_to_invoice(self, operator, value): + query = """ + SELECT so.id + FROM sale_order so + WHERE so.invoice_status != 'invoiced' + AND so.invoice_status != 'no' + """ + operator_new = 'inselect' + if(bool(operator == '=') ^ bool(value)): + operator_new = 'not inselect' + return [('sale_order_id', operator_new, (query, ()))] + + def action_create_invoice(self): + # ensure the SO exists before invoicing, then confirm it + so_to_confirm = self.filtered( + lambda task: task.sale_order_id and task.sale_order_id.state in ['draft', 'sent'] + ).mapped('sale_order_id') + so_to_confirm.action_confirm() + + # redirect create invoice wizard (of the Sales Order) + action = self.env["ir.actions.actions"]._for_xml_id("sale.action_view_sale_advance_payment_inv") + context = literal_eval(action.get('context', "{}")) + context.update({ + 'active_id': self.sale_order_id.id if len(self) == 1 else False, + 'active_ids': self.mapped('sale_order_id').ids, + 'default_company_id': self.company_id.id, + }) + action['context'] = context + return action + +class ProjectTaskRecurrence(models.Model): + _inherit = 'project.task.recurrence' + + def _new_task_values(self, task): + values = super(ProjectTaskRecurrence, self)._new_task_values(task) + task = self.sudo().task_ids[0] + values['sale_line_id'] = self._get_sale_line_id(task) + return values + + def _get_sale_line_id(self, task): + return task.sale_line_id.id |
