diff --git a/src/dispatch/case/flows.py b/src/dispatch/case/flows.py index 3128c8bf7b31..2d58d35cb393 100644 --- a/src/dispatch/case/flows.py +++ b/src/dispatch/case/flows.py @@ -33,12 +33,13 @@ from dispatch.storage.enums import StorageAction from dispatch.ticket import flows as ticket_flows +from .enums import CaseResolutionReason, CaseStatus from .messaging import ( send_case_created_notifications, send_case_rating_feedback_message, send_case_update_notifications, ) -from .models import Case, CaseStatus +from .models import Case from .service import get log = logging.getLogger(__name__) @@ -191,6 +192,26 @@ def update_conversation(case: Case, db_session: Session) -> None: ) +def case_auto_close_flow(case: Case, db_session: Session): + "Runs the case auto close flow." + # we mark the case as closed + case.resolution = "Auto closed via case type auto close configuration." + case.resolution_reason = CaseResolutionReason.user_acknowledge + case.status = CaseStatus.closed + db_session.add(case) + db_session.commit() + + # we transition the case from the new to the closed state + case_triage_status_flow( + case=case, + db_session=db_session, + ) + case_closed_status_flow( + case=case, + db_session=db_session, + ) + + def case_new_create_flow( *, case_id: int, @@ -253,6 +274,10 @@ def case_new_create_flow( log.warning("Case assignee not paged. No plugin of type oncall enabled.") return case + if case and case.case_type.auto_close: + # we transition the case to the closed state if its case type has auto close enabled + case_auto_close_flow(case=case, db_session=db_session) + return case diff --git a/src/dispatch/case/type/models.py b/src/dispatch/case/type/models.py index 416bccb121df..ab5e03c2f4f4 100644 --- a/src/dispatch/case/type/models.py +++ b/src/dispatch/case/type/models.py @@ -1,10 +1,11 @@ from typing import List, Optional -from pydantic import Field, validator, AnyHttpUrl +from pydantic import AnyHttpUrl, Field, validator from sqlalchemy import JSON, Boolean, Column, ForeignKey, Integer, String from sqlalchemy.event import listen from sqlalchemy.ext.hybrid import hybrid_method from sqlalchemy.orm import relationship +from sqlalchemy.sql import false from sqlalchemy.sql.schema import UniqueConstraint from sqlalchemy_utils import TSVectorType @@ -27,6 +28,7 @@ class CaseType(ProjectMixin, Base): exclude_from_metrics = Column(Boolean, default=False) plugin_metadata = Column(JSON, default=[]) conversation_target = Column(String) + auto_close = Column(Boolean, default=False, server_default=false()) # the catalog here is simple to help matching "named entities" search_vector = Column(TSVectorType("name", regconfig="pg_catalog.simple")) @@ -100,6 +102,7 @@ class CaseTypeBase(DispatchBase): project: Optional[ProjectRead] visibility: Optional[str] = Field(None, nullable=True) cost_model: Optional[CostModelRead] = None + auto_close: Optional[bool] = False @validator("plugin_metadata", pre=True) def replace_none_with_empty_list(cls, value): diff --git a/src/dispatch/database/revisions/tenant/versions/2024-10-29_3edb0476365a.py b/src/dispatch/database/revisions/tenant/versions/2024-10-29_3edb0476365a.py new file mode 100644 index 000000000000..53f964fba5fa --- /dev/null +++ b/src/dispatch/database/revisions/tenant/versions/2024-10-29_3edb0476365a.py @@ -0,0 +1,31 @@ +"""Adds auto_close column to case_type model + +Revision ID: 3edb0476365a +Revises: 24322617ce9a +Create Date: 2024-10-29 13:26:29.001448 + +""" + +import sqlalchemy as sa +from alembic import op + +# revision identifiers, used by Alembic. +revision = "3edb0476365a" +down_revision = "24322617ce9a" +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.add_column( + "case_type", + sa.Column("auto_close", sa.Boolean(), server_default=sa.text("false"), nullable=True), + ) + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.drop_column("case_type", "auto_close") + # ### end Alembic commands ### diff --git a/src/dispatch/static/dispatch/src/case/type/NewEditSheet.vue b/src/dispatch/static/dispatch/src/case/type/NewEditSheet.vue index bc9b75150613..6c30938c06e1 100644 --- a/src/dispatch/static/dispatch/src/case/type/NewEditSheet.vue +++ b/src/dispatch/static/dispatch/src/case/type/NewEditSheet.vue @@ -114,9 +114,9 @@ @@ -128,9 +128,16 @@ + + + @@ -184,6 +191,7 @@ export default { computed: { ...mapFields("case_type", [ "dialogs.showCreateEdit", + "selected.auto_close", "selected.case_template_document", "selected.conversation_target", "selected.cost_model", diff --git a/src/dispatch/static/dispatch/src/case/type/Table.vue b/src/dispatch/static/dispatch/src/case/type/Table.vue index c72c6214a6de..7e795a71af4c 100644 --- a/src/dispatch/static/dispatch/src/case/type/Table.vue +++ b/src/dispatch/static/dispatch/src/case/type/Table.vue @@ -51,10 +51,13 @@ {{ item.incident_type.name }} + -