File size: 5,610 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 | """Module for contract."""
from __future__ import annotations
import contextlib
from typing import TYPE_CHECKING, Any, ClassVar
from django.db import models
from django.utils import timezone
from django.utils.translation import gettext_lazy as _
from apps.core.models.enums import CaseType
if TYPE_CHECKING:
from django.db.models.fields.related_descriptors import RelatedManager
from apps.cases.models import Case
from .client_payment import ClientPaymentRecord
from .finalized_material import FinalizedMaterial
from .finance import ContractFinanceLog
from .folder_binding import ContractFolderBinding
from .party import ContractAssignment, ContractParty
from .payment import ContractPayment
from .supplementary import SupplementaryAgreement
class ContractStatus(models.TextChoices):
ACTIVE = "active", _("在办")
CLOSED = "closed", _("已结案")
ARCHIVED = "archived", _("已归档")
class FeeMode(models.TextChoices):
FIXED = "FIXED", _("固定收费")
SEMI_RISK = "SEMI_RISK", _("半风险收费")
FULL_RISK = "FULL_RISK", _("全风险收费")
CUSTOM = "CUSTOM", _("自定义")
class Contract(models.Model):
id: int
name: models.CharField = models.CharField(max_length=100, verbose_name=_("合同名称"))
case_type: models.CharField = models.CharField(max_length=32, choices=CaseType.choices, verbose_name=_("合同类型"))
status: models.CharField = models.CharField(
max_length=32, choices=ContractStatus.choices, default=ContractStatus.ACTIVE, verbose_name=_("合同状态")
)
specified_date: models.DateField = models.DateField(default=timezone.localdate, verbose_name=_("指定日期"))
start_date: models.DateField = models.DateField(blank=True, null=True, verbose_name=_("开始日期"))
end_date: models.DateField = models.DateField(blank=True, null=True, verbose_name=_("结束日期"))
is_archived: models.BooleanField = models.BooleanField(default=False, verbose_name=_("是否已建档"))
filing_number: models.CharField = models.CharField(
max_length=50,
blank=True,
null=True,
unique=True,
verbose_name=_("建档编号"),
help_text=_("格式: {年份}_{合同类型}_{HT}_{序号}"),
)
fee_mode: models.CharField = models.CharField(
max_length=16, choices=FeeMode.choices, default=FeeMode.FIXED, verbose_name=_("收费模式")
)
fixed_amount: models.DecimalField = models.DecimalField(
max_digits=14, decimal_places=2, blank=True, null=True, verbose_name=_("固定/前期律师费")
)
risk_rate: models.DecimalField = models.DecimalField(
max_digits=5, decimal_places=2, blank=True, null=True, verbose_name=_("风险比例(%)")
)
custom_terms: models.TextField = models.TextField(blank=True, null=True, verbose_name=_("自定义收费条款"))
law_firm_oa_url: models.URLField = models.URLField(
max_length=500,
blank=True,
null=True,
verbose_name=_("律所OA链接"),
help_text=_("跳转至律所OA系统中该合同的页面"),
)
law_firm_oa_case_number: models.CharField = models.CharField(
max_length=100,
blank=True,
null=True,
verbose_name=_("律所OA案件编号"),
help_text=_("律所OA系统中的案件编号"),
)
representation_stages: Any = models.JSONField(default=list, blank=True, verbose_name=_("代理阶段"))
if TYPE_CHECKING:
cases: RelatedManager[Case]
finance_logs: RelatedManager[ContractFinanceLog]
folder_binding: ContractFolderBinding
contract_parties: RelatedManager[ContractParty]
assignments: RelatedManager[ContractAssignment]
payments: RelatedManager[ContractPayment]
supplementary_agreements: RelatedManager[SupplementaryAgreement]
finalized_materials: RelatedManager[FinalizedMaterial]
client_payment_records: RelatedManager[ClientPaymentRecord]
class Meta:
verbose_name = _("合同")
verbose_name_plural = _("合同")
indexes: ClassVar = [
# 单字段索引 - 用于基本过滤
models.Index(fields=["case_type"]),
models.Index(fields=["status"]),
models.Index(fields=["is_archived"]),
models.Index(fields=["specified_date"]),
models.Index(fields=["-specified_date"]),
models.Index(fields=["filing_number"]),
# 复合索引 - 用于常见的组合查询
# 按案件类型和状态查询(常用于列表过滤)
models.Index(fields=["case_type", "status"]),
# 按状态和指定日期查询(常用于时间范围过滤)
models.Index(fields=["status", "-specified_date"]),
# 按是否建档和指定日期查询(常用于归档管理)
models.Index(fields=["is_archived", "-specified_date"]),
# 按案件类型、状态和指定日期查询(常用于复杂过滤)
models.Index(fields=["case_type", "status", "-specified_date"]),
]
def __str__(self) -> str:
return str(self.name)
def clean(self) -> None:
from apps.contracts.validators import normalize_representation_stages
from apps.core.exceptions import ValidationException
ctype = getattr(self, "case_type", None)
rep = getattr(self, "representation_stages", None)
with contextlib.suppress(ValidationException):
self.representation_stages = normalize_representation_stages(ctype, rep, strict=False)
|