diff --git a/backend/app/services/contract_generation_service.py b/backend/app/services/contract_generation_service.py index eddbf591..a4868216 100644 --- a/backend/app/services/contract_generation_service.py +++ b/backend/app/services/contract_generation_service.py @@ -62,8 +62,11 @@ DOCUMENT_TYPE_LEGAL_QUERY_TERMS = { } TEMPLATE_DOCUMENT_TYPE_CODE_ALIASES = { - "property_service": "property_service_agreement", - "rescission_notice": "contract_termination_notice", + "property_service": ["property_service", "property_service_agreement"], + "property_service_agreement": ["property_service", "property_service_agreement"], + "rescission_notice": ["rescission_notice", "contract_termination_notice"], + "contract_termination_notice": ["rescission_notice", "contract_termination_notice"], + "termination_notice": ["rescission_notice", "contract_termination_notice"], } CONTRACT_LIFECYCLE_STATUSES = { @@ -126,18 +129,31 @@ class ContractGenerationService: tenant_id = current_user["tenant_id"] normalized_document_type_code = None if document_type_code is not None: - normalized_document_type_code = TEMPLATE_DOCUMENT_TYPE_CODE_ALIASES.get( - document_type_code.strip().lower().replace("-", "_"), - document_type_code.strip().lower().replace("-", "_"), + normalized_document_type_code = document_type_code.strip().lower().replace("-", "_") + + document_type_candidates = [None] + if normalized_document_type_code is not None: + document_type_candidates = TEMPLATE_DOCUMENT_TYPE_CODE_ALIASES.get( + normalized_document_type_code, + [normalized_document_type_code], ) - # 获取启用的模板 - templates = self.repository.list_enabled( - tenant_id=tenant_id, - contract_type=contract_type, - template_category=template_category, - industry_category=industry_category, - document_type_code=normalized_document_type_code, + templates_by_id: Dict[str, Any] = {} + for candidate in document_type_candidates: + matched_templates = self.repository.list_enabled( + tenant_id=tenant_id, + contract_type=contract_type, + template_category=template_category, + industry_category=industry_category, + document_type_code=candidate, + ) + for template in matched_templates: + templates_by_id[template.id] = template + + templates = sorted( + templates_by_id.values(), + key=lambda template: (template.updated_at, template.created_at), + reverse=True, ) return [ diff --git a/backend/app/services/legal_document_draft_service.py b/backend/app/services/legal_document_draft_service.py index 2013a0e5..669e2287 100644 --- a/backend/app/services/legal_document_draft_service.py +++ b/backend/app/services/legal_document_draft_service.py @@ -15,8 +15,11 @@ from app.services.legal_document_element_schema_service import LegalDocumentElem VALID_DRAFT_STATUS = {"draft", "generated", "deleted"} TEMPLATE_DOCUMENT_TYPE_CODE_ALIASES = { - "property_service": "property_service_agreement", - "rescission_notice": "contract_termination_notice", + "property_service": ["property_service", "property_service_agreement"], + "property_service_agreement": ["property_service", "property_service_agreement"], + "rescission_notice": ["rescission_notice", "contract_termination_notice"], + "contract_termination_notice": ["rescission_notice", "contract_termination_notice"], + "termination_notice": ["rescission_notice", "contract_termination_notice"], } @@ -170,14 +173,15 @@ class LegalDocumentDraftService: if template is None or not template.enabled: raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="template not found") return template - normalized_document_type_code = TEMPLATE_DOCUMENT_TYPE_CODE_ALIASES.get( - document_type_code.strip().lower().replace("-", "_"), - document_type_code.strip().lower().replace("-", "_"), - ) - templates = repository.list_enabled(tenant_id=tenant_id, document_type_code=normalized_document_type_code) - if not templates: + normalized_document_type_code = document_type_code.strip().lower().replace("-", "_") + candidates = TEMPLATE_DOCUMENT_TYPE_CODE_ALIASES.get(normalized_document_type_code, [normalized_document_type_code]) + for candidate in candidates: + templates = repository.list_enabled(tenant_id=tenant_id, document_type_code=candidate) + if templates: + return templates[0] + if not candidates: raise HTTPException(status_code=status.HTTP_409_CONFLICT, detail="no enabled template for document type") - return templates[0] + raise HTTPException(status_code=status.HTTP_409_CONFLICT, detail="no enabled template for document type") def _require_accessible_draft(self, current_user: dict, draft_id: str) -> LegalDocumentDraft: self._require_authenticated(current_user) diff --git a/backend/app/services/template_service.py b/backend/app/services/template_service.py index c3958002..eab9dc75 100644 --- a/backend/app/services/template_service.py +++ b/backend/app/services/template_service.py @@ -309,7 +309,7 @@ DEFAULT_TEMPLATES: list[dict[str, Any]] = [ }, { "name": "投资协议模板", - "contract_type": "investment", + "contract_type": "investment_agreement", "document_type_code": "investment_agreement", "template_category": "investment-finance", "industry_category": "general-enterprise", @@ -651,7 +651,7 @@ DEFAULT_TEMPLATES: list[dict[str, Any]] = [ "TPL-CONTRACT-TERMINATION-NOTICE-001", name="解除合同通知书模板", contract_type="rescission_notice", - document_type_code="contract_termination_notice", + document_type_code="rescission_notice", template_category="notices-letters", industry_category="general-enterprise", description="适用于依法或依约解除合同并保留证据的通知场景。", @@ -681,7 +681,7 @@ DEFAULT_TEMPLATES: list[dict[str, Any]] = [ "TPL-PROPERTY-SERVICE-001", name="物业服务合同模板", contract_type="property_service", - document_type_code="property_service_agreement", + document_type_code="property_service", template_category="real-estate", industry_category="real-estate", description="适用于住宅、商办园区和产业园区的物业服务、收费、维修资金、安全协助和资料交接场景。", @@ -1175,8 +1175,8 @@ class TemplateService: normalized = document_type_code.strip().lower().replace("-", "_") if document_type_code else "" if normalized: return { - "property_service": "property_service_agreement", - "rescission_notice": "contract_termination_notice", + "property_service_agreement": "property_service", + "contract_termination_notice": "rescission_notice", }.get(normalized, normalized) return { "labor": "labor_contract", @@ -1210,14 +1210,14 @@ class TemplateService: "power_of_attorney": "power_of_attorney", "legal-opinion": "legal_opinion", "legal_opinion": "legal_opinion", - "property-service": "property_service_agreement", - "property_service": "property_service_agreement", + "property-service": "property_service", + "property_service": "property_service", "demand-letter": "demand_letter", "demand_letter": "demand_letter", - "rescission-notice": "contract_termination_notice", - "rescission_notice": "contract_termination_notice", - "termination-notice": "contract_termination_notice", - "contract_termination_notice": "contract_termination_notice", + "rescission-notice": "rescission_notice", + "rescission_notice": "rescission_notice", + "termination-notice": "rescission_notice", + "contract_termination_notice": "rescission_notice", "procurement": "procurement_goods", "purchase": "procurement_goods", "sales": "procurement_goods", diff --git a/backend/tests/test_template_service.py b/backend/tests/test_template_service.py index 9537248e..4436e022 100644 --- a/backend/tests/test_template_service.py +++ b/backend/tests/test_template_service.py @@ -329,7 +329,7 @@ class TemplateServiceTestCase(unittest.TestCase): self.assertEqual(created["contract_type"], "rescission-notice") self.assertEqual(created["template_code"], "TPL-CONTRACT-TERMINATION-NOTICE-001") - self.assertEqual(created["document_type_code"], "contract_termination_notice") + self.assertEqual(created["document_type_code"], "rescission_notice") self.assertEqual(created["template_category"], "notices-letters") self.assertEqual(created["industry_category"], "general-enterprise") @@ -388,14 +388,14 @@ class TemplateServiceTestCase(unittest.TestCase): created = self.service.create_template( self.current_user, name="投资协议模板", - contract_type=" investment ", + contract_type=" investment_agreement ", description="Investment agreement template", content="Boundary clause", variables_json=None, enabled=True, ) - self.assertEqual(created["contract_type"], "investment") + self.assertEqual(created["contract_type"], "investment_agreement") self.assertEqual(created["template_code"], "TPL-INVESTMENT-AGREEMENT-001") self.assertEqual(created["document_type_code"], "investment_agreement") self.assertEqual(created["template_category"], "investment-finance") @@ -499,7 +499,7 @@ class TemplateServiceTestCase(unittest.TestCase): self.assertEqual(created["contract_type"], "property-service") self.assertEqual(created["template_code"], "TPL-PROPERTY-SERVICE-001") - self.assertEqual(created["document_type_code"], "property_service_agreement") + self.assertEqual(created["document_type_code"], "property_service") self.assertEqual(created["template_category"], "real-estate") self.assertEqual(created["industry_category"], "real-estate") diff --git a/backend/tools/asset_inventory_gap_report.py b/backend/tools/asset_inventory_gap_report.py index edd1109a..b9eea80b 100644 --- a/backend/tools/asset_inventory_gap_report.py +++ b/backend/tools/asset_inventory_gap_report.py @@ -26,7 +26,7 @@ EXPECTED_RUNTIME_TEMPLATES = [ {"template_code": "TPL-COOPERATION-FRAMEWORK-001", "name": "合作框架协议模板", "document_type_code": "cooperation_framework"}, {"template_code": "TPL-EQUITY-TRANSFER-001", "name": "股权转让协议模板", "document_type_code": "equity_transfer"}, {"template_code": "TPL-DEMAND-LETTER-001", "name": "催告函模板", "document_type_code": "demand_letter"}, - {"template_code": "TPL-CONTRACT-TERMINATION-NOTICE-001", "name": "解除合同通知书模板", "document_type_code": "contract_termination_notice"}, + {"template_code": "TPL-CONTRACT-TERMINATION-NOTICE-001", "name": "解除合同通知书模板", "document_type_code": "rescission_notice"}, {"template_code": "TPL-AGENCY-DISTRIBUTION-001", "name": "代理/经销协议模板", "document_type_code": "agency_distribution_agreement"}, {"template_code": "TPL-FRANCHISE-001", "name": "加盟合同模板", "document_type_code": "franchise_agreement"}, {"template_code": "TPL-INTERCOMPANY-LOAN-001", "name": "企业间借款合同模板", "document_type_code": "intercompany_loan_agreement"}, @@ -35,7 +35,7 @@ EXPECTED_RUNTIME_TEMPLATES = [ {"template_code": "TPL-SHAREHOLDER-RESOLUTION-001", "name": "股东会决议模板", "document_type_code": "shareholder_resolution"}, {"template_code": "TPL-POWER-OF-ATTORNEY-001", "name": "授权委托书模板", "document_type_code": "power_of_attorney"}, {"template_code": "TPL-LEGAL-OPINION-001", "name": "法律意见书模板", "document_type_code": "legal_opinion"}, - {"template_code": "TPL-PROPERTY-SERVICE-001", "name": "物业服务合同模板", "document_type_code": "property_service_agreement"}, + {"template_code": "TPL-PROPERTY-SERVICE-001", "name": "物业服务合同模板", "document_type_code": "property_service"}, ] REQUIRED_PROMPT_TYPES = [ diff --git a/backend/tools/seed_phase13_template_rule_clause_assets.py b/backend/tools/seed_phase13_template_rule_clause_assets.py index 00f20edb..1b8f7819 100644 --- a/backend/tools/seed_phase13_template_rule_clause_assets.py +++ b/backend/tools/seed_phase13_template_rule_clause_assets.py @@ -25,7 +25,7 @@ TEMPLATE_DOCUMENT_TYPE_CODES = { "TPL-COMMISSION-SERVICE-001": "commission_service", "TPL-COOPERATION-FRAMEWORK-001": "cooperation_framework", "TPL-DEMAND-LETTER-001": "demand_letter", - "TPL-CONTRACT-TERMINATION-NOTICE-001": "contract_termination_notice", + "TPL-CONTRACT-TERMINATION-NOTICE-001": "rescission_notice", "TPL-AGENCY-DISTRIBUTION-001": "agency_distribution_agreement", "TPL-FRANCHISE-001": "franchise_agreement", "TPL-INTERCOMPANY-LOAN-001": "intercompany_loan_agreement", @@ -34,7 +34,7 @@ TEMPLATE_DOCUMENT_TYPE_CODES = { "TPL-SHAREHOLDER-RESOLUTION-001": "shareholder_resolution", "TPL-POWER-OF-ATTORNEY-001": "power_of_attorney", "TPL-LEGAL-OPINION-001": "legal_opinion", - "TPL-PROPERTY-SERVICE-001": "property_service_agreement", + "TPL-PROPERTY-SERVICE-001": "property_service", "TPL-LABOR-CONTRACT-001": "labor_contract", "TPL-LABOR-SERVICE-001": "labor_service_agreement", "TPL-EQUITY-TRANSFER-001": "equity_transfer", @@ -73,7 +73,7 @@ TEMPLATE_CATEGORIES = { "demand-letter": "notices-letters", "demand_letter": "notices-letters", "termination-notice": "notices-letters", - "contract_termination_notice": "notices-letters", + "rescission_notice": "notices-letters", "labor": "employment-hr", "labor-service": "employment-hr", "labor_service": "employment-hr",