Skip to content
This repository was archived by the owner on Feb 16, 2026. It is now read-only.

Commit 60e358f

Browse files
committed
[MIG] project_forecast_line: Migration to 16.0
1 parent 412be4a commit 60e358f

8 files changed

Lines changed: 38 additions & 36 deletions

File tree

project_forecast_line/README.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ Project Forecast Lines
77
!! This file is generated by oca-gen-addon-readme !!
88
!! changes will be overwritten. !!
99
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
10-
!! source digest: sha256:b255437243142fe16938f1ea597859a0e0f286aa510e66d4e0a5646b7228478a
10+
!! source digest: sha256:f6a3feb1d9b6593b5322c73cc13b475f38f01898496e691b2b2efc14fe320404
1111
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1212
1313
.. |badge1| image:: https://img.shields.io/badge/maturity-Alpha-red.png

project_forecast_line/__manifest__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
{
44
"name": "Project Forecast Lines",
55
"summary": "Project Forecast Lines",
6-
"version": "15.0.1.3.3",
6+
"version": "16.0.1.0.0",
77
"author": "Camptocamp SA, Odoo Community Association (OCA)",
88
"license": "AGPL-3",
99
"category": "Project",

project_forecast_line/models/forecast_line_mixin.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ class ForecastLineModelMixin(models.Model):
99

1010
def _get_forecast_lines(self, domain=None):
1111
self.ensure_one()
12-
base_domain = [("res.model", "=", self._name), ("res_id", "=", self.id)]
12+
base_domain = [("res_model", "=", self._name), ("res_id", "=", self.id)]
1313
if domain is not None:
1414
base_domain += domain
1515
return self.env["forecast.line"].search(base_domain)

project_forecast_line/models/hr_employee.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,7 @@ def _update_forecast_lines(self):
156156
date_from=date_start,
157157
date_to=date_end,
158158
forecast_hours=forecast * rec.rate / 100.0,
159-
unit_cost=rec.employee_id.timesheet_cost, # XXX to check
159+
unit_cost=rec.employee_id.hourly_cost, # XXX to check
160160
ttype="confirmed",
161161
forecast_role_id=rec.role_id.id,
162162
employee_id=rec.employee_id.id,

project_forecast_line/models/hr_leave.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ def _update_forecast_lines(self):
5858
forecast_hours=ForecastLine.convert_days_to_hours(
5959
-1 * leave.number_of_days
6060
),
61-
unit_cost=leave.employee_id.timesheet_cost,
61+
unit_cost=leave.employee_id.hourly_cost,
6262
forecast_role_id=leave.employee_id.main_role_id.id,
6363
hr_leave_id=leave.id,
6464
employee_id=leave.employee_id.id,

project_forecast_line/models/sale_order_line.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -95,8 +95,8 @@ def _write(self, values):
9595
return res
9696

9797
@api.onchange("product_id")
98-
def product_id_change(self):
99-
res = super().product_id_change()
98+
def _onchange_product_id_warning(self):
99+
res = super()._onchange_product_id_warning()
100100
for line in self:
101101
if not line.product_id.forecast_role_id:
102102
line.forecast_date_start = False

project_forecast_line/static/description/index.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -367,7 +367,7 @@ <h1 class="title">Project Forecast Lines</h1>
367367
!! This file is generated by oca-gen-addon-readme !!
368368
!! changes will be overwritten. !!
369369
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
370-
!! source digest: sha256:b255437243142fe16938f1ea597859a0e0f286aa510e66d4e0a5646b7228478a
370+
!! source digest: sha256:f6a3feb1d9b6593b5322c73cc13b475f38f01898496e691b2b2efc14fe320404
371371
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
372372
<p><a class="reference external image-reference" href="https://odoo-community.org/page/development-status"><img alt="Alpha" src="https://img.shields.io/badge/maturity-Alpha-red.png" /></a> <a class="reference external image-reference" href="http://www.gnu.org/licenses/agpl-3.0-standalone.html"><img alt="License: AGPL-3" src="https://img.shields.io/badge/licence-AGPL--3-blue.png" /></a> <a class="reference external image-reference" href="https://github.com/OCA/project/tree/16.0/project_forecast_line"><img alt="OCA/project" src="https://img.shields.io/badge/github-OCA%2Fproject-lightgray.png?logo=github" /></a> <a class="reference external image-reference" href="https://translation.odoo-community.org/projects/project-16-0/project-16-0-project_forecast_line"><img alt="Translate me on Weblate" src="https://img.shields.io/badge/weblate-Translate%20me-F47D42.png" /></a> <a class="reference external image-reference" href="https://runboat.odoo-community.org/builds?repo=OCA/project&amp;target_branch=16.0"><img alt="Try me on Runboat" src="https://img.shields.io/badge/runboat-Try%20me-875A7B.png" /></a></p>
373373
<p>This module allows to plan your resources using forecast lines.</p>

project_forecast_line/tests/test_forecast_line.py

Lines changed: 30 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ def setUpClass(cls):
7373
"name": "development time and material",
7474
"detailed_type": "service",
7575
"service_tracking": "task_in_project",
76-
"price": 95,
76+
"price_extra": 95,
7777
"standard_price": 75,
7878
"forecast_role_id": cls.role_developer.id,
7979
"uom_id": cls.env.ref("uom.product_uom_hour").id,
@@ -85,7 +85,7 @@ def setUpClass(cls):
8585
"name": "consultant time and material",
8686
"detailed_type": "service",
8787
"service_tracking": "task_in_project",
88-
"price": 100,
88+
"price_extra": 100,
8989
"standard_price": 80,
9090
"forecast_role_id": cls.role_consultant.id,
9191
"uom_id": cls.env.ref("uom.product_uom_hour").id,
@@ -98,7 +98,7 @@ def setUpClass(cls):
9898
"name": "pm time and material",
9999
"detailed_type": "service",
100100
"service_tracking": "task_in_project",
101-
"price": 120,
101+
"price_extra": 120,
102102
"standard_price": 100,
103103
"forecast_role_id": cls.role_consultant.id,
104104
"uom_id": cls.env.ref("uom.product_uom_hour").id,
@@ -187,7 +187,7 @@ def test_employee_forecast_change_roles(self):
187187
# employee becomes 50% consultant, 50% PM on Feb 1st
188188
roles = self.employee_consultant.role_ids
189189
roles.write({"date_end": "2022-01-31"})
190-
self.env["base"].flush()
190+
self.env["base"].flush_model()
191191
lines = self.env["forecast.line"].search(
192192
[
193193
("employee_id", "=", self.employee_consultant.id),
@@ -215,7 +215,7 @@ def test_employee_forecast_change_roles(self):
215215
},
216216
]
217217
)
218-
self.env["base"].flush()
218+
self.env["base"].flush_model()
219219
lines = self.env["forecast.line"].search(
220220
[
221221
("employee_id", "=", self.employee_consultant.id),
@@ -254,7 +254,7 @@ def test_forecast_with_calendar(self):
254254
"time_type": "leave",
255255
}
256256
)
257-
self.env["base"].flush()
257+
self.env["base"].flush_model()
258258
lines = self.env["forecast.line"].search(
259259
[
260260
("employee_id", "=", self.employee_dev.id),
@@ -282,20 +282,21 @@ def _create_sale(
282282
):
283283
with Form(self.env["sale.order"]) as form:
284284
form.partner_id = self.customer
285-
form.date_order = "2022-01-10 08:00:00"
286-
form.default_forecast_date_start = default_forecast_date_start
287-
form.default_forecast_date_end = default_forecast_date_end
288285
with form.order_line.new() as line:
289286
line.product_id = self.product_dev_tm
290287
line.product_uom_qty = uom_qty # 1 FTE sold
291288
line.product_uom = self.env.ref("uom.product_uom_day")
292289
so = form.save()
290+
so.date_order = "2022-01-10 08:00:00"
291+
so.default_forecast_date_start = default_forecast_date_start
292+
so.default_forecast_date_end = default_forecast_date_end
293293
return so
294294

295295
@freeze_time("2022-01-01")
296296
def test_draft_sale_order_creates_negative_forecast_forecast(self):
297297
so = self._create_sale("2022-02-07", "2022-02-20")
298298
line = so.order_line[0]
299+
line._onchange_product_id_warning()
299300
self.assertEqual(line.forecast_date_start, date(2022, 2, 7))
300301
self.assertEqual(line.forecast_date_end, date(2022, 2, 20))
301302
forecast_lines = self.env["forecast.line"].search(
@@ -333,6 +334,7 @@ def test_draft_sale_order_without_dates_no_forecast(self):
333334
"""a draft sale order with no dates on the line does not create forecast"""
334335
so = self._create_sale("2022-02-07", False)
335336
line = so.order_line[0]
337+
line._onchange_product_id_warning()
336338
self.assertEqual(line.forecast_date_start, date(2022, 2, 7))
337339
self.assertEqual(line.forecast_date_end, False)
338340
forecast_lines = self.env["forecast.line"].search(
@@ -348,6 +350,7 @@ def test_draft_sale_order_forecast_spread(self):
348350
so = self._create_sale("2022-02-07", "2022-04-17", uom_qty=100)
349351

350352
line = so.order_line[0]
353+
line._onchange_product_id_warning()
351354
self.assertEqual(line.forecast_date_start, date(2022, 2, 7))
352355
self.assertEqual(line.forecast_date_end, date(2022, 4, 17))
353356
forecast_lines = self.env["forecast.line"].search(
@@ -424,16 +427,16 @@ def test_timesheet_forecast_lines(self):
424427
with freeze_time("2022-01-01"):
425428
with Form(self.env["sale.order"]) as form:
426429
form.partner_id = self.customer
427-
form.date_order = "2022-01-10 08:00:00"
428-
form.default_forecast_date_start = "2022-02-14"
429-
form.default_forecast_date_end = "2022-04-17"
430430
with form.order_line.new() as line:
431431
line.product_id = self.product_dev_tm
432432
line.product_uom_qty = (
433433
45 * 2
434434
) # 45 working days in the period, sell 2 FTE
435435
line.product_uom = self.env.ref("uom.product_uom_day")
436436
so = form.save()
437+
so.date_order = "2022-01-10 08:00:00"
438+
so.default_forecast_date_start = "2022-02-14"
439+
so.default_forecast_date_end = "2022-04-17"
437440
so.action_confirm()
438441

439442
with freeze_time("2022-02-14"):
@@ -448,7 +451,7 @@ def test_timesheet_forecast_lines(self):
448451
"unit_amount": 8,
449452
}
450453
)
451-
task.flush()
454+
task.flush_recordset()
452455
forecast_lines = self.env["forecast.line"].search(
453456
[("res_id", "=", task.id), ("res_model", "=", "project.task")]
454457
)
@@ -538,7 +541,7 @@ def setUpClass(cls):
538541
# flush needed here to trigger the recomputation with the correct
539542
# frozen time (otherwise it is called by the test runner before the
540543
# tests, outside of the context manager.
541-
cls.task.flush()
544+
cls.task.flush_recordset()
542545

543546
@freeze_time("2022-02-01 12:00:00")
544547
def test_task_unlink(self):
@@ -557,7 +560,7 @@ def test_task_forecast_line_reschedule_employee(self):
557560
)
558561
self.assertEqual(task_forecast.mapped("employee_id"), self.employee_consultant)
559562
self.task.user_ids = self.user_pm
560-
self.task.flush()
563+
self.task.flush_recordset()
561564
task_forecast_after = self.env["forecast.line"].search(
562565
[("task_id", "=", self.task.id)]
563566
)
@@ -578,7 +581,7 @@ def test_task_forecast_line_reschedule_dates(self):
578581
"forecast_date_planned_end": "2022-02-16",
579582
}
580583
)
581-
self.task.flush()
584+
self.task.flush_recordset()
582585
task_forecast_after = self.env["forecast.line"].search(
583586
[("task_id", "=", self.task.id)]
584587
)
@@ -595,13 +598,13 @@ def test_task_forecast_line_reschedule_dates(self):
595598
def test_task_forecast_line_reschedule_time(self):
596599
"""changing the remaining time will keep the forecast lines"""
597600
self.task.user_ids = self.user_consultant
598-
self.task.flush()
601+
self.task.flush_recordset()
599602
task_forecast = self.env["forecast.line"].search(
600603
[("task_id", "=", self.task.id)]
601604
)
602605
self.assertEqual(task_forecast.mapped("forecast_hours"), [-8, -8])
603606
self.task.write({"planned_hours": 24})
604-
self.task.flush()
607+
self.task.flush_recordset()
605608
task_forecast_after = self.env["forecast.line"].search(
606609
[("task_id", "=", self.task.id)]
607610
)
@@ -612,13 +615,13 @@ def test_task_forecast_line_reschedule_time(self):
612615
def test_task_forecast_line_reschedule_time_no_employee(self):
613616
"""changing the remaining time will keep the forecast lines, even when no
614617
employee assigned"""
615-
self.task.flush()
618+
self.task.flush_recordset()
616619
task_forecast = self.env["forecast.line"].search(
617620
[("task_id", "=", self.task.id)]
618621
)
619622
self.assertEqual(task_forecast.mapped("forecast_hours"), [-8, -8])
620623
self.task.write({"planned_hours": 24})
621-
self.task.flush()
624+
self.task.flush_recordset()
622625
task_forecast_after = self.env["forecast.line"].search(
623626
[("task_id", "=", self.task.id)]
624627
)
@@ -682,7 +685,7 @@ def test_task_forecast_lines_consolidated_forecast(self):
682685
project_1 = ProjectProject.create({"name": "TestProject1"})
683686
# set project in stage "to do" to get forecast
684687
project_1.stage_id = self.env.ref("project.project_project_stage_0")
685-
project_1.flush()
688+
project_1.flush_recordset()
686689
task_values = {
687690
"project_id": project_1.id,
688691
"forecast_role_id": self.role_consultant.id,
@@ -696,11 +699,10 @@ def test_task_forecast_lines_consolidated_forecast(self):
696699
task_values.update({"name": "Task2"})
697700
task_2 = ProjectTask.create(task_values)
698701
task_2.user_ids = self.user_consultant
699-
700702
# Project 2 is in stage "in rogress" to get forecast
701703
project_2 = ProjectProject.create({"name": "TestProject2"})
702704
project_2.stage_id = self.env.ref("project.project_project_stage_1")
703-
project_2.flush()
705+
project_2.flush_recordset()
704706
task_values.update({"project_id": project_2.id, "name": "Task3"})
705707
task_3 = ProjectTask.create(task_values)
706708
task_3.user_ids = self.user_consultant
@@ -768,7 +770,7 @@ def test_forecast_with_holidays(self):
768770
# create new ones -> we check that the project task lines are
769771
# automatically related to the new newly created employee role lines.
770772
leave_request.action_validate()
771-
leave_request.flush()
773+
leave_request.flush_recordset()
772774
forecast_lines = self.env["forecast.line"].search(
773775
[
774776
("employee_id", "=", self.employee_consultant.id),
@@ -803,7 +805,7 @@ def test_task_forecast_lines_consolidated_forecast_overallocation(self):
803805
project = ProjectProject.create({"name": "TestProject"})
804806
# set project in stage "in progress" to get confirmed forecast
805807
project.stage_id = self.env.ref("project.project_project_stage_1")
806-
project.flush()
808+
project.flush_recordset()
807809
task = ProjectTask.create(
808810
{
809811
"name": "Task1",
@@ -847,7 +849,7 @@ def test_task_forecast_lines_consolidated_forecast_overallocation_multiple_tasks
847849
project = ProjectProject.create({"name": "TestProject"})
848850
# set project in stage "in progress" to get confirmed forecast
849851
project.stage_id = self.env.ref("project.project_project_stage_1")
850-
project.flush()
852+
project.flush_recordset()
851853
task1 = ProjectTask.create(
852854
{
853855
"name": "Task1",
@@ -929,7 +931,7 @@ def test_task_forecast_lines_employee_different_roles(self):
929931
project = ProjectProject.create({"name": "TestProjectDiffRoles"})
930932
# set project in stage "in progress" to get confirmed forecast
931933
project.stage_id = self.env.ref("project.project_project_stage_1")
932-
project.flush()
934+
project.flush_recordset()
933935
task = ProjectTask.create(
934936
{
935937
"name": "TaskDiffRoles",
@@ -998,7 +1000,7 @@ def test_task_forecast_lines_employee_main_role(self):
9981000
project = ProjectProject.create({"name": "TestProjectDiffRoles"})
9991001
# set project in stage "in progress" to get confirmed forecast
10001002
project.stage_id = self.env.ref("project.project_project_stage_1")
1001-
project.flush()
1003+
project.flush_recordset()
10021004
task = ProjectTask.create(
10031005
{
10041006
"name": "TaskDiffRoles",

0 commit comments

Comments
 (0)