mirror of
https://origin.cursor.com/mrdevmx/panels.git
synced 2026-10-09 18:03:18 +00:00
170 lines
5.5 KiB
TypeScript
170 lines
5.5 KiB
TypeScript
/** Vigencia documental y contexto de alta IMSS. */
|
|
|
|
export type ValidityMode = "none" | "freshness" | "expiry";
|
|
export type ValidityStatus = "ok" | "missing" | "expired" | "stale_for_alta" | "missing_dates";
|
|
export type ImssStatus = "sin_alta" | "alta" | "baja_imss";
|
|
|
|
export type DocTypePolicy = {
|
|
code: string;
|
|
label: string;
|
|
required: boolean;
|
|
validity_mode: ValidityMode;
|
|
freshness_days: number | null;
|
|
requires_issued_at: boolean;
|
|
requires_expires_at: boolean;
|
|
};
|
|
|
|
export type CurrentDocDates = {
|
|
type_code: string;
|
|
issued_at: string | null;
|
|
expires_at: string | null;
|
|
uploaded_at: string;
|
|
imss_alta_at?: string | null;
|
|
};
|
|
|
|
export type WorkerImssContext = {
|
|
imss_status: ImssStatus;
|
|
last_rehire_at: string | null;
|
|
/** Fecha de la alta IMSS vigente (documento o worker.imss_alta_at). */
|
|
current_alta_at: string | null;
|
|
};
|
|
|
|
function parseDay(value: string | null | undefined): Date | null {
|
|
if (!value) return null;
|
|
const raw = String(value).trim().slice(0, 10);
|
|
if (!/^\d{4}-\d{2}-\d{2}$/.test(raw)) return null;
|
|
const d = new Date(`${raw}T12:00:00`);
|
|
return Number.isNaN(d.getTime()) ? null : d;
|
|
}
|
|
|
|
function daysBetween(from: Date, to: Date): number {
|
|
return Math.floor((to.getTime() - from.getTime()) / 86_400_000);
|
|
}
|
|
|
|
/** True cuando el expediente debe exigir frescor (alta o reingreso pendiente de alta). */
|
|
export function freshnessRequired(ctx: WorkerImssContext): boolean {
|
|
if (ctx.imss_status !== "alta") return true;
|
|
if (!ctx.last_rehire_at) return false;
|
|
const rehire = parseDay(ctx.last_rehire_at);
|
|
const alta = parseDay(ctx.current_alta_at);
|
|
if (!rehire) return false;
|
|
if (!alta) return true;
|
|
return alta < rehire;
|
|
}
|
|
|
|
export function evaluateDocumentValidity(
|
|
policy: DocTypePolicy,
|
|
doc: CurrentDocDates | null | undefined,
|
|
ctx: WorkerImssContext,
|
|
today = new Date(),
|
|
): {
|
|
present: boolean;
|
|
valid: boolean;
|
|
validity_status: ValidityStatus;
|
|
issued_at: string | null;
|
|
expires_at: string | null;
|
|
} {
|
|
if (!doc) {
|
|
return {
|
|
present: false,
|
|
valid: false,
|
|
validity_status: "missing",
|
|
issued_at: null,
|
|
expires_at: null,
|
|
};
|
|
}
|
|
|
|
const issuedAt = doc.issued_at;
|
|
const expiresAt = doc.expires_at;
|
|
const needFresh = freshnessRequired(ctx);
|
|
|
|
if (policy.validity_mode === "expiry") {
|
|
if (policy.requires_expires_at && !expiresAt) {
|
|
return {
|
|
present: true,
|
|
valid: false,
|
|
validity_status: "missing_dates",
|
|
issued_at: issuedAt,
|
|
expires_at: expiresAt,
|
|
};
|
|
}
|
|
const exp = parseDay(expiresAt);
|
|
if (exp && exp < new Date(today.toISOString().slice(0, 10) + "T12:00:00")) {
|
|
return {
|
|
present: true,
|
|
valid: false,
|
|
validity_status: "expired",
|
|
issued_at: issuedAt,
|
|
expires_at: expiresAt,
|
|
};
|
|
}
|
|
return {
|
|
present: true,
|
|
valid: true,
|
|
validity_status: "ok",
|
|
issued_at: issuedAt,
|
|
expires_at: expiresAt,
|
|
};
|
|
}
|
|
|
|
if (policy.validity_mode === "freshness") {
|
|
if (policy.requires_issued_at && !issuedAt) {
|
|
return {
|
|
present: true,
|
|
valid: false,
|
|
validity_status: "missing_dates",
|
|
issued_at: issuedAt,
|
|
expires_at: expiresAt,
|
|
};
|
|
}
|
|
if (needFresh && issuedAt) {
|
|
const issued = parseDay(issuedAt);
|
|
const days = policy.freshness_days ?? 90;
|
|
if (issued && daysBetween(issued, today) > days) {
|
|
return {
|
|
present: true,
|
|
valid: false,
|
|
validity_status: "stale_for_alta",
|
|
issued_at: issuedAt,
|
|
expires_at: expiresAt,
|
|
};
|
|
}
|
|
}
|
|
return {
|
|
present: true,
|
|
valid: true,
|
|
validity_status: "ok",
|
|
issued_at: issuedAt,
|
|
expires_at: expiresAt,
|
|
};
|
|
}
|
|
|
|
return {
|
|
present: true,
|
|
valid: true,
|
|
validity_status: "ok",
|
|
issued_at: issuedAt,
|
|
expires_at: expiresAt,
|
|
};
|
|
}
|
|
|
|
export const DOCUMENT_TYPE_SEED: Array<{
|
|
code: string;
|
|
label: string;
|
|
required: number;
|
|
validity_mode: ValidityMode;
|
|
freshness_days: number | null;
|
|
requires_issued_at: number;
|
|
requires_expires_at: number;
|
|
}> = [
|
|
{ code: "foto", label: "Foto", required: 1, validity_mode: "none", freshness_days: null, requires_issued_at: 0, requires_expires_at: 0 },
|
|
{ code: "ine", label: "INE", required: 1, validity_mode: "expiry", freshness_days: null, requires_issued_at: 0, requires_expires_at: 1 },
|
|
{ code: "curp", label: "Constancia CURP", required: 1, validity_mode: "freshness", freshness_days: 90, requires_issued_at: 1, requires_expires_at: 0 },
|
|
{ code: "nss", label: "Constancia NSS", required: 1, validity_mode: "freshness", freshness_days: 90, requires_issued_at: 1, requires_expires_at: 0 },
|
|
{ code: "rfc", label: "Constancia RFC", required: 1, validity_mode: "freshness", freshness_days: 90, requires_issued_at: 1, requires_expires_at: 0 },
|
|
{ code: "domicilio", label: "Comprobante de domicilio", required: 1, validity_mode: "freshness", freshness_days: 90, requires_issued_at: 1, requires_expires_at: 0 },
|
|
{ code: "alta_imss", label: "Alta IMSS", required: 0, validity_mode: "none", freshness_days: null, requires_issued_at: 0, requires_expires_at: 0 },
|
|
{ code: "baja_imss", label: "Baja IMSS", required: 0, validity_mode: "none", freshness_days: null, requires_issued_at: 0, requires_expires_at: 0 },
|
|
{ code: "contrato", label: "Contrato", required: 0, validity_mode: "none", freshness_days: null, requires_issued_at: 0, requires_expires_at: 0 },
|
|
{ code: "otro", label: "Otro", required: 0, validity_mode: "none", freshness_days: null, requires_issued_at: 0, requires_expires_at: 0 },
|
|
];
|