feat(cartoes): exigir limite e bloquear lançamentos acima do disponível

- campo limite passa a ser NOT NULL DEFAULT 0 no schema (migration 0029)
- validação Zod com requiredDecimalSchema garante valor positivo no formulário
- validateCardLimit() em transactions/actions/core.ts bloqueia criação e edição
  de despesas em cartão que ultrapassem o limite disponível, retornando mensagem
  com o valor exato restante
- tipos Card.limit e Card.limitAvailable deixam de ser nullable
- branch "sem limite registrado" removido de card-item.tsx

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Felipe Coutinho
2026-05-02 22:07:56 +00:00
parent 19b5aa00ee
commit 8389752172
11 changed files with 400 additions and 290 deletions

View File

@@ -31,6 +31,38 @@ export const optionalDecimalSchema = z.union([
.transform((value) => (value === null ? null : Number.parseFloat(value))),
]);
/**
* Required positive decimal schema — accepts number or numeric string.
*/
export const requiredDecimalSchema = (fieldName: string = "valor") =>
z
.union([
z.number(),
z
.string()
.trim()
.min(1, `Informe o ${fieldName}.`)
.transform((value) => value.replace(",", ".")),
])
.transform((value, ctx) => {
const parsed = typeof value === "number" ? value : Number.parseFloat(value);
if (Number.isNaN(parsed)) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: "Informe um valor numérico válido.",
});
return z.NEVER;
}
if (parsed <= 0) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: `Informe um ${fieldName} maior que zero.`,
});
return z.NEVER;
}
return parsed;
});
/**
* Day of month schema (1-31)
*/