diff --git a/src/dispatch/case/type/service.py b/src/dispatch/case/type/service.py
index f77d933cc574..31317f8509dd 100644
--- a/src/dispatch/case/type/service.py
+++ b/src/dispatch/case/type/service.py
@@ -167,7 +167,7 @@ def update(*, db_session, case_type: CaseType, case_type_in: CaseTypeUpdate) ->
# Calculate the cost of all non-closed cases associated with this case type
cases = case_service.get_all_open_by_case_type(db_session=db_session, case_type_id=case_type.id)
for case in cases:
- case_cost_service.calculate_case_response_cost(case_id=case.id, db_session=db_session)
+ case_cost_service.calculate_case_response_cost(case=case, db_session=db_session)
if case_type_in.case_template_document:
case_template_document = document_service.get(
diff --git a/src/dispatch/case_cost/models.py b/src/dispatch/case_cost/models.py
index ca37c529ebe4..0507fde7c0ba 100644
--- a/src/dispatch/case_cost/models.py
+++ b/src/dispatch/case_cost/models.py
@@ -1,3 +1,5 @@
+from datetime import datetime
+
from sqlalchemy import Column, ForeignKey, Integer, Numeric
from sqlalchemy.ext.associationproxy import association_proxy
from sqlalchemy.orm import relationship
@@ -40,6 +42,7 @@ class CaseCostUpdate(CaseCostBase):
class CaseCostRead(CaseCostBase):
id: PrimaryKey
case_cost_type: CaseCostTypeRead
+ updated_at: Optional[datetime] = None
class CaseCostPagination(Pagination):
diff --git a/src/dispatch/case_cost/scheduled.py b/src/dispatch/case_cost/scheduled.py
index 2871e3b72e31..ca8def52711b 100644
--- a/src/dispatch/case_cost/scheduled.py
+++ b/src/dispatch/case_cost/scheduled.py
@@ -13,6 +13,7 @@
from .service import (
calculate_case_response_cost,
+ update_case_response_cost,
get_or_create_default_case_response_cost,
)
@@ -56,7 +57,8 @@ def calculate_cases_response_cost(db_session: Session, project: Project):
continue
# we calculate the response cost amount
- amount = calculate_case_response_cost(case.id, db_session)
+ update_case_response_cost(case, db_session)
+ amount = calculate_case_response_cost(case, db_session)
# we don't need to update the cost amount if it hasn't changed
if case_response_cost.amount == amount:
continue
diff --git a/src/dispatch/case_cost/service.py b/src/dispatch/case_cost/service.py
index d7b3d10b6e47..23135fd88039 100644
--- a/src/dispatch/case_cost/service.py
+++ b/src/dispatch/case_cost/service.py
@@ -6,7 +6,6 @@
from dispatch.database.core import SessionLocal
from dispatch.cost_model.models import CostModelActivity
from dispatch.case import service as case_service
-from dispatch.case.enums import CaseStatus
from dispatch.case.models import Case
from dispatch.case.type.models import CaseType
from dispatch.case_cost_type import service as case_cost_type_service
@@ -14,8 +13,7 @@
from dispatch.participant import service as participant_service
from dispatch.participant.models import ParticipantRead
from dispatch.participant_activity import service as participant_activity_service
-from dispatch.participant_activity.models import ParticipantActivityCreate
-from dispatch.participant_role.models import ParticipantRoleType, ParticipantRole
+from dispatch.participant_activity.models import ParticipantActivityCreate, ParticipantActivity
from dispatch.plugin import service as plugin_service
from .models import CaseCost, CaseCostCreate, CaseCostUpdate
@@ -44,7 +42,8 @@ def get_by_case_id_and_case_cost_type_id(
db_session.query(CaseCost)
.filter(CaseCost.case_id == case_id)
.filter(CaseCost.case_cost_type_id == case_cost_type_id)
- .one_or_none()
+ .order_by(CaseCost.id.asc())
+ .first()
)
@@ -107,7 +106,7 @@ def update_case_response_cost_for_case_type(db_session, case_type: CaseType) ->
"""Calculate the response cost of all non-closed cases associated with this case type."""
cases = case_service.get_all_open_by_case_type(db_session=db_session, case_type_id=case_type.id)
for case in cases:
- update_case_response_cost(case_id=case.id, db_session=db_session)
+ update_case_response_cost(case=case, db_session=db_session)
def calculate_response_cost(hourly_rate, total_response_time_seconds) -> int:
@@ -171,6 +170,18 @@ def get_or_create_default_case_response_cost(
def fetch_case_events(
case: Case, activity: CostModelActivity, oldest: str, db_session: SessionLocal
) -> List[Optional[tuple[datetime.timestamp, str]]]:
+ """Fetches case events for a given case and cost model activity.
+
+ Args:
+ case: The case to fetch events for.
+ activity: The activity to fetch events for. This defines the plugin event to fetch and how much response effort each event requires.
+ oldest: The timestamp to start fetching events from.
+ db_session: The database session.
+
+ Returns:
+ List[Optional[tuple[datetime.timestamp, str]]]: A list of tuples containing the timestamp and user_id of each event.
+ """
+
plugin_instance = plugin_service.get_active_instance_by_slug(
db_session=db_session,
slug=activity.plugin_event.plugin.slug,
@@ -182,7 +193,6 @@ def fetch_case_events(
)
return []
- # Array of sorted (timestamp, user_id) tuples.
return plugin_instance.instance.fetch_events(
db_session=db_session,
subject=case,
@@ -191,21 +201,38 @@ def fetch_case_events(
)
-def calculate_case_response_cost_with_cost_model(case: Case, db_session: SessionLocal) -> int:
- """Calculates the cost of a case using the case's cost model.
+def update_case_participant_activities(
+ case: Case, db_session: SessionLocal
+) -> ParticipantActivity | None:
+ """Records or updates case participant activities using the case's cost model.
- This function aggregates all new case costs based on plugin activity since the last case cost update.
- If this is the first time performing cost calculation for this case, it computes the total costs from the case's creation.
+ This function records and/or updates all new case participant activities since the last case cost update.
Args:
case: The case to calculate the case response cost for.
db_session: The database session.
Returns:
- int: The case response cost in dollars.
+ ParticipantActivity | None: The most recent participant activity created or updated.
"""
+ if not case:
+ log.warning(f"Case with id {case.id} not found.")
+ return
- participants_total_response_time_seconds = 0
+ case_type = case.case_type
+ if not case_type:
+ log.debug(f"Case type for case {case.name} not found.")
+ return
+
+ if not case_type.cost_model:
+ log.debug("No case cost model found. Skipping this case.")
+ return
+
+ if not case_type.cost_model.enabled:
+ log.debug("Case cost model is not enabled. Skipping this case.")
+ return
+
+ log.debug(f"Calculating {case.name} case cost with model {case_type.cost_model}.")
oldest = case.created_at.replace(tzinfo=timezone.utc).timestamp()
# Used for determining whether we've previously calculated the case cost.
@@ -213,174 +240,82 @@ def calculate_case_response_cost_with_cost_model(case: Case, db_session: Session
case_response_cost = get_or_create_default_case_response_cost(case=case, db_session=db_session)
if not case_response_cost:
- log.warning(f"Cannot calculate case response cost for case {case.name}.")
- return 0
+ log.warning(
+ f"Cannot calculate case response cost for case {case.name}. No default case response cost type created or found."
+ )
+ return
+ most_recent_activity = None
# Ignore events that happened before the last case cost update.
if case_response_cost.updated_at < current_time:
oldest = case_response_cost.updated_at.replace(tzinfo=timezone.utc).timestamp()
+ case_events = []
+ # Get the cost model. Iterate through all the listed activities we want to record.
if case.case_type.cost_model:
- # Get the cost model. Iterate through all the listed activities we want to record.
for activity in case.case_type.cost_model.activities:
# Array of sorted (timestamp, user_id) tuples.
- case_events = fetch_case_events(
- case=case, activity=activity, oldest=oldest, db_session=db_session
- )
-
- for ts, user_id in case_events:
- participant = participant_service.get_by_case_id_and_conversation_id(
- db_session=db_session,
- case_id=case.id,
- user_conversation_id=user_id,
- )
- if not participant:
- log.warning("Cannot resolve participant.")
- continue
-
- activity_in = ParticipantActivityCreate(
- plugin_event=activity.plugin_event,
- started_at=ts,
- ended_at=ts + timedelta(seconds=activity.response_time_seconds),
- participant=ParticipantRead(id=participant.id),
- case=case,
+ case_events.extend(
+ fetch_case_events(
+ case=case, activity=activity, oldest=oldest, db_session=db_session
)
+ )
- if participant_response_time := participant_activity_service.create_or_update(
- db_session=db_session, activity_in=activity_in
- ):
- participants_total_response_time_seconds += (
- participant_response_time.total_seconds()
- )
-
- hourly_rate = get_hourly_rate(case.project)
- amount = calculate_response_cost(
- hourly_rate=hourly_rate,
- total_response_time_seconds=participants_total_response_time_seconds,
- )
-
- return case.total_cost + amount
-
-
-def get_participant_role_time_seconds(
- case: Case, participant_role: ParticipantRole, start_at: datetime
-) -> int:
- """Calculates the time spent by a participant in a case role starting from a given time.
-
- Args:
- case: The case the participant is part of.
- participant_role: The role of the participant and the time they assumed and renounced the role.
- start_at: Only time spent after this will be considered.
-
- Returns:
- int: The time spent by the participant in the case role in seconds.
- """
- if participant_role.renounced_at and participant_role.renounced_at < start_at:
- # skip calculating already-recorded activity
- return 0
-
- if participant_role.role == ParticipantRoleType.observer:
- # skip calculating cost for participants with the observer role
- return 0
-
- if participant_role.activity == 0:
- # skip calculating cost for roles that have no activity
- return 0
-
- participant_role_assumed_at = participant_role.assumed_at
-
- # we set the renounced_at default time to the current time
- participant_role_renounced_at = datetime.now(tz=timezone.utc).replace(tzinfo=None)
-
- if case.status in [CaseStatus.new, CaseStatus.triage]:
- if participant_role.renounced_at:
- # the participant left the conversation or got assigned another role
- # we use the role's renounced_at time
- participant_role_renounced_at = participant_role.renounced_at
- else:
- # we set the renounced_at default time to when the case was marked as escalated or closed
- if case.escalated_at:
- participant_role_renounced_at = case.escalated_at
-
- if case.closed_at:
- participant_role_renounced_at = case.closed_at
-
- if participant_role.renounced_at:
- # the participant left the conversation or got assigned another role
- if participant_role.renounced_at < participant_role_renounced_at:
- # we use the role's renounced_at time if it happened before the
- # case was marked as stable or closed
- participant_role_renounced_at = participant_role.renounced_at
-
- # the time the participant has spent in the case role since the last case cost update
- participant_role_time = participant_role_renounced_at - max(
- participant_role_assumed_at, start_at
- )
- if participant_role_time.total_seconds() < 0:
- # the participant was added after the case was marked as stable
- return 0
-
- # we calculate the number of hours the participant has spent in the case role
- participant_role_time_hours = participant_role_time.total_seconds() / SECONDS_IN_HOUR
-
- # we make the assumption that participants spend more or less time based on their role
- # and we adjust the time spent based on that
- return participant_role_time_hours * SECONDS_IN_HOUR
-
-
-def get_total_participant_roles_time_seconds(case: Case, start_at: datetime) -> int:
- """Calculates the time spent by all participants in this case starting from a given time.
-
- Args:
- case: The case the participant is part of.
- participant_role: The role of the participant and the time they assumed and renounced the role.
- start_at: Only time spent after this will be considered.
-
- Returns:
- int: The total time spent by all participants in the case roles in seconds.
+ # Sort case_events by timestamp
+ sorted(case_events, key=lambda x: x[0])
- """
- total_participants_roles_time_seconds = 0
- for participant in case.participants:
- for participant_role in participant.participant_roles:
- total_participants_roles_time_seconds += get_participant_role_time_seconds(
+ for ts, user_id in case_events:
+ participant = participant_service.get_by_case_id_and_conversation_id(
+ db_session=db_session,
+ case_id=case.id,
+ user_conversation_id=user_id,
+ )
+ if not participant:
+ log.warning("Cannot resolve participant.")
+ continue
+
+ activity_in = ParticipantActivityCreate(
+ plugin_event=activity.plugin_event,
+ started_at=ts,
+ ended_at=ts + timedelta(seconds=activity.response_time_seconds),
+ participant=ParticipantRead(id=participant.id),
case=case,
- participant_role=participant_role,
- start_at=start_at,
)
- return total_participants_roles_time_seconds
-
-
-def calculate_case_response_cost(case_id: int, db_session: SessionLocal) -> int:
- """Calculates the response cost of a given case.
-
- If there is no cost model, the case cost will not be calculated.
- """
- case = case_service.get(db_session=db_session, case_id=case_id)
- if not case:
- log.warning(f"Case with id {case_id} not found.")
- return 0
- case_type = case.case_type
- if not case_type:
- log.debug(f"Case type for case {case.name} not found.")
- return case.total_cost
+ most_recent_activity = participant_activity_service.create_or_update(
+ db_session=db_session, activity_in=activity_in
+ )
+ return most_recent_activity
- if not case_type.cost_model:
- log.debug("No case cost model found. Skipping this case.")
- return case.total_cost
- if not case_type.cost_model.enabled:
- log.debug("Case cost model is not enabled. Skipping this case.")
- return case.total_cost
+def calculate_case_response_cost(case: Case, db_session: SessionLocal) -> int:
+ """Calculates the response cost of a given case."""
+ # Iterate through all the listed activities and aggregate the total participant response time spent on the case.
+ participants_total_response_time = timedelta(0)
+ particpant_activities = (
+ participant_activity_service.get_all_case_participant_activities_for_case(
+ db_session=db_session, case_id=case.id
+ )
+ )
+ for participant_activity in particpant_activities:
+ participants_total_response_time += (
+ participant_activity.ended_at - participant_activity.started_at
+ )
- log.debug(f"Calculating {case.name} case cost with model {case_type.cost_model}.")
- return calculate_case_response_cost_with_cost_model(case=case, db_session=db_session)
+ # Calculate the cost based on the total participant response time.
+ hourly_rate = get_hourly_rate(case.project)
+ amount = calculate_response_cost(
+ hourly_rate=hourly_rate,
+ total_response_time_seconds=participants_total_response_time.total_seconds(),
+ )
+ return amount
-def update_case_response_cost(case_id: int, db_session: SessionLocal) -> int:
+def update_case_response_cost(case: Case, db_session: SessionLocal) -> int:
"""Updates the response cost of a given case.
+ This function logs all case participant activities since the last case cost update and recalculates the case response cost based on all logged participant activities.
+
Args:
case_id: The case id.
db_session: The database session.
@@ -388,17 +323,16 @@ def update_case_response_cost(case_id: int, db_session: SessionLocal) -> int:
Returns:
int: The case response cost in dollars.
"""
- case = case_service.get(db_session=db_session, case_id=case_id)
-
- amount = calculate_case_response_cost(case_id=case.id, db_session=db_session)
-
- case_response_cost = get_default_case_response_cost(case=case, db_session=db_session)
+ # We update the case participant activities before calculating the case response cost
+ update_case_participant_activities(case=case, db_session=db_session)
+ amount = calculate_case_response_cost(case=case, db_session=db_session)
+ case_response_cost = get_or_create_default_case_response_cost(case=case, db_session=db_session)
if not case_response_cost:
log.warning(f"Cannot calculate case response cost for case {case.name}.")
return 0
- # we update the cost amount only if the case cost has changed
+ # We update the cost amount only if the case cost has changed
if case_response_cost.amount != amount:
case_response_cost.amount = amount
case.case_costs.append(case_response_cost)
diff --git a/src/dispatch/participant_activity/service.py b/src/dispatch/participant_activity/service.py
index 8a16496fef77..44ad00b4eb8a 100644
--- a/src/dispatch/participant_activity/service.py
+++ b/src/dispatch/participant_activity/service.py
@@ -65,7 +65,10 @@ def get_all_case_participant_activities_for_case(
) -> list[ParticipantActivityRead]:
"""Fetches all recorded participant case activities for a given case."""
return (
- db_session.query(ParticipantActivity).filter(ParticipantActivity.case_id == case_id).all()
+ db_session.query(ParticipantActivity)
+ .filter(ParticipantActivity.case_id == case_id)
+ .order_by(ParticipantActivity.started_at.asc())
+ .all()
)
@@ -77,6 +80,7 @@ def get_all_incident_participant_activities_for_incident(
return (
db_session.query(ParticipantActivity)
.filter(ParticipantActivity.incident_id == incident_id)
+ .order_by(ParticipantActivity.started_at.asc())
.all()
)
diff --git a/src/dispatch/static/dispatch/src/case/CostsTab.vue b/src/dispatch/static/dispatch/src/case/CostsTab.vue
index 41b68c0a1c9a..46417e947a42 100644
--- a/src/dispatch/static/dispatch/src/case/CostsTab.vue
+++ b/src/dispatch/static/dispatch/src/case/CostsTab.vue
@@ -23,6 +23,9 @@
{{ cost.case_cost_type.name }}
{{ cost.case_cost_type.description }}
+
+ Updated At: {{ formatRelativeDate(cost.updated_at) }}
+
{{ toUSD(cost.amount) }}
@@ -41,6 +44,7 @@
import { mapMutations } from "vuex"
import { mapMultiRowFields } from "vuex-map-fields"
import { toUSD } from "@/filters"
+import { formatRelativeDate } from "@/filters"
import CaseCostInput from "@/case_cost/CaseCostInput.vue"
@@ -52,7 +56,7 @@ export default {
},
setup() {
- return { toUSD }
+ return { toUSD, formatRelativeDate }
},
computed: {
diff --git a/tests/case_cost/test_case_cost_service.py b/tests/case_cost/test_case_cost_service.py
index 5827d485db80..21e52d0ba672 100644
--- a/tests/case_cost/test_case_cost_service.py
+++ b/tests/case_cost/test_case_cost_service.py
@@ -96,34 +96,26 @@ def test_fetch_case_event__no_enabled_plugins(
assert not fetch_case_events(case, cost_model_activity, oldest="0", db_session=session)
-def test_calculate_case_response_cost(
- session,
+def test_update_case_participant_activities__create(
case,
- case_cost_type,
- cost_model_activity,
+ session,
conversation_plugin_instance,
- conversation,
+ cost_model_activity,
participant,
+ case_cost_type,
+ conversation,
):
- """Tests that the case cost is calculated correctly when a cost model is enabled."""
- from datetime import timedelta
- import math
- from dispatch.case_cost.service import update_case_response_cost, get_hourly_rate
+ """Tests that case participant activity is created the first time a case updates its response cost."""
+ from dispatch.case_cost.service import update_case_participant_activities
from dispatch.case_cost_type import service as case_cost_type_service
- from dispatch.participant_activity.service import (
- get_all_case_participant_activities_for_case,
- )
-
- SECONDS_IN_HOUR = 3600
- orig_total_case_cost = case.total_cost
- # Set incoming plugin events.
+ # Set up incoming plugin events.
conversation_plugin_instance.project_id = case.project.id
cost_model_activity.plugin_event.plugin = conversation_plugin_instance.plugin
participant.user_conversation_id = "0XDECAFBAD"
participant.case = case
- # Set up a default case costs type.
+ # Set up a default case cost type.
for cost_type in case_cost_type_service.get_all(db_session=session):
cost_type.default = False
case_cost_type.default = True
@@ -133,56 +125,53 @@ def test_calculate_case_response_cost(
case.case_type.cost_model.enabled = True
case.case_type.cost_model.activities = [cost_model_activity]
+ # Set up case conversation.
case.conversation = conversation
case.dedicated_channel = True
- # Calculates and updates the case cost.
- cost = update_case_response_cost(case_id=case.id, db_session=session)
- activities = get_all_case_participant_activities_for_case(db_session=session, case_id=case.id)
- assert activities
-
- # Evaluate expected case cost.
- participants_total_response_time_seconds = timedelta(seconds=0)
- for activity in activities:
- participants_total_response_time_seconds += activity.ended_at - activity.started_at
- hourly_rate = get_hourly_rate(case.project)
- expected_case_cost = (
- math.ceil(
- (participants_total_response_time_seconds.seconds / SECONDS_IN_HOUR) * hourly_rate
- )
- + orig_total_case_cost
+ # Assert that the case participant activity is created.
+ assert update_case_participant_activities(case=case, db_session=session)
+
+
+def test_update_case_participant_activities__no_cost_model(case, session):
+ """Tests that the case participant activity is not created if the cost model is not enabled."""
+ from dispatch.case_cost.service import update_case_participant_activities
+ from dispatch.participant_activity.service import (
+ get_all_case_participant_activities_for_case,
)
- assert cost
- assert cost == expected_case_cost
- assert cost == case.total_cost
+ case.cost_model = None
+ assert not update_case_participant_activities(case=case, db_session=session)
+ activities = get_all_case_participant_activities_for_case(db_session=session, case_id=case.id)
+ assert not activities
-def test_calculate_case_response_cost__no_enabled_plugins(
- session,
+
+def test_update_case_participant_activities__no_enabled_plugins(
case,
- case_cost_type,
+ session,
cost_model_activity,
- plugin_instance,
- conversation,
participant,
+ case_cost_type,
+ conversation,
+ plugin_instance,
):
- """Tests that the case cost is calculated correctly when a cost model is enabled."""
+ """Tests that the case participant activity is not created if plugins are not enabled."""
from dispatch.case.service import get
- from dispatch.case_cost.service import update_case_response_cost
+ from dispatch.case_cost.service import update_case_participant_activities
from dispatch.case_cost_type import service as case_cost_type_service
from dispatch.participant_activity.service import (
get_all_case_participant_activities_for_case,
)
- # Disable the plugin instance for the cost model plugin event.
+ # Set up. Disable the plugin instance for the cost model plugin event.
plugin_instance.project_id = case.project.id
plugin_instance.enabled = False
cost_model_activity.plugin_event.plugin = plugin_instance.plugin
participant.user_conversation_id = "0XDECAFBAD"
participant.case = case
- # Set up a default case costs type.
+ # Set up a default case cost type.
for cost_type in case_cost_type_service.get_all(db_session=session):
cost_type.default = False
case_cost_type.default = True
@@ -194,52 +183,68 @@ def test_calculate_case_response_cost__no_enabled_plugins(
case.case_type.cost_model.activities = [cost_model_activity]
case.conversation = conversation
- # Calculates and updates the case cost.
- cost = update_case_response_cost(case_id=case.id, db_session=session)
+ # Assert that no participant activity was created.
+ assert not update_case_participant_activities(case=case, db_session=session)
+
activities = get_all_case_participant_activities_for_case(db_session=session, case_id=case.id)
assert not activities
- assert not cost
- assert not case.total_cost
-def test_update_case_response_cost__no_cost_model(case, session, case_cost_type):
- """Tests that the case response cost is not created if the case type has no cost model."""
- from dispatch.case import service as case_service
- from dispatch.case_cost.service import update_case_response_cost
+def test_calculate_case_response_cost(case, session, participant_activity):
+ """Tests that the case response cost is calculated correctly."""
+ from dispatch.case_cost.service import (
+ calculate_case_response_cost,
+ calculate_response_cost,
+ get_hourly_rate,
+ )
+
+ # Set up participant activity.
+ participant_activity.case = case
+
+ # Assert that the response cost was calculated correctly.
+ response_cost = calculate_case_response_cost(case=case, db_session=session)
+ assert response_cost == calculate_response_cost(
+ hourly_rate=get_hourly_rate(case.project),
+ total_response_time_seconds=(
+ participant_activity.ended_at - participant_activity.started_at
+ ).total_seconds(),
+ )
+
+
+def test_update_case_response_cost(case, session, participant_activity, case_cost_type):
+ """Tests that the case response cost is created correctly."""
+ from dispatch.case_cost.service import (
+ update_case_response_cost,
+ )
from dispatch.case_cost_type import service as case_cost_type_service
- # Set up a default case costs type.
+ # Set up participant activity.
+ participant_activity.case = case
+
+ # Set up default case cost type.
for cost_type in case_cost_type_service.get_all(db_session=session):
cost_type.default = False
- case_cost_type.default = True
case_cost_type.project = case.project
+ case_cost_type.default = True
- case = case_service.get(db_session=session, case_id=case.id)
- case.case_type.cost_model = None
-
- # The case response cost should not be created without a cost model.
- case_response_cost_amount = update_case_response_cost(case_id=case.id, db_session=session)
-
- assert not case_response_cost_amount
+ # Assert that there exists a case response cost.
+ assert update_case_response_cost(case=case, db_session=session)
-def test_update_case_response_cost__fail(case, session):
- """Tests that the case response cost is not created if the project has no default cost_type."""
- from dispatch.case import service as case_service
+def test_update_case_response_cost__fail_no_default_cost_type(case, session):
+ """Tests that the case response cost is not created if the project has no default cost type."""
from dispatch.case_cost.service import (
update_case_response_cost,
get_by_case_id,
)
from dispatch.case_cost_type import service as case_cost_type_service
- case = case_service.get(db_session=session, case_id=case.id)
-
# Ensure there is no default cost type for this project.
for cost_type in case_cost_type_service.get_all(db_session=session):
cost_type.default = False
- # Fail to create the initial case response cost.
- assert not update_case_response_cost(case_id=case.id, db_session=session)
+ # Assert that the initial case response cost creation failed.
+ assert not update_case_response_cost(case=case, db_session=session)
- # Validate that the case cost was not created nor saved in the database.
+ # Validate that the case cost was neither created nor saved in the database.
assert not get_by_case_id(db_session=session, case_id=case.id)