File size: 9,443 Bytes
b4f2238
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
"""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}"