1.5.2
- feat: order confirmation PDF generation with VAT support - feat: order confirmation modal with custom item editing - fix: attendance negative duration clamping and switchProject timing - fix: Quill editor locked to Tahoma 14px, PDF heading sizes - fix: invoice/offer PDF font consistency (Tahoma enforcement) - fix: invoice alert cron improvements - fix: NAS financials manager edge cases - refactor: numbering service with unique sequence constraints Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,9 @@
|
||||
import prisma from "../config/database";
|
||||
import { generateSharedNumber } from "./numbering.service";
|
||||
import {
|
||||
generateSharedNumber,
|
||||
previewSharedNumber,
|
||||
releaseSharedNumber,
|
||||
} from "./numbering.service";
|
||||
|
||||
interface OrderItemInput {
|
||||
description?: string | null;
|
||||
@@ -180,10 +184,10 @@ export async function createOrderFromQuotation(
|
||||
status: 400,
|
||||
} as const;
|
||||
|
||||
const orderNumber = await generateSharedNumber();
|
||||
const projectNumber = await generateSharedNumber();
|
||||
|
||||
const result = await prisma.$transaction(async (tx) => {
|
||||
const orderNumber = await generateSharedNumber(tx);
|
||||
const projectNumber = orderNumber;
|
||||
|
||||
const order = await tx.orders.create({
|
||||
data: {
|
||||
order_number: orderNumber,
|
||||
@@ -249,14 +253,14 @@ export async function createOrderFromQuotation(
|
||||
},
|
||||
});
|
||||
|
||||
return { order, project };
|
||||
return { order, project, orderNumber };
|
||||
});
|
||||
|
||||
return {
|
||||
data: {
|
||||
order_id: result.order.id,
|
||||
id: result.order.id,
|
||||
order_number: orderNumber,
|
||||
order_number: result.orderNumber,
|
||||
quotationId,
|
||||
},
|
||||
};
|
||||
@@ -281,9 +285,14 @@ interface CreateOrderData {
|
||||
}
|
||||
|
||||
export async function createOrder(body: CreateOrderData) {
|
||||
const orderNumber =
|
||||
body.order_number !== undefined && body.order_number !== null
|
||||
? String(body.order_number)
|
||||
: await generateSharedNumber();
|
||||
|
||||
const order = await prisma.orders.create({
|
||||
data: {
|
||||
order_number: body.order_number ?? null,
|
||||
order_number: orderNumber,
|
||||
customer_order_number: body.customer_order_number ?? null,
|
||||
quotation_id: body.quotation_id ?? null,
|
||||
customer_id: body.customer_id ?? null,
|
||||
@@ -343,6 +352,16 @@ export async function updateOrder(id: number, body: UpdateOrderData) {
|
||||
|
||||
const currentStatus = existing.status as string;
|
||||
|
||||
if (
|
||||
body.order_number !== undefined &&
|
||||
String(body.order_number) !== existing.order_number
|
||||
) {
|
||||
return {
|
||||
error: "Číslo objednávky nelze změnit",
|
||||
status: 400,
|
||||
} as const;
|
||||
}
|
||||
|
||||
if (body.status !== undefined && String(body.status) !== currentStatus) {
|
||||
const newStatus = String(body.status);
|
||||
const allowed = VALID_TRANSITIONS[currentStatus] || [];
|
||||
@@ -356,7 +375,6 @@ export async function updateOrder(id: number, body: UpdateOrderData) {
|
||||
|
||||
const data: Record<string, unknown> = { modified_at: new Date() };
|
||||
const strFields = [
|
||||
"order_number",
|
||||
"customer_order_number",
|
||||
"status",
|
||||
"currency",
|
||||
@@ -377,17 +395,6 @@ export async function updateOrder(id: number, body: UpdateOrderData) {
|
||||
|
||||
await prisma.orders.update({ where: { id }, data });
|
||||
|
||||
// Sync project_number when order_number changes (matching PHP)
|
||||
if (
|
||||
body.order_number !== undefined &&
|
||||
String(body.order_number) !== existing.order_number
|
||||
) {
|
||||
await prisma.projects.updateMany({
|
||||
where: { order_id: id },
|
||||
data: { project_number: String(body.order_number) },
|
||||
});
|
||||
}
|
||||
|
||||
// Sync project status when order status changes (matching PHP)
|
||||
if (body.status !== undefined && String(body.status) !== currentStatus) {
|
||||
const statusMap: Record<string, string> = {
|
||||
@@ -405,6 +412,12 @@ export async function updateOrder(id: number, body: UpdateOrderData) {
|
||||
}
|
||||
|
||||
if (Array.isArray(body.items) || Array.isArray(body.sections)) {
|
||||
if (currentStatus !== "prijata" && currentStatus !== "v_realizaci") {
|
||||
return {
|
||||
error: "Nelze upravit položky dokončené/stornované objednávky",
|
||||
status: 400,
|
||||
} as const;
|
||||
}
|
||||
await prisma.$transaction(async (tx) => {
|
||||
if (Array.isArray(body.items)) {
|
||||
await tx.order_items.deleteMany({ where: { order_id: id } });
|
||||
@@ -453,7 +466,7 @@ export async function deleteOrder(id: number) {
|
||||
// Delete linked project and its notes (matching PHP)
|
||||
const linkedProjects = await prisma.projects.findMany({
|
||||
where: { order_id: id },
|
||||
select: { id: true },
|
||||
select: { id: true, created_at: true },
|
||||
});
|
||||
if (linkedProjects.length > 0) {
|
||||
const projectIds = linkedProjects.map((p) => p.id);
|
||||
@@ -464,9 +477,23 @@ export async function deleteOrder(id: number) {
|
||||
}
|
||||
|
||||
await prisma.orders.delete({ where: { id } });
|
||||
|
||||
const year = existing.created_at
|
||||
? new Date(existing.created_at).getFullYear()
|
||||
: new Date().getFullYear();
|
||||
await releaseSharedNumber(year);
|
||||
|
||||
// Release the linked project's shared number(s) too
|
||||
for (const p of linkedProjects) {
|
||||
const pYear = p.created_at
|
||||
? new Date(p.created_at).getFullYear()
|
||||
: new Date().getFullYear();
|
||||
await releaseSharedNumber(pYear);
|
||||
}
|
||||
|
||||
return { data: { id, order_number: existing.order_number } };
|
||||
}
|
||||
|
||||
export async function getNextOrderNumber() {
|
||||
return generateSharedNumber();
|
||||
return previewSharedNumber();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user