Skip to content

Commit efb7753

Browse files
ft-close-incident-reports: update incident report transitional state
- update model signals - Add unit tests for the updated signals [Delivers #164101493]
1 parent 4cfb72e commit efb7753

File tree

4 files changed

+176
-171
lines changed

4 files changed

+176
-171
lines changed

core/models/asset.py

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -554,11 +554,17 @@ def save(self, *args, **kwargs):
554554
def _create_asset_status_when_asset_is_allocated(self):
555555
last_status = AssetStatus.objects.filter(asset=self.asset).latest("created_at")
556556
if self.current_assignee:
557-
AssetStatus.objects.create(
558-
asset=self.asset,
559-
current_status=constants.ALLOCATED,
560-
previous_status=last_status.current_status,
561-
)
557+
# if Asset status for an asset exits update it else create it
558+
try:
559+
asset_status = AssetStatus.objects.get(asset=self.asset)
560+
asset_status.current_status = constants.ALLOCATED
561+
asset_status.save()
562+
except AssetStatus.DoesNotExist:
563+
AssetStatus.objects.create(
564+
asset=self.asset,
565+
current_status=constants.ALLOCATED,
566+
previous_status=last_status.current_status,
567+
)
562568

563569
def _send_notification(self):
564570
asset = self.asset
@@ -673,7 +679,6 @@ class Meta:
673679
verbose_name_plural = "State Transitions"
674680

675681
def save(self, *args, **kwargs):
676-
677682
# admin updates asset_status if damaged
678683
if self.asset_state_from_report == "Damaged":
679684
AssetStatus.objects.create(

core/signals.py

Lines changed: 52 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,65 +1,66 @@
11
# Third-Party Imports
2-
from django.db.models.signals import post_save
2+
from django.db.models.signals import pre_save
33
from django.dispatch import receiver
44

55
# App Imports
66
from core import constants
7-
from core.models import AssetIncidentReport, StateTransition
7+
from core.models import AssetIncidentReport, AssetStatus, StateTransition
88

9-
# TODO add functionality to close incident report when asset statuses are changed. story clarification being awaited
10-
# @receiver(post_save, sender=AssetStatus)
11-
# def close_incident_report(**kwargs):
12-
# """
13-
# close an incident report if asset status is updated to [`damaged`,`lost`,'available]
14-
# :param kwargs:
15-
# :return:
16-
# """
17-
# instance = kwargs.get("instance")
18-
# current_asset_status = instance.current_status
19-
#
20-
# valid_asset_states = [constants.DAMAGED, constants.LOST, constants.AVAILABLE]
21-
#
22-
# if current_asset_status in valid_asset_states:
23-
# if current_asset_status == constants.AVAILABLE:
24-
# # if an incident report already exists, update the transitional state to closed
25-
# try:
26-
# incident_report = AssetIncidentReport.objects.get(asset=instance.asset)
27-
# transition_state = StateTransition.objects.get_or_create(asset_incident_report=incident_report)
28-
# transition_state.incident_report_state = constants.CLOSED
29-
# transition_state.save()
30-
# except AssetIncidentReport.DoesNotExist:
31-
# pass
32-
# else:
33-
# # incident_report =
34-
# pass
359

36-
37-
@receiver(post_save, sender=AssetIncidentReport)
10+
@receiver(pre_save, sender=AssetStatus)
3811
def update_transition_state(**kwargs):
3912
"""
40-
update the transition state when an Asset incident report is filed
13+
update the transition state to CLOSED when an Asset status is changed from (DAMAGED,LOST) to
14+
either (AVAILABLE,ALLOCATED)
4115
:param kwargs:
4216
:return None:
4317
"""
4418
instance = kwargs.get("instance") # the instance of the incident report being saved
45-
current_incident_type = instance.incident_type
46-
transition_state = StateTransition.objects.get_or_create(
47-
asset_incident_report=instance
48-
)
49-
transition_state = transition_state[0]
50-
current_transition_state = transition_state.incident_report_state
19+
valid_previous_asset_types = (constants.DAMAGED, constants.LOST)
20+
valid_current_asset_types = (constants.AVAILABLE, constants.ALLOCATED)
21+
22+
try:
23+
previous_instance = AssetStatus.objects.get(id=instance.id)
24+
current_asset_status = instance.current_status
25+
previous_asset_status = previous_instance.current_status
26+
27+
if previous_asset_status in valid_previous_asset_types:
28+
if current_asset_status in valid_current_asset_types:
29+
# get the latest incident report
30+
# filter out all results that dont have a recorded created_at date
31+
latest_incident_report = AssetIncidentReport.objects.filter(
32+
asset=instance.asset, created_at__isnull=False
33+
).latest('created_at')
34+
# get the transition state associated with the incident report
35+
transition_state = StateTransition.objects.get_or_create(
36+
asset_incident_report=latest_incident_report
37+
)
38+
transition_state = transition_state[0]
39+
# update the transition state
40+
transition_state.incident_report_state = constants.CLOSED
41+
transition_state.save()
42+
43+
except AssetStatus.DoesNotExist:
44+
pass
5145

52-
# tuples of states for which the logic of this signal is triggered
53-
valid_incident_types = (constants.LOSS, constants.DAMAGE)
54-
valid_transition_states = (
55-
constants.INTERNAL_ASSESSMENT,
56-
constants.EXTERNAL_ASSESSMENT,
57-
constants.OUT_FOR_REPAIR,
58-
)
59-
# if the current incident type and transition state are valid, update the state
60-
if (
61-
current_incident_type in valid_incident_types
62-
and current_transition_state in valid_transition_states
63-
):
64-
transition_state.incident_report_state = constants.CLOSED
65-
transition_state.save()
46+
# current_incident_type = instance.incident_type
47+
# transition_state = StateTransition.objects.get_or_create(
48+
# asset_incident_report=instance
49+
# )
50+
# transition_state = transition_state[0]
51+
# current_transition_state = transition_state.incident_report_state
52+
#
53+
# # tuples of states for which the logic of this signal is triggered
54+
# valid_incident_types = (constants.LOSS, constants.DAMAGE)
55+
# valid_transition_states = (
56+
# constants.INTERNAL_ASSESSMENT,
57+
# constants.EXTERNAL_ASSESSMENT,
58+
# constants.OUT_FOR_REPAIR,
59+
# )
60+
# # if the current incident type and transition state are valid, update the state
61+
# if (
62+
# current_incident_type in valid_incident_types
63+
# and current_transition_state in valid_transition_states
64+
# ):
65+
# transition_state.incident_report_state = constants.CLOSED
66+
# transition_state.save()

core/tests/test_model_signals.py

Lines changed: 0 additions & 112 deletions
This file was deleted.

core/tests/test_state_transition_model.py

Lines changed: 113 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,17 @@
11
# App Imports
2-
from core.models import AssetIncidentReport, StateTransition
2+
from core import constants
3+
from core.models import (
4+
AllocationHistory,
5+
Asset,
6+
AssetIncidentReport,
7+
AssetStatus,
8+
StateTransition,
9+
)
310
from core.tests import CoreBaseTestCase
411

512

613
class StateTransitionModelTest(CoreBaseTestCase):
7-
"Tests state trensition model"
14+
"""Tests state transition model"""
815

916
def test_create_incident_report_creates_new_state_transition(self):
1017
incident_report_count = AssetIncidentReport.objects.count()
@@ -22,3 +29,107 @@ def test_create_incident_report_creates_new_state_transition(self):
2229
)
2330
self.assertEqual(AssetIncidentReport.objects.count(), incident_report_count + 1)
2431
self.assertEqual(StateTransition.objects.count(), state_transition_count + 1)
32+
33+
34+
class TestTransitionStateUpdateFromAssetStatusModification(CoreBaseTestCase):
35+
"""
36+
Test updating of transition state when Asset status is modified
37+
update the transition state to CLOSED when an Asset status is changed from (DAMAGED,LOST) to
38+
either (AVAILABLE,ALLOCATED)
39+
"""
40+
41+
def setUp(self):
42+
self.asset = Asset.objects.create(
43+
asset_code="IC0019009",
44+
serial_number="SN001000098",
45+
model_number=self.test_assetmodel,
46+
purchase_date="2019-07-10",
47+
)
48+
self.incident_report = AssetIncidentReport.objects.create(asset=self.asset)
49+
self.transition_state = StateTransition.objects.get_or_create(
50+
asset_incident_report_id=self.incident_report.id
51+
)
52+
self.transition_state = self.transition_state[0]
53+
54+
def test_update_asset_status_from_lost_to_available(self):
55+
status = AssetStatus.objects.get(asset=self.asset)
56+
# update status to lost
57+
status.current_status = constants.LOST
58+
status.save()
59+
# update status to available
60+
status = AssetStatus.objects.get(asset=self.asset)
61+
status.current_status = constants.AVAILABLE
62+
status.save()
63+
# verify that transition state has been updated to closed
64+
transition_state = StateTransition.objects.get(
65+
asset_incident_report=self.incident_report
66+
)
67+
self.assertEqual(transition_state.incident_report_state, constants.CLOSED)
68+
69+
def test_update_asset_status_from_damaged_to_available(self):
70+
status = AssetStatus.objects.get(asset=self.asset)
71+
72+
# update status to lost
73+
status.current_status = constants.DAMAGED
74+
status.save()
75+
# update status to available
76+
status = AssetStatus.objects.get(asset=self.asset)
77+
status.current_status = constants.AVAILABLE
78+
status.save()
79+
# verify that transition state has been updated to closed
80+
transition_state = StateTransition.objects.get(
81+
asset_incident_report=self.incident_report
82+
)
83+
self.assertEqual(transition_state.incident_report_state, constants.CLOSED)
84+
85+
def test_update_asset_status_from_lost_to_allocated(self):
86+
status = AssetStatus.objects.get(asset=self.asset)
87+
88+
# update status to lost
89+
status.current_status = constants.LOST
90+
status.save()
91+
92+
# allocate asset to a user
93+
AllocationHistory.objects.create(
94+
asset=self.asset, current_assignee=self.asset_assignee2
95+
)
96+
97+
transition_state = StateTransition.objects.get(
98+
asset_incident_report=self.incident_report
99+
)
100+
101+
self.assertEqual(transition_state.incident_report_state, constants.CLOSED)
102+
103+
def test_update_asset_status_from_damaged_to_allocated(self):
104+
status = AssetStatus.objects.get(asset=self.asset)
105+
106+
# update status to lost
107+
status.current_status = constants.DAMAGED
108+
status.save()
109+
# allocate asset to a user
110+
AllocationHistory.objects.create(
111+
asset=self.asset, current_assignee=self.asset_assignee2
112+
)
113+
114+
transition_state = StateTransition.objects.get(
115+
asset_incident_report=self.incident_report
116+
)
117+
118+
self.assertEqual(transition_state.incident_report_state, constants.CLOSED)
119+
120+
def test_update_asset_status_from_allocated_to_available(self):
121+
status = AssetStatus.objects.get(asset=self.asset)
122+
self.assertEqual(status.current_status, constants.AVAILABLE)
123+
# allocate asset to a user
124+
AllocationHistory.objects.create(
125+
asset=self.asset, current_assignee=self.asset_assignee2
126+
)
127+
status = AssetStatus.objects.get(asset=self.asset)
128+
self.assertEqual(status.current_status, constants.ALLOCATED)
129+
130+
transition_state = StateTransition.objects.get(
131+
asset_incident_report=self.incident_report
132+
)
133+
self.assertEqual(
134+
transition_state.incident_report_state, constants.NEWLY_REPORTED
135+
)

0 commit comments

Comments
 (0)