Files
butcem/src/shared/schemas/in-kind.ts
T

123 lines
4.2 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { z } from "zod";
import { tr } from "../../i18n/tr";
import { ASSET_KEYS } from "../assets";
import { parseMoney } from "../money";
import { parseQuantity } from "../quantity";
const v = tr.validation;
const k = tr.inKind.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(),
);
const quantity = z
.string()
.trim()
.transform((s, ctx) => {
const q = parseQuantity(s);
if (q === null || q <= 0) {
ctx.addIssue({ code: "custom", message: k.quantity });
return z.NEVER;
}
return q;
});
/** Fiyat girişi: satış boşsa alışla aynı (tek fiyat girilirse ikisine de yazılır — Faz 0 kararı). */
export const assetPriceFormSchema = z
.object({
assetKey: z.enum(ASSET_KEYS),
date: ymd,
buy: optionalMoney,
sell: optionalMoney,
})
.superRefine((x, ctx) => {
if (x.buy === null && x.sell === null)
ctx.addIssue({ code: "custom", path: ["buy"], message: k.price });
})
.transform((x) => ({
assetKey: x.assetKey,
date: x.date,
buyMinor: (x.buy ?? x.sell)!,
sellMinor: (x.sell ?? x.buy)!,
}));
export type AssetPriceForm = z.output<typeof assetPriceFormSchema>;
/** Aynî borç/alacak kaydı. Birim fiyat boşsa fiyat tablosundan (o tarihteki son fiyat) alınır. */
export const inKindFormSchema = z.object({
direction: z.enum(["debt", "receivable"]),
assetKey: z.enum(ASSET_KEYS),
quantity,
date: ymd,
unitPrice: optionalMoney,
note: z.string().trim().max(500).default(""),
savingsAccountId: z.preprocess(emptyToNull, objectId.nullable()),
monthlyPlan: optionalMoney,
});
export type InKindForm = z.output<typeof inKindFormSchema>;
/** Birikim katkısı: hesaptan borcun birikim hesabına transfer. */
export const contributionSchema = z.object({
fromAccountId: objectId,
amount: z
.string()
.trim()
.transform((s, ctx) => {
const minor = parseMoney(s);
if (minor === null || minor <= 0) {
ctx.addIssue({ code: "custom", message: tr.transactions.validation.amountPositive });
return z.NEVER;
}
return minor;
}),
date: ymd,
});
export type ContributionForm = z.output<typeof contributionSchema>;
/**
* "Ödeme gerçekleşti". Aynı varlıkla: miktar zorunlu, TL (varlığı almak için harcanan) isteğe
* bağlı. TL karşılığıyla: tutar ve hesap zorunlu; miktar boşsa güncel fiyattan hesaplanır.
*/
export const paymentFormSchema = z
.object({
method: z.enum(["asset", "cash"]),
date: ymd,
quantity: z.preprocess(emptyToNull, quantity.nullable()),
amount: optionalMoney,
accountId: z.preprocess(emptyToNull, objectId.nullable()),
/** Aynı varlıkla ödemede bağlanacak varlık (Faz 9b): borçta varlıktan düşer, alacakta eklenir. */
assetId: z.preprocess(emptyToNull, objectId.nullable()).default(null),
note: z.string().trim().max(300).default(""),
})
.superRefine((x, ctx) => {
if (x.assetId && x.method !== "asset")
ctx.addIssue({ code: "custom", path: ["assetId"], message: k.assetOnlyWithAsset });
if (x.assetId && x.amount)
ctx.addIssue({ code: "custom", path: ["amount"], message: k.noAmountWithAsset });
if (x.method === "asset" && x.quantity === null)
ctx.addIssue({ code: "custom", path: ["quantity"], message: k.quantity });
if (x.method === "cash") {
if (!x.amount) ctx.addIssue({ code: "custom", path: ["amount"], message: k.amount });
if (!x.accountId) ctx.addIssue({ code: "custom", path: ["accountId"], message: k.account });
}
if (x.amount && !x.accountId)
ctx.addIssue({ code: "custom", path: ["accountId"], message: k.account });
});
export type PaymentForm = z.output<typeof paymentFormSchema>;