style: run prettier on entire codebase

This commit is contained in:
BOHA
2026-03-24 19:59:14 +01:00
parent 872be42107
commit 3c167cf5c4
148 changed files with 26740 additions and 13990 deletions

View File

@@ -1,35 +1,59 @@
import prisma from '../config/database';
import prisma from "../config/database";
// Re-export for convenience
// Status transition rules matching PHP
const VALID_TRANSITIONS: Record<string, string[]> = {
issued: ['paid'],
overdue: ['paid'],
issued: ["paid"],
overdue: ["paid"],
paid: [],
};
const ALLOWED_SORT_FIELDS = ['id', 'invoice_number', 'status', 'issue_date', 'due_date', 'currency'];
const ALLOWED_SORT_FIELDS = [
"id",
"invoice_number",
"status",
"issue_date",
"due_date",
"currency",
];
interface InvoiceItemInput { description?: string; quantity?: number; unit?: string; unit_price?: number; vat_rate?: number; position?: number }
interface InvoiceItemInput {
description?: string;
quantity?: number;
unit?: string;
unit_price?: number;
vat_rate?: number;
position?: number;
}
interface ListInvoicesParams {
page: number;
limit: number;
skip: number;
sort: string;
order: 'asc' | 'desc';
order: "asc" | "desc";
search: string;
status?: string;
customer_id?: number;
}
function computeInvoiceTotals(items: Array<{ quantity: unknown; unit_price: unknown; vat_rate: unknown }>, applyVat: boolean | null, defaultVatRate: unknown) {
const subtotal = items.reduce((s, i) => s + (Number(i.quantity) || 0) * (Number(i.unit_price) || 0), 0);
function computeInvoiceTotals(
items: Array<{ quantity: unknown; unit_price: unknown; vat_rate: unknown }>,
applyVat: boolean | null,
defaultVatRate: unknown,
) {
const subtotal = items.reduce(
(s, i) => s + (Number(i.quantity) || 0) * (Number(i.unit_price) || 0),
0,
);
const vatAmount = applyVat
? items.reduce((s, i) => {
const base = (Number(i.quantity) || 0) * (Number(i.unit_price) || 0);
return s + base * ((Number(i.vat_rate) || Number(defaultVatRate) || 21) / 100);
return (
s +
base * ((Number(i.vat_rate) || Number(defaultVatRate) || 21) / 100)
);
}, 0)
: 0;
return {
@@ -42,15 +66,18 @@ function computeInvoiceTotals(items: Array<{ quantity: unknown; unit_price: unkn
export async function markOverdueInvoices() {
try {
await prisma.invoices.updateMany({
where: { status: 'issued', due_date: { lt: new Date() } },
data: { status: 'overdue' },
where: { status: "issued", due_date: { lt: new Date() } },
data: { status: "overdue" },
});
} catch { /* silent */ }
} catch {
/* silent */
}
}
export async function listInvoices(params: ListInvoicesParams) {
const { page, limit, skip, sort, order, search, status, customer_id } = params;
const sortField = ALLOWED_SORT_FIELDS.includes(sort) ? sort : 'id';
const { page, limit, skip, sort, order, search, status, customer_id } =
params;
const sortField = ALLOWED_SORT_FIELDS.includes(sort) ? sort : "id";
const where: Record<string, unknown> = {};
if (status) where.status = status;
@@ -80,8 +107,12 @@ export async function listInvoices(params: ListInvoicesParams) {
prisma.invoices.count({ where }),
]);
const enriched = invoices.map(inv => {
const totals = computeInvoiceTotals(inv.invoice_items, inv.apply_vat, inv.vat_rate);
const enriched = invoices.map((inv) => {
const totals = computeInvoiceTotals(
inv.invoice_items,
inv.apply_vat,
inv.vat_rate,
);
const { invoice_items, ...rest } = inv;
return {
...rest,
@@ -96,8 +127,10 @@ export async function listInvoices(params: ListInvoicesParams) {
}
export async function getNextInvoiceNumberFormatted() {
const settings = await prisma.company_settings.findFirst({ select: { invoice_type_code: true } });
const typeCode = settings?.invoice_type_code || '81';
const settings = await prisma.company_settings.findFirst({
select: { invoice_type_code: true },
});
const typeCode = settings?.invoice_type_code || "81";
const yy = String(new Date().getFullYear()).slice(-2);
const prefix = `${yy}${typeCode}`;
const prefixLen = prefix.length;
@@ -110,14 +143,14 @@ export async function getNextInvoiceNumberFormatted() {
WHERE invoice_number LIKE ${likePattern}
`;
const nextNum = Number(result[0]?.max_num ?? 0) + 1;
const number = `${prefix}${String(nextNum).padStart(4, '0')}`;
const number = `${prefix}${String(nextNum).padStart(4, "0")}`;
return { number, next_number: number };
}
export async function getInvoiceStats(queryMonth?: number, queryYear?: number) {
const now = new Date();
const year = queryYear || now.getFullYear();
const month = queryMonth || (now.getMonth() + 1);
const month = queryMonth || now.getMonth() + 1;
const monthStart = new Date(year, month - 1, 1);
const monthEnd = new Date(year, month, 0, 23, 59, 59);
@@ -127,12 +160,18 @@ export async function getInvoiceStats(queryMonth?: number, queryYear?: number) {
});
// Helper: compute invoice total WITH VAT (matching PHP)
const invoiceTotalWithVat = (inv: typeof allInvoices[0]) => {
const sub = inv.invoice_items.reduce((s, i) => s + (Number(i.quantity) || 0) * (Number(i.unit_price) || 0), 0);
const invoiceTotalWithVat = (inv: (typeof allInvoices)[0]) => {
const sub = inv.invoice_items.reduce(
(s, i) => s + (Number(i.quantity) || 0) * (Number(i.unit_price) || 0),
0,
);
const vat = inv.apply_vat
? inv.invoice_items.reduce((s, i) => {
const base = (Number(i.quantity) || 0) * (Number(i.unit_price) || 0);
return s + base * ((Number(i.vat_rate) || Number(inv.vat_rate) || 21) / 100);
return (
s +
base * ((Number(i.vat_rate) || Number(inv.vat_rate) || 21) / 100)
);
}, 0)
: 0;
return sub + vat;
@@ -142,10 +181,15 @@ export async function getInvoiceStats(queryMonth?: number, queryYear?: number) {
const aggregateByCurrency = (invoices: typeof allInvoices) => {
const map: Record<string, number> = {};
for (const inv of invoices) {
const cur = inv.currency || 'CZK';
const cur = inv.currency || "CZK";
map[cur] = (map[cur] || 0) + invoiceTotalWithVat(inv);
}
return Object.entries(map).filter(([, v]) => v > 0).map(([currency, amount]) => ({ amount: Math.round(amount * 100) / 100, currency }));
return Object.entries(map)
.filter(([, v]) => v > 0)
.map(([currency, amount]) => ({
amount: Math.round(amount * 100) / 100,
currency,
}));
};
const sumCzk = (invoices: typeof allInvoices) => {
@@ -156,27 +200,34 @@ export async function getInvoiceStats(queryMonth?: number, queryYear?: number) {
return Math.round(total * 100) / 100;
};
const monthInvoices = allInvoices.filter(inv => {
const monthInvoices = allInvoices.filter((inv) => {
const issueDate = inv.issue_date ? new Date(inv.issue_date) : null;
return issueDate && issueDate >= monthStart && issueDate <= monthEnd;
});
const paidInvoices = monthInvoices.filter(i => i.status === 'paid');
const awaitingInvoices = allInvoices.filter(i => i.status === 'issued');
const overdueInvoices = allInvoices.filter(i => i.status === 'overdue');
const paidInvoices = monthInvoices.filter((i) => i.status === "paid");
const awaitingInvoices = allInvoices.filter((i) => i.status === "issued");
const overdueInvoices = allInvoices.filter((i) => i.status === "overdue");
// VAT by currency
const vatMap: Record<string, number> = {};
for (const inv of monthInvoices) {
if (!inv.apply_vat) continue;
const cur = inv.currency || 'CZK';
const cur = inv.currency || "CZK";
for (const item of inv.invoice_items) {
const base = (Number(item.quantity) || 0) * (Number(item.unit_price) || 0);
const vat = base * ((Number(item.vat_rate) || Number(inv.vat_rate) || 21) / 100);
const base =
(Number(item.quantity) || 0) * (Number(item.unit_price) || 0);
const vat =
base * ((Number(item.vat_rate) || Number(inv.vat_rate) || 21) / 100);
vatMap[cur] = (vatMap[cur] || 0) + vat;
}
}
const vatAmounts = Object.entries(vatMap).filter(([, v]) => v > 0).map(([currency, amount]) => ({ amount: Math.round(amount * 100) / 100, currency }));
const vatAmounts = Object.entries(vatMap)
.filter(([, v]) => v > 0)
.map(([currency, amount]) => ({
amount: Math.round(amount * 100) / 100,
currency,
}));
let vatCzk = 0;
for (const [, v] of Object.entries(vatMap)) vatCzk += v;
@@ -202,12 +253,16 @@ export async function getOrderDataForInvoice(orderId: number) {
where: { id: orderId },
include: {
customers: true,
order_items: { orderBy: { position: 'asc' } },
order_items: { orderBy: { position: "asc" } },
},
});
if (!order) return null;
const { order_items, customers, ...rest } = order;
return { ...rest, items: order_items, customer_name: customers?.name || null };
return {
...rest,
items: order_items,
customer_name: customers?.name || null,
};
}
export async function getInvoice(id: number) {
@@ -215,7 +270,7 @@ export async function getInvoice(id: number) {
where: { id },
include: {
customers: true,
invoice_items: { orderBy: { position: 'asc' } },
invoice_items: { orderBy: { position: "asc" } },
orders: { select: { id: true, order_number: true } },
},
});
@@ -237,12 +292,14 @@ export async function createInvoice(body: Record<string, any>) {
invoice_number: body.invoice_number ? String(body.invoice_number) : null,
order_id: body.order_id ? Number(body.order_id) : null,
customer_id: body.customer_id ? Number(body.customer_id) : null,
status: body.status ? String(body.status) : 'issued',
currency: body.currency ? String(body.currency) : 'CZK',
status: body.status ? String(body.status) : "issued",
currency: body.currency ? String(body.currency) : "CZK",
vat_rate: body.vat_rate ? Number(body.vat_rate) : 21.0,
apply_vat: body.apply_vat !== false,
payment_method: body.payment_method ? String(body.payment_method) : null,
constant_symbol: body.constant_symbol ? String(body.constant_symbol) : null,
constant_symbol: body.constant_symbol
? String(body.constant_symbol)
: null,
bank_name: body.bank_name ? String(body.bank_name) : null,
bank_swift: body.bank_swift ? String(body.bank_swift) : null,
bank_iban: body.bank_iban ? String(body.bank_iban) : null,
@@ -276,7 +333,7 @@ export async function createInvoice(body: Record<string, any>) {
export async function updateInvoice(id: number, body: Record<string, any>) {
const existing = await prisma.invoices.findUnique({ where: { id } });
if (!existing) return { error: 'not_found' as const };
if (!existing) return { error: "not_found" as const };
const currentStatus = existing.status as string;
@@ -285,43 +342,68 @@ export async function updateInvoice(id: number, body: Record<string, any>) {
const newStatus = String(body.status);
const allowed = VALID_TRANSITIONS[currentStatus] || [];
if (!allowed.includes(newStatus)) {
return { error: 'invalid_transition' as const, currentStatus, newStatus };
return { error: "invalid_transition" as const, currentStatus, newStatus };
}
}
const data: Record<string, unknown> = { modified_at: new Date() };
// Only allow full editing in 'issued' state
const isDraft = currentStatus === 'issued';
const isDraft = currentStatus === "issued";
if (isDraft) {
const strFields = ['currency', 'payment_method', 'constant_symbol', 'bank_name', 'bank_swift', 'bank_iban', 'bank_account', 'issued_by', 'billing_text'];
const strFields = [
"currency",
"payment_method",
"constant_symbol",
"bank_name",
"bank_swift",
"bank_iban",
"bank_account",
"issued_by",
"billing_text",
];
for (const f of strFields) {
if (body[f] !== undefined) data[f] = body[f] ? String(body[f]) : null;
}
if (body.customer_id !== undefined) data.customer_id = body.customer_id ? Number(body.customer_id) : null;
if (body.customer_id !== undefined)
data.customer_id = body.customer_id ? Number(body.customer_id) : null;
if (body.vat_rate !== undefined) data.vat_rate = Number(body.vat_rate);
if (body.apply_vat !== undefined) data.apply_vat = body.apply_vat === true || body.apply_vat === 1 || body.apply_vat === '1';
if (body.issue_date !== undefined) data.issue_date = body.issue_date ? new Date(String(body.issue_date)) : null;
if (body.due_date !== undefined) data.due_date = body.due_date ? new Date(String(body.due_date)) : null;
if (body.tax_date !== undefined) data.tax_date = body.tax_date ? new Date(String(body.tax_date)) : null;
if (body.apply_vat !== undefined)
data.apply_vat =
body.apply_vat === true ||
body.apply_vat === 1 ||
body.apply_vat === "1";
if (body.issue_date !== undefined)
data.issue_date = body.issue_date
? new Date(String(body.issue_date))
: null;
if (body.due_date !== undefined)
data.due_date = body.due_date ? new Date(String(body.due_date)) : null;
if (body.tax_date !== undefined)
data.tax_date = body.tax_date ? new Date(String(body.tax_date)) : null;
}
// Notes editable in issued/overdue
if (currentStatus === 'issued' || currentStatus === 'overdue') {
if (body.notes !== undefined) data.notes = body.notes ? String(body.notes) : null;
if (body.internal_notes !== undefined) data.internal_notes = body.internal_notes ? String(body.internal_notes) : null;
if (currentStatus === "issued" || currentStatus === "overdue") {
if (body.notes !== undefined)
data.notes = body.notes ? String(body.notes) : null;
if (body.internal_notes !== undefined)
data.internal_notes = body.internal_notes
? String(body.internal_notes)
: null;
}
// Status change
if (body.status !== undefined) {
data.status = String(body.status);
// Auto-set paid_date when transitioning to paid
if (String(body.status) === 'paid' && !existing.paid_date) {
if (String(body.status) === "paid" && !existing.paid_date) {
data.paid_date = new Date();
}
}
if (body.paid_date !== undefined) data.paid_date = body.paid_date ? new Date(String(body.paid_date)) : null;
if (body.paid_date !== undefined)
data.paid_date = body.paid_date ? new Date(String(body.paid_date)) : null;
await prisma.invoices.update({ where: { id }, data });