feat(08-05-contract-detail-workspace): 完成合同详情与终止状态治理
这个提交包含在:
@@ -2,7 +2,8 @@ import { apiRequestBlob, apiRequestData } from "./http";
|
||||
import type { ConfidentialityLevel, EmployeeBasic } from "./projects";
|
||||
|
||||
export type ContractRegistryType = "purchase" | "sales" | "service" | "framework" | "other";
|
||||
export type ContractRegistryPersistedStatus = "draft" | "active" | "archived";
|
||||
export type ContractRegistryPersistedStatus = "draft" | "active" | "terminated" | "archived";
|
||||
export type ContractRegistryWritableStatus = Exclude<ContractRegistryPersistedStatus, "terminated">;
|
||||
export type ContractRegistryStatus = ContractRegistryPersistedStatus | "expired";
|
||||
export type ContractRegistryExportFormat = "csv" | "xlsx";
|
||||
|
||||
@@ -33,12 +34,46 @@ export type ContractRegistryItem = {
|
||||
ownerEmployeeId: string;
|
||||
status: ContractRegistryStatus;
|
||||
persistedStatus: ContractRegistryPersistedStatus;
|
||||
/** Detail-only terminal evidence is omitted from list responses. */
|
||||
terminatedAt?: string | null;
|
||||
terminatedReason?: string | null;
|
||||
terminatedBy?: string | null;
|
||||
createdAt: string | null;
|
||||
updatedAt: string | null;
|
||||
owner: EmployeeBasic | null;
|
||||
terminator?: EmployeeBasic | null;
|
||||
project: { id: string; projectNo: string; name: string } | null;
|
||||
};
|
||||
|
||||
export type ContractDocumentRefSummary = {
|
||||
id: string;
|
||||
title: string;
|
||||
documentNo: string | null;
|
||||
confidentialityLevel: ConfidentialityLevel;
|
||||
storageType: string;
|
||||
offlineArchiveNo: string | null;
|
||||
createdAt: string | null;
|
||||
updatedAt: string | null;
|
||||
owner: EmployeeBasic | null;
|
||||
};
|
||||
|
||||
export type ContractHistoryItem = {
|
||||
id: string;
|
||||
action: string;
|
||||
summary: string;
|
||||
createdAt: string | null;
|
||||
actor: EmployeeBasic | null;
|
||||
};
|
||||
|
||||
export type ContractRegistryDetail = Omit<ContractRegistryItem, "terminatedAt" | "terminatedReason" | "terminatedBy" | "terminator"> & {
|
||||
terminatedAt: string | null;
|
||||
terminatedReason: string | null;
|
||||
terminatedBy: string | null;
|
||||
terminator: EmployeeBasic | null;
|
||||
documentRefs: ContractDocumentRefSummary[];
|
||||
history: ContractHistoryItem[];
|
||||
};
|
||||
|
||||
function buildFiltersSearch(filters?: ContractRegistryFilters | string) {
|
||||
const normalized =
|
||||
typeof filters === "string"
|
||||
@@ -67,6 +102,10 @@ export async function fetchExpiringContracts(days = 30) {
|
||||
);
|
||||
}
|
||||
|
||||
export async function fetchContractRegistryDetail(id: string) {
|
||||
return apiRequestData<{ item: ContractRegistryDetail }>(`/api/contracts-registry/${id}`);
|
||||
}
|
||||
|
||||
export async function createContractRegistry(payload: {
|
||||
contractNo: string;
|
||||
projectId?: string | null;
|
||||
@@ -82,7 +121,7 @@ export async function createContractRegistry(payload: {
|
||||
archiveLocation: string;
|
||||
offlineArchiveNo?: string | null;
|
||||
ownerEmployeeId?: string;
|
||||
status?: ContractRegistryPersistedStatus;
|
||||
status?: ContractRegistryWritableStatus;
|
||||
}) {
|
||||
return apiRequestData<{ item: ContractRegistryItem }>("/api/contracts-registry", {
|
||||
method: "POST",
|
||||
@@ -107,7 +146,7 @@ export async function patchContractRegistry(
|
||||
archiveLocation: string;
|
||||
offlineArchiveNo: string | null;
|
||||
ownerEmployeeId: string;
|
||||
status: ContractRegistryPersistedStatus;
|
||||
status: ContractRegistryWritableStatus;
|
||||
}>,
|
||||
) {
|
||||
return apiRequestData<{ item: ContractRegistryItem }>(`/api/contracts-registry/${id}`, {
|
||||
@@ -125,6 +164,20 @@ export async function renewContractRegistry(id: string) {
|
||||
);
|
||||
}
|
||||
|
||||
export async function terminateContractRegistry(id: string, reason: string) {
|
||||
return apiRequestData<{ item: ContractRegistryItem }>(`/api/contracts-registry/${id}/terminate`, {
|
||||
method: "POST",
|
||||
body: JSON.stringify({ reason }),
|
||||
});
|
||||
}
|
||||
|
||||
export async function appendContractRegistryCorrection(id: string, summary: string) {
|
||||
return apiRequestData<{ accepted: true }>(`/api/contracts-registry/${id}/corrections`, {
|
||||
method: "POST",
|
||||
body: JSON.stringify({ summary }),
|
||||
});
|
||||
}
|
||||
|
||||
export async function exportContractsRegistry(params: ContractRegistryFilters & { format: ContractRegistryExportFormat }) {
|
||||
return apiRequestBlob("/api/exports/contracts-registry", {
|
||||
method: "POST",
|
||||
|
||||
在新工单中引用
屏蔽一个用户