Coverage for backend/django/Economics/costing/costable_items/deleted_objects.py: 100%
28 statements
« prev ^ index » next coverage.py v7.10.7, created at 2026-06-23 21:51 +0000
« prev ^ index » next coverage.py v7.10.7, created at 2026-06-23 21:51 +0000
1from __future__ import annotations
3from collections.abc import Iterable
5from django.db.models import Q
7from Economics.costing.models import CapitalCostLine, OperatingCostLine
9from Economics.studies.models import EconomicsStudy
10from Economics.formulas.native_properties.sync import sync_economics_native_properties_for_study
11from Economics.results.services.lifecycle.runs import mark_result_runs_stale_for_study
14def delete_economics_lines_for_simulation_objects(simulation_object_ids: Iterable[int]) -> int:
15 """Delete study cost lines backed by deleted streams or unit operations.
17 Simulation objects are soft-deleted, so ordinary FK cascade behaviour does not
18 remove economics lines whose source property or costable item belongs to a
19 deleted object. This cleanup keeps studies from retaining orphaned cost rows.
20 """
22 object_ids = {object_id for object_id in simulation_object_ids if object_id is not None}
23 if not object_ids:
24 return 0
26 operating_lines = OperatingCostLine.objects.filter(
27 Q(source_property_info__set__simulationObject_id__in=object_ids)
28 | Q(costable_item__simulation_object_id__in=object_ids)
29 )
30 capital_lines = CapitalCostLine.objects.filter(costable_item__simulation_object_id__in=object_ids)
31 affected_study_ids = set(operating_lines.values_list("study_id", flat=True))
32 affected_study_ids.update(capital_lines.values_list("study_id", flat=True))
33 deleted_operating_count, _ = operating_lines.delete()
34 deleted_capital_count, _ = capital_lines.delete()
35 deleted_count = deleted_operating_count + deleted_capital_count
36 if deleted_count:
37 for study in EconomicsStudy.objects.filter(pk__in=affected_study_ids):
38 sync_economics_native_properties_for_study(study)
39 mark_result_runs_stale_for_study(
40 study=study,
41 reason="flowsheet_simulation_object_deleted",
42 requires_solve=True,
43 )
44 return deleted_count
47def delete_economics_lines_for_deleted_simulation_objects(study: EconomicsStudy) -> int:
48 """Delete study cost lines that still point at already soft-deleted objects."""
50 deleted_object_ids = set(
51 OperatingCostLine.objects.filter(
52 study=study,
53 source_property_info__set__simulationObject__is_deleted=True,
54 ).values_list("source_property_info__set__simulationObject_id", flat=True)
55 )
56 deleted_object_ids.update(
57 OperatingCostLine.objects.filter(
58 study=study,
59 costable_item__simulation_object__is_deleted=True,
60 ).values_list("costable_item__simulation_object_id", flat=True)
61 )
62 deleted_object_ids.update(
63 CapitalCostLine.objects.filter(
64 study=study,
65 costable_item__simulation_object__is_deleted=True,
66 ).values_list("costable_item__simulation_object_id", flat=True)
67 )
68 return delete_economics_lines_for_simulation_objects(deleted_object_ids)