import { ok, type CommandContext } from "@tailor-platform/erp-kit/core"; import type { Transaction } from "../generated/kysely-tailordb"; function todayUtcMidnight(): Date { const now = new Date(); return new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate())); } export interface ExpireLeaveGrantsInput { /** Calendar date the sweep runs as-of; defaults to today (UTC) when omitted. Exposed for testability. */ asOf?: Date; } export async function run(db: Transaction, input: ExpireLeaveGrantsInput, _ctx: CommandContext) { const asOf = input.asOf ?? todayUtcMidnight(); const now = new Date(); const candidates = await db .selectFrom("LeaveGrant") .selectAll() .where("expirationDate", "<", asOf) .where("remainingDays", ">", "0") .where("expiredAt", "is", null) .forUpdate() .execute(); let expired = 0; let failed = 0; for (const grant of candidates) { try { await db .updateTable("LeaveGrant") .set({ remainingDays: "0", expiredAt: now }) .where("id", "=", grant.id) .execute(); expired++; } catch { // Individual failures are retried on the next run without blocking other grants // (EXPIRATION_SWEEP_PARTIAL_FAILURE — batch job; no user-facing error scenario). failed++; } } return ok({ expired, failed }); }