[IMP] sale_timesheet: allow to timesheet on project without putting the task_id
Currently timesheeting on a project without specifying the task does not increase the delivered_qty of the related sale order line. This could potentially lead to issues with the Helpdesk module for instance. When a project is allow_billable = false, the user is not required to specify the task_id on the helpdesk's ticket. This means that hours timesheeted while working on this ticket will not be added to the delivered_qty of the related SOL. This commit "fixes" this by allowing to timesheet on project without specifying the task_id. So now, even if an helpdesk's ticket has no task_id, the timesheeted hours will be added to the related SOL delivered_qty. Because it was initially impossible to timesheet on project without setting the task_id, some tests had to be rewritten. closes odoo/odoo#53172 Signed-off-by: Yannick Tivisse (yti) <yti@odoo.com>
This commit is contained in:
committed by
Yannick Tivisse
parent
42abf8f496
commit
dc20520034
@@ -44,7 +44,7 @@ class AccountAnalyticLine(models.Model):
|
||||
if self.task_id.billable_type == 'task_rate':
|
||||
self.so_line = self.task_id.sale_line_id
|
||||
elif self.task_id.billable_type == 'employee_rate':
|
||||
self.so_line = self._timesheet_determine_sale_line(self.task_id, self.employee_id)
|
||||
self.so_line = self._timesheet_determine_sale_line(self.task_id, self.employee_id, self.project_id)
|
||||
else:
|
||||
self.so_line = False
|
||||
|
||||
@@ -75,33 +75,40 @@ class AccountAnalyticLine(models.Model):
|
||||
values['company_id'] = task.analytic_account_id.company_id.id
|
||||
values = super(AccountAnalyticLine, self)._timesheet_preprocess(values)
|
||||
# task implies so line (at create)
|
||||
if 'task_id' in values and not values.get('so_line') and (values.get('employee_id') or self.mapped('employee_id')):
|
||||
if any([field_name in values for field_name in ['task_id', 'project_id']]) and not values.get('so_line') and (values.get('employee_id') or self.mapped('employee_id')):
|
||||
if not values.get('employee_id') and len(self.mapped('employee_id')) > 1:
|
||||
raise UserError(_('You can not modify timesheets from different employees'))
|
||||
task = self.env['project.task'].sudo().browse(values['task_id'])
|
||||
task = self.env['project.task'].sudo().browse(values['task_id']) if values.get('task_id') else self.env['project.task']
|
||||
employee = self.env['hr.employee'].sudo().browse(values['employee_id']) if values.get('employee_id') else self.mapped('employee_id')
|
||||
values['so_line'] = self._timesheet_determine_sale_line(task, employee).id
|
||||
project = self.env['project.project'].sudo().browse(values['project_id']) if values.get('project_id') else task.project_id
|
||||
values['so_line'] = self._timesheet_determine_sale_line(task, employee, project).id
|
||||
return values
|
||||
|
||||
def _timesheet_postprocess_values(self, values):
|
||||
result = super(AccountAnalyticLine, self)._timesheet_postprocess_values(values)
|
||||
# (re)compute the sale line
|
||||
if any(field_name in values for field_name in ['task_id', 'employee_id']):
|
||||
if any(field_name in values for field_name in ['task_id', 'employee_id', 'project_id']):
|
||||
for timesheet in self:
|
||||
result[timesheet.id].update({
|
||||
'so_line': timesheet._timesheet_determine_sale_line(timesheet.task_id, timesheet.employee_id).id,
|
||||
'so_line': timesheet._timesheet_determine_sale_line(timesheet.task_id, timesheet.employee_id, timesheet.project_id).id,
|
||||
})
|
||||
return result
|
||||
|
||||
@api.model
|
||||
def _timesheet_determine_sale_line(self, task, employee):
|
||||
def _timesheet_determine_sale_line(self, task, employee, project):
|
||||
""" Deduce the SO line associated to the timesheet line:
|
||||
1/ timesheet on task rate: the so line will be the one from the task
|
||||
2/ timesheet on employee rate task: find the SO line in the map of the project (even for subtask), or fallback on the SO line of the task, or fallback
|
||||
on the one on the project
|
||||
NOTE: this have to be consistent with `_compute_billable_type` on project.task.
|
||||
"""
|
||||
if task.billable_type != 'no':
|
||||
if not project.allow_billable and project.sale_line_id and not task:
|
||||
if project.billable_type == 'employee_rate':
|
||||
map_entry = self.env['project.sale.line.employee.map'].search([('project_id', '=', project.id), ('employee_id', '=', employee.id)])
|
||||
if map_entry:
|
||||
return map_entry.sale_line_id
|
||||
return project.sale_line_id
|
||||
elif task.billable_type != 'no':
|
||||
if task.billable_type == 'employee_rate':
|
||||
map_entry = self.env['project.sale.line.employee.map'].search([('project_id', '=', task.project_id.id), ('employee_id', '=', employee.id)])
|
||||
if map_entry:
|
||||
|
||||
@@ -231,7 +231,7 @@ class TestCommonSaleTimesheet(TestSaleCommon):
|
||||
'invoice_policy': 'delivery',
|
||||
'uom_id': cls.env.ref('uom.product_uom_hour').id,
|
||||
'uom_po_id': cls.env.ref('uom.product_uom_hour').id,
|
||||
'default_code': 'SERV-DELI4',
|
||||
'default_code': 'SERV-DELI5',
|
||||
'service_type': 'timesheet',
|
||||
'service_tracking': 'project_only',
|
||||
'project_template_id': cls.project_template.id,
|
||||
|
||||
@@ -183,10 +183,10 @@ class TestReporting(TestCommonSaleTimesheet):
|
||||
# deliver project should now have cost and something to invoice
|
||||
project_so_1_stat = self.env['project.profitability.report'].read_group([('project_id', 'in', project_so_1.ids)], ['project_id', 'amount_untaxed_to_invoice', 'amount_untaxed_invoiced', 'timesheet_unit_amount', 'timesheet_cost', 'expense_cost', 'expense_amount_untaxed_to_invoice', 'expense_amount_untaxed_invoiced'], ['project_id'])[0]
|
||||
project_so_1_timesheet_cost = timesheet1.amount + timesheet3.amount + timesheet5.amount
|
||||
project_so_1_timesheet_sold_unit = timesheet3.unit_amount + timesheet5.unit_amount
|
||||
project_so_1_timesheet_sold_unit = timesheet1.unit_amount + timesheet3.unit_amount + timesheet5.unit_amount
|
||||
self.assertTrue(float_is_zero(project_so_1_stat['amount_untaxed_invoiced'], precision_rounding=rounding), "The invoiced amount of the project from SO1 should be 0.0")
|
||||
self.assertEqual(float_compare(project_so_1_stat['amount_untaxed_to_invoice'], self.so_line_deliver_project.price_unit * project_so_1_timesheet_sold_unit, precision_rounding=rounding), 0, "The amount to invoice of the project from SO1 should only include timesheet linked to task")
|
||||
self.assertEqual(float_compare(project_so_1_stat['timesheet_unit_amount'], project_so_1_timesheet_sold_unit + timesheet1.unit_amount, precision_rounding=rounding), 0, "The timesheet unit amount of the project from SO1 should include all timesheet in project")
|
||||
self.assertEqual(float_compare(project_so_1_stat['timesheet_unit_amount'], project_so_1_timesheet_sold_unit, precision_rounding=rounding), 0, "The timesheet unit amount of the project from SO1 should include all timesheet in project")
|
||||
self.assertEqual(float_compare(project_so_1_stat['timesheet_cost'], project_so_1_timesheet_cost, precision_rounding=rounding), 0, "The timesheet cost of the project from SO1 should include all timesheet")
|
||||
self.assertTrue(float_is_zero(project_so_1_stat['expense_amount_untaxed_to_invoice'], precision_rounding=rounding), "The expense cost to reinvoice of the project from SO1 should be 0.0")
|
||||
self.assertTrue(float_is_zero(project_so_1_stat['expense_amount_untaxed_invoiced'], precision_rounding=rounding), "The expense invoiced amount of the project from SO1 should be 0.0")
|
||||
@@ -195,10 +195,10 @@ class TestReporting(TestCommonSaleTimesheet):
|
||||
# order project still have something to invoice but has costs now
|
||||
project_so_2_stat = self.env['project.profitability.report'].read_group([('project_id', 'in', project_so_2.ids)], ['project_id', 'amount_untaxed_to_invoice', 'amount_untaxed_invoiced', 'timesheet_unit_amount', 'timesheet_cost', 'expense_cost', 'expense_amount_untaxed_to_invoice', 'expense_amount_untaxed_invoiced'], ['project_id'])[0]
|
||||
project_so_2_timesheet_cost = timesheet2.amount + timesheet4.amount + timesheet6.amount
|
||||
project_so_2_timesheet_sold_unit = timesheet4.unit_amount + timesheet6.unit_amount
|
||||
project_so_2_timesheet_sold_unit = timesheet2.unit_amount + timesheet4.unit_amount + timesheet6.unit_amount
|
||||
self.assertTrue(float_is_zero(project_so_2_stat['amount_untaxed_invoiced'], precision_rounding=rounding), "The invoiced amount of the project from SO2 should be 0.0")
|
||||
self.assertEqual(float_compare(project_so_2_stat['amount_untaxed_to_invoice'], self.so_line_order_project.price_unit * self.so_line_order_project.qty_to_invoice, precision_rounding=rounding), 0, "The amount to invoice should be the one from the SO line, as we are in ordered quantity")
|
||||
self.assertEqual(float_compare(project_so_2_stat['timesheet_unit_amount'], project_so_2_timesheet_sold_unit + timesheet2.unit_amount, precision_rounding=rounding), 0, "The timesheet unit amount of the project from SO2 should include all timesheet")
|
||||
self.assertEqual(float_compare(project_so_2_stat['timesheet_unit_amount'], project_so_2_timesheet_sold_unit, precision_rounding=rounding), 0, "The timesheet unit amount of the project from SO2 should include all timesheet")
|
||||
self.assertEqual(float_compare(project_so_2_stat['timesheet_cost'], project_so_2_timesheet_cost, precision_rounding=rounding), 0, "The timesheet cost of the project from SO2 should include all timesheet")
|
||||
self.assertTrue(float_is_zero(project_so_2_stat['expense_amount_untaxed_to_invoice'], precision_rounding=rounding), "The expense cost to reinvoice of the project from SO2 should be 0.0")
|
||||
self.assertTrue(float_is_zero(project_so_2_stat['expense_amount_untaxed_invoiced'], precision_rounding=rounding), "The expense invoiced amount of the project from SO1 should be 0.0")
|
||||
@@ -238,11 +238,11 @@ class TestReporting(TestCommonSaleTimesheet):
|
||||
# deliver project should now have cost and something invoiced
|
||||
project_so_1_stat = self.env['project.profitability.report'].read_group([('project_id', 'in', project_so_1.ids)], ['project_id', 'amount_untaxed_to_invoice', 'amount_untaxed_invoiced', 'timesheet_unit_amount', 'timesheet_cost', 'expense_cost', 'expense_amount_untaxed_to_invoice', 'expense_amount_untaxed_invoiced'], ['project_id'])[0]
|
||||
project_so_1_timesheet_cost = timesheet1.amount + timesheet3.amount + timesheet5.amount
|
||||
project_so_1_timesheet_sold_unit = timesheet3.unit_amount + timesheet5.unit_amount
|
||||
project_so_1_timesheet_sold_unit = timesheet1.unit_amount + timesheet3.unit_amount + timesheet5.unit_amount
|
||||
|
||||
self.assertEqual(float_compare(project_so_1_stat['amount_untaxed_invoiced'], self.so_line_deliver_project.price_unit * project_so_1_timesheet_sold_unit, precision_rounding=rounding), 0, "The invoiced amount of the project from SO1 should only include timesheet linked to task")
|
||||
self.assertTrue(float_is_zero(project_so_1_stat['amount_untaxed_to_invoice'], precision_rounding=rounding), "The amount to invoice of the project from SO1 should be 0.0")
|
||||
self.assertEqual(float_compare(project_so_1_stat['timesheet_unit_amount'], project_so_1_timesheet_sold_unit + timesheet1.unit_amount, precision_rounding=rounding), 0, "The timesheet unit amount of the project from SO1 should include all timesheet in project")
|
||||
self.assertEqual(float_compare(project_so_1_stat['timesheet_unit_amount'], project_so_1_timesheet_sold_unit, precision_rounding=rounding), 0, "The timesheet unit amount of the project from SO1 should include all timesheet in project")
|
||||
self.assertEqual(float_compare(project_so_1_stat['timesheet_cost'], project_so_1_timesheet_cost, precision_rounding=rounding), 0, "The timesheet cost of the project from SO1 should include all timesheet")
|
||||
self.assertTrue(float_is_zero(project_so_1_stat['expense_amount_untaxed_to_invoice'], precision_rounding=rounding), "The expense cost to reinvoice of the project from SO1 should be 0.0")
|
||||
self.assertTrue(float_is_zero(project_so_1_stat['expense_amount_untaxed_invoiced'], precision_rounding=rounding), "The expense invoiced amount of the project from SO1 should be 0.0")
|
||||
@@ -251,10 +251,10 @@ class TestReporting(TestCommonSaleTimesheet):
|
||||
# order project has still nothing invoiced
|
||||
project_so_2_stat = self.env['project.profitability.report'].read_group([('project_id', 'in', project_so_2.ids)], ['project_id', 'amount_untaxed_to_invoice', 'amount_untaxed_invoiced', 'timesheet_unit_amount', 'timesheet_cost', 'expense_cost', 'expense_amount_untaxed_to_invoice', 'expense_amount_untaxed_invoiced'], ['project_id'])[0]
|
||||
project_so_2_timesheet_cost = timesheet2.amount + timesheet4.amount + timesheet6.amount
|
||||
project_so_2_timesheet_sold_unit = timesheet4.unit_amount + timesheet6.unit_amount
|
||||
project_so_2_timesheet_sold_unit = timesheet2.unit_amount + timesheet4.unit_amount + timesheet6.unit_amount
|
||||
self.assertTrue(float_is_zero(project_so_2_stat['amount_untaxed_invoiced'], precision_rounding=rounding), "The invoiced amount of the project from SO2 should be 0.0")
|
||||
self.assertEqual(float_compare(project_so_2_stat['amount_untaxed_to_invoice'], self.so_line_order_project.price_unit * self.so_line_order_project.qty_to_invoice, precision_rounding=rounding), 0, "The amount to invoice should be the one from the SO line, as we are in ordered quantity")
|
||||
self.assertEqual(float_compare(project_so_2_stat['timesheet_unit_amount'], project_so_2_timesheet_sold_unit + timesheet2.unit_amount, precision_rounding=rounding), 0, "The timesheet unit amount of the project from SO2 should include all timesheet")
|
||||
self.assertEqual(float_compare(project_so_2_stat['timesheet_unit_amount'], project_so_2_timesheet_sold_unit, precision_rounding=rounding), 0, "The timesheet unit amount of the project from SO2 should include all timesheet")
|
||||
self.assertEqual(float_compare(project_so_2_stat['timesheet_cost'], project_so_2_timesheet_cost, precision_rounding=rounding), 0, "The timesheet cost of the project from SO2 should include all timesheet")
|
||||
self.assertTrue(float_is_zero(project_so_2_stat['expense_amount_untaxed_to_invoice'], precision_rounding=rounding), "The expense cost to reinvoice of the project from SO2 should be 0.0")
|
||||
self.assertTrue(float_is_zero(project_so_2_stat['expense_amount_untaxed_invoiced'], precision_rounding=rounding), "The expense invoiced amount of the project from SO1 should be 0.0")
|
||||
@@ -293,10 +293,10 @@ class TestReporting(TestCommonSaleTimesheet):
|
||||
# deliver project should not be impacted by the invoice of the other SO
|
||||
project_so_1_stat = self.env['project.profitability.report'].read_group([('project_id', 'in', project_so_1.ids)], ['project_id', 'amount_untaxed_to_invoice', 'amount_untaxed_invoiced', 'timesheet_unit_amount', 'timesheet_cost', 'expense_cost', 'expense_amount_untaxed_to_invoice', 'expense_amount_untaxed_invoiced'], ['project_id'])[0]
|
||||
project_so_1_timesheet_cost = timesheet1.amount + timesheet3.amount + timesheet5.amount
|
||||
project_so_1_timesheet_sold_unit = timesheet3.unit_amount + timesheet5.unit_amount
|
||||
project_so_1_timesheet_sold_unit = timesheet1.unit_amount + timesheet3.unit_amount + timesheet5.unit_amount
|
||||
self.assertEqual(float_compare(project_so_1_stat['amount_untaxed_invoiced'], self.so_line_deliver_project.price_unit * project_so_1_timesheet_sold_unit, precision_rounding=rounding), 0, "The invoiced amount of the project from SO1 should only include timesheet linked to task")
|
||||
self.assertTrue(float_is_zero(project_so_1_stat['amount_untaxed_to_invoice'], precision_rounding=rounding), "The amount to invoice of the project from SO1 should be 0.0")
|
||||
self.assertEqual(float_compare(project_so_1_stat['timesheet_unit_amount'], project_so_1_timesheet_sold_unit + timesheet1.unit_amount, precision_rounding=rounding), 0, "The timesheet unit amount of the project from SO1 should include all timesheet in project")
|
||||
self.assertEqual(float_compare(project_so_1_stat['timesheet_unit_amount'], project_so_1_timesheet_sold_unit, precision_rounding=rounding), 0, "The timesheet unit amount of the project from SO1 should include all timesheet in project")
|
||||
self.assertEqual(float_compare(project_so_1_stat['timesheet_cost'], project_so_1_timesheet_cost, precision_rounding=rounding), 0, "The timesheet cost of the project from SO1 should include all timesheet")
|
||||
self.assertTrue(float_is_zero(project_so_1_stat['expense_amount_untaxed_to_invoice'], precision_rounding=rounding), "The expense cost to reinvoice of the project from SO1 should be 0.0")
|
||||
self.assertTrue(float_is_zero(project_so_1_stat['expense_amount_untaxed_invoiced'], precision_rounding=rounding), "The expense invoiced amount of the project from SO1 should be 0.0")
|
||||
@@ -305,10 +305,10 @@ class TestReporting(TestCommonSaleTimesheet):
|
||||
# order project is now totally invoiced, as we are in ordered qty
|
||||
project_so_2_stat = self.env['project.profitability.report'].read_group([('project_id', 'in', project_so_2.ids)], ['project_id', 'amount_untaxed_to_invoice', 'amount_untaxed_invoiced', 'timesheet_unit_amount', 'timesheet_cost', 'expense_cost', 'expense_amount_untaxed_to_invoice', 'expense_amount_untaxed_invoiced'], ['project_id'])[0]
|
||||
project_so_2_timesheet_cost = timesheet2.amount + timesheet4.amount + timesheet6.amount
|
||||
project_so_2_timesheet_sold_unit = timesheet4.unit_amount + timesheet6.unit_amount
|
||||
project_so_2_timesheet_sold_unit = timesheet2.unit_amount + timesheet4.unit_amount + timesheet6.unit_amount
|
||||
self.assertEqual(float_compare(project_so_2_stat['amount_untaxed_invoiced'], self.so_line_order_project.price_unit * self.so_line_order_project.product_uom_qty, precision_rounding=rounding), 0, "The invoiced amount should be the one from the SO line, as we are in ordered quantity")
|
||||
self.assertTrue(float_is_zero(project_so_2_stat['amount_untaxed_to_invoice'], precision_rounding=rounding), "The amount to invoice should be the one 0.0, as all ordered quantity is invoiced")
|
||||
self.assertEqual(float_compare(project_so_2_stat['timesheet_unit_amount'], project_so_2_timesheet_sold_unit + timesheet2.unit_amount, precision_rounding=rounding), 0, "The timesheet unit amount of the project from SO2 should include all timesheet")
|
||||
self.assertEqual(float_compare(project_so_2_stat['timesheet_unit_amount'], project_so_2_timesheet_sold_unit, precision_rounding=rounding), 0, "The timesheet unit amount of the project from SO2 should include all timesheet")
|
||||
self.assertEqual(float_compare(project_so_2_stat['timesheet_cost'], project_so_2_timesheet_cost, precision_rounding=rounding), 0, "The timesheet cost of the project from SO2 should include all timesheet")
|
||||
self.assertTrue(float_is_zero(project_so_2_stat['expense_amount_untaxed_to_invoice'], precision_rounding=rounding), "The expense cost to reinvoice of the project from SO2 should be 0.0")
|
||||
self.assertTrue(float_is_zero(project_so_2_stat['expense_amount_untaxed_invoiced'], precision_rounding=rounding), "The expense invoiced amount of the project from SO1 should be 0.0")
|
||||
@@ -364,10 +364,10 @@ class TestReporting(TestCommonSaleTimesheet):
|
||||
# deliver project should now have expense cost, and expense to reinvoice as there is a still open sales order linked to the AA1
|
||||
project_so_1_stat = self.env['project.profitability.report'].read_group([('project_id', 'in', project_so_1.ids)], ['project_id', 'amount_untaxed_to_invoice', 'amount_untaxed_invoiced', 'timesheet_unit_amount', 'timesheet_cost', 'expense_cost', 'expense_amount_untaxed_to_invoice', 'expense_amount_untaxed_invoiced'], ['project_id'])[0]
|
||||
project_so_1_timesheet_cost = timesheet1.amount + timesheet3.amount + timesheet5.amount
|
||||
project_so_1_timesheet_sold_unit = timesheet3.unit_amount + timesheet5.unit_amount
|
||||
project_so_1_timesheet_sold_unit = timesheet1.unit_amount + timesheet3.unit_amount + timesheet5.unit_amount
|
||||
self.assertEqual(float_compare(project_so_1_stat['amount_untaxed_invoiced'], self.so_line_deliver_project.price_unit * project_so_1_timesheet_sold_unit, precision_rounding=rounding), 0, "The invoiced amount of the project from SO1 should only include timesheet linked to task")
|
||||
self.assertTrue(float_is_zero(project_so_1_stat['amount_untaxed_to_invoice'], precision_rounding=rounding), "The amount to invoice of the project from SO1 should be 0.0")
|
||||
self.assertEqual(float_compare(project_so_1_stat['timesheet_unit_amount'], project_so_1_timesheet_sold_unit + timesheet1.unit_amount, precision_rounding=rounding), 0, "The timesheet unit amount of the project from SO1 should include all timesheet in project")
|
||||
self.assertEqual(float_compare(project_so_1_stat['timesheet_unit_amount'], project_so_1_timesheet_sold_unit, precision_rounding=rounding), 0, "The timesheet unit amount of the project from SO1 should include all timesheet in project")
|
||||
self.assertEqual(float_compare(project_so_1_stat['timesheet_cost'], project_so_1_timesheet_cost, precision_rounding=rounding), 0, "The timesheet cost of the project from SO1 should include all timesheet")
|
||||
self.assertEqual(float_compare(project_so_1_stat['expense_amount_untaxed_to_invoice'], -1 * expense1.amount, precision_rounding=rounding), 0, "The expense cost to reinvoice of the project from SO1 should be 0.0")
|
||||
self.assertTrue(float_is_zero(project_so_1_stat['expense_amount_untaxed_invoiced'], precision_rounding=rounding), "The expense invoiced amount of the project from SO1 should be 0.0")
|
||||
@@ -376,10 +376,10 @@ class TestReporting(TestCommonSaleTimesheet):
|
||||
# order project is not impacted by the expenses
|
||||
project_so_2_stat = self.env['project.profitability.report'].read_group([('project_id', 'in', project_so_2.ids)], ['project_id', 'amount_untaxed_to_invoice', 'amount_untaxed_invoiced', 'timesheet_unit_amount', 'timesheet_cost', 'expense_cost', 'expense_amount_untaxed_to_invoice', 'expense_amount_untaxed_invoiced'], ['project_id'])[0]
|
||||
project_so_2_timesheet_cost = timesheet2.amount + timesheet4.amount + timesheet6.amount
|
||||
project_so_2_timesheet_sold_unit = timesheet4.unit_amount + timesheet6.unit_amount
|
||||
project_so_2_timesheet_sold_unit = timesheet2.unit_amount + timesheet4.unit_amount + timesheet6.unit_amount
|
||||
self.assertEqual(float_compare(project_so_2_stat['amount_untaxed_invoiced'], self.so_line_order_project.price_unit * self.so_line_order_project.product_uom_qty, precision_rounding=rounding), 0, "The invoiced amount should be the one from the SO line, as we are in ordered quantity")
|
||||
self.assertTrue(float_is_zero(project_so_2_stat['amount_untaxed_to_invoice'], precision_rounding=rounding), "The amount to invoice should be the one 0.0, as all ordered quantity is invoiced")
|
||||
self.assertEqual(float_compare(project_so_2_stat['timesheet_unit_amount'], project_so_2_timesheet_sold_unit + timesheet2.unit_amount, precision_rounding=rounding), 0, "The timesheet unit amount of the project from SO2 should include all timesheet")
|
||||
self.assertEqual(float_compare(project_so_2_stat['timesheet_unit_amount'], project_so_2_timesheet_sold_unit, precision_rounding=rounding), 0, "The timesheet unit amount of the project from SO2 should include all timesheet")
|
||||
self.assertEqual(float_compare(project_so_2_stat['timesheet_cost'], project_so_2_timesheet_cost, precision_rounding=rounding), 0, "The timesheet cost of the project from SO2 should include all timesheet")
|
||||
self.assertTrue(float_is_zero(project_so_2_stat['expense_amount_untaxed_to_invoice'], precision_rounding=rounding), "The expense cost to reinvoice of the project from SO2 should be 0.0")
|
||||
self.assertTrue(float_is_zero(project_so_2_stat['expense_amount_untaxed_invoiced'], precision_rounding=rounding), "The expense invoiced amount of the project from SO1 should be 0.0")
|
||||
|
||||
@@ -269,7 +269,7 @@ class TestSaleTimesheet(TestCommonSaleTimesheet):
|
||||
'employee_id': self.employee_user.id,
|
||||
})
|
||||
self.assertTrue(float_is_zero(so_line_deliver_only_project.qty_delivered, precision_digits=2), "Timesheeting on project should not incremented the delivered quantity on the SO line")
|
||||
self.assertEqual(sale_order.invoice_status, 'no', 'Sale Timesheet: "invoice on delivery" timesheets should be invoiced completely by now')
|
||||
self.assertEqual(sale_order.invoice_status, 'to invoice', 'Sale Timesheet: "invoice on delivery" timesheets should have quantity to invoice')
|
||||
self.assertEqual(timesheet3.timesheet_invoice_type, 'non_billable_project', "Timesheets without task shoulbe be 'no project found'")
|
||||
self.assertFalse(timesheet3.timesheet_invoice_id, "The timesheet3 should not be linked to the invoice yet")
|
||||
|
||||
|
||||
Reference in New Issue
Block a user