from __future__ import annotations from datetime import date, datetime, timezone from sqlalchemy import Boolean, Date, DateTime, ForeignKey, Integer, String, Text, UniqueConstraint from sqlalchemy.orm import Mapped, mapped_column, relationship from app.core.db.common import CommonBase class ServiceCategory(CommonBase): __tablename__ = "service_categories" __table_args__ = ( UniqueConstraint("code", name="uq_service_categories_code"), UniqueConstraint("name", name="uq_service_categories_name"), ) id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True) code: Mapped[str] = mapped_column(String(50), nullable=False, index=True) name: Mapped[str] = mapped_column(String(100), nullable=False, index=True) sort_order: Mapped[int] = mapped_column(Integer, nullable=False, default=100) is_active: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False) catalogue_items = relationship("ServiceCatalogue", back_populates="service_category") class ServiceCatalogue(CommonBase): __tablename__ = "service_catalogues" __table_args__ = ( UniqueConstraint("service_code", name="uq_service_catalogues_code"), ) id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True) service_code: Mapped[str] = mapped_column(String(50), nullable=False, index=True) service_name: Mapped[str] = mapped_column(String(200), nullable=False, index=True) category: Mapped[str | None] = mapped_column(String(100), nullable=True) category_id: Mapped[int | None] = mapped_column(ForeignKey("service_categories.id"), nullable=True, index=True) recurrence_type: Mapped[str | None] = mapped_column(String(30), nullable=True, index=True) engagement_type: Mapped[str] = mapped_column(String(20), nullable=False, default="non_assurance", index=True) sort_order: Mapped[int] = mapped_column(Integer, nullable=False, default=100) description: Mapped[str | None] = mapped_column(Text, nullable=True) applicable_individual: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False) applicable_proprietorship: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False) applicable_partnership: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False) applicable_llp: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False) applicable_company: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False) applicable_trust: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False) applicable_society: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False) is_active: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False) is_client_requestable: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False) is_consultant_requestable: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False) created_by_user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id"), nullable=True) updated_by_user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id"), nullable=True) created_at_utc: Mapped[datetime] = mapped_column( DateTime(timezone=True), default=lambda: datetime.now(timezone.utc), nullable=False ) updated_at_utc: Mapped[datetime] = mapped_column( DateTime(timezone=True), default=lambda: datetime.now(timezone.utc), onupdate=lambda: datetime.now(timezone.utc), nullable=False, ) service_category = relationship("ServiceCategory", back_populates="catalogue_items") firm_services = relationship( "FirmServiceSelection", back_populates="catalogue", cascade="all, delete-orphan", passive_deletes=True, ) task_templates = relationship( "FirmServiceTaskTemplate", back_populates="catalogue", cascade="all, delete-orphan", passive_deletes=True, ) default_task_templates = relationship( "ServiceDefaultTaskTemplate", back_populates="catalogue", cascade="all, delete-orphan", passive_deletes=True, order_by="ServiceDefaultTaskTemplate.sequence_no.asc()", ) due_date_rules = relationship( "ServiceDueDateRule", back_populates="catalogue", cascade="all, delete-orphan", passive_deletes=True, order_by="ServiceDueDateRule.sort_order.asc()", ) class ServiceDefaultTaskTemplate(CommonBase): __tablename__ = "service_default_task_templates" __table_args__ = ( UniqueConstraint("service_catalogue_id", "sequence_no", name="uq_service_default_task_templates_sequence"), ) id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True) service_catalogue_id: Mapped[int] = mapped_column( ForeignKey("service_catalogues.id", ondelete="CASCADE"), nullable=False, index=True ) task_name: Mapped[str] = mapped_column(String(200), nullable=False) description: Mapped[str | None] = mapped_column(Text, nullable=True) sequence_no: Mapped[int] = mapped_column(Integer, nullable=False, default=1) default_role_name: Mapped[str | None] = mapped_column(String(100), nullable=True) is_mandatory: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False) requires_review: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False) # AQMM task tags. These flags allow the existing service checklist to become # the engagement quality checklist for assurance engagements, without creating # a separate duplicate AQMM checklist module. is_aqmm_task: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False, index=True) aqmm_mandatory: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False) aqmm_evidence_required: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False) aqmm_manager_review_required: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False) aqmm_partner_review_required: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False) aqmm_review_partner_required: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False) aqmm_blocks_final_release: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False) aqmm_reference: Mapped[str | None] = mapped_column(String(120), nullable=True) is_active: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False) catalogue = relationship("ServiceCatalogue", back_populates="default_task_templates") class FirmServiceSelection(CommonBase): __tablename__ = "firm_service_selections" __table_args__ = ( UniqueConstraint("tenant_id", "service_catalogue_id", name="uq_firm_service_selections_tenant_catalogue"), ) id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True) tenant_id: Mapped[int] = mapped_column(ForeignKey("tenants.id", ondelete="CASCADE"), nullable=False, index=True) service_catalogue_id: Mapped[int] = mapped_column( ForeignKey("service_catalogues.id", ondelete="CASCADE"), nullable=False, index=True ) is_enabled: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False) default_branch_id: Mapped[int | None] = mapped_column(ForeignKey("branches.id"), nullable=True, index=True) activated_by_user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id"), nullable=True) updated_by_user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id"), nullable=True) activated_at_utc: Mapped[datetime] = mapped_column( DateTime(timezone=True), default=lambda: datetime.now(timezone.utc), nullable=False ) updated_at_utc: Mapped[datetime] = mapped_column( DateTime(timezone=True), default=lambda: datetime.now(timezone.utc), onupdate=lambda: datetime.now(timezone.utc), nullable=False, ) catalogue = relationship("ServiceCatalogue", back_populates="firm_services") class FirmServiceTaskTemplate(CommonBase): __tablename__ = "firm_service_task_templates" __table_args__ = ( UniqueConstraint( "tenant_id", "service_catalogue_id", "sequence_no", name="uq_firm_service_task_templates_sequence", ), ) id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True) tenant_id: Mapped[int] = mapped_column(ForeignKey("tenants.id", ondelete="CASCADE"), nullable=False, index=True) service_catalogue_id: Mapped[int] = mapped_column( ForeignKey("service_catalogues.id", ondelete="CASCADE"), nullable=False, index=True ) task_name: Mapped[str] = mapped_column(String(200), nullable=False) description: Mapped[str | None] = mapped_column(Text, nullable=True) sequence_no: Mapped[int] = mapped_column(Integer, nullable=False, default=1) default_role_name: Mapped[str | None] = mapped_column(String(100), nullable=True) is_mandatory: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False) requires_review: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False) # AQMM task tags copied into generated ClientServiceTaskInstance rows. is_aqmm_task: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False, index=True) aqmm_mandatory: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False) aqmm_evidence_required: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False) aqmm_manager_review_required: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False) aqmm_partner_review_required: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False) aqmm_review_partner_required: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False) aqmm_blocks_final_release: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False) aqmm_reference: Mapped[str | None] = mapped_column(String(120), nullable=True) is_active: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False) created_by_user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id"), nullable=True) updated_by_user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id"), nullable=True) created_at_utc: Mapped[datetime] = mapped_column( DateTime(timezone=True), default=lambda: datetime.now(timezone.utc), nullable=False ) updated_at_utc: Mapped[datetime] = mapped_column( DateTime(timezone=True), default=lambda: datetime.now(timezone.utc), onupdate=lambda: datetime.now(timezone.utc), nullable=False, ) catalogue = relationship("ServiceCatalogue", back_populates="task_templates") document_requirements = relationship( "FirmTaskDocumentRequirement", back_populates="task_template", cascade="all, delete-orphan", passive_deletes=True, order_by="FirmTaskDocumentRequirement.sort_order.asc()", ) document_templates = relationship( "FirmTaskDocumentTemplate", back_populates="task_template", cascade="all, delete-orphan", passive_deletes=True, order_by="FirmTaskDocumentTemplate.uploaded_at_utc.desc()", ) class FirmTaskDocumentRequirement(CommonBase): """Document required at service task-template level. These rows define what must/should be collected when engagement task instances are generated from a firm task template. Actual uploaded files are linked to ClientServiceTaskInstance through EngagementDocument.task_instance_id. """ __tablename__ = "firm_task_document_requirements" __table_args__ = ( UniqueConstraint( "tenant_id", "firm_task_template_id", "document_name", name="uq_firm_task_document_requirements_name", ), ) id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True) tenant_id: Mapped[int] = mapped_column(ForeignKey("tenants.id", ondelete="CASCADE"), nullable=False, index=True) service_catalogue_id: Mapped[int] = mapped_column(ForeignKey("service_catalogues.id", ondelete="CASCADE"), nullable=False, index=True) firm_task_template_id: Mapped[int] = mapped_column(ForeignKey("firm_service_task_templates.id", ondelete="CASCADE"), nullable=False, index=True) document_name: Mapped[str] = mapped_column(String(200), nullable=False) document_type: Mapped[str] = mapped_column(String(80), nullable=False, default="GENERAL", index=True) is_mandatory: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False) allowed_file_types: Mapped[str | None] = mapped_column(String(255), nullable=True) instructions: Mapped[str | None] = mapped_column(Text, nullable=True) sort_order: Mapped[int] = mapped_column(Integer, nullable=False, default=100) is_active: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False) created_by_user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id"), nullable=True) updated_by_user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id"), nullable=True) created_at_utc: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=lambda: datetime.now(timezone.utc), nullable=False) updated_at_utc: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=lambda: datetime.now(timezone.utc), onupdate=lambda: datetime.now(timezone.utc), nullable=False) task_template = relationship("FirmServiceTaskTemplate", back_populates="document_requirements") catalogue = relationship("ServiceCatalogue") class FirmTaskDocumentTemplate(CommonBase): """Reusable uploaded template file attached to a firm task template. Example: GST registration NOC format, partnership deed format, agreement draft, company incorporation checklist, board resolution format etc. """ __tablename__ = "firm_task_document_templates" id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True) tenant_id: Mapped[int] = mapped_column(ForeignKey("tenants.id", ondelete="CASCADE"), nullable=False, index=True) service_catalogue_id: Mapped[int] = mapped_column(ForeignKey("service_catalogues.id", ondelete="CASCADE"), nullable=False, index=True) firm_task_template_id: Mapped[int] = mapped_column(ForeignKey("firm_service_task_templates.id", ondelete="CASCADE"), nullable=False, index=True) template_name: Mapped[str] = mapped_column(String(200), nullable=False) template_category: Mapped[str | None] = mapped_column(String(100), nullable=True, index=True) description: Mapped[str | None] = mapped_column(Text, nullable=True) original_filename: Mapped[str] = mapped_column(String(255), nullable=False) stored_filename: Mapped[str] = mapped_column(String(255), nullable=False) content_type: Mapped[str | None] = mapped_column(String(150), nullable=True) file_size_bytes: Mapped[int] = mapped_column(Integer, nullable=False, default=0) local_relative_path: Mapped[str] = mapped_column(String(1000), nullable=False) is_active: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False, index=True) uploaded_by_user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id"), nullable=True) uploaded_at_utc: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=lambda: datetime.now(timezone.utc), nullable=False) task_template = relationship("FirmServiceTaskTemplate", back_populates="document_templates") catalogue = relationship("ServiceCatalogue") class ServiceDueDateRule(CommonBase): """Statutory/compliance due-date rule attached to a service catalogue item.""" __tablename__ = "service_due_date_rules" __table_args__ = ( UniqueConstraint("service_catalogue_id", "rule_name", name="uq_service_due_date_rules_catalogue_name"), ) id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True) service_catalogue_id: Mapped[int] = mapped_column( ForeignKey("service_catalogues.id", ondelete="CASCADE"), nullable=False, index=True ) rule_name: Mapped[str] = mapped_column(String(150), nullable=False) period_type: Mapped[str] = mapped_column(String(30), nullable=False, default="yearly", index=True) due_year_basis: Mapped[str] = mapped_column(String(40), nullable=False, default="assessment_year_start") due_day: Mapped[int | None] = mapped_column(Integer, nullable=True) due_month: Mapped[int | None] = mapped_column(Integer, nullable=True) due_month_offset: Mapped[int] = mapped_column(Integer, nullable=False, default=0) days_offset_after_event: Mapped[int | None] = mapped_column(Integer, nullable=True) renewal_days_before_expiry: Mapped[int | None] = mapped_column(Integer, nullable=True) sort_order: Mapped[int] = mapped_column(Integer, nullable=False, default=100) is_active: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False, index=True) remarks: Mapped[str | None] = mapped_column(Text, nullable=True) created_by_user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id"), nullable=True) updated_by_user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id"), nullable=True) created_at_utc: Mapped[datetime] = mapped_column( DateTime(timezone=True), default=lambda: datetime.now(timezone.utc), nullable=False ) updated_at_utc: Mapped[datetime] = mapped_column( DateTime(timezone=True), default=lambda: datetime.now(timezone.utc), onupdate=lambda: datetime.now(timezone.utc), nullable=False, ) catalogue = relationship("ServiceCatalogue", back_populates="due_date_rules") class ServiceDueDateExtension(CommonBase): """History of statutory due-date extensions for a service/rule/FY/period.""" __tablename__ = "service_due_date_extensions" id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True) tenant_id: Mapped[int] = mapped_column(ForeignKey("tenants.id", ondelete="CASCADE"), nullable=False, index=True) service_catalogue_id: Mapped[int] = mapped_column( ForeignKey("service_catalogues.id", ondelete="CASCADE"), nullable=False, index=True ) due_date_rule_id: Mapped[int | None] = mapped_column( ForeignKey("service_due_date_rules.id", ondelete="SET NULL"), nullable=True, index=True ) financial_year: Mapped[str] = mapped_column(String(9), nullable=False, index=True) assessment_year: Mapped[str | None] = mapped_column(String(9), nullable=True, index=True) period_label: Mapped[str | None] = mapped_column(String(30), nullable=True, index=True) previous_due_date: Mapped[date | None] = mapped_column(Date, nullable=True) extended_due_date: Mapped[date] = mapped_column(Date, nullable=False, index=True) extension_sequence: Mapped[int] = mapped_column(Integer, nullable=False, default=1) notification_reference: Mapped[str | None] = mapped_column(String(200), nullable=True) notification_date: Mapped[date | None] = mapped_column(Date, nullable=True) remarks: Mapped[str | None] = mapped_column(Text, nullable=True) created_by_user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id"), nullable=True) updated_by_user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id"), nullable=True) created_at_utc: Mapped[datetime] = mapped_column( DateTime(timezone=True), default=lambda: datetime.now(timezone.utc), nullable=False ) updated_at_utc: Mapped[datetime] = mapped_column( DateTime(timezone=True), default=lambda: datetime.now(timezone.utc), onupdate=lambda: datetime.now(timezone.utc), nullable=False, ) catalogue = relationship("ServiceCatalogue") due_rule = relationship("ServiceDueDateRule") class ClientServiceSubscription(CommonBase): """Firm-level subscription of an enabled service to a specific client.""" __tablename__ = "client_service_subscriptions" __table_args__ = ( UniqueConstraint( "tenant_id", "client_id", "service_catalogue_id", "financial_year", "period_label", name="uq_client_service_subscription_tenant_client_service_year_period", ), ) id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True) tenant_id: Mapped[int] = mapped_column(ForeignKey("tenants.id", ondelete="CASCADE"), nullable=False, index=True) branch_id: Mapped[int | None] = mapped_column(ForeignKey("branches.id"), nullable=True, index=True) client_id: Mapped[int] = mapped_column(ForeignKey("clients.id", ondelete="CASCADE"), nullable=False, index=True) service_catalogue_id: Mapped[int] = mapped_column(ForeignKey("service_catalogues.id", ondelete="CASCADE"), nullable=False, index=True) firm_service_selection_id: Mapped[int | None] = mapped_column(ForeignKey("firm_service_selections.id", ondelete="SET NULL"), nullable=True, index=True) assigned_partner_user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id"), nullable=True, index=True) performing_partner_user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id"), nullable=True, index=True) assigned_manager_user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id"), nullable=True, index=True) assigned_staff_user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id"), nullable=True, index=True) review_partner_user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id"), nullable=True, index=True) financial_year: Mapped[str] = mapped_column(String(9), nullable=False, default="2025-26", index=True) period_label: Mapped[str] = mapped_column(String(30), nullable=False, default="", server_default="", index=True) assessment_year: Mapped[str | None] = mapped_column(String(9), nullable=True, index=True) engagement_type: Mapped[str] = mapped_column(String(20), nullable=False, default="non_assurance", index=True) due_date_rule_id: Mapped[int | None] = mapped_column(ForeignKey("service_due_date_rules.id", ondelete="SET NULL"), nullable=True, index=True) original_due_date: Mapped[date | None] = mapped_column(Date, nullable=True, index=True) current_due_date: Mapped[date | None] = mapped_column(Date, nullable=True, index=True) due_date_source: Mapped[str | None] = mapped_column(String(30), nullable=True) expiry_date: Mapped[date | None] = mapped_column(Date, nullable=True, index=True) start_date: Mapped[date | None] = mapped_column(Date, nullable=True) end_date: Mapped[date | None] = mapped_column(Date, nullable=True) status: Mapped[str] = mapped_column(String(30), nullable=False, default="active", index=True) remarks: Mapped[str | None] = mapped_column(Text, nullable=True) # AQMM / engagement-level quality workflow. This is intentionally attached # to the engagement/subscription, not to the client master, because one # client can have both assurance and non-assurance services in the same FY. quality_workflow_required: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False, index=True) quality_workflow_status: Mapped[str] = mapped_column(String(40), nullable=False, default="not_required", index=True) quality_acceptance_status: Mapped[str] = mapped_column(String(40), nullable=False, default="not_required", index=True) quality_independence_status: Mapped[str] = mapped_column(String(40), nullable=False, default="not_required", index=True) quality_conflict_status: Mapped[str] = mapped_column(String(40), nullable=False, default="not_required", index=True) quality_kyc_status: Mapped[str] = mapped_column(String(40), nullable=False, default="not_required", index=True) quality_engagement_letter_status: Mapped[str] = mapped_column(String(40), nullable=False, default="not_required", index=True) quality_approved_by_user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id"), nullable=True, index=True) quality_approved_at_utc: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True) quality_block_reason: Mapped[str | None] = mapped_column(Text, nullable=True) # Employee engagement workflow pause/dependency metadata (Phase 3). workflow_pause_reason: Mapped[str | None] = mapped_column(String(60), nullable=True, index=True) workflow_pause_notes: Mapped[str | None] = mapped_column(Text, nullable=True) workflow_follow_up_date: Mapped[date | None] = mapped_column(Date, nullable=True, index=True) workflow_paused_at_utc: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True) workflow_paused_by_user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id"), nullable=True, index=True) workflow_resumed_at_utc: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True) workflow_resumed_by_user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id"), nullable=True, index=True) is_active: Mapped[bool] = mapped_column(Boolean, nullable=False, default=True, index=True) is_locked: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False, index=True) locked_at_utc: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True) locked_by_user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id"), nullable=True) created_by_user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id"), nullable=True) updated_by_user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id"), nullable=True) created_at_utc: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=lambda: datetime.now(timezone.utc), nullable=False) updated_at_utc: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=lambda: datetime.now(timezone.utc), onupdate=lambda: datetime.now(timezone.utc), nullable=False) client = relationship("Client") catalogue = relationship("ServiceCatalogue") due_date_rule = relationship("ServiceDueDateRule", foreign_keys=[due_date_rule_id]) firm_selection = relationship("FirmServiceSelection") assigned_partner = relationship("User", foreign_keys=[assigned_partner_user_id]) performing_partner = relationship("User", foreign_keys=[performing_partner_user_id]) assigned_manager = relationship("User", foreign_keys=[assigned_manager_user_id]) assigned_staff = relationship("User", foreign_keys=[assigned_staff_user_id]) review_partner = relationship("User", foreign_keys=[review_partner_user_id]) locked_by = relationship("User", foreign_keys=[locked_by_user_id]) quality_approved_by = relationship("User", foreign_keys=[quality_approved_by_user_id]) workflow_paused_by = relationship("User", foreign_keys=[workflow_paused_by_user_id]) workflow_resumed_by = relationship("User", foreign_keys=[workflow_resumed_by_user_id]) class EngagementQualityDeclaration(CommonBase): """Digital AQMM declaration attached to an assurance engagement. Used for independence and conflict confirmations from the assigned partner, review partner, manager, staff and any other engagement team member. """ __tablename__ = "engagement_quality_declarations" __table_args__ = ( UniqueConstraint( "subscription_id", "declaration_type", "requested_user_id", name="uq_engagement_quality_declaration_user_type", ), ) id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True) tenant_id: Mapped[int] = mapped_column(ForeignKey("tenants.id", ondelete="CASCADE"), nullable=False, index=True) branch_id: Mapped[int | None] = mapped_column(ForeignKey("branches.id", ondelete="SET NULL"), nullable=True, index=True) client_id: Mapped[int] = mapped_column(ForeignKey("clients.id", ondelete="CASCADE"), nullable=False, index=True) subscription_id: Mapped[int] = mapped_column(ForeignKey("client_service_subscriptions.id", ondelete="CASCADE"), nullable=False, index=True) declaration_type: Mapped[str] = mapped_column(String(40), nullable=False, index=True) # independence / conflict requested_user_id: Mapped[int] = mapped_column(ForeignKey("users.id", ondelete="CASCADE"), nullable=False, index=True) requested_role: Mapped[str | None] = mapped_column(String(80), nullable=True) status: Mapped[str] = mapped_column(String(40), nullable=False, default="pending", index=True) declaration_text: Mapped[str | None] = mapped_column(Text, nullable=True) response_notes: Mapped[str | None] = mapped_column(Text, nullable=True) responded_at_utc: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True) ip_address: Mapped[str | None] = mapped_column(String(80), nullable=True) user_agent: Mapped[str | None] = mapped_column(String(500), nullable=True) created_by_user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id", ondelete="SET NULL"), nullable=True) created_at_utc: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=lambda: datetime.now(timezone.utc), nullable=False) updated_at_utc: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=lambda: datetime.now(timezone.utc), onupdate=lambda: datetime.now(timezone.utc), nullable=False) subscription = relationship("ClientServiceSubscription") client = relationship("Client") requested_user = relationship("User", foreign_keys=[requested_user_id]) created_by = relationship("User", foreign_keys=[created_by_user_id]) class EngagementKycVerification(CommonBase): """KYC verification for an assurance engagement using existing permanent documents.""" __tablename__ = "engagement_kyc_verifications" id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True) tenant_id: Mapped[int] = mapped_column(ForeignKey("tenants.id", ondelete="CASCADE"), nullable=False, index=True) branch_id: Mapped[int | None] = mapped_column(ForeignKey("branches.id", ondelete="SET NULL"), nullable=True, index=True) client_id: Mapped[int] = mapped_column(ForeignKey("clients.id", ondelete="CASCADE"), nullable=False, index=True) subscription_id: Mapped[int] = mapped_column(ForeignKey("client_service_subscriptions.id", ondelete="CASCADE"), nullable=False, index=True) status: Mapped[str] = mapped_column(String(40), nullable=False, default="pending_verification", index=True) source: Mapped[str] = mapped_column(String(80), nullable=False, default="permanent_documents") verification_notes: Mapped[str | None] = mapped_column(Text, nullable=True) verified_by_user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id", ondelete="SET NULL"), nullable=True, index=True) verified_at_utc: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True) created_by_user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id", ondelete="SET NULL"), nullable=True) created_at_utc: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=lambda: datetime.now(timezone.utc), nullable=False) updated_at_utc: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=lambda: datetime.now(timezone.utc), onupdate=lambda: datetime.now(timezone.utc), nullable=False) subscription = relationship("ClientServiceSubscription") client = relationship("Client") verified_by = relationship("User", foreign_keys=[verified_by_user_id]) created_by = relationship("User", foreign_keys=[created_by_user_id]) class EngagementLetter(CommonBase): """Engagement-specific letter acceptance evidence. Supports both client OTP acceptance and manual signed PDF upload/verification. File upload storage can be extended without changing the AQMM gate fields. """ __tablename__ = "engagement_letters" id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True) tenant_id: Mapped[int] = mapped_column(ForeignKey("tenants.id", ondelete="CASCADE"), nullable=False, index=True) branch_id: Mapped[int | None] = mapped_column(ForeignKey("branches.id", ondelete="SET NULL"), nullable=True, index=True) client_id: Mapped[int] = mapped_column(ForeignKey("clients.id", ondelete="CASCADE"), nullable=False, index=True) subscription_id: Mapped[int] = mapped_column(ForeignKey("client_service_subscriptions.id", ondelete="CASCADE"), nullable=False, index=True) title: Mapped[str] = mapped_column(String(255), nullable=False, default="Engagement Letter") version_no: Mapped[int] = mapped_column(Integer, nullable=False, default=1) status: Mapped[str] = mapped_column(String(50), nullable=False, default="draft_pending", index=True) acceptance_mode: Mapped[str | None] = mapped_column(String(40), nullable=True) # digital_otp / manual_signed_upload body_text: Mapped[str | None] = mapped_column(Text, nullable=True) pdf_sha256: Mapped[str | None] = mapped_column(String(128), nullable=True) manual_upload_filename: Mapped[str | None] = mapped_column(String(255), nullable=True) manual_upload_path: Mapped[str | None] = mapped_column(String(500), nullable=True) client_acceptance_declaration: Mapped[str | None] = mapped_column(Text, nullable=True) partner_approved_by_user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id", ondelete="SET NULL"), nullable=True, index=True) partner_approved_at_utc: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True) client_accepted_by_user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id", ondelete="SET NULL"), nullable=True, index=True) client_accepted_at_utc: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True) manual_verified_by_user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id", ondelete="SET NULL"), nullable=True, index=True) manual_verified_at_utc: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True) ip_address: Mapped[str | None] = mapped_column(String(80), nullable=True) user_agent: Mapped[str | None] = mapped_column(String(500), nullable=True) created_by_user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id", ondelete="SET NULL"), nullable=True) created_at_utc: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=lambda: datetime.now(timezone.utc), nullable=False) updated_at_utc: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=lambda: datetime.now(timezone.utc), onupdate=lambda: datetime.now(timezone.utc), nullable=False) subscription = relationship("ClientServiceSubscription") client = relationship("Client") partner_approved_by = relationship("User", foreign_keys=[partner_approved_by_user_id]) client_accepted_by = relationship("User", foreign_keys=[client_accepted_by_user_id]) manual_verified_by = relationship("User", foreign_keys=[manual_verified_by_user_id]) created_by = relationship("User", foreign_keys=[created_by_user_id]) class EngagementClosureChecklist(CommonBase): """Engagement closure checklist attached to the existing engagement. Closure is the final file-completion gate. It summarises evidence already available in AQMM acceptance, AQMM-tagged tasks, task documents/reviews, final released documents and UDIN controls. Only a few closure confirmations are manual; the rest are calculated from the existing workflow. """ __tablename__ = "engagement_closure_checklists" __table_args__ = ( UniqueConstraint("subscription_id", name="uq_engagement_closure_subscription"), ) id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True) tenant_id: Mapped[int] = mapped_column(ForeignKey("tenants.id", ondelete="CASCADE"), nullable=False, index=True) branch_id: Mapped[int | None] = mapped_column(ForeignKey("branches.id", ondelete="SET NULL"), nullable=True, index=True) client_id: Mapped[int] = mapped_column(ForeignKey("clients.id", ondelete="CASCADE"), nullable=False, index=True) subscription_id: Mapped[int] = mapped_column(ForeignKey("client_service_subscriptions.id", ondelete="CASCADE"), nullable=False, index=True) closure_status: Mapped[str] = mapped_column(String(40), nullable=False, default="not_started", index=True) aqmm_acceptance_completed: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False) aqmm_tasks_completed: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False) evidence_review_completed: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False) final_documents_released: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False) udin_completed: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False) normal_tasks_completed: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False) deliverables_sent_to_client: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False) billing_reviewed: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False) open_points_closed: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False) client_communication_completed: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False) closure_note: Mapped[str | None] = mapped_column(Text, nullable=True) closure_block_reason: Mapped[str | None] = mapped_column(Text, nullable=True) closure_approved_by_user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id", ondelete="SET NULL"), nullable=True, index=True) closure_approved_at_utc: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True) reopened_by_user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id", ondelete="SET NULL"), nullable=True, index=True) reopened_at_utc: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True) reopen_reason: Mapped[str | None] = mapped_column(Text, nullable=True) created_by_user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id", ondelete="SET NULL"), nullable=True) created_at_utc: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=lambda: datetime.now(timezone.utc), nullable=False) updated_at_utc: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=lambda: datetime.now(timezone.utc), onupdate=lambda: datetime.now(timezone.utc), nullable=False) subscription = relationship("ClientServiceSubscription") client = relationship("Client") closure_approved_by = relationship("User", foreign_keys=[closure_approved_by_user_id]) reopened_by = relationship("User", foreign_keys=[reopened_by_user_id]) created_by = relationship("User", foreign_keys=[created_by_user_id]) class ClientServiceTaskInstance(CommonBase): """Execution task generated from a firm service task template for a client-service subscription.""" __tablename__ = "client_service_task_instances" __table_args__ = ( UniqueConstraint( "subscription_id", "firm_task_template_id", "financial_year", name="uq_client_service_task_subscription_template_year", ), ) id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True) tenant_id: Mapped[int] = mapped_column(ForeignKey("tenants.id", ondelete="CASCADE"), nullable=False, index=True) branch_id: Mapped[int | None] = mapped_column(ForeignKey("branches.id"), nullable=True, index=True) subscription_id: Mapped[int] = mapped_column(ForeignKey("client_service_subscriptions.id", ondelete="CASCADE"), nullable=False, index=True) client_id: Mapped[int] = mapped_column(ForeignKey("clients.id", ondelete="CASCADE"), nullable=False, index=True) service_catalogue_id: Mapped[int] = mapped_column(ForeignKey("service_catalogues.id", ondelete="CASCADE"), nullable=False, index=True) firm_task_template_id: Mapped[int | None] = mapped_column(ForeignKey("firm_service_task_templates.id", ondelete="SET NULL"), nullable=True, index=True) financial_year: Mapped[str] = mapped_column(String(9), nullable=False, default="2025-26", index=True) period_label: Mapped[str] = mapped_column(String(30), nullable=False, default="", server_default="", index=True) assessment_year: Mapped[str | None] = mapped_column(String(9), nullable=True, index=True) task_name: Mapped[str] = mapped_column(String(200), nullable=False) description: Mapped[str | None] = mapped_column(Text, nullable=True) sequence_no: Mapped[int] = mapped_column(Integer, nullable=False, default=1) default_role_name: Mapped[str | None] = mapped_column(String(100), nullable=True) assigned_to_user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id"), nullable=True, index=True) execution_mode: Mapped[str] = mapped_column(String(30), nullable=False, default="internal", index=True) assigned_consultant_id: Mapped[int | None] = mapped_column(ForeignKey("consultant_profiles.id", ondelete="SET NULL"), nullable=True, index=True) consultant_assignment_status: Mapped[str] = mapped_column(String(30), nullable=False, default="not_applicable", index=True) consultant_due_date: Mapped[date | None] = mapped_column(Date, nullable=True, index=True) consultant_offered_at_utc: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True) consultant_accepted_at_utc: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True) consultant_declined_at_utc: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True) consultant_decline_reason: Mapped[str | None] = mapped_column(Text, nullable=True) consultant_started_at_utc: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True) consultant_submitted_at_utc: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True) consultant_submission_note: Mapped[str | None] = mapped_column(Text, nullable=True) consultant_approved_at_utc: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True) consultant_approved_by_user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id", ondelete="SET NULL"), nullable=True, index=True) internal_target_date: Mapped[date | None] = mapped_column(Date, nullable=True, index=True) status: Mapped[str] = mapped_column(String(30), nullable=False, default="pending", index=True) priority: Mapped[str] = mapped_column(String(20), nullable=False, default="normal") # Copied from FirmServiceTaskTemplate at generation time. Existing task # completion/evidence upload flow is reused to calculate AQMM quality status. is_aqmm_task: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False, index=True) aqmm_mandatory: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False) aqmm_evidence_required: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False) aqmm_manager_review_required: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False) aqmm_partner_review_required: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False) aqmm_review_partner_required: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False) aqmm_blocks_final_release: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False) aqmm_reference: Mapped[str | None] = mapped_column(String(120), nullable=True) aqmm_status: Mapped[str] = mapped_column(String(40), default="not_required", nullable=False, index=True) aqmm_review_status: Mapped[str] = mapped_column(String(40), default="not_required", nullable=False, index=True) aqmm_completed_at_utc: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True) # Phase 4 - AQMM task evidence and review notes. # For AQMM-tagged tasks, documents, work notes and review notes on this # task become the AQMM evidence trail. Normal non-AQMM tasks continue to use # the same fields as optional operational notes. evidence_status: Mapped[str] = mapped_column(String(30), nullable=False, default="not_required", index=True) submitted_for_review_by_user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id"), nullable=True, index=True) submitted_for_review_at_utc: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True) manager_review_status: Mapped[str] = mapped_column(String(30), nullable=False, default="not_required", index=True) manager_review_note: Mapped[str | None] = mapped_column(Text, nullable=True) manager_reviewed_by_user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id"), nullable=True, index=True) manager_reviewed_at_utc: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True) partner_review_status: Mapped[str] = mapped_column(String(30), nullable=False, default="not_required", index=True) partner_review_note: Mapped[str | None] = mapped_column(Text, nullable=True) partner_reviewed_by_user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id"), nullable=True, index=True) partner_reviewed_at_utc: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True) review_partner_review_status: Mapped[str] = mapped_column(String(30), nullable=False, default="not_required", index=True) review_partner_review_note: Mapped[str | None] = mapped_column(Text, nullable=True) review_partner_reviewed_by_user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id"), nullable=True, index=True) review_partner_reviewed_at_utc: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True) rework_status: Mapped[str] = mapped_column(String(30), nullable=False, default="none", index=True) rework_reason: Mapped[str | None] = mapped_column(Text, nullable=True) rework_requested_by_user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id"), nullable=True, index=True) rework_requested_at_utc: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True) rework_resolved_at_utc: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True) remarks: Mapped[str | None] = mapped_column(Text, nullable=True) started_at_utc: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True) completed_at_utc: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True) is_active: Mapped[bool] = mapped_column(Boolean, nullable=False, default=True, index=True) is_locked: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False, index=True) locked_at_utc: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True) locked_by_user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id"), nullable=True) created_by_user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id"), nullable=True) updated_by_user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id"), nullable=True) created_at_utc: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=lambda: datetime.now(timezone.utc), nullable=False) updated_at_utc: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=lambda: datetime.now(timezone.utc), onupdate=lambda: datetime.now(timezone.utc), nullable=False) subscription = relationship("ClientServiceSubscription") client = relationship("Client") catalogue = relationship("ServiceCatalogue") template = relationship("FirmServiceTaskTemplate") assigned_to = relationship("User", foreign_keys=[assigned_to_user_id]) assigned_consultant = relationship("ConsultantProfile", foreign_keys=[assigned_consultant_id]) consultant_approved_by = relationship("User", foreign_keys=[consultant_approved_by_user_id]) locked_by = relationship("User", foreign_keys=[locked_by_user_id]) documents = relationship( "EngagementDocument", primaryjoin="ClientServiceTaskInstance.id == foreign(EngagementDocument.task_instance_id)", viewonly=True, order_by="EngagementDocument.updated_at_utc.desc()", ) document_requests = relationship( "ServiceTaskDocumentRequest", back_populates="task", cascade="all, delete-orphan", passive_deletes=True, order_by="ServiceTaskDocumentRequest.created_at_utc.desc()", ) comments = relationship( "ServiceTaskComment", back_populates="task", cascade="all, delete-orphan", passive_deletes=True, order_by="ServiceTaskComment.created_at_utc.desc()", ) class ServiceTaskDocumentRequest(CommonBase): """Live document/clarification requirement raised against an engagement task.""" __tablename__ = "service_task_document_requests" id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True) tenant_id: Mapped[int] = mapped_column(ForeignKey("tenants.id", ondelete="CASCADE"), nullable=False, index=True) branch_id: Mapped[int | None] = mapped_column(ForeignKey("branches.id"), nullable=True, index=True) subscription_id: Mapped[int] = mapped_column(ForeignKey("client_service_subscriptions.id", ondelete="CASCADE"), nullable=False, index=True) task_instance_id: Mapped[int] = mapped_column(ForeignKey("client_service_task_instances.id", ondelete="CASCADE"), nullable=False, index=True) client_id: Mapped[int] = mapped_column(ForeignKey("clients.id", ondelete="CASCADE"), nullable=False, index=True) request_type: Mapped[str] = mapped_column(String(30), nullable=False, default="document", index=True) title: Mapped[str] = mapped_column(String(200), nullable=False) description: Mapped[str | None] = mapped_column(Text, nullable=True) requested_from: Mapped[str] = mapped_column(String(30), nullable=False, default="client_and_consultant", index=True) due_date: Mapped[date | None] = mapped_column(Date, nullable=True, index=True) status: Mapped[str] = mapped_column(String(30), nullable=False, default="pending", index=True) response_note: Mapped[str | None] = mapped_column(Text, nullable=True) received_at_utc: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True) verified_at_utc: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True) requested_by_user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id", ondelete="SET NULL"), nullable=True) responded_by_user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id", ondelete="SET NULL"), nullable=True) verified_by_user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id", ondelete="SET NULL"), nullable=True) created_at_utc: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=lambda: datetime.now(timezone.utc), nullable=False) updated_at_utc: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=lambda: datetime.now(timezone.utc), onupdate=lambda: datetime.now(timezone.utc), nullable=False) is_active: Mapped[bool] = mapped_column(Boolean, nullable=False, default=True, index=True) task = relationship("ClientServiceTaskInstance", back_populates="document_requests") subscription = relationship("ClientServiceSubscription") client = relationship("Client") requested_by = relationship("User", foreign_keys=[requested_by_user_id]) responded_by = relationship("User", foreign_keys=[responded_by_user_id]) verified_by = relationship("User", foreign_keys=[verified_by_user_id]) class ServiceTaskComment(CommonBase): """Communication timeline entry linked to a service task instance.""" __tablename__ = "service_task_comments" id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True) tenant_id: Mapped[int] = mapped_column(ForeignKey("tenants.id", ondelete="CASCADE"), nullable=False, index=True) branch_id: Mapped[int | None] = mapped_column(ForeignKey("branches.id"), nullable=True, index=True) subscription_id: Mapped[int] = mapped_column(ForeignKey("client_service_subscriptions.id", ondelete="CASCADE"), nullable=False, index=True) task_instance_id: Mapped[int] = mapped_column(ForeignKey("client_service_task_instances.id", ondelete="CASCADE"), nullable=False, index=True) comment_type: Mapped[str] = mapped_column(String(40), nullable=False, default="internal_note", index=True) visibility: Mapped[str] = mapped_column(String(30), nullable=False, default="internal", index=True) message: Mapped[str] = mapped_column(Text, nullable=False) created_by_user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id"), nullable=True, index=True) created_at_utc: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=lambda: datetime.now(timezone.utc), nullable=False) updated_at_utc: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=lambda: datetime.now(timezone.utc), onupdate=lambda: datetime.now(timezone.utc), nullable=False) is_deleted: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False, index=True) task = relationship("ClientServiceTaskInstance", back_populates="comments") subscription = relationship("ClientServiceSubscription") created_by = relationship("User", foreign_keys=[created_by_user_id])