import { z } from "zod"; import { tr } from "../../i18n/tr"; import { ASSET_KEYS } from "../assets"; import { parseMoney } from "../money"; import { MOVEMENT_KINDS } from "../portfolio"; import { parseQuantity } from "../quantity"; const v = tr.validation; const a = tr.assets.validation; const ymd = z.string().regex(/^\d{4}-\d{2}-\d{2}$/, v.date); const objectId = z.string().regex(/^[a-f0-9]{24}$/i, tr.transactions.validation.selectAccount); const emptyToNull = (x: unknown) => (x === "" || x === undefined ? null : x); /** Boş → null; dolu → pozitif kuruş. */ const optionalMoney = z.preprocess( emptyToNull, z .string() .trim() .transform((s, ctx) => { const minor = parseMoney(s); if (minor === null || minor <= 0) { ctx.addIssue({ code: "custom", message: v.money }); return z.NEVER; } return minor; }) .nullable(), ); /** Varlık (birikim kalemi) formu. */ export const assetFormSchema = z.object({ assetKey: z.enum(ASSET_KEYS), name: z.string().trim().min(1, tr.common.required).max(80, v.nameMax), note: z.string().trim().max(500).default(""), isPrivate: z.boolean().default(false), }); export type AssetFormInput = z.input; export type AssetForm = z.output; /** * Varlık hareketi. Tutar ve birim fiyat isteğe bağlı: tutar varsa birim fiyat ondan, yalnızca * birim fiyat varsa tutar ondan hesaplanır; ikisi de boşsa fiyat tablosu kullanılır * (alım → satış fiyatı, satış ve gelen hediye → alış fiyatı). Hesap yalnızca alım/satışta. */ export const movementFormSchema = z .object({ kind: z.enum(MOVEMENT_KINDS), date: ymd, quantity: z .string() .trim() .transform((s, ctx) => { const q = parseQuantity(s); if (q === null || q <= 0) { ctx.addIssue({ code: "custom", message: tr.inKind.validation.quantity }); return z.NEVER; } return q; }), unitPrice: optionalMoney, amount: optionalMoney, accountId: z.preprocess(emptyToNull, objectId.nullable()), storage: z.string().trim().max(120).default(""), note: z.string().trim().max(300).default(""), }) .superRefine((x, ctx) => { if ((x.kind === "gift_in" || x.kind === "gift_out") && x.accountId) ctx.addIssue({ code: "custom", path: ["accountId"], message: a.noAccountForGift }); }) .transform((x) => x.kind === "gift_out" ? { ...x, unitPrice: null, amount: null, accountId: null } : x, ); export type MovementFormInput = z.input; export type MovementForm = z.output;