"""Module for case.""" from __future__ import annotations from typing import TYPE_CHECKING, ClassVar from django.db import models from django.utils.translation import gettext_lazy as _ from apps.core.models.enums import AuthorityType, CaseStage, CaseStatus, SimpleCaseType if TYPE_CHECKING: from django.db.models.fields.related_descriptors import RelatedManager from .chat import CaseChat, ChatAuditLog from .log import CaseLog from .material import CaseFolderBinding, CaseMaterial, CaseMaterialGroupOrder from .party import CaseAccessGrant, CaseAssignment, CaseParty from .template_binding import CaseTemplateBinding class Case(models.Model): id: int contract = models.ForeignKey( "contracts.Contract", on_delete=models.CASCADE, null=True, blank=True, related_name="cases", verbose_name=_("关联合同"), ) is_archived = models.BooleanField(default=False, verbose_name=_("是否已建档")) filing_number = models.CharField( max_length=50, blank=True, null=True, unique=True, verbose_name=_("建档编号"), help_text=_("格式: {年份}_{案件类型}_{AJ}_{序号}"), ) name = models.CharField(max_length=255, verbose_name=_("案件名称")) status = models.CharField( max_length=32, choices=CaseStatus.choices, default=CaseStatus.ACTIVE, verbose_name=_("案件状态") ) start_date = models.DateField(auto_now_add=True, verbose_name=_("收案日期")) effective_date = models.DateField(blank=True, null=True, verbose_name=_("生效日期")) specified_date = models.DateField(blank=True, null=True, verbose_name=_("指定日期")) cause_of_action = models.CharField(max_length=128, blank=True, null=True, verbose_name=_("案由")) target_amount = models.DecimalField( max_digits=14, decimal_places=2, blank=True, null=True, verbose_name=_("涉案金额") ) preservation_amount = models.DecimalField( max_digits=14, decimal_places=2, blank=True, null=True, verbose_name=_("财产保全金额") ) case_type = models.CharField( max_length=32, choices=SimpleCaseType.choices, default=SimpleCaseType.CIVIL, blank=True, null=True, verbose_name=_("案件类型"), ) current_stage = models.CharField( max_length=64, choices=CaseStage.choices, blank=True, null=True, verbose_name=_("当前阶段") ) if TYPE_CHECKING: case_numbers: RelatedManager[CaseNumber] supervising_authorities: RelatedManager[SupervisingAuthority] parties: RelatedManager[CaseParty] assignments: RelatedManager[CaseAssignment] access_grants: RelatedManager[CaseAccessGrant] logs: RelatedManager[CaseLog] chats: RelatedManager[CaseChat] chat_audit_logs: RelatedManager[ChatAuditLog] materials: RelatedManager[CaseMaterial] material_group_orders: RelatedManager[CaseMaterialGroupOrder] folder_binding: CaseFolderBinding template_bindings: RelatedManager[CaseTemplateBinding] class Meta: verbose_name = _("案件") verbose_name_plural = _("案件") indexes: ClassVar = [ models.Index(fields=["contract"]), models.Index(fields=["is_archived"]), models.Index(fields=["filing_number"]), models.Index(fields=["start_date"]), models.Index(fields=["current_stage"]), models.Index(fields=["-start_date"]), ] def __str__(self) -> str: return f"{self.name}" def clean(self) -> None: """ 基础数据验证 复杂业务逻辑已移至 CaseService """ from django.core.exceptions import ValidationError if self.current_stage: valid_stages = {c[0] for c in CaseStage.choices} if self.current_stage not in valid_stages: raise ValidationError({"current_stage": _("无效的案件阶段")}) class CaseFilingNumberSequence(models.Model): id: int year = models.IntegerField(unique=True, verbose_name=_("年份")) next_value = models.IntegerField(default=1, verbose_name=_("下一个序号")) updated_at = models.DateTimeField(auto_now=True, verbose_name=_("更新时间")) class Meta: verbose_name = _("案件建档编号序列") verbose_name_plural = _("案件建档编号序列") indexes: ClassVar = [models.Index(fields=["year"])] class CaseNumber(models.Model): YEAR_DAYS_CHOICES: ClassVar = ( (360, _("360天")), (365, _("365天")), (0, _("按实际天数")), ) DATE_INCLUSION_CHOICES: ClassVar = ( ("both", _("起止日都计入")), ("start_only", _("仅计入起始日")), ("end_only", _("仅计入截止日")), ("neither", _("起止日都不计入")), ) id: int case = models.ForeignKey(Case, on_delete=models.CASCADE, related_name="case_numbers", verbose_name=_("案件")) number = models.CharField(max_length=128, verbose_name=_("案号")) document_name = models.CharField( max_length=128, blank=True, null=True, verbose_name=_("文书名称"), help_text=_("如:民事判决书、民事调解书、执行证书等"), ) document_file = models.FileField( upload_to="case_documents/%Y/%m/", blank=True, null=True, verbose_name=_("裁判文书文件"), help_text=_("上传PDF格式的裁判文书,用于自动提取执行依据主文"), ) document_content = models.TextField( blank=True, null=True, verbose_name=_("执行依据主文"), help_text=_("从裁判文书自动提取的判决/调解主文内容"), ) is_active = models.BooleanField(default=False, verbose_name=_("是否已生效")) execution_cutoff_date = models.DateField( blank=True, null=True, verbose_name=_("执行事项截止日"), help_text=_("申请执行事项中利息计算截至日期;为空时优先按案件“指定日期”计算,未填写指定日期时按当天计算"), ) execution_paid_amount = models.DecimalField( max_digits=14, decimal_places=2, default=0, verbose_name=_("已付款金额"), help_text=_("用于按抵扣顺序重算未付款项,默认 0"), ) execution_use_deduction_order = models.BooleanField( default=False, verbose_name=_("启用抵扣顺序"), help_text=_("启用后按文书条款中的抵扣顺序处理已付款"), ) execution_year_days = models.PositiveSmallIntegerField( choices=YEAR_DAYS_CHOICES, default=360, verbose_name=_("年基准天数"), help_text=_("利息计算参数:360 / 365 / 按实际天数"), ) execution_date_inclusion = models.CharField( max_length=16, choices=DATE_INCLUSION_CHOICES, default="both", verbose_name=_("日期包含方式"), help_text=_("利息计算参数:起止日期是否计入"), ) execution_manual_text = models.TextField( blank=True, null=True, verbose_name=_("申请执行事项(手工最终文本)"), help_text=_("有值时模板生成优先使用该文本"), ) remarks = models.TextField(blank=True, null=True, verbose_name=_("备注")) created_at = models.DateTimeField(auto_now_add=True, verbose_name=_("创建时间")) class Meta: verbose_name = _("案件案号") verbose_name_plural = _("案件案号") ordering: ClassVar = ["created_at"] def __str__(self) -> str: return f"{self.number}" def get_full_number(self) -> str: """获取完整案号(案号+文书名称)""" if self.document_name: return f"{self.number}《{self.document_name}》" return self.number class SupervisingAuthority(models.Model): """主管机关""" id: int case = models.ForeignKey( Case, on_delete=models.CASCADE, related_name="supervising_authorities", verbose_name=_("案件") ) name = models.CharField(max_length=255, blank=True, null=True, verbose_name=_("名称")) authority_type = models.CharField( max_length=32, choices=AuthorityType.choices, default=AuthorityType.TRIAL, blank=True, null=True, verbose_name=_("性质"), ) created_at = models.DateTimeField(auto_now_add=True, verbose_name=_("创建时间")) if TYPE_CHECKING: materials: RelatedManager[CaseMaterial] material_group_orders: RelatedManager[CaseMaterialGroupOrder] class Meta: verbose_name = _("主管机关") verbose_name_plural = _("主管机关") ordering: ClassVar = ["created_at"] indexes: ClassVar = [ models.Index(fields=["case"]), models.Index(fields=["authority_type"]), ] def __str__(self) -> str: authority_type_display = self.get_authority_type_display() if self.name and self.authority_type: return f"{authority_type_display} - {self.name}" elif self.name: return self.name elif self.authority_type: return str(authority_type_display) return f"主管机关 #{self.id}"