feat: vylepšení objednávek
CI / Generate TypeScript types (pull_request) Successful in 20s
CI / Server unit tests (pull_request) Failing after 20s
CI / Build client (pull_request) Failing after 30s
CI / Build server (pull_request) Successful in 3m13s
CI / Playwright E2E tests (pull_request) Has been skipped
CI / Build and push Docker image (pull_request) Has been skipped
CI / Notify (pull_request) Has been skipped
CI / Build client (push) Failing after 10m5s
CI / Generate TypeScript types (push) Successful in 10s
CI / Server unit tests (push) Failing after 22s
CI / Build server (push) Successful in 41s
CI / Playwright E2E tests (push) Has been skipped
CI / Build and push Docker image (push) Has been skipped
CI / Notify (push) Successful in 4s

This commit is contained in:
2026-05-07 09:50:51 +02:00
parent 1efe2b8f7d
commit c7f78cf2c9
14 changed files with 1163 additions and 186 deletions
+14
View File
@@ -147,6 +147,20 @@ export async function markGroupMemberPaid(login: string, groupId: string, date?:
return saveExtraData(data, date);
}
export async function updateGroupFees(login: string, groupId: string, fees?: number, shipping?: number, tip?: number, discountType?: string, discountValue?: number, date?: Date): Promise<ClientData> {
const data = await getExtraData(date);
const group = findGroup(data, groupId);
if (!group) throw new Error('Skupina nebyla nalezena');
if (group.creatorLogin !== login) throw new Error('Poplatky může měnit pouze zakladatel');
if (group.state === GroupState.ORDERED) throw new Error('Skupinu ve stavu "objednáno" nelze upravovat');
if (fees !== undefined) group.fees = fees > 0 ? fees : undefined;
if (shipping !== undefined) group.shipping = shipping > 0 ? shipping : undefined;
if (tip !== undefined) group.tip = tip > 0 ? tip : undefined;
if (discountType !== undefined) group.discountType = (discountType as any) || undefined;
if (discountValue !== undefined) group.discountValue = discountValue > 0 ? discountValue : undefined;
return saveExtraData(data, date);
}
export async function updateGroupTimes(login: string, groupId: string, orderedAt?: string, deliveryAt?: string, date?: Date): Promise<ClientData> {
const data = await getExtraData(date);
const group = findGroup(data, groupId);
+27 -1
View File
@@ -2,7 +2,7 @@ import express, { Request } from "express";
import { getLogin } from "../auth";
import { parseToken } from "../utils";
import { getWebsocket } from "../websocket";
import { createGroup, deleteGroup, addGroupMember, removeGroupMember, updateGroupMember, setGroupState, updateGroupTimes } from "../groups";
import { createGroup, deleteGroup, addGroupMember, removeGroupMember, updateGroupMember, setGroupState, updateGroupTimes, updateGroupFees } from "../groups";
import { GroupState } from "../../../types/gen/types.gen";
const router = express.Router();
@@ -109,6 +109,32 @@ router.post("/setState", async (req: Request, res, next) => {
} catch (e: any) { next(e); }
});
router.post("/updateFees", async (req: Request, res, next) => {
const login = getLogin(parseToken(req));
const { id, fees, shipping, tip, discountType, discountValue } = req.body ?? {};
if (!id) return res.status(400).json({ error: 'Nebylo předáno ID skupiny' });
if (fees !== undefined && (typeof fees !== 'number' || !Number.isFinite(fees) || fees < 0)) {
return res.status(400).json({ error: 'Neplatná výše poplatků' });
}
if (shipping !== undefined && (typeof shipping !== 'number' || !Number.isFinite(shipping) || shipping < 0)) {
return res.status(400).json({ error: 'Neplatná výše dopravy' });
}
if (tip !== undefined && (typeof tip !== 'number' || !Number.isFinite(tip) || tip < 0)) {
return res.status(400).json({ error: 'Neplatná výše spropitného' });
}
if (discountType !== undefined && discountType !== '' && !['percent', 'fixed'].includes(discountType)) {
return res.status(400).json({ error: 'Neplatný typ slevy' });
}
if (discountValue !== undefined && (typeof discountValue !== 'number' || !Number.isFinite(discountValue) || discountValue < 0)) {
return res.status(400).json({ error: 'Neplatná výše slevy' });
}
try {
const data = await updateGroupFees(login, id, fees, shipping, tip, discountType, discountValue);
broadcastExtra(data);
res.status(200).json(data);
} catch (e: any) { next(e); }
});
router.post("/updateTimes", async (req: Request, res, next) => {
const login = getLogin(parseToken(req));
const { id, orderedAt, deliveryAt } = req.body ?? {};
+6 -3
View File
@@ -3,6 +3,7 @@ import { getLogin } from "../auth";
import { parseToken, formatDate } from "../utils";
import { generateQr } from "../qr";
import { addPendingQr } from "../pizza";
import { emitToUser } from "../websocket";
import { GenerateQrData } from "../../../types";
import crypto from "crypto";
@@ -48,15 +49,17 @@ router.post("/generate", async (req: Request<{}, any, GenerateQrData["body"]>, r
const id = crypto.randomUUID();
await generateQr(recipient.login, bankAccount, bankAccountHolder, recipient.amount, recipient.purpose, id);
// Uložit jako nevyřízený QR kód
await addPendingQr(recipient.login, {
// Uložit jako nevyřízený QR kód a okamžitě doručit příjemci
const pendingQr = {
id,
date: today,
creator: login,
totalPrice: recipient.amount,
purpose: recipient.purpose,
...(groupId ? { groupId } : {}),
});
};
await addPendingQr(recipient.login, pendingQr);
emitToUser(recipient.login, 'pendingQr', pendingQr);
}
res.status(200).json({ success: true, count: recipients.length });
+11 -2
View File
@@ -11,6 +11,12 @@ export const initWebsocket = (server: any) => {
io.on("connection", (socket) => {
console.log(`New client connected: ${socket.id}`);
socket.on("join", (login: string) => {
if (login && typeof login === "string") {
socket.join(`user:${login}`);
}
});
socket.on("message", (message) => {
io.emit("message", message);
});
@@ -22,6 +28,9 @@ export const initWebsocket = (server: any) => {
return io;
}
export const getWebsocket = () => {
return io;
export const getWebsocket = () => io;
/** Pošle event konkrétnímu přihlášenému uživateli (pokud je připojen). */
export const emitToUser = (login: string, event: string, data: unknown) => {
io.to(`user:${login}`).emit(event, data);
}