[IMP] sale_timesheet: timesheet_invoice_type computed field
As we plan to remove the timesheet_revenue field, we can make 'timesheet_invoice_type' a computed field, to simplify code and readability. Before, this field was readonly, and set automatically on timesheet creation. To allow searching on it, we keep it stored. Task #1857652
This commit is contained in:
@@ -17,10 +17,26 @@ class AccountAnalyticLine(models.Model):
|
||||
('billable_time', 'Billable Time'),
|
||||
('billable_fixed', 'Billable Fixed'),
|
||||
('non_billable', 'Non Billable'),
|
||||
('non_billable_project', 'No task found')], string="Billable Type", readonly=True, copy=False)
|
||||
('non_billable_project', 'No task found')], string="Billable Type", compute='_compute_timesheet_invoice_type', store=True, readonly=True)
|
||||
timesheet_invoice_id = fields.Many2one('account.invoice', string="Invoice", readonly=True, copy=False, help="Invoice created from the timesheet")
|
||||
timesheet_revenue = fields.Monetary("Revenue", default=0.0, readonly=True, copy=False)
|
||||
|
||||
@api.multi
|
||||
@api.depends('so_line.product_id', 'project_id', 'task_id')
|
||||
def _compute_timesheet_invoice_type(self):
|
||||
for timesheet in self:
|
||||
if timesheet.project_id: # AAL will be set to False
|
||||
invoice_type = 'non_billable_project' if not timesheet.task_id else 'non_billable'
|
||||
if timesheet.task_id and timesheet.so_line.product_id.type == 'service':
|
||||
if timesheet.so_line.product_id.invoice_policy == 'delivery':
|
||||
if timesheet.so_line.product_id.service_type == 'timesheet':
|
||||
invoice_type = 'billable_time'
|
||||
else:
|
||||
invoice_type = 'billable_fixed'
|
||||
elif timesheet.so_line.product_id.invoice_policy == 'order':
|
||||
invoice_type = 'billable_fixed'
|
||||
timesheet.timesheet_invoice_type = invoice_type
|
||||
|
||||
@api.multi
|
||||
def write(self, values):
|
||||
# prevent to update invoiced timesheets if one line is of type delivery
|
||||
@@ -92,7 +108,6 @@ class AccountAnalyticLine(models.Model):
|
||||
so_line = timesheet.so_line
|
||||
values = {
|
||||
'timesheet_revenue': 0.0,
|
||||
'timesheet_invoice_type': 'non_billable_project' if not timesheet.task_id else 'non_billable',
|
||||
}
|
||||
# set the revenue and billable type according to the product and the SO line
|
||||
if timesheet.task_id and so_line.product_id.type == 'service':
|
||||
@@ -106,7 +121,6 @@ class AccountAnalyticLine(models.Model):
|
||||
# calculate the revenue on the timesheet
|
||||
if so_line.product_id.invoice_policy == 'delivery':
|
||||
values['timesheet_revenue'] = analytic_account.currency_id.round(unit_amount * sale_price * (1-(so_line.discount/100)))
|
||||
values['timesheet_invoice_type'] = 'billable_time' if so_line.product_id.service_type == 'timesheet' else 'billable_fixed'
|
||||
elif so_line.product_id.invoice_policy == 'order' and so_line.product_id.service_type == 'timesheet':
|
||||
quantity_hour = unit_amount
|
||||
if so_line.product_uom.category_id == timesheet_uom.category_id:
|
||||
@@ -125,11 +139,8 @@ class AccountAnalyticLine(models.Model):
|
||||
so_line.price_unit, analytic_account.currency_id, so_line.company_id, fields.Date.today()) * (1-so_line.discount)),
|
||||
total_revenue_so - total_revenue_invoiced
|
||||
)
|
||||
values['timesheet_invoice_type'] = 'billable_fixed'
|
||||
# if the so line is already invoiced, and the delivered qty is still smaller than the ordered, then link the timesheet to the invoice
|
||||
if so_line.invoice_status == 'invoiced':
|
||||
values['timesheet_invoice_id'] = so_line.invoice_lines and so_line.invoice_lines[0].invoice_id.id
|
||||
elif so_line.product_id.invoice_policy == 'order' and so_line.product_id.service_type != 'timesheet':
|
||||
values['timesheet_invoice_type'] = 'billable_fixed'
|
||||
|
||||
return values
|
||||
|
||||
@@ -66,7 +66,7 @@ class TestSaleTimesheet(TestCommonSaleTimesheetNoChart):
|
||||
sale_order.action_invoice_create()
|
||||
|
||||
# let's log some timesheets (on the project created by so_line_ordered_project_only)
|
||||
self.env['account.analytic.line'].create({
|
||||
timesheet1 = self.env['account.analytic.line'].create({
|
||||
'name': 'Test Line',
|
||||
'project_id': task_serv2.project_id.id,
|
||||
'task_id': task_serv2.id,
|
||||
@@ -75,8 +75,9 @@ class TestSaleTimesheet(TestCommonSaleTimesheetNoChart):
|
||||
})
|
||||
self.assertEqual(so_line_ordered_global_project.qty_delivered, 10.5, 'Timesheet directly on project does not increase delivered quantity on so line')
|
||||
self.assertEqual(sale_order.invoice_status, 'invoiced', 'Sale Timesheet: "invoice on order" timesheets should not modify the invoice_status of the so')
|
||||
self.assertEqual(timesheet1.timesheet_invoice_type, 'billable_fixed', "Timesheets linked to SO line with ordered product shoulbe be billable fixed")
|
||||
|
||||
self.env['account.analytic.line'].create({
|
||||
timesheet2 = self.env['account.analytic.line'].create({
|
||||
'name': 'Test Line',
|
||||
'project_id': task_serv2.project_id.id,
|
||||
'task_id': task_serv2.id,
|
||||
@@ -85,14 +86,16 @@ class TestSaleTimesheet(TestCommonSaleTimesheetNoChart):
|
||||
})
|
||||
self.assertEqual(so_line_ordered_global_project.qty_delivered, 50, 'Sale Timesheet: timesheet does not increase delivered quantity on so line')
|
||||
self.assertEqual(sale_order.invoice_status, 'invoiced', 'Sale Timesheet: "invoice on order" timesheets should not modify the invoice_status of the so')
|
||||
self.assertEqual(timesheet2.timesheet_invoice_type, 'billable_fixed', "Timesheets linked to SO line with ordered product shoulbe be billable fixed")
|
||||
|
||||
self.env['account.analytic.line'].create({
|
||||
timesheet3 = self.env['account.analytic.line'].create({
|
||||
'name': 'Test Line',
|
||||
'project_id': task_serv2.project_id.id,
|
||||
'unit_amount': 10,
|
||||
'employee_id': self.employee_user.id,
|
||||
})
|
||||
self.assertEqual(so_line_ordered_project_only.qty_delivered, 0.0, 'Timesheet directly on project does not increase delivered quantity on so line')
|
||||
self.assertEqual(timesheet3.timesheet_invoice_type, 'non_billable_project', "Timesheets without task shoulbe be 'no project found'")
|
||||
|
||||
# log timesheet on task in global project (higher than the initial ordrered qty)
|
||||
self.env['account.analytic.line'].create({
|
||||
@@ -175,7 +178,7 @@ class TestSaleTimesheet(TestCommonSaleTimesheetNoChart):
|
||||
self.assertEqual(sale_order.analytic_account_id, project_serv2.analytic_account_id, "The created project should be linked to the analytic account of the SO")
|
||||
|
||||
# let's log some timesheets
|
||||
self.env['account.analytic.line'].create({
|
||||
timesheet1 = self.env['account.analytic.line'].create({
|
||||
'name': 'Test Line',
|
||||
'project_id': task_serv1.project_id.id, # global project
|
||||
'task_id': task_serv1.id,
|
||||
@@ -185,6 +188,7 @@ class TestSaleTimesheet(TestCommonSaleTimesheetNoChart):
|
||||
self.assertEqual(so_line_deliver_global_project.invoice_status, 'to invoice', 'Sale Timesheet: "invoice on delivery" timesheets should set the so line in "to invoice" status when logged')
|
||||
self.assertEqual(so_line_deliver_task_project.invoice_status, 'no', 'Sale Timesheet: so line invoice status should not change when no timesheet linked to the line')
|
||||
self.assertEqual(sale_order.invoice_status, 'to invoice', 'Sale Timesheet: "invoice on delivery" timesheets should set the so in "to invoice" status when logged')
|
||||
self.assertEqual(timesheet1.timesheet_invoice_type, 'billable_time', "Timesheets linked to SO line with delivered product shoulbe be billable time")
|
||||
|
||||
# invoice SO
|
||||
invoice_id = sale_order.action_invoice_create()
|
||||
@@ -192,7 +196,7 @@ class TestSaleTimesheet(TestCommonSaleTimesheetNoChart):
|
||||
self.assertTrue(float_is_zero(invoice.amount_total - so_line_deliver_global_project.price_unit * 10.5, precision_digits=2), 'Sale: invoice generation on timesheets product is wrong')
|
||||
|
||||
# log some timesheets again
|
||||
self.env['account.analytic.line'].create({
|
||||
timesheet2 = self.env['account.analytic.line'].create({
|
||||
'name': 'Test Line',
|
||||
'project_id': task_serv1.project_id.id, # global project
|
||||
'task_id': task_serv1.id,
|
||||
@@ -202,6 +206,7 @@ class TestSaleTimesheet(TestCommonSaleTimesheetNoChart):
|
||||
self.assertEqual(so_line_deliver_global_project.invoice_status, 'to invoice', 'Sale Timesheet: "invoice on delivery" timesheets should set the so line in "to invoice" status when logged')
|
||||
self.assertEqual(so_line_deliver_task_project.invoice_status, 'no', 'Sale Timesheet: so line invoice status should not change when no timesheet linked to the line')
|
||||
self.assertEqual(sale_order.invoice_status, 'to invoice', 'Sale Timesheet: "invoice on delivery" timesheets should not modify the invoice_status of the so')
|
||||
self.assertEqual(timesheet2.timesheet_invoice_type, 'billable_time', "Timesheets linked to SO line with delivered product shoulbe be billable time")
|
||||
|
||||
# create a second invoice
|
||||
sale_order.action_invoice_create()
|
||||
@@ -220,7 +225,7 @@ class TestSaleTimesheet(TestCommonSaleTimesheetNoChart):
|
||||
self.assertEqual(len(sale_order.project_ids), 2, "No new project should have been created by the SO, when selling 'project only' product, since it reuse the one from 'new task in new project'.")
|
||||
|
||||
# let's log some timesheets on the project
|
||||
self.env['account.analytic.line'].create({
|
||||
timesheet3 = self.env['account.analytic.line'].create({
|
||||
'name': 'Test Line',
|
||||
'project_id': project_serv2.id, # global project
|
||||
'unit_amount': 7,
|
||||
@@ -228,6 +233,7 @@ class TestSaleTimesheet(TestCommonSaleTimesheetNoChart):
|
||||
})
|
||||
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(timesheet3.timesheet_invoice_type, 'non_billable_project', "Timesheets without task shoulbe be 'no project found'")
|
||||
|
||||
def test_timesheet_manual(self):
|
||||
""" Test timesheet invoicing with 'invoice on delivery' timetracked products
|
||||
@@ -288,3 +294,6 @@ class TestSaleTimesheet(TestCommonSaleTimesheetNoChart):
|
||||
self.assertEqual(so_line_manual_global_project.qty_to_invoice, 0.0, "Manual service should not be affected by timesheet on their created task.")
|
||||
self.assertEqual(so_line_manual_only_project.qty_to_invoice, 0.0, "Manual service should not be affected by timesheet on their created project.")
|
||||
self.assertEqual(sale_order.invoice_status, 'no', 'Sale Timesheet: "invoice on delivery" should not need to be invoiced on so confirmation')
|
||||
|
||||
self.assertEqual(timesheet1.timesheet_invoice_type, 'billable_fixed', "Timesheets linked to SO line with ordered product shoulbe be billable fixed since it is a milestone")
|
||||
self.assertEqual(timesheet2.timesheet_invoice_type, 'non_billable_project', "Timesheets without task shoulbe be 'no project found'")
|
||||
|
||||
Reference in New Issue
Block a user