feat(api): add staff order detail and compliance eligibility

This commit is contained in:
zouantchaw
2026-03-19 20:17:48 +01:00
parent 4d74fa52ab
commit d2bcb9f3ba
18 changed files with 1051 additions and 42 deletions

View File

@@ -225,6 +225,78 @@ async function appendVerificationEvent(client, {
);
}
function normalizeArtifactStatus(status) {
switch (`${status || ''}`.toUpperCase()) {
case VerificationStatus.AUTO_PASS:
case VerificationStatus.APPROVED:
return 'VERIFIED';
case VerificationStatus.AUTO_FAIL:
case VerificationStatus.REJECTED:
return 'REJECTED';
case VerificationStatus.PENDING:
case VerificationStatus.PROCESSING:
case VerificationStatus.NEEDS_REVIEW:
case VerificationStatus.ERROR:
default:
return 'PENDING';
}
}
function looksLikeUuid(value) {
return /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(`${value || ''}`);
}
async function syncVerificationSubjectStatus(client, job) {
const subjectType = job.subject_type || job.subjectType || null;
const subjectId = job.subject_id || job.subjectId || null;
const tenantId = job.tenant_id || job.tenantId || null;
const staffId = job.staff_id || job.staffId || null;
const verificationId = job.id || job.verificationId || null;
if (!subjectType || !subjectId || !tenantId || !staffId || !verificationId) {
return;
}
const nextStatus = normalizeArtifactStatus(job.status);
const metadataPatch = JSON.stringify({
verificationStatus: job.status,
verificationJobId: verificationId,
syncedFromVerification: true,
});
const subjectIdIsUuid = looksLikeUuid(subjectId);
if (subjectType === 'staff_document' || subjectType === 'attire_item' || (subjectType === 'worker' && subjectIdIsUuid)) {
await client.query(
`
UPDATE staff_documents
SET status = $4,
metadata = COALESCE(metadata, '{}'::jsonb) || $5::jsonb,
updated_at = NOW()
WHERE tenant_id = $1
AND staff_id = $2
AND document_id::text = $3
`,
[tenantId, staffId, subjectId, nextStatus, metadataPatch]
);
return;
}
if (subjectType === 'certificate' || (subjectType === 'worker' && !subjectIdIsUuid)) {
await client.query(
`
UPDATE certificates
SET status = $4,
metadata = COALESCE(metadata, '{}'::jsonb) || $5::jsonb,
updated_at = NOW()
WHERE tenant_id = $1
AND staff_id = $2
AND certificate_type = $3
`,
[tenantId, staffId, subjectId, nextStatus, metadataPatch]
);
}
}
async function runAttireChecks(job) {
if (process.env.VERIFICATION_ATTIRE_AUTOPASS === 'true') {
return {
@@ -324,6 +396,13 @@ function getProviderConfig(type) {
token: process.env.VERIFICATION_GOV_ID_PROVIDER_TOKEN,
};
}
if (type === 'tax_form') {
return {
name: 'tax-form-provider',
url: process.env.VERIFICATION_TAX_FORM_PROVIDER_URL,
token: process.env.VERIFICATION_TAX_FORM_PROVIDER_TOKEN,
};
}
return {
name: 'certification-provider',
url: process.env.VERIFICATION_CERT_PROVIDER_URL,
@@ -458,7 +537,7 @@ async function processVerificationJob(verificationId) {
: await runThirdPartyChecks(startedJob, startedJob.type);
await withTransaction(async (client) => {
await client.query(
const updated = await client.query(
`
UPDATE verification_jobs
SET status = $2,
@@ -469,6 +548,7 @@ async function processVerificationJob(verificationId) {
provider_reference = $7,
updated_at = NOW()
WHERE id = $1
RETURNING *
`,
[
verificationId,
@@ -481,6 +561,8 @@ async function processVerificationJob(verificationId) {
]
);
await syncVerificationSubjectStatus(client, updated.rows[0]);
await appendVerificationEvent(client, {
verificationJobId: verificationId,
fromStatus: VerificationStatus.PROCESSING,
@@ -494,7 +576,7 @@ async function processVerificationJob(verificationId) {
});
} catch (error) {
await withTransaction(async (client) => {
await client.query(
const updated = await client.query(
`
UPDATE verification_jobs
SET status = $2,
@@ -503,6 +585,7 @@ async function processVerificationJob(verificationId) {
provider_reference = $4,
updated_at = NOW()
WHERE id = $1
RETURNING *
`,
[
verificationId,
@@ -512,6 +595,8 @@ async function processVerificationJob(verificationId) {
]
);
await syncVerificationSubjectStatus(client, updated.rows[0]);
await appendVerificationEvent(client, {
verificationJobId: verificationId,
fromStatus: VerificationStatus.PROCESSING,
@@ -703,17 +788,20 @@ export async function reviewVerificationJob(verificationId, actorUid, review) {
reasonCode: review.reasonCode || 'MANUAL_REVIEW',
};
await client.query(
const updatedResult = await client.query(
`
UPDATE verification_jobs
SET status = $2,
review = $3::jsonb,
updated_at = NOW()
WHERE id = $1
RETURNING *
`,
[verificationId, review.decision, JSON.stringify(reviewPayload)]
);
await syncVerificationSubjectStatus(client, updatedResult.rows[0]);
await client.query(
`
INSERT INTO verification_reviews (
@@ -800,7 +888,7 @@ export async function retryVerificationJob(verificationId, actorUid) {
});
}
await client.query(
const updatedResult = await client.query(
`
UPDATE verification_jobs
SET status = $2,
@@ -812,10 +900,13 @@ export async function retryVerificationJob(verificationId, actorUid) {
review = '{}'::jsonb,
updated_at = NOW()
WHERE id = $1
RETURNING *
`,
[verificationId, VerificationStatus.PENDING]
);
await syncVerificationSubjectStatus(client, updatedResult.rows[0]);
await appendVerificationEvent(client, {
verificationJobId: verificationId,
fromStatus: job.status,