feat(08-05-contract-detail-workspace): 完成合同详情与终止状态治理

这个提交包含在:
2026-08-08 02:02:52 +08:00
父节点 7546d372d6
当前提交 21851774dd
共修改 25 个文件,包含 2516 行新增和 1021 行删除
+381 -44
查看文件
@@ -1,9 +1,9 @@
const { z } = require("zod");
const {
CONTRACT_REGISTRY_PERSISTED_STATUS_VALUES,
CONTRACT_REGISTRY_TYPE_VALUES,
buildContractRegistryExpiringWhere,
buildContractRegistryScopeWhere,
buildContractRegistryValidationError,
buildContractRegistryWhere,
canManageContractRegistry,
@@ -24,7 +24,8 @@ const UUID_SCHEMA = z.string().uuid();
const DATE_ONLY_SCHEMA = z.string().regex(/^\d{4}-\d{2}-\d{2}$/);
const CONFIDENTIALITY_SCHEMA = z.enum(["public", "internal", "confidential", "restricted"]);
const CONTRACT_TYPE_SCHEMA = z.enum(CONTRACT_REGISTRY_TYPE_VALUES);
const STATUS_WRITE_SCHEMA = z.enum(CONTRACT_REGISTRY_PERSISTED_STATUS_VALUES);
const GENERAL_WRITE_STATUS_VALUES = ["draft", "active", "archived"];
const STATUS_WRITE_SCHEMA = z.enum(GENERAL_WRITE_STATUS_VALUES);
const MONEY_INPUT_SCHEMA = z.string().regex(/^\d{1,12}(\.\d{1,2})?$/);
const CURRENCY_SCHEMA = z
.string()
@@ -57,11 +58,51 @@ const PATCH_SCHEMA = WRITE_SCHEMA.partial().refine((value) => Object.keys(value)
message: "empty_patch",
});
const TERMINATE_SCHEMA = z
.object({
reason: z.string().trim().min(10).max(1000),
})
.strict();
const CORRECTION_SCHEMA = z
.object({
summary: z.string().trim().min(10).max(1000),
})
.strict();
const CONTRACT_REGISTRY_INCLUDE = {
owner: { select: { id: true, employee_no: true, name: true } },
project: { select: { id: true, project_no: true, name: true } },
terminator: { select: { id: true, employee_no: true, name: true } },
};
const CONTRACT_REGISTRY_DETAIL_INCLUDE = {
...CONTRACT_REGISTRY_INCLUDE,
document_refs: {
orderBy: [{ updated_at: "desc" }, { id: "desc" }],
select: {
id: true,
title: true,
document_no: true,
confidentiality_level: true,
storage_type: true,
offline_archive_no: true,
created_at: true,
updated_at: true,
owner: { select: { id: true, employee_no: true, name: true } },
},
},
};
const CONTRACT_HISTORY_ACTIONS = [
"CONTRACT_REGISTRY_CREATED",
"CONTRACT_REGISTRY_UPDATED",
"CONTRACT_REGISTRY_STATUS_CHANGED",
"CONTRACT_REGISTRY_RENEWED",
"CONTRACT_REGISTRY_TERMINATED",
"CONTRACT_REGISTRY_CORRECTION_APPENDED",
];
function parseBody(schema, body) {
const parsed = schema.safeParse(body ?? {});
if (!parsed.success) {
@@ -102,11 +143,78 @@ async function writeAudit(prisma, request, action, targetId, extra = {}) {
}
}
async function writeRequiredAudit(prisma, request, action, targetId, extra = {}) {
return prisma.auditLog.create({
data: buildAuditLogData(request, {
actorId: request.employee.id,
action,
targetType: "contract_registry",
targetId,
extra,
}),
});
}
function parseOptionalDate(value) {
return value ? parseDateOnlyString(value) : null;
}
function mapEmployee(item) {
return item ? { id: item.id, employeeNo: item.employee_no, name: item.name } : null;
}
function assertTerminationInvariant(item) {
const terminationFields = [item?.terminated_at, item?.terminated_reason, item?.terminated_by];
const allPresent = terminationFields.every((value) => value !== null && value !== undefined && value !== "");
const anyPresent = terminationFields.some((value) => value !== null && value !== undefined && value !== "");
const valid = item?.status === "terminated" ? allPresent : !anyPresent;
if (!valid) {
throw new AppError({
code: "SYS_001",
message: "合同状态数据不一致",
statusCode: 500,
});
}
}
function contractStatusLabel(value) {
if (value === "draft") return "草稿";
if (value === "active") return "生效中";
if (value === "expired") return "已过期";
if (value === "archived") return "已归档";
if (value === "terminated") return "已终止";
return "未知状态";
}
function mapHistorySummary(item) {
const extra = item.extra && typeof item.extra === "object" && !Array.isArray(item.extra) ? item.extra : {};
if (item.action === "CONTRACT_REGISTRY_CREATED") return "创建合同台账";
if (item.action === "CONTRACT_REGISTRY_UPDATED") return "更新合同信息";
if (item.action === "CONTRACT_REGISTRY_RENEWED") {
return extra.renewedContractNo ? `续约为 ${String(extra.renewedContractNo)}` : "创建续约草稿";
}
if (item.action === "CONTRACT_REGISTRY_TERMINATED") {
const reason = extra.reasonSummary ?? extra.reason;
return reason ? `终止原因:${String(reason)}` : "合同已终止";
}
if (item.action === "CONTRACT_REGISTRY_CORRECTION_APPENDED") {
return extra.summary ? `追加修正:${String(extra.summary)}` : "追加归档修正记录";
}
if (item.action === "CONTRACT_REGISTRY_STATUS_CHANGED") {
const fromStatus = contractStatusLabel(extra.fromStatus);
const toStatus = contractStatusLabel(extra.toStatus);
return `状态由“${fromStatus}”变为“${toStatus}”`;
}
return "合同记录已更新";
}
function buildAuditReasonSummary(value) {
const normalized = String(value ?? "").replace(/\s+/g, " ").trim();
return normalized.length > 160 ? `${normalized.slice(0, 160)}…` : normalized;
}
function mapRow(item, today) {
assertTerminationInvariant(item);
return {
id: item.id,
contractNo: item.contract_no,
@@ -125,17 +233,63 @@ function mapRow(item, today) {
ownerEmployeeId: item.owner_employee_id,
status: resolveContractRegistryEffectiveStatus(item, today),
persistedStatus: item.status,
terminatedAt: item.terminated_at?.toISOString?.() ?? null,
terminatedReason: item.terminated_reason ?? null,
terminatedBy: item.terminated_by ?? null,
createdAt: item.created_at?.toISOString?.() ?? null,
updatedAt: item.updated_at?.toISOString?.() ?? null,
owner: item.owner
? { id: item.owner.id, employeeNo: item.owner.employee_no, name: item.owner.name }
: null,
owner: mapEmployee(item.owner),
terminator: mapEmployee(item.terminator),
project: item.project
? { id: item.project.id, projectNo: item.project.project_no, name: item.project.name }
: null,
};
}
function mapListRow(item, today) {
const row = mapRow(item, today);
const {
terminatedAt: _terminatedAt,
terminatedReason: _terminatedReason,
terminatedBy: _terminatedBy,
terminator: _terminator,
...listRow
} = row;
return listRow;
}
function mapDocumentRef(item) {
return {
id: item.id,
title: item.title,
documentNo: item.document_no ?? null,
confidentialityLevel: item.confidentiality_level,
storageType: item.storage_type,
offlineArchiveNo: item.offline_archive_no ?? null,
createdAt: item.created_at?.toISOString?.() ?? null,
updatedAt: item.updated_at?.toISOString?.() ?? null,
owner: mapEmployee(item.owner),
};
}
function mapHistoryItem(item) {
return {
id: item.id,
action: item.action,
summary: mapHistorySummary(item),
createdAt: item.created_at?.toISOString?.() ?? null,
actor: mapEmployee(item.actor),
};
}
function buildScopedContractWhere(fastify, request, contractId) {
const scopeWhere = buildContractRegistryScopeWhere({
employee: request.employee,
hasPermission: fastify.hasPermission,
});
return Object.keys(scopeWhere).length ? { AND: [{ id: contractId }, scopeWhere] } : { id: contractId };
}
function buildNotFoundError() {
return new AppError({ code: "SYS_404", message: "Not Found", statusCode: 404 });
}
@@ -154,6 +308,57 @@ function buildRenewalContractNo(baseContractNo, rows) {
return `${baseContractNo}-R${maxRenewalIndex + 1}`;
}
function buildContractPatchData(body) {
return {
...(body.contractNo !== undefined ? { contract_no: body.contractNo } : {}),
...(body.projectId !== undefined ? { project_id: body.projectId ?? null } : {}),
...(body.contractType !== undefined ? { contract_type: body.contractType } : {}),
...(body.counterpartyName !== undefined ? { counterparty_name: body.counterpartyName } : {}),
...(body.summary !== undefined ? { summary: body.summary } : {}),
...(body.amount !== undefined ? { amount: body.amount ?? null } : {}),
...(body.currency !== undefined ? { currency: body.currency } : {}),
...(body.signedDate !== undefined ? { signed_date: parseOptionalDate(body.signedDate) } : {}),
...(body.effectiveDate !== undefined ? { effective_date: parseOptionalDate(body.effectiveDate) } : {}),
...(body.expiryDate !== undefined ? { expiry_date: parseOptionalDate(body.expiryDate) } : {}),
...(body.confidentialityLevel !== undefined ? { confidentiality_level: body.confidentialityLevel } : {}),
...(body.archiveLocation !== undefined ? { archive_location: body.archiveLocation } : {}),
...(body.offlineArchiveNo !== undefined ? { offline_archive_no: body.offlineArchiveNo ?? null } : {}),
...(body.ownerEmployeeId !== undefined ? { owner_employee_id: body.ownerEmployeeId } : {}),
...(body.status !== undefined ? { status: body.status } : {}),
};
}
function buildContractStateGuard(item) {
return {
id: item.id,
status: item.status,
updated_at: item.updated_at,
terminated_at: item.terminated_at ?? null,
terminated_reason: item.terminated_reason ?? null,
terminated_by: item.terminated_by ?? null,
};
}
async function updateContractWithStateGuard(prisma, existing, data) {
const changed = await prisma.contractRegistry.updateMany({
where: buildContractStateGuard(existing),
data,
});
if (changed.count !== 1) {
throw buildContractRegistryValidationError({ status: "contract_state_conflict" }, 409);
}
const updated = await prisma.contractRegistry.findUnique({
where: { id: existing.id },
include: CONTRACT_REGISTRY_INCLUDE,
});
if (!updated) {
throw buildNotFoundError();
}
assertTerminationInvariant(updated);
return updated;
}
async function contractsRegistryRoutes(fastify) {
fastify.get("/contracts-registry", { preHandler: fastify.authenticate }, async (request, reply) => {
await ensureFeatureEnabled(fastify, request);
@@ -170,7 +375,7 @@ async function contractsRegistryRoutes(fastify) {
include: CONTRACT_REGISTRY_INCLUDE,
});
return sendData(reply, request, { items: rows.map((item) => mapRow(item, today)) });
return sendData(reply, request, { items: rows.map((item) => mapListRow(item, today)) });
});
fastify.get("/contracts-registry/expiring", { preHandler: fastify.authenticate }, async (request, reply) => {
@@ -191,7 +396,39 @@ async function contractsRegistryRoutes(fastify) {
return sendData(reply, request, {
days,
total: rows.length,
items: rows.map((item) => mapRow(item, today)),
items: rows.map((item) => mapListRow(item, today)),
});
});
fastify.get("/contracts-registry/:id", { preHandler: fastify.authenticate }, async (request, reply) => {
await ensureFeatureEnabled(fastify, request);
const contractId = UUID_SCHEMA.parse(request.params?.id);
const today = resolveContractRegistryToday(fastify.config.appTimezone);
const item = await fastify.prisma.contractRegistry.findFirst({
where: buildScopedContractWhere(fastify, request, contractId),
include: CONTRACT_REGISTRY_DETAIL_INCLUDE,
});
if (!item) {
throw buildNotFoundError();
}
const history = await fastify.prisma.auditLog.findMany({
where: {
target_type: "contract_registry",
target_id: contractId,
action: { in: CONTRACT_HISTORY_ACTIONS },
},
orderBy: [{ created_at: "desc" }, { id: "desc" }],
take: 100,
include: { actor: { select: { id: true, employee_no: true, name: true } } },
});
return sendData(reply, request, {
item: {
...mapRow(item, today),
documentRefs: (item.document_refs ?? []).map(mapDocumentRef),
history: history.map(mapHistoryItem),
},
});
});
@@ -253,50 +490,153 @@ async function contractsRegistryRoutes(fastify) {
const body = parseBody(PATCH_SCHEMA, request.body);
const existing = await fastify.prisma.contractRegistry.findUnique({
where: { id: contractId },
select: { id: true, status: true, contract_no: true },
select: {
id: true,
status: true,
contract_no: true,
terminated_at: true,
terminated_reason: true,
terminated_by: true,
updated_at: true,
},
});
if (!existing) {
throw buildNotFoundError();
}
assertTerminationInvariant(existing);
if (existing.status === "terminated") {
throw buildContractRegistryValidationError({ status: "terminated_is_final" }, 409);
}
const patchFields = Object.keys(body);
if (existing.status === "archived" && !(patchFields.length === 1 && body.status === "active")) {
throw buildContractRegistryValidationError({ status: "archived_fields_locked" }, 409);
}
const today = resolveContractRegistryToday(fastify.config.appTimezone);
const updated = await fastify.prisma.contractRegistry.update({
where: { id: contractId },
data: {
...(body.contractNo !== undefined ? { contract_no: body.contractNo } : {}),
...(body.projectId !== undefined ? { project_id: body.projectId ?? null } : {}),
...(body.contractType !== undefined ? { contract_type: body.contractType } : {}),
...(body.counterpartyName !== undefined ? { counterparty_name: body.counterpartyName } : {}),
...(body.summary !== undefined ? { summary: body.summary } : {}),
...(body.amount !== undefined ? { amount: body.amount ?? null } : {}),
...(body.currency !== undefined ? { currency: body.currency } : {}),
...(body.signedDate !== undefined ? { signed_date: parseOptionalDate(body.signedDate) } : {}),
...(body.effectiveDate !== undefined ? { effective_date: parseOptionalDate(body.effectiveDate) } : {}),
...(body.expiryDate !== undefined ? { expiry_date: parseOptionalDate(body.expiryDate) } : {}),
...(body.confidentialityLevel !== undefined
? { confidentiality_level: body.confidentialityLevel }
: {}),
...(body.archiveLocation !== undefined ? { archive_location: body.archiveLocation } : {}),
...(body.offlineArchiveNo !== undefined ? { offline_archive_no: body.offlineArchiveNo ?? null } : {}),
...(body.ownerEmployeeId !== undefined ? { owner_employee_id: body.ownerEmployeeId } : {}),
...(body.status !== undefined ? { status: body.status } : {}),
},
include: CONTRACT_REGISTRY_INCLUDE,
const statusChanged = body.status !== undefined && body.status !== existing.status;
const updated = await fastify.prisma.$transaction(async (tx) => {
const next = await updateContractWithStateGuard(tx, existing, buildContractPatchData(body));
if (statusChanged) {
await writeRequiredAudit(tx, request, "CONTRACT_REGISTRY_STATUS_CHANGED", next.id, {
fromStatus: existing.status,
toStatus: body.status,
});
await writeRequiredAudit(tx, request, "CONTRACT_REGISTRY_UPDATED", next.id, {
fields: patchFields,
});
}
return next;
});
await writeAudit(fastify.prisma, request, "CONTRACT_REGISTRY_UPDATED", updated.id, {
fields: Object.keys(body),
});
if (body.status !== undefined && body.status !== existing.status) {
await writeAudit(fastify.prisma, request, "CONTRACT_REGISTRY_STATUS_CHANGED", updated.id, {
fromStatus: existing.status,
toStatus: body.status,
if (!statusChanged) {
await writeAudit(fastify.prisma, request, "CONTRACT_REGISTRY_UPDATED", updated.id, {
fields: patchFields,
});
}
return sendData(reply, request, { item: mapRow(updated, today) });
});
fastify.post("/contracts-registry/:id/terminate", { preHandler: fastify.authenticate }, async (request, reply) => {
await ensureFeatureEnabled(fastify, request);
if (!canManageContractRegistry(fastify.hasPermission, request.employee)) {
throw new AppError({
code: "PERM_001",
message: "无权限",
statusCode: 403,
details: { permission: "project.manage" },
});
}
const contractId = UUID_SCHEMA.parse(request.params?.id);
const body = parseBody(TERMINATE_SCHEMA, request.body);
const today = resolveContractRegistryToday(fastify.config.appTimezone);
const result = await fastify.prisma.$transaction(async (tx) => {
const existing = await tx.contractRegistry.findUnique({
where: { id: contractId },
include: CONTRACT_REGISTRY_INCLUDE,
});
if (!existing) {
throw buildNotFoundError();
}
assertTerminationInvariant(existing);
const effectiveStatus = resolveContractRegistryEffectiveStatus(existing, today);
if (!["active", "expired"].includes(effectiveStatus)) {
throw buildContractRegistryValidationError({ status: "terminate_not_allowed" }, 409);
}
const changed = await tx.contractRegistry.updateMany({
where: buildContractStateGuard(existing),
data: {
status: "terminated",
terminated_at: new Date(),
terminated_reason: body.reason,
terminated_by: request.employee.id,
},
});
if (changed.count !== 1) {
throw buildContractRegistryValidationError({ status: "terminate_conflict" }, 409);
}
const updated = await tx.contractRegistry.findUnique({
where: { id: contractId },
include: CONTRACT_REGISTRY_INCLUDE,
});
assertTerminationInvariant(updated);
await writeRequiredAudit(tx, request, "CONTRACT_REGISTRY_TERMINATED", contractId, {
contractNo: existing.contract_no,
fromStatus: existing.status,
toStatus: "terminated",
reasonSummary: buildAuditReasonSummary(body.reason),
});
return updated;
});
return sendData(reply, request, { item: mapRow(result, today) });
});
fastify.post("/contracts-registry/:id/corrections", { preHandler: fastify.authenticate }, async (request, reply) => {
await ensureFeatureEnabled(fastify, request);
if (!canManageContractRegistry(fastify.hasPermission, request.employee)) {
throw new AppError({
code: "PERM_001",
message: "无权限",
statusCode: 403,
details: { permission: "project.manage" },
});
}
const contractId = UUID_SCHEMA.parse(request.params?.id);
const body = parseBody(CORRECTION_SCHEMA, request.body);
await fastify.prisma.$transaction(async (tx) => {
const item = await tx.contractRegistry.findUnique({
where: { id: contractId },
select: { id: true, status: true, updated_at: true },
});
if (!item) {
throw buildNotFoundError();
}
if (item.status !== "archived") {
throw buildContractRegistryValidationError({ status: "correction_requires_archived" }, 409);
}
const claimed = await tx.contractRegistry.updateMany({
where: { id: item.id, status: "archived", updated_at: item.updated_at },
data: { updated_at: new Date() },
});
if (claimed.count !== 1) {
throw buildContractRegistryValidationError({ status: "correction_state_conflict" }, 409);
}
await writeRequiredAudit(tx, request, "CONTRACT_REGISTRY_CORRECTION_APPENDED", contractId, {
summary: body.summary,
});
});
return sendData(reply, request, { accepted: true });
});
fastify.post("/contracts-registry/:id/renew", { preHandler: fastify.authenticate }, async (request, reply) => {
await ensureFeatureEnabled(fastify, request);
if (!canManageContractRegistry(fastify.hasPermission, request.employee)) {
@@ -318,6 +658,7 @@ async function contractsRegistryRoutes(fastify) {
if (!original) {
throw buildNotFoundError();
}
assertTerminationInvariant(original);
const effectiveStatus = resolveContractRegistryEffectiveStatus(original, today);
if (!["active", "expired"].includes(effectiveStatus)) {
@@ -352,18 +693,14 @@ async function contractsRegistryRoutes(fastify) {
include: CONTRACT_REGISTRY_INCLUDE,
});
const archivedSource = await tx.contractRegistry.update({
where: { id: original.id },
data: { status: "archived" },
include: CONTRACT_REGISTRY_INCLUDE,
});
const archivedSource = await updateContractWithStateGuard(tx, original, { status: "archived" });
await writeAudit(tx, request, "CONTRACT_REGISTRY_RENEWED", renewed.id, {
await writeRequiredAudit(tx, request, "CONTRACT_REGISTRY_RENEWED", renewed.id, {
sourceContractId: original.id,
sourceContractNo: original.contract_no,
renewedContractNo: renewed.contract_no,
});
await writeAudit(tx, request, "CONTRACT_REGISTRY_STATUS_CHANGED", archivedSource.id, {
await writeRequiredAudit(tx, request, "CONTRACT_REGISTRY_STATUS_CHANGED", archivedSource.id, {
fromStatus: original.status,
toStatus: "archived",
reason: "renew",
+2 -1
查看文件
@@ -411,8 +411,9 @@ function contractRegistryStatusLabel(value) {
if (value === "draft") return "草稿";
if (value === "active") return "生效中";
if (value === "expired") return "已过期";
if (value === "terminated") return "已终止";
if (value === "archived") return "已归档";
return value ?? "";
return "未知状态";
}
function paymentBatchEntityTypeLabel(value) {
+35 -7
查看文件
@@ -7,6 +7,23 @@ const ExcelJS = require('exceljs');
const { createPrismaClient } = require('../prismaClient');
const SUPPORTED_TYPES = ['projects', 'members', 'milestones', 'risks', 'contracts'];
const CONTRACT_IMPORT_STATUS_VALUES = new Set(['draft', 'active', 'archived']);
function validateContractImportStatus(importedStatus, currentStatus = null) {
if (!CONTRACT_IMPORT_STATUS_VALUES.has(importedStatus)) {
return `status_not_importable:${importedStatus}`;
}
if (currentStatus === 'terminated') {
return 'terminated_contract_is_final';
}
if (currentStatus === 'archived') {
return 'archived_contract_requires_correction_api';
}
if (currentStatus && currentStatus !== importedStatus) {
return 'status_change_requires_contract_api';
}
return null;
}
function printHelp() {
console.log(`用法:
@@ -310,7 +327,7 @@ async function handleRisks({ prisma, rows, apply, summary }) {
async function handleContracts({ prisma, rows, apply, summary }) {
assertRequiredHeaders(rows, ['contract_no', 'counterparty_name', 'summary', 'archive_location', 'owner_employee_no']);
const refs = await buildReferenceMaps(prisma);
const existing = new Map((await prisma.contractRegistry.findMany({ select: { id: true, contract_no: true } })).map((item) => [item.contract_no, item]));
const existing = new Map((await prisma.contractRegistry.findMany({ select: { id: true, contract_no: true, status: true } })).map((item) => [item.contract_no, item]));
for (const row of rows) {
const data = row.data;
@@ -325,6 +342,14 @@ async function handleContracts({ prisma, rows, apply, summary }) {
continue;
}
const importedStatus = data.status || 'active';
const current = existing.get(data.contract_no);
const statusError = validateContractImportStatus(importedStatus, current?.status ?? null);
if (statusError) {
pushError(summary, row.rowNumber, statusError);
continue;
}
const payload = {
contract_no: data.contract_no,
project_id: project?.id ?? null,
@@ -334,10 +359,9 @@ async function handleContracts({ prisma, rows, apply, summary }) {
archive_location: data.archive_location,
offline_archive_no: data.offline_archive_no || null,
owner_employee_id: owner.id,
status: data.status || 'active',
status: importedStatus,
};
const current = existing.get(data.contract_no);
if (!current) summary.createCount += 1;
else summary.updateCount += 1;
@@ -400,7 +424,11 @@ async function main() {
}
}
main().catch((error) => {
console.error(error?.message ?? error);
process.exit(1);
});
if (require.main === module) {
main().catch((error) => {
console.error(error?.message ?? error);
process.exit(1);
});
}
module.exports = { validateContractImportStatus };
@@ -4,7 +4,7 @@ const { AppError } = require("../utils/AppError");
const { currentDateString, parseDateOnlyString } = require("../utils/date");
const CONTRACT_REGISTRY_TYPE_VALUES = ["purchase", "sales", "service", "framework", "other"];
const CONTRACT_REGISTRY_PERSISTED_STATUS_VALUES = ["draft", "active", "archived"];
const CONTRACT_REGISTRY_PERSISTED_STATUS_VALUES = ["draft", "active", "terminated", "archived"];
const CONTRACT_REGISTRY_EFFECTIVE_STATUS_VALUES = [...CONTRACT_REGISTRY_PERSISTED_STATUS_VALUES, "expired"];
const DATE_ONLY_SCHEMA = z.string().regex(/^\d{4}-\d{2}-\d{2}$/);
@@ -115,6 +115,10 @@ function buildEffectiveStatusWhere(status, today) {
return { status: "archived" };
}
if (status === "terminated") {
return { status: "terminated" };
}
if (status === "expired") {
return {
status: "active",
@@ -221,6 +225,7 @@ module.exports = {
CONTRACT_REGISTRY_PERSISTED_STATUS_VALUES,
CONTRACT_REGISTRY_TYPE_VALUES,
buildContractRegistryExpiringWhere,
buildContractRegistryScopeWhere,
buildContractRegistryValidationError,
buildContractRegistryWhere,
canManageContractRegistry,