feat(contacts): cari kişiler, hesap ↔ cari transferleri, cari ekstresi ve vade uyarısı, 007 migration

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-11-05 11:58:37 +03:00
parent dfb83d6fd4
commit 5d370a062e
18 changed files with 1060 additions and 26 deletions
+58
View File
@@ -0,0 +1,58 @@
import { z } from "zod";
import { tr } from "../../i18n/tr";
import { CONTACT_ENTRY_KINDS, CONTACT_RELATIONS } from "../contacts";
import { parseMoney } from "../money";
import { moneyInputSchema } from "./account";
const v = tr.validation;
const ymd = z.string().regex(/^\d{4}-\d{2}-\d{2}$/, v.date);
const optionalYmd = z.preprocess((x) => (x === "" || x === undefined ? null : x), ymd.nullable());
const reminderDays = z.coerce.number().int().min(0).max(60).default(3);
/** Cari kişi formu (web + mobil). */
export const contactFormSchema = z.object({
name: z.string().trim().min(1, tr.common.required).max(80, v.nameMax),
relation: z.enum(CONTACT_RELATIONS).default("other"),
phone: z
.string()
.trim()
.max(30)
.regex(/^[0-9+()\s-]*$/, v.phone)
.default(""),
note: z.string().trim().max(500).default(""),
/** + bana borçlu, − ben borçluyum. */
openingBalance: z.string().default("").pipe(moneyInputSchema),
openingDate: ymd,
isPrivate: z.boolean().default(false),
dueDate: optionalYmd,
reminderDays,
});
export type ContactFormInput = z.input<typeof contactFormSchema>;
export type ContactForm = z.output<typeof contactFormSchema>;
/** Cari hareketi: hesaptan cariye veya cariden hesaba transfer. */
export const contactEntrySchema = z.object({
kind: z.enum(CONTACT_ENTRY_KINDS),
accountId: z.string().regex(/^[a-f0-9]{24}$/i, tr.transactions.validation.selectAccount),
amount: z
.string()
.trim()
.transform((s, ctx) => {
const minor = parseMoney(s);
if (minor === null || minor <= 0) {
ctx.addIssue({
code: "custom",
message: minor === null ? v.money : tr.transactions.validation.amountPositive,
});
return z.NEVER;
}
return minor;
}),
date: ymd,
description: z.string().trim().max(500).default(""),
/** Verilirse carinin vadesi güncellenir (boş string → vade kaldırılır). */
dueDate: optionalYmd.optional(),
reminderDays: reminderDays.optional(),
});
export type ContactEntryInput = z.input<typeof contactEntrySchema>;
export type ContactEntry = z.output<typeof contactEntrySchema>;