借款对账 S4–S8:人工未提交、晚到补记、追回与界面收口(09-17-company-loan-principal-repayment)
新增三类证据与双人复核的人工未提交、逐原因冻结、晚到付款逐借款补记和实际退款追回;补齐对账与补证恢复界面,保护已消费依据、最终归属重验及全局交易唯一性。 验证:81 迁移、HTTP 95 组、浏览器 13 组、数据库约束 9 组;13 项有效变异被捕获,部分由下游或数据库保护检出。构建、类型、合同和治理检查通过;修复后同模型只读静态复核通过。 真实回单待验、整体业务验收未通过;S0 存量、人员、银行材料与业务独立验收未完成。3 个新增迁移须纳入获批发布清单。本次仅本地提交,不推送、不部署。
这个提交包含在:
@@ -750,6 +750,12 @@ model Employee {
|
||||
loan_recoveries_reviewed LoanReleaseRecovery[] @relation("LoanReleaseReviewer")
|
||||
loan_recoveries_consumed LoanReleaseRecovery[] @relation("LoanReleaseConsumer")
|
||||
loan_recovery_events LoanReleaseRecoveryEvent[]
|
||||
loan_not_submitted_proposals LoanNotSubmittedProposal[]
|
||||
loan_not_submitted_reviews LoanNotSubmittedReview[]
|
||||
loan_freezes_created LoanFreezeReason[] @relation("LoanFreezeCreator")
|
||||
loan_freezes_closed LoanFreezeReason[] @relation("LoanFreezeCloser")
|
||||
loan_bank_events_proposed LoanBankResultEvent[] @relation("LoanBankEventProposer")
|
||||
loan_bank_events_reviewed LoanBankResultEvent[] @relation("LoanBankEventReviewer")
|
||||
|
||||
}
|
||||
|
||||
@@ -1999,6 +2005,9 @@ model PaymentBatch {
|
||||
loan_failure_evidence_uses LoanFailureEvidenceUse[]
|
||||
@@map("payment_batches")
|
||||
loan_release_recoveries LoanReleaseRecovery[]
|
||||
loan_freeze_reasons LoanFreezeReason[]
|
||||
loan_bank_result_events LoanBankResultEvent[]
|
||||
loan_not_submitted_proposals LoanNotSubmittedProposal[]
|
||||
loan_material_revision LoanReleaseMaterialRevision?
|
||||
|
||||
}
|
||||
@@ -3897,6 +3906,7 @@ model LoanLender {
|
||||
bank_institution BankInstitution @relation(fields: [bank_institution_id], references: [id], onDelete: Restrict)
|
||||
loans CompanyLoan[]
|
||||
repayments LoanRepayment[]
|
||||
freeze_reasons LoanFreezeReason[]
|
||||
|
||||
@@unique([legal_entity_id, id])
|
||||
@@unique([created_by, request_key])
|
||||
@@ -3931,6 +3941,8 @@ model CompanyLoan {
|
||||
allocations LoanRepaymentAllocation[]
|
||||
evidences LoanEvidence[]
|
||||
|
||||
supplemental_settlements LoanSupplementalSettlement[]
|
||||
|
||||
@@unique([created_by, request_key])
|
||||
@@unique([legal_entity_id, lender_id, origin_reference])
|
||||
@@index([lender_id, confirmation_status, received_date])
|
||||
@@ -4125,6 +4137,7 @@ model LoanBankSubmissionAttestation {
|
||||
// Loan bank-result reconciliation S3 (migration 20261008010000_loan_bank_terminal_facts).
|
||||
// Evidence is stored apart from payment receipts; history triggers live in the migration.
|
||||
model LoanBankTerminalEvidence {
|
||||
evidence_kind String @default("bank_terminal") @db.VarChar(32)
|
||||
id String @id @default(uuid()) @db.Uuid
|
||||
payment_batch_id String @db.Uuid
|
||||
storage_path String
|
||||
@@ -4141,6 +4154,8 @@ model LoanBankTerminalEvidence {
|
||||
payment_batch PaymentBatch @relation(fields: [payment_batch_id], references: [id], onDelete: Restrict)
|
||||
uploader Employee @relation(fields: [uploaded_by], references: [id], onDelete: Restrict)
|
||||
facts LoanBankTerminalFact[]
|
||||
bank_result_events LoanBankResultEvent[] @relation("LoanBankEventEvidence")
|
||||
bank_result_corrections LoanBankResultEvent[] @relation("LoanBankEventCorrection")
|
||||
|
||||
@@index([payment_batch_id], map: "loan_bank_terminal_evidences_batch_idx")
|
||||
@@map("loan_bank_terminal_evidences")
|
||||
@@ -4244,3 +4259,107 @@ model LoanReleaseMaterialRevision {
|
||||
payment_batch PaymentBatch @relation(fields: [payment_batch_id], references: [id], onDelete: Restrict, onUpdate: NoAction)
|
||||
@@map("loan_release_material_revisions")
|
||||
}
|
||||
|
||||
model LoanNotSubmittedProposal {
|
||||
id String @id @default(uuid()) @db.Uuid
|
||||
payment_batch_id String @db.Uuid
|
||||
sequence Int
|
||||
status String @default("proposed") @db.VarChar(16)
|
||||
proposed_by String @db.Uuid
|
||||
proposed_at DateTime @default(now())
|
||||
payload_json Json
|
||||
snapshot_json Json
|
||||
payment_batch PaymentBatch @relation(fields: [payment_batch_id], references: [id], onDelete: Restrict, onUpdate: NoAction)
|
||||
proposer Employee @relation(fields: [proposed_by], references: [id], onDelete: Restrict, onUpdate: NoAction)
|
||||
reviews LoanNotSubmittedReview[]
|
||||
@@unique([payment_batch_id, sequence])
|
||||
@@map("loan_not_submitted_proposals")
|
||||
}
|
||||
|
||||
model LoanNotSubmittedReview {
|
||||
id String @id @default(uuid()) @db.Uuid
|
||||
proposal_id String @db.Uuid
|
||||
reviewed_by String @db.Uuid
|
||||
decision String @db.VarChar(16)
|
||||
reason String @db.VarChar(500)
|
||||
reviewed_at DateTime @default(now())
|
||||
proposal LoanNotSubmittedProposal @relation(fields: [proposal_id], references: [id], onDelete: Restrict, onUpdate: NoAction)
|
||||
reviewer Employee @relation(fields: [reviewed_by], references: [id], onDelete: Restrict, onUpdate: NoAction)
|
||||
@@unique([proposal_id, reviewed_by])
|
||||
@@map("loan_not_submitted_reviews")
|
||||
}
|
||||
|
||||
model LoanFreezeReason {
|
||||
id String @id @default(uuid()) @db.Uuid
|
||||
lender_id String @db.Uuid
|
||||
payment_batch_id String @db.Uuid
|
||||
kind String @db.VarChar(32)
|
||||
source_key String @db.VarChar(160)
|
||||
status String @default("active") @db.VarChar(16)
|
||||
created_by String @db.Uuid
|
||||
created_at DateTime @default(now())
|
||||
closed_by String? @db.Uuid
|
||||
closed_at DateTime?
|
||||
closure_basis Json?
|
||||
lender LoanLender @relation(fields: [lender_id], references: [id], onDelete: Restrict, onUpdate: NoAction)
|
||||
payment_batch PaymentBatch @relation(fields: [payment_batch_id], references: [id], onDelete: Restrict, onUpdate: NoAction)
|
||||
creator Employee @relation("LoanFreezeCreator", fields: [created_by], references: [id], onDelete: Restrict, onUpdate: NoAction)
|
||||
closer Employee? @relation("LoanFreezeCloser", fields: [closed_by], references: [id], onDelete: Restrict, onUpdate: NoAction)
|
||||
@@unique([kind, source_key])
|
||||
@@index([lender_id, status])
|
||||
@@map("loan_freeze_reasons")
|
||||
}
|
||||
|
||||
model LoanBankResultEvent {
|
||||
id String @id @default(uuid()) @db.Uuid
|
||||
payment_batch_id String @db.Uuid
|
||||
kind String @db.VarChar(16)
|
||||
parent_id String? @db.Uuid
|
||||
evidence_id String @db.Uuid
|
||||
request_key String @db.Uuid
|
||||
proposed_by String @db.Uuid
|
||||
proposed_at DateTime @default(now())
|
||||
payload_encrypted String
|
||||
fingerprint String @db.VarChar(64)
|
||||
key_id String @db.VarChar(64)
|
||||
amount Decimal @db.Decimal(14,2)
|
||||
snapshot_json Json
|
||||
status String @default("proposed") @db.VarChar(16)
|
||||
reviewed_by String? @db.Uuid
|
||||
reviewed_at DateTime?
|
||||
review_note String? @db.VarChar(1000)
|
||||
correction_evidence_id String? @db.Uuid
|
||||
payment_batch PaymentBatch @relation(fields: [payment_batch_id], references: [id], onDelete: Restrict, onUpdate: NoAction)
|
||||
evidence LoanBankTerminalEvidence @relation("LoanBankEventEvidence", fields: [evidence_id], references: [id], onDelete: Restrict, onUpdate: NoAction)
|
||||
correction_evidence LoanBankTerminalEvidence? @relation("LoanBankEventCorrection", fields: [correction_evidence_id], references: [id], onDelete: Restrict, onUpdate: NoAction)
|
||||
proposer Employee @relation("LoanBankEventProposer", fields: [proposed_by], references: [id], onDelete: Restrict, onUpdate: NoAction)
|
||||
reviewer Employee? @relation("LoanBankEventReviewer", fields: [reviewed_by], references: [id], onDelete: Restrict, onUpdate: NoAction)
|
||||
parent LoanBankResultEvent? @relation("LoanBankEventParent", fields: [parent_id], references: [id], onDelete: Restrict, onUpdate: NoAction)
|
||||
children LoanBankResultEvent[] @relation("LoanBankEventParent")
|
||||
settlements LoanSupplementalSettlement[]
|
||||
claim LoanBankEventClaim?
|
||||
@@unique([payment_batch_id, request_key])
|
||||
@@index([payment_batch_id, kind])
|
||||
@@map("loan_bank_result_events")
|
||||
}
|
||||
model LoanSupplementalSettlement {
|
||||
id String @id @default(uuid()) @db.Uuid
|
||||
event_id String @db.Uuid
|
||||
loan_id String @db.Uuid
|
||||
bank_amount Decimal @db.Decimal(14,2)
|
||||
settled_amount Decimal @db.Decimal(14,2)
|
||||
receivable_amount Decimal @db.Decimal(14,2)
|
||||
created_at DateTime @default(now())
|
||||
event LoanBankResultEvent @relation(fields: [event_id], references: [id], onDelete: Restrict, onUpdate: NoAction)
|
||||
loan CompanyLoan @relation(fields: [loan_id], references: [id], onDelete: Restrict, onUpdate: NoAction)
|
||||
@@unique([event_id, loan_id])
|
||||
@@map("loan_supplemental_settlements")
|
||||
}
|
||||
model LoanBankEventClaim {
|
||||
fingerprint String @id @db.VarChar(64)
|
||||
key_id String @db.VarChar(64)
|
||||
event_id String @unique @db.Uuid
|
||||
created_at DateTime @default(now())
|
||||
event LoanBankResultEvent @relation(fields: [event_id], references: [id], onDelete: Restrict, onUpdate: NoAction)
|
||||
@@map("loan_bank_event_claims")
|
||||
}
|
||||
|
||||
在新工单中引用
屏蔽一个用户