From 9f72d70c75b42aa73b3d24e19d705f9ce62a6f9e Mon Sep 17 00:00:00 2001 From: "Xavier BOL (xbo)" Date: Wed, 20 Jan 2021 11:55:07 +0100 Subject: [PATCH] [IMP] sale_timesheet: remove project.task.create.sale.order model This commit removes the project.task.create.sale.order model because this wizard is no longer used. Before we use this wizard to create a SO from ticket and task, but in reality this feature is no used. The test using this wizard has been changed to keep the behaviour of it. This test checks if the invoiced timesheet does not change the project when the linked task changes the project. Moreover, the both projects used in this test have been switched because when we invoice the SO the timesheet is in the template project and this one is not billable. Thus, we create no invoice. To create an invoice as we expect, we must have the timesheet in the global project. task-2439304 --- addons/sale_timesheet/__manifest__.py | 1 - .../security/ir.model.access.csv | 1 - .../tests/test_sale_timesheet.py | 89 +++++------ addons/sale_timesheet/wizard/__init__.py | 1 - .../wizard/project_task_create_sale_order.py | 145 ------------------ .../project_task_create_sale_order_views.xml | 46 ------ 6 files changed, 42 insertions(+), 241 deletions(-) delete mode 100644 addons/sale_timesheet/wizard/project_task_create_sale_order.py delete mode 100644 addons/sale_timesheet/wizard/project_task_create_sale_order_views.xml diff --git a/addons/sale_timesheet/__manifest__.py b/addons/sale_timesheet/__manifest__.py index 6b3c91b6723..00d5cdeef96 100644 --- a/addons/sale_timesheet/__manifest__.py +++ b/addons/sale_timesheet/__manifest__.py @@ -31,7 +31,6 @@ have real delivered quantities in sales orders. 'wizard/project_create_sale_order_views.xml', 'wizard/project_create_invoice_views.xml', 'wizard/sale_make_invoice_advance_views.xml', - 'wizard/project_task_create_sale_order_views.xml' ], 'qweb': [ 'static/src/xml/sale_project_templates.xml', diff --git a/addons/sale_timesheet/security/ir.model.access.csv b/addons/sale_timesheet/security/ir.model.access.csv index a391d6610e4..cda5ac60cc7 100644 --- a/addons/sale_timesheet/security/ir.model.access.csv +++ b/addons/sale_timesheet/security/ir.model.access.csv @@ -5,4 +5,3 @@ access_project_sale_line_employee_map_manager,access_project_sale_line_employee_ access_project_create_sale_order,access.project.create.sale.order,model_project_create_sale_order,sales_team.group_sale_salesman,1,1,1,0 access_project_create_sale_order_line,access.project.create.sale.order.line,model_project_create_sale_order_line,sales_team.group_sale_salesman,1,1,1,1 access_project_create_invoice,access.project.create.invoice,model_project_create_invoice,sales_team.group_sale_salesman_all_leads,1,1,1,0 -access_project_task_create_sale_order,access.project.task.create.sale.order,model_project_task_create_sale_order,sales_team.group_sale_salesman,1,1,1,0 diff --git a/addons/sale_timesheet/tests/test_sale_timesheet.py b/addons/sale_timesheet/tests/test_sale_timesheet.py index 9e3411e1845..ca1a5e0bcec 100644 --- a/addons/sale_timesheet/tests/test_sale_timesheet.py +++ b/addons/sale_timesheet/tests/test_sale_timesheet.py @@ -530,14 +530,15 @@ class TestSaleTimesheet(TestCommonSaleTimesheet): Task = self.env['project.task'] today = Date.context_today(self.env.user) - task = Task.with_context(default_project_id=self.project_global.id).create({ + task = Task.with_context(default_project_id=self.project_template.id).create({ 'name': 'first task', - 'partner_id': self.partner_a.id, + 'partner_id': self.partner_b.id, 'planned_hours': 10, + 'sale_line_id': self.so.order_line[0].id }) Timesheet.create({ - 'project_id': self.project_global.id, + 'project_id': self.project_template.id, 'task_id': task.id, 'name': 'my first timesheet', 'unit_amount': 4, @@ -545,51 +546,11 @@ class TestSaleTimesheet(TestCommonSaleTimesheet): timesheet_count1 = Timesheet.search_count([('project_id', '=', self.project_global.id)]) timesheet_count2 = Timesheet.search_count([('project_id', '=', self.project_template.id)]) - self.assertEqual(timesheet_count1, 1, "One timesheet in project_global") - self.assertEqual(timesheet_count2, 0, "No timesheet in project_template") + self.assertEqual(timesheet_count1, 0, "No timesheet in project_global") + self.assertEqual(timesheet_count2, 1, "One timesheet in project_template") self.assertEqual(len(task.timesheet_ids), 1, "The timesheet should be linked to task") # change project of task, as the timesheet is not yet invoiced, the timesheet will change his project - task.write({ - 'project_id': self.project_template.id - }) - - timesheet_count1 = Timesheet.search_count([('project_id', '=', self.project_global.id)]) - timesheet_count2 = Timesheet.search_count([('project_id', '=', self.project_template.id)]) - self.assertEqual(timesheet_count1, 0, "No timesheet in project_global") - self.assertEqual(timesheet_count2, 1, "One timesheet in project_template") - self.assertEqual(len(task.timesheet_ids), 1, "The timesheet still should be linked to task") - - wizard = self.env['project.task.create.sale.order'].with_context(active_id=task.id, active_model='project.task').create({ - 'product_id': self.product_delivery_timesheet3.id - }) - - # We create the SO and the invoice - action = wizard.action_create_sale_order() - sale_order = self.env['sale.order'].browse(action['res_id']) - self.context = { - 'active_model': 'sale.order', - 'active_ids': [sale_order.id], - 'active_id': sale_order.id, - 'default_journal_id': self.company_data['default_journal_sale'].id - } - wizard = self.env['sale.advance.payment.inv'].with_context(self.context).create({ - 'advance_payment_method': 'delivered', - 'date_start_invoice_timesheet': today - timedelta(days=4), - 'date_end_invoice_timesheet': today - }) - wizard.create_invoices() - - Timesheet.create({ - 'project_id': self.project_template.id, - 'task_id': task.id, - 'name': 'my second timesheet', - 'unit_amount': 6, - }) - - self.assertEqual(Timesheet.search_count([('project_id', '=', self.project_template.id)]), 2, "2 timesheets in project_template") - - # change project of task, the timesheet not yet invoiced will change its project. The timesheet already invoiced will not change his project. task.write({ 'project_id': self.project_global.id }) @@ -597,5 +558,39 @@ class TestSaleTimesheet(TestCommonSaleTimesheet): timesheet_count1 = Timesheet.search_count([('project_id', '=', self.project_global.id)]) timesheet_count2 = Timesheet.search_count([('project_id', '=', self.project_template.id)]) self.assertEqual(timesheet_count1, 1, "One timesheet in project_global") - self.assertEqual(timesheet_count2, 1, "Still one timesheet in project_template") - self.assertEqual(len(task.timesheet_ids), 2, "The 2 timesheet still should be linked to task") + self.assertEqual(timesheet_count2, 0, "No timesheet in project_template") + self.assertEqual(len(task.timesheet_ids), 1, "The timesheet still should be linked to task") + + # Create an invoice + context = { + 'active_model': 'sale.order', + 'active_ids': [self.so.id], + 'active_id': self.so.id, + 'default_journal_id': self.company_data['default_journal_sale'].id + } + wizard = self.env['sale.advance.payment.inv'].with_context(context).create({ + 'advance_payment_method': 'delivered', + 'date_start_invoice_timesheet': today - timedelta(days=4), + 'date_end_invoice_timesheet': today, + }) + wizard.create_invoices() + + Timesheet.create({ + 'project_id': self.project_global.id, + 'task_id': task.id, + 'name': 'my second timesheet', + 'unit_amount': 6, + }) + + self.assertEqual(Timesheet.search_count([('project_id', '=', self.project_global.id)]), 2, "2 timesheets in project_global") + + # change project of task, the timesheet not yet invoiced will change its project. The timesheet already invoiced will not change his project. + task.write({ + 'project_id': self.project_template.id + }) + + timesheet_count1 = Timesheet.search_count([('project_id', '=', self.project_global.id)]) + timesheet_count2 = Timesheet.search_count([('project_id', '=', self.project_template.id)]) + self.assertEqual(timesheet_count1, 1, "Still one timesheet in project_global") + self.assertEqual(timesheet_count2, 1, "One timesheet in project_template") + self.assertEqual(len(task.timesheet_ids), 2, "The 2 timesheets still should be linked to task") diff --git a/addons/sale_timesheet/wizard/__init__.py b/addons/sale_timesheet/wizard/__init__.py index 42502ae67a9..a2800ce6ba3 100644 --- a/addons/sale_timesheet/wizard/__init__.py +++ b/addons/sale_timesheet/wizard/__init__.py @@ -4,4 +4,3 @@ from . import project_create_sale_order from . import project_create_invoice from . import sale_make_invoice_advance -from . import project_task_create_sale_order diff --git a/addons/sale_timesheet/wizard/project_task_create_sale_order.py b/addons/sale_timesheet/wizard/project_task_create_sale_order.py deleted file mode 100644 index 723ea2111c4..00000000000 --- a/addons/sale_timesheet/wizard/project_task_create_sale_order.py +++ /dev/null @@ -1,145 +0,0 @@ -# -*- 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 ProjectTaskCreateSalesOrder(models.TransientModel): - _name = 'project.task.create.sale.order' - _description = "Create SO from task" - - @api.model - def default_get(self, fields): - result = super(ProjectTaskCreateSalesOrder, self).default_get(fields) - - active_model = self._context.get('active_model') - if active_model != 'project.task': - raise UserError(_("You can only apply this action from a task.")) - - active_id = self._context.get('active_id') - if 'task_id' in fields and active_id: - task = self.env['project.task'].browse(active_id) - if task.sale_order_id: - raise UserError(_("The task has already a sale order.")) - result['task_id'] = active_id - if not result.get('partner_id', False): - result['partner_id'] = task.partner_id.id - return result - - link_selection = fields.Selection([('create', 'Create a new sales order'), ('link', 'Link to an existing sales order')], required=True, default='create') - - task_id = fields.Many2one('project.task', "Task", domain=[('sale_line_id', '=', False)], help="Task for which we are creating a sales order", required=True) - partner_id = fields.Many2one('res.partner', string="Customer", help="Customer of the sales order", required=True) - product_id = fields.Many2one('product.product', domain=[('type', '=', 'service'), ('invoice_policy', '=', 'delivery'), ('service_type', '=', 'timesheet')], string="Service", help="Product of the sales order item. Must be a service invoiced based on timesheets on tasks. The existing timesheet will be linked to this product.", required=True) - price_unit = fields.Float("Unit Price", help="Unit price of the sales order item.") - currency_id = fields.Many2one('res.currency', string="Currency", related='product_id.currency_id', readonly=False) - commercial_partner_id = fields.Many2one(related='partner_id.commercial_partner_id') - sale_order_id = fields.Many2one( - 'sale.order', string="Sales Order", - domain="['|', '|', ('partner_id', '=', partner_id), ('partner_id', 'child_of', commercial_partner_id), ('partner_id', 'parent_of', partner_id)]") - sale_line_id = fields.Many2one( - 'sale.order.line', 'Sales Order Item', - domain="[('is_service', '=', True), ('order_partner_id', 'child_of', commercial_partner_id), ('is_expense', '=', False), ('state', 'in', ['sale', 'done']), ('order_id', '=?', sale_order_id)]") - - info_invoice = fields.Char(compute='_compute_info_invoice') - - @api.depends('sale_line_id', 'price_unit', 'link_selection') - def _compute_info_invoice(self): - for line in self: - domain = self.env['sale.order.line']._timesheet_compute_delivered_quantity_domain() - timesheet = self.env['account.analytic.line'].read_group(domain + [('task_id', '=', self.task_id.id), ('so_line', '=', False), ('timesheet_invoice_id', '=', False)], ['unit_amount'], ['task_id']) - unit_amount = round(timesheet[0].get('unit_amount', 0), 2) if timesheet else 0 - if not unit_amount: - line.info_invoice = False - continue - company_uom = self.env.company.timesheet_encode_uom_id - label = _("hours") - if company_uom == self.env.ref('uom.product_uom_day'): - label = _("days") - if line.link_selection == 'create' and line.price_unit: - line.info_invoice = _("%(amount)s %(label)s will be added to the new Sales Order.", amount=unit_amount, label=label) - else: - line.info_invoice = _("%(amount)s %(label)s will be added to the selected Sales Order.", amount=unit_amount, label=label) - - @api.onchange('product_id') - def _onchange_product_id(self): - if self.product_id: - self.price_unit = self.product_id.lst_price - else: - self.price_unit = 0.0 - - @api.onchange('partner_id') - def _onchange_partner_id(self): - self.sale_order_id = False - self.sale_line_id = False - - @api.onchange('sale_order_id') - def _onchange_sale_order_id(self): - self.sale_line_id = False - - def action_link_sale_order(self): - # link task to SOL - self.task_id.write({ - 'sale_line_id': self.sale_line_id.id, - 'partner_id': self.partner_id.id, - 'email_from': self.partner_id.email, - }) - - # assign SOL to timesheets - self.env['account.analytic.line'].search([('task_id', '=', self.task_id.id), ('so_line', '=', False), ('timesheet_invoice_id', '=', False)]).write({ - 'so_line': self.sale_line_id.id - }) - - def action_create_sale_order(self): - sale_order = self._prepare_sale_order() - sale_order.action_confirm() - view_form_id = self.env.ref('sale.view_order_form').id - action = self.env["ir.actions.actions"]._for_xml_id("sale.action_orders") - action.update({ - 'views': [(view_form_id, 'form')], - 'view_mode': 'form', - 'name': sale_order.name, - 'res_id': sale_order.id, - }) - return action - - def _prepare_sale_order(self): - # if task linked to SO line, then we consider it as billable. - if self.task_id.sale_line_id: - raise UserError(_("The task is already linked to a sales order item.")) - - # create SO - sale_order = self.env['sale.order'].create({ - 'partner_id': self.partner_id.id, - 'company_id': self.task_id.company_id.id, - 'analytic_account_id': self.task_id.project_id.analytic_account_id.id, - }) - sale_order.onchange_partner_id() - sale_order.onchange_partner_shipping_id() - # rewrite the user as the onchange_partner_id erases it - sale_order.write({'user_id': self.task_id.user_id.id}) - sale_order.onchange_user_id() - - 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.task_id.project_id.id, # prevent to re-create a project on confirmation - 'task_id': self.task_id.id, - 'product_uom_qty': round(sum(self.task_id.timesheet_ids.filtered(lambda t: not t.so_line).mapped('unit_amount')), 2), - }) - - # link task to SOL - self.task_id.write({ - 'sale_line_id': sale_order_line.id, - 'partner_id': sale_order.partner_id.id, - 'email_from': sale_order.partner_id.email, - }) - - # assign SOL to timesheets - self.env['account.analytic.line'].search([('task_id', '=', self.task_id.id), ('so_line', '=', False), ('timesheet_invoice_id', '=', False)]).write({ - 'so_line': sale_order_line.id - }) - - return sale_order diff --git a/addons/sale_timesheet/wizard/project_task_create_sale_order_views.xml b/addons/sale_timesheet/wizard/project_task_create_sale_order_views.xml deleted file mode 100644 index 2521b53bd36..00000000000 --- a/addons/sale_timesheet/wizard/project_task_create_sale_order_views.xml +++ /dev/null @@ -1,46 +0,0 @@ - - - - - project.task.create.sale.order.wizard.form - project.task.create.sale.order - -
- - - - - - - - - - - - - - - - - - - - -
-
- -
-
- - - Create a Sales Order - project.task.create.sale.order - form - - new - - -