refactor: Split ai-kurzy.service.js into domain-specific files
Split 445-line monolith into: - ai-kurzy/kurzy.service.js: course CRUD + stats - ai-kurzy/ucastnici.service.js: participant CRUD - ai-kurzy/registracie.service.js: registration CRUD, combined table, field updates, and document (prilohy) operations Original ai-kurzy.service.js becomes a barrel export preserving all existing import paths. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,445 +1,7 @@
|
|||||||
import { db } from '../config/database.js';
|
export { getAllKurzy, getKurzById, createKurz, updateKurz, deleteKurz, getKurzyStats } from './ai-kurzy/kurzy.service.js';
|
||||||
import { kurzy, ucastnici, registracie, prilohy } from '../db/schema.js';
|
export { getAllUcastnici, getUcastnikById, createUcastnik, updateUcastnik, deleteUcastnik } from './ai-kurzy/ucastnici.service.js';
|
||||||
import { and, desc, eq, sql, asc } from 'drizzle-orm';
|
export {
|
||||||
import { NotFoundError } from '../utils/errors.js';
|
getAllRegistracie, getRegistraciaById, createRegistracia, updateRegistracia, deleteRegistracia,
|
||||||
|
getCombinedTableData, updateField,
|
||||||
// ==================== KURZY (Courses) ====================
|
getPrilohyByRegistracia, createPriloha, deletePriloha,
|
||||||
|
} from './ai-kurzy/registracie.service.js';
|
||||||
export const getAllKurzy = async () => {
|
|
||||||
const result = await db
|
|
||||||
.select({
|
|
||||||
id: kurzy.id,
|
|
||||||
nazov: kurzy.nazov,
|
|
||||||
typKurzu: kurzy.typKurzu,
|
|
||||||
popis: kurzy.popis,
|
|
||||||
cena: kurzy.cena,
|
|
||||||
maxKapacita: kurzy.maxKapacita,
|
|
||||||
aktivny: kurzy.aktivny,
|
|
||||||
farba: kurzy.farba,
|
|
||||||
createdAt: kurzy.createdAt,
|
|
||||||
registraciiCount: sql`(SELECT COUNT(*) FROM registracie WHERE kurz_id = ${kurzy.id})::int`,
|
|
||||||
})
|
|
||||||
.from(kurzy)
|
|
||||||
.orderBy(asc(kurzy.nazov));
|
|
||||||
|
|
||||||
return result;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const getKurzById = async (id) => {
|
|
||||||
const [kurz] = await db
|
|
||||||
.select()
|
|
||||||
.from(kurzy)
|
|
||||||
.where(eq(kurzy.id, id))
|
|
||||||
.limit(1);
|
|
||||||
|
|
||||||
if (!kurz) {
|
|
||||||
throw new NotFoundError('Kurz nenájdený');
|
|
||||||
}
|
|
||||||
|
|
||||||
return kurz;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const createKurz = async (data) => {
|
|
||||||
const [newKurz] = await db
|
|
||||||
.insert(kurzy)
|
|
||||||
.values({
|
|
||||||
nazov: data.nazov,
|
|
||||||
typKurzu: data.typKurzu,
|
|
||||||
popis: data.popis || null,
|
|
||||||
cena: data.cena,
|
|
||||||
maxKapacita: data.maxKapacita || null,
|
|
||||||
aktivny: data.aktivny !== undefined ? data.aktivny : true,
|
|
||||||
farba: data.farba || null,
|
|
||||||
})
|
|
||||||
.returning();
|
|
||||||
|
|
||||||
return newKurz;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const updateKurz = async (id, data) => {
|
|
||||||
await getKurzById(id);
|
|
||||||
|
|
||||||
const updateData = {
|
|
||||||
nazov: data.nazov,
|
|
||||||
typKurzu: data.typKurzu,
|
|
||||||
popis: data.popis !== undefined ? data.popis : undefined,
|
|
||||||
cena: data.cena,
|
|
||||||
maxKapacita: data.maxKapacita !== undefined ? data.maxKapacita : undefined,
|
|
||||||
aktivny: data.aktivny !== undefined ? data.aktivny : undefined,
|
|
||||||
farba: data.farba !== undefined ? data.farba : undefined,
|
|
||||||
updatedAt: new Date(),
|
|
||||||
};
|
|
||||||
|
|
||||||
// Remove undefined values
|
|
||||||
Object.keys(updateData).forEach(key => {
|
|
||||||
if (updateData[key] === undefined) {
|
|
||||||
delete updateData[key];
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const [updated] = await db
|
|
||||||
.update(kurzy)
|
|
||||||
.set(updateData)
|
|
||||||
.where(eq(kurzy.id, id))
|
|
||||||
.returning();
|
|
||||||
|
|
||||||
return updated;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const deleteKurz = async (id) => {
|
|
||||||
await getKurzById(id);
|
|
||||||
await db.delete(kurzy).where(eq(kurzy.id, id));
|
|
||||||
return { success: true, message: 'Kurz bol odstránený' };
|
|
||||||
};
|
|
||||||
|
|
||||||
// ==================== UCASTNICI (Participants) ====================
|
|
||||||
|
|
||||||
export const getAllUcastnici = async () => {
|
|
||||||
const result = await db
|
|
||||||
.select({
|
|
||||||
id: ucastnici.id,
|
|
||||||
titul: ucastnici.titul,
|
|
||||||
meno: ucastnici.meno,
|
|
||||||
priezvisko: ucastnici.priezvisko,
|
|
||||||
email: ucastnici.email,
|
|
||||||
telefon: ucastnici.telefon,
|
|
||||||
firma: ucastnici.firma,
|
|
||||||
firmaIco: ucastnici.firmaIco,
|
|
||||||
firmaDic: ucastnici.firmaDic,
|
|
||||||
firmaIcDph: ucastnici.firmaIcDph,
|
|
||||||
firmaSidlo: ucastnici.firmaSidlo,
|
|
||||||
mesto: ucastnici.mesto,
|
|
||||||
ulica: ucastnici.ulica,
|
|
||||||
psc: ucastnici.psc,
|
|
||||||
createdAt: ucastnici.createdAt,
|
|
||||||
registraciiCount: sql`(SELECT COUNT(*) FROM registracie WHERE ucastnik_id = ${ucastnici.id})::int`,
|
|
||||||
})
|
|
||||||
.from(ucastnici)
|
|
||||||
.orderBy(asc(ucastnici.priezvisko), asc(ucastnici.meno));
|
|
||||||
|
|
||||||
return result;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const getUcastnikById = async (id) => {
|
|
||||||
const [ucastnik] = await db
|
|
||||||
.select()
|
|
||||||
.from(ucastnici)
|
|
||||||
.where(eq(ucastnici.id, id))
|
|
||||||
.limit(1);
|
|
||||||
|
|
||||||
if (!ucastnik) {
|
|
||||||
throw new NotFoundError('Účastník nenájdený');
|
|
||||||
}
|
|
||||||
|
|
||||||
return ucastnik;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const createUcastnik = async (data) => {
|
|
||||||
const [newUcastnik] = await db
|
|
||||||
.insert(ucastnici)
|
|
||||||
.values({
|
|
||||||
titul: data.titul || null,
|
|
||||||
meno: data.meno,
|
|
||||||
priezvisko: data.priezvisko,
|
|
||||||
email: data.email,
|
|
||||||
telefon: data.telefon || null,
|
|
||||||
firma: data.firma || null,
|
|
||||||
firmaIco: data.firmaIco || null,
|
|
||||||
firmaDic: data.firmaDic || null,
|
|
||||||
firmaIcDph: data.firmaIcDph || null,
|
|
||||||
firmaSidlo: data.firmaSidlo || null,
|
|
||||||
mesto: data.mesto || null,
|
|
||||||
ulica: data.ulica || null,
|
|
||||||
psc: data.psc || null,
|
|
||||||
})
|
|
||||||
.returning();
|
|
||||||
|
|
||||||
return newUcastnik;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const updateUcastnik = async (id, data) => {
|
|
||||||
await getUcastnikById(id);
|
|
||||||
|
|
||||||
const [updated] = await db
|
|
||||||
.update(ucastnici)
|
|
||||||
.set({
|
|
||||||
...data,
|
|
||||||
updatedAt: new Date(),
|
|
||||||
})
|
|
||||||
.where(eq(ucastnici.id, id))
|
|
||||||
.returning();
|
|
||||||
|
|
||||||
return updated;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const deleteUcastnik = async (id) => {
|
|
||||||
await getUcastnikById(id);
|
|
||||||
await db.delete(ucastnici).where(eq(ucastnici.id, id));
|
|
||||||
return { success: true, message: 'Účastník bol odstránený' };
|
|
||||||
};
|
|
||||||
|
|
||||||
// ==================== REGISTRACIE (Registrations) ====================
|
|
||||||
|
|
||||||
export const getAllRegistracie = async (kurzId = null) => {
|
|
||||||
const conditions = kurzId ? [eq(registracie.kurzId, kurzId)] : [];
|
|
||||||
|
|
||||||
const result = await db
|
|
||||||
.select({
|
|
||||||
id: registracie.id,
|
|
||||||
kurzId: registracie.kurzId,
|
|
||||||
ucastnikId: registracie.ucastnikId,
|
|
||||||
datumOd: registracie.datumOd,
|
|
||||||
datumDo: registracie.datumDo,
|
|
||||||
formaKurzu: registracie.formaKurzu,
|
|
||||||
pocetUcastnikov: registracie.pocetUcastnikov,
|
|
||||||
fakturaCislo: registracie.fakturaCislo,
|
|
||||||
fakturaVystavena: registracie.fakturaVystavena,
|
|
||||||
zaplatene: registracie.zaplatene,
|
|
||||||
stav: registracie.stav,
|
|
||||||
poznamka: registracie.poznamka,
|
|
||||||
createdAt: registracie.createdAt,
|
|
||||||
// Kurz info
|
|
||||||
kurzNazov: kurzy.nazov,
|
|
||||||
kurzTyp: kurzy.typKurzu,
|
|
||||||
// Ucastnik info
|
|
||||||
ucastnikMeno: ucastnici.meno,
|
|
||||||
ucastnikPriezvisko: ucastnici.priezvisko,
|
|
||||||
ucastnikEmail: ucastnici.email,
|
|
||||||
ucastnikFirma: ucastnici.firma,
|
|
||||||
})
|
|
||||||
.from(registracie)
|
|
||||||
.leftJoin(kurzy, eq(registracie.kurzId, kurzy.id))
|
|
||||||
.leftJoin(ucastnici, eq(registracie.ucastnikId, ucastnici.id))
|
|
||||||
.where(conditions.length > 0 ? and(...conditions) : undefined)
|
|
||||||
.orderBy(desc(registracie.datumOd), desc(registracie.createdAt));
|
|
||||||
|
|
||||||
return result;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const getRegistraciaById = async (id) => {
|
|
||||||
const [reg] = await db
|
|
||||||
.select({
|
|
||||||
id: registracie.id,
|
|
||||||
kurzId: registracie.kurzId,
|
|
||||||
ucastnikId: registracie.ucastnikId,
|
|
||||||
formaKurzu: registracie.formaKurzu,
|
|
||||||
pocetUcastnikov: registracie.pocetUcastnikov,
|
|
||||||
fakturaCislo: registracie.fakturaCislo,
|
|
||||||
fakturaVystavena: registracie.fakturaVystavena,
|
|
||||||
zaplatene: registracie.zaplatene,
|
|
||||||
stav: registracie.stav,
|
|
||||||
poznamka: registracie.poznamka,
|
|
||||||
createdAt: registracie.createdAt,
|
|
||||||
kurzNazov: kurzy.nazov,
|
|
||||||
kurzTyp: kurzy.typKurzu,
|
|
||||||
ucastnikMeno: ucastnici.meno,
|
|
||||||
ucastnikPriezvisko: ucastnici.priezvisko,
|
|
||||||
ucastnikEmail: ucastnici.email,
|
|
||||||
})
|
|
||||||
.from(registracie)
|
|
||||||
.leftJoin(kurzy, eq(registracie.kurzId, kurzy.id))
|
|
||||||
.leftJoin(ucastnici, eq(registracie.ucastnikId, ucastnici.id))
|
|
||||||
.where(eq(registracie.id, id))
|
|
||||||
.limit(1);
|
|
||||||
|
|
||||||
if (!reg) {
|
|
||||||
throw new NotFoundError('Registrácia nenájdená');
|
|
||||||
}
|
|
||||||
|
|
||||||
return reg;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const createRegistracia = async (data) => {
|
|
||||||
const [newReg] = await db
|
|
||||||
.insert(registracie)
|
|
||||||
.values({
|
|
||||||
kurzId: data.kurzId,
|
|
||||||
ucastnikId: data.ucastnikId,
|
|
||||||
datumOd: data.datumOd ? new Date(data.datumOd) : null,
|
|
||||||
datumDo: data.datumDo ? new Date(data.datumDo) : null,
|
|
||||||
formaKurzu: data.formaKurzu || 'prezencne',
|
|
||||||
pocetUcastnikov: data.pocetUcastnikov || 1,
|
|
||||||
fakturaCislo: data.fakturaCislo || null,
|
|
||||||
fakturaVystavena: data.fakturaVystavena || false,
|
|
||||||
zaplatene: data.zaplatene || false,
|
|
||||||
stav: data.stav || 'registrovany',
|
|
||||||
poznamka: data.poznamka || null,
|
|
||||||
})
|
|
||||||
.returning();
|
|
||||||
|
|
||||||
return newReg;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const updateRegistracia = async (id, data) => {
|
|
||||||
await getRegistraciaById(id);
|
|
||||||
|
|
||||||
const [updated] = await db
|
|
||||||
.update(registracie)
|
|
||||||
.set({
|
|
||||||
...data,
|
|
||||||
updatedAt: new Date(),
|
|
||||||
})
|
|
||||||
.where(eq(registracie.id, id))
|
|
||||||
.returning();
|
|
||||||
|
|
||||||
return updated;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const deleteRegistracia = async (id) => {
|
|
||||||
await getRegistraciaById(id);
|
|
||||||
await db.delete(registracie).where(eq(registracie.id, id));
|
|
||||||
return { success: true, message: 'Registrácia bola odstránená' };
|
|
||||||
};
|
|
||||||
|
|
||||||
// ==================== COMBINED TABLE VIEW (Excel-style) ====================
|
|
||||||
|
|
||||||
export const getCombinedTableData = async () => {
|
|
||||||
const result = await db
|
|
||||||
.select({
|
|
||||||
// Registration ID (main row identifier)
|
|
||||||
id: registracie.id,
|
|
||||||
// Ucastnik fields
|
|
||||||
ucastnikId: ucastnici.id,
|
|
||||||
titul: ucastnici.titul,
|
|
||||||
meno: ucastnici.meno,
|
|
||||||
priezvisko: ucastnici.priezvisko,
|
|
||||||
email: ucastnici.email,
|
|
||||||
telefon: ucastnici.telefon,
|
|
||||||
firma: ucastnici.firma,
|
|
||||||
firmaIco: ucastnici.firmaIco,
|
|
||||||
firmaDic: ucastnici.firmaDic,
|
|
||||||
firmaIcDph: ucastnici.firmaIcDph,
|
|
||||||
firmaSidlo: ucastnici.firmaSidlo,
|
|
||||||
mesto: ucastnici.mesto,
|
|
||||||
ulica: ucastnici.ulica,
|
|
||||||
psc: ucastnici.psc,
|
|
||||||
// Kurz fields
|
|
||||||
kurzId: kurzy.id,
|
|
||||||
kurzNazov: kurzy.nazov,
|
|
||||||
kurzTyp: kurzy.typKurzu,
|
|
||||||
kurzFarba: kurzy.farba,
|
|
||||||
// Registration fields (dates are now here)
|
|
||||||
datumOd: registracie.datumOd,
|
|
||||||
datumDo: registracie.datumDo,
|
|
||||||
formaKurzu: registracie.formaKurzu,
|
|
||||||
pocetUcastnikov: registracie.pocetUcastnikov,
|
|
||||||
fakturaCislo: registracie.fakturaCislo,
|
|
||||||
fakturaVystavena: registracie.fakturaVystavena,
|
|
||||||
zaplatene: registracie.zaplatene,
|
|
||||||
stav: registracie.stav,
|
|
||||||
poznamka: registracie.poznamka,
|
|
||||||
createdAt: registracie.createdAt,
|
|
||||||
// Document count
|
|
||||||
dokumentyCount: sql`(SELECT COUNT(*) FROM prilohy WHERE registracia_id = ${registracie.id})::int`,
|
|
||||||
})
|
|
||||||
.from(registracie)
|
|
||||||
.innerJoin(ucastnici, eq(registracie.ucastnikId, ucastnici.id))
|
|
||||||
.innerJoin(kurzy, eq(registracie.kurzId, kurzy.id))
|
|
||||||
.orderBy(desc(registracie.datumOd), desc(registracie.createdAt));
|
|
||||||
|
|
||||||
return result;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Update a single field (for inline editing)
|
|
||||||
export const updateField = async (registrationId, field, value) => {
|
|
||||||
// Determine which table to update based on the field
|
|
||||||
const ucastnikFields = ['titul', 'meno', 'priezvisko', 'email', 'telefon', 'firma', 'firmaIco', 'firmaDic', 'firmaIcDph', 'firmaSidlo', 'mesto', 'ulica', 'psc'];
|
|
||||||
const registraciaFields = ['datumOd', 'datumDo', 'formaKurzu', 'pocetUcastnikov', 'fakturaCislo', 'fakturaVystavena', 'zaplatene', 'stav', 'poznamka', 'kurzId'];
|
|
||||||
const dateFields = ['datumOd', 'datumDo'];
|
|
||||||
|
|
||||||
// Get the registration to find ucastnikId
|
|
||||||
const [reg] = await db
|
|
||||||
.select({ ucastnikId: registracie.ucastnikId })
|
|
||||||
.from(registracie)
|
|
||||||
.where(eq(registracie.id, registrationId))
|
|
||||||
.limit(1);
|
|
||||||
|
|
||||||
if (!reg) {
|
|
||||||
throw new NotFoundError('Registrácia nenájdená');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert date strings to Date objects
|
|
||||||
let processedValue = value;
|
|
||||||
if (dateFields.includes(field)) {
|
|
||||||
processedValue = value ? new Date(value) : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ucastnikFields.includes(field)) {
|
|
||||||
// Update ucastnik table
|
|
||||||
await db
|
|
||||||
.update(ucastnici)
|
|
||||||
.set({ [field]: processedValue, updatedAt: new Date() })
|
|
||||||
.where(eq(ucastnici.id, reg.ucastnikId));
|
|
||||||
} else if (registraciaFields.includes(field)) {
|
|
||||||
// Update registracie table
|
|
||||||
await db
|
|
||||||
.update(registracie)
|
|
||||||
.set({ [field]: processedValue, updatedAt: new Date() })
|
|
||||||
.where(eq(registracie.id, registrationId));
|
|
||||||
} else {
|
|
||||||
throw new Error(`Unknown field: ${field}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
return { success: true };
|
|
||||||
};
|
|
||||||
|
|
||||||
// ==================== PRILOHY (Documents) ====================
|
|
||||||
|
|
||||||
export const getPrilohyByRegistracia = async (registraciaId) => {
|
|
||||||
const result = await db
|
|
||||||
.select()
|
|
||||||
.from(prilohy)
|
|
||||||
.where(eq(prilohy.registraciaId, registraciaId))
|
|
||||||
.orderBy(desc(prilohy.createdAt));
|
|
||||||
|
|
||||||
return result;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const createPriloha = async (data) => {
|
|
||||||
const [newPriloha] = await db
|
|
||||||
.insert(prilohy)
|
|
||||||
.values({
|
|
||||||
registraciaId: data.registraciaId,
|
|
||||||
nazovSuboru: data.nazovSuboru,
|
|
||||||
typPrilohy: data.typPrilohy || 'ine',
|
|
||||||
cestaKSuboru: data.cestaKSuboru,
|
|
||||||
mimeType: data.mimeType || null,
|
|
||||||
velkostSuboru: data.velkostSuboru || null,
|
|
||||||
popis: data.popis || null,
|
|
||||||
})
|
|
||||||
.returning();
|
|
||||||
|
|
||||||
return newPriloha;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const deletePriloha = async (id) => {
|
|
||||||
const [priloha] = await db
|
|
||||||
.select()
|
|
||||||
.from(prilohy)
|
|
||||||
.where(eq(prilohy.id, id))
|
|
||||||
.limit(1);
|
|
||||||
|
|
||||||
if (!priloha) {
|
|
||||||
throw new NotFoundError('Príloha nenájdená');
|
|
||||||
}
|
|
||||||
|
|
||||||
await db.delete(prilohy).where(eq(prilohy.id, id));
|
|
||||||
return { success: true, filePath: priloha.cestaKSuboru };
|
|
||||||
};
|
|
||||||
|
|
||||||
// ==================== STATISTICS ====================
|
|
||||||
|
|
||||||
export const getKurzyStats = async () => {
|
|
||||||
const [stats] = await db
|
|
||||||
.select({
|
|
||||||
totalKurzy: sql`(SELECT COUNT(*) FROM kurzy)::int`,
|
|
||||||
aktivneKurzy: sql`(SELECT COUNT(*) FROM kurzy WHERE aktivny = true)::int`,
|
|
||||||
totalUcastnici: sql`(SELECT COUNT(*) FROM ucastnici)::int`,
|
|
||||||
totalRegistracie: sql`(SELECT COUNT(*) FROM registracie)::int`,
|
|
||||||
zaplateneRegistracie: sql`(SELECT COUNT(*) FROM registracie WHERE zaplatene = true)::int`,
|
|
||||||
absolvovaneRegistracie: sql`(SELECT COUNT(*) FROM registracie WHERE stav = 'absolvoval')::int`,
|
|
||||||
})
|
|
||||||
.from(sql`(SELECT 1) AS dummy`);
|
|
||||||
|
|
||||||
return stats;
|
|
||||||
};
|
|
||||||
|
|||||||
105
src/services/ai-kurzy/kurzy.service.js
Normal file
105
src/services/ai-kurzy/kurzy.service.js
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
import { db } from '../../config/database.js';
|
||||||
|
import { kurzy } from '../../db/schema.js';
|
||||||
|
import { eq, sql, asc } from 'drizzle-orm';
|
||||||
|
import { NotFoundError } from '../../utils/errors.js';
|
||||||
|
|
||||||
|
export const getAllKurzy = async () => {
|
||||||
|
const result = await db
|
||||||
|
.select({
|
||||||
|
id: kurzy.id,
|
||||||
|
nazov: kurzy.nazov,
|
||||||
|
typKurzu: kurzy.typKurzu,
|
||||||
|
popis: kurzy.popis,
|
||||||
|
cena: kurzy.cena,
|
||||||
|
maxKapacita: kurzy.maxKapacita,
|
||||||
|
aktivny: kurzy.aktivny,
|
||||||
|
farba: kurzy.farba,
|
||||||
|
createdAt: kurzy.createdAt,
|
||||||
|
registraciiCount: sql`(SELECT COUNT(*) FROM registracie WHERE kurz_id = ${kurzy.id})::int`,
|
||||||
|
})
|
||||||
|
.from(kurzy)
|
||||||
|
.orderBy(asc(kurzy.nazov));
|
||||||
|
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getKurzById = async (id) => {
|
||||||
|
const [kurz] = await db
|
||||||
|
.select()
|
||||||
|
.from(kurzy)
|
||||||
|
.where(eq(kurzy.id, id))
|
||||||
|
.limit(1);
|
||||||
|
|
||||||
|
if (!kurz) {
|
||||||
|
throw new NotFoundError('Kurz nenájdený');
|
||||||
|
}
|
||||||
|
|
||||||
|
return kurz;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const createKurz = async (data) => {
|
||||||
|
const [newKurz] = await db
|
||||||
|
.insert(kurzy)
|
||||||
|
.values({
|
||||||
|
nazov: data.nazov,
|
||||||
|
typKurzu: data.typKurzu,
|
||||||
|
popis: data.popis || null,
|
||||||
|
cena: data.cena,
|
||||||
|
maxKapacita: data.maxKapacita || null,
|
||||||
|
aktivny: data.aktivny !== undefined ? data.aktivny : true,
|
||||||
|
farba: data.farba || null,
|
||||||
|
})
|
||||||
|
.returning();
|
||||||
|
|
||||||
|
return newKurz;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const updateKurz = async (id, data) => {
|
||||||
|
await getKurzById(id);
|
||||||
|
|
||||||
|
const updateData = {
|
||||||
|
nazov: data.nazov,
|
||||||
|
typKurzu: data.typKurzu,
|
||||||
|
popis: data.popis !== undefined ? data.popis : undefined,
|
||||||
|
cena: data.cena,
|
||||||
|
maxKapacita: data.maxKapacita !== undefined ? data.maxKapacita : undefined,
|
||||||
|
aktivny: data.aktivny !== undefined ? data.aktivny : undefined,
|
||||||
|
farba: data.farba !== undefined ? data.farba : undefined,
|
||||||
|
updatedAt: new Date(),
|
||||||
|
};
|
||||||
|
|
||||||
|
Object.keys(updateData).forEach(key => {
|
||||||
|
if (updateData[key] === undefined) {
|
||||||
|
delete updateData[key];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const [updated] = await db
|
||||||
|
.update(kurzy)
|
||||||
|
.set(updateData)
|
||||||
|
.where(eq(kurzy.id, id))
|
||||||
|
.returning();
|
||||||
|
|
||||||
|
return updated;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const deleteKurz = async (id) => {
|
||||||
|
await getKurzById(id);
|
||||||
|
await db.delete(kurzy).where(eq(kurzy.id, id));
|
||||||
|
return { success: true, message: 'Kurz bol odstránený' };
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getKurzyStats = async () => {
|
||||||
|
const [stats] = await db
|
||||||
|
.select({
|
||||||
|
totalKurzy: sql`(SELECT COUNT(*) FROM kurzy)::int`,
|
||||||
|
aktivneKurzy: sql`(SELECT COUNT(*) FROM kurzy WHERE aktivny = true)::int`,
|
||||||
|
totalUcastnici: sql`(SELECT COUNT(*) FROM ucastnici)::int`,
|
||||||
|
totalRegistracie: sql`(SELECT COUNT(*) FROM registracie)::int`,
|
||||||
|
zaplateneRegistracie: sql`(SELECT COUNT(*) FROM registracie WHERE zaplatene = true)::int`,
|
||||||
|
absolvovaneRegistracie: sql`(SELECT COUNT(*) FROM registracie WHERE stav = 'absolvoval')::int`,
|
||||||
|
})
|
||||||
|
.from(sql`(SELECT 1) AS dummy`);
|
||||||
|
|
||||||
|
return stats;
|
||||||
|
};
|
||||||
234
src/services/ai-kurzy/registracie.service.js
Normal file
234
src/services/ai-kurzy/registracie.service.js
Normal file
@@ -0,0 +1,234 @@
|
|||||||
|
import { db } from '../../config/database.js';
|
||||||
|
import { kurzy, ucastnici, registracie, prilohy } from '../../db/schema.js';
|
||||||
|
import { and, desc, eq, sql } from 'drizzle-orm';
|
||||||
|
import { NotFoundError } from '../../utils/errors.js';
|
||||||
|
|
||||||
|
export const getAllRegistracie = async (kurzId = null) => {
|
||||||
|
const conditions = kurzId ? [eq(registracie.kurzId, kurzId)] : [];
|
||||||
|
|
||||||
|
const result = await db
|
||||||
|
.select({
|
||||||
|
id: registracie.id,
|
||||||
|
kurzId: registracie.kurzId,
|
||||||
|
ucastnikId: registracie.ucastnikId,
|
||||||
|
datumOd: registracie.datumOd,
|
||||||
|
datumDo: registracie.datumDo,
|
||||||
|
formaKurzu: registracie.formaKurzu,
|
||||||
|
pocetUcastnikov: registracie.pocetUcastnikov,
|
||||||
|
fakturaCislo: registracie.fakturaCislo,
|
||||||
|
fakturaVystavena: registracie.fakturaVystavena,
|
||||||
|
zaplatene: registracie.zaplatene,
|
||||||
|
stav: registracie.stav,
|
||||||
|
poznamka: registracie.poznamka,
|
||||||
|
createdAt: registracie.createdAt,
|
||||||
|
kurzNazov: kurzy.nazov,
|
||||||
|
kurzTyp: kurzy.typKurzu,
|
||||||
|
ucastnikMeno: ucastnici.meno,
|
||||||
|
ucastnikPriezvisko: ucastnici.priezvisko,
|
||||||
|
ucastnikEmail: ucastnici.email,
|
||||||
|
ucastnikFirma: ucastnici.firma,
|
||||||
|
})
|
||||||
|
.from(registracie)
|
||||||
|
.leftJoin(kurzy, eq(registracie.kurzId, kurzy.id))
|
||||||
|
.leftJoin(ucastnici, eq(registracie.ucastnikId, ucastnici.id))
|
||||||
|
.where(conditions.length > 0 ? and(...conditions) : undefined)
|
||||||
|
.orderBy(desc(registracie.datumOd), desc(registracie.createdAt));
|
||||||
|
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getRegistraciaById = async (id) => {
|
||||||
|
const [reg] = await db
|
||||||
|
.select({
|
||||||
|
id: registracie.id,
|
||||||
|
kurzId: registracie.kurzId,
|
||||||
|
ucastnikId: registracie.ucastnikId,
|
||||||
|
formaKurzu: registracie.formaKurzu,
|
||||||
|
pocetUcastnikov: registracie.pocetUcastnikov,
|
||||||
|
fakturaCislo: registracie.fakturaCislo,
|
||||||
|
fakturaVystavena: registracie.fakturaVystavena,
|
||||||
|
zaplatene: registracie.zaplatene,
|
||||||
|
stav: registracie.stav,
|
||||||
|
poznamka: registracie.poznamka,
|
||||||
|
createdAt: registracie.createdAt,
|
||||||
|
kurzNazov: kurzy.nazov,
|
||||||
|
kurzTyp: kurzy.typKurzu,
|
||||||
|
ucastnikMeno: ucastnici.meno,
|
||||||
|
ucastnikPriezvisko: ucastnici.priezvisko,
|
||||||
|
ucastnikEmail: ucastnici.email,
|
||||||
|
})
|
||||||
|
.from(registracie)
|
||||||
|
.leftJoin(kurzy, eq(registracie.kurzId, kurzy.id))
|
||||||
|
.leftJoin(ucastnici, eq(registracie.ucastnikId, ucastnici.id))
|
||||||
|
.where(eq(registracie.id, id))
|
||||||
|
.limit(1);
|
||||||
|
|
||||||
|
if (!reg) {
|
||||||
|
throw new NotFoundError('Registrácia nenájdená');
|
||||||
|
}
|
||||||
|
|
||||||
|
return reg;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const createRegistracia = async (data) => {
|
||||||
|
const [newReg] = await db
|
||||||
|
.insert(registracie)
|
||||||
|
.values({
|
||||||
|
kurzId: data.kurzId,
|
||||||
|
ucastnikId: data.ucastnikId,
|
||||||
|
datumOd: data.datumOd ? new Date(data.datumOd) : null,
|
||||||
|
datumDo: data.datumDo ? new Date(data.datumDo) : null,
|
||||||
|
formaKurzu: data.formaKurzu || 'prezencne',
|
||||||
|
pocetUcastnikov: data.pocetUcastnikov || 1,
|
||||||
|
fakturaCislo: data.fakturaCislo || null,
|
||||||
|
fakturaVystavena: data.fakturaVystavena || false,
|
||||||
|
zaplatene: data.zaplatene || false,
|
||||||
|
stav: data.stav || 'registrovany',
|
||||||
|
poznamka: data.poznamka || null,
|
||||||
|
})
|
||||||
|
.returning();
|
||||||
|
|
||||||
|
return newReg;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const updateRegistracia = async (id, data) => {
|
||||||
|
await getRegistraciaById(id);
|
||||||
|
|
||||||
|
const [updated] = await db
|
||||||
|
.update(registracie)
|
||||||
|
.set({
|
||||||
|
...data,
|
||||||
|
updatedAt: new Date(),
|
||||||
|
})
|
||||||
|
.where(eq(registracie.id, id))
|
||||||
|
.returning();
|
||||||
|
|
||||||
|
return updated;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const deleteRegistracia = async (id) => {
|
||||||
|
await getRegistraciaById(id);
|
||||||
|
await db.delete(registracie).where(eq(registracie.id, id));
|
||||||
|
return { success: true, message: 'Registrácia bola odstránená' };
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getCombinedTableData = async () => {
|
||||||
|
const result = await db
|
||||||
|
.select({
|
||||||
|
id: registracie.id,
|
||||||
|
ucastnikId: ucastnici.id,
|
||||||
|
titul: ucastnici.titul,
|
||||||
|
meno: ucastnici.meno,
|
||||||
|
priezvisko: ucastnici.priezvisko,
|
||||||
|
email: ucastnici.email,
|
||||||
|
telefon: ucastnici.telefon,
|
||||||
|
firma: ucastnici.firma,
|
||||||
|
firmaIco: ucastnici.firmaIco,
|
||||||
|
firmaDic: ucastnici.firmaDic,
|
||||||
|
firmaIcDph: ucastnici.firmaIcDph,
|
||||||
|
firmaSidlo: ucastnici.firmaSidlo,
|
||||||
|
mesto: ucastnici.mesto,
|
||||||
|
ulica: ucastnici.ulica,
|
||||||
|
psc: ucastnici.psc,
|
||||||
|
kurzId: kurzy.id,
|
||||||
|
kurzNazov: kurzy.nazov,
|
||||||
|
kurzTyp: kurzy.typKurzu,
|
||||||
|
kurzFarba: kurzy.farba,
|
||||||
|
datumOd: registracie.datumOd,
|
||||||
|
datumDo: registracie.datumDo,
|
||||||
|
formaKurzu: registracie.formaKurzu,
|
||||||
|
pocetUcastnikov: registracie.pocetUcastnikov,
|
||||||
|
fakturaCislo: registracie.fakturaCislo,
|
||||||
|
fakturaVystavena: registracie.fakturaVystavena,
|
||||||
|
zaplatene: registracie.zaplatene,
|
||||||
|
stav: registracie.stav,
|
||||||
|
poznamka: registracie.poznamka,
|
||||||
|
createdAt: registracie.createdAt,
|
||||||
|
dokumentyCount: sql`(SELECT COUNT(*) FROM prilohy WHERE registracia_id = ${registracie.id})::int`,
|
||||||
|
})
|
||||||
|
.from(registracie)
|
||||||
|
.innerJoin(ucastnici, eq(registracie.ucastnikId, ucastnici.id))
|
||||||
|
.innerJoin(kurzy, eq(registracie.kurzId, kurzy.id))
|
||||||
|
.orderBy(desc(registracie.datumOd), desc(registracie.createdAt));
|
||||||
|
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const updateField = async (registrationId, field, value) => {
|
||||||
|
const ucastnikFields = ['titul', 'meno', 'priezvisko', 'email', 'telefon', 'firma', 'firmaIco', 'firmaDic', 'firmaIcDph', 'firmaSidlo', 'mesto', 'ulica', 'psc'];
|
||||||
|
const registraciaFields = ['datumOd', 'datumDo', 'formaKurzu', 'pocetUcastnikov', 'fakturaCislo', 'fakturaVystavena', 'zaplatene', 'stav', 'poznamka', 'kurzId'];
|
||||||
|
const dateFields = ['datumOd', 'datumDo'];
|
||||||
|
|
||||||
|
const [reg] = await db
|
||||||
|
.select({ ucastnikId: registracie.ucastnikId })
|
||||||
|
.from(registracie)
|
||||||
|
.where(eq(registracie.id, registrationId))
|
||||||
|
.limit(1);
|
||||||
|
|
||||||
|
if (!reg) {
|
||||||
|
throw new NotFoundError('Registrácia nenájdená');
|
||||||
|
}
|
||||||
|
|
||||||
|
let processedValue = value;
|
||||||
|
if (dateFields.includes(field)) {
|
||||||
|
processedValue = value ? new Date(value) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ucastnikFields.includes(field)) {
|
||||||
|
await db
|
||||||
|
.update(ucastnici)
|
||||||
|
.set({ [field]: processedValue, updatedAt: new Date() })
|
||||||
|
.where(eq(ucastnici.id, reg.ucastnikId));
|
||||||
|
} else if (registraciaFields.includes(field)) {
|
||||||
|
await db
|
||||||
|
.update(registracie)
|
||||||
|
.set({ [field]: processedValue, updatedAt: new Date() })
|
||||||
|
.where(eq(registracie.id, registrationId));
|
||||||
|
} else {
|
||||||
|
throw new Error(`Unknown field: ${field}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return { success: true };
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getPrilohyByRegistracia = async (registraciaId) => {
|
||||||
|
const result = await db
|
||||||
|
.select()
|
||||||
|
.from(prilohy)
|
||||||
|
.where(eq(prilohy.registraciaId, registraciaId))
|
||||||
|
.orderBy(desc(prilohy.createdAt));
|
||||||
|
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const createPriloha = async (data) => {
|
||||||
|
const [newPriloha] = await db
|
||||||
|
.insert(prilohy)
|
||||||
|
.values({
|
||||||
|
registraciaId: data.registraciaId,
|
||||||
|
nazovSuboru: data.nazovSuboru,
|
||||||
|
typPrilohy: data.typPrilohy || 'ine',
|
||||||
|
cestaKSuboru: data.cestaKSuboru,
|
||||||
|
mimeType: data.mimeType || null,
|
||||||
|
velkostSuboru: data.velkostSuboru || null,
|
||||||
|
popis: data.popis || null,
|
||||||
|
})
|
||||||
|
.returning();
|
||||||
|
|
||||||
|
return newPriloha;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const deletePriloha = async (id) => {
|
||||||
|
const [priloha] = await db
|
||||||
|
.select()
|
||||||
|
.from(prilohy)
|
||||||
|
.where(eq(prilohy.id, id))
|
||||||
|
.limit(1);
|
||||||
|
|
||||||
|
if (!priloha) {
|
||||||
|
throw new NotFoundError('Príloha nenájdená');
|
||||||
|
}
|
||||||
|
|
||||||
|
await db.delete(prilohy).where(eq(prilohy.id, id));
|
||||||
|
return { success: true, filePath: priloha.cestaKSuboru };
|
||||||
|
};
|
||||||
88
src/services/ai-kurzy/ucastnici.service.js
Normal file
88
src/services/ai-kurzy/ucastnici.service.js
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
import { db } from '../../config/database.js';
|
||||||
|
import { ucastnici } from '../../db/schema.js';
|
||||||
|
import { eq, sql, asc } from 'drizzle-orm';
|
||||||
|
import { NotFoundError } from '../../utils/errors.js';
|
||||||
|
|
||||||
|
export const getAllUcastnici = async () => {
|
||||||
|
const result = await db
|
||||||
|
.select({
|
||||||
|
id: ucastnici.id,
|
||||||
|
titul: ucastnici.titul,
|
||||||
|
meno: ucastnici.meno,
|
||||||
|
priezvisko: ucastnici.priezvisko,
|
||||||
|
email: ucastnici.email,
|
||||||
|
telefon: ucastnici.telefon,
|
||||||
|
firma: ucastnici.firma,
|
||||||
|
firmaIco: ucastnici.firmaIco,
|
||||||
|
firmaDic: ucastnici.firmaDic,
|
||||||
|
firmaIcDph: ucastnici.firmaIcDph,
|
||||||
|
firmaSidlo: ucastnici.firmaSidlo,
|
||||||
|
mesto: ucastnici.mesto,
|
||||||
|
ulica: ucastnici.ulica,
|
||||||
|
psc: ucastnici.psc,
|
||||||
|
createdAt: ucastnici.createdAt,
|
||||||
|
registraciiCount: sql`(SELECT COUNT(*) FROM registracie WHERE ucastnik_id = ${ucastnici.id})::int`,
|
||||||
|
})
|
||||||
|
.from(ucastnici)
|
||||||
|
.orderBy(asc(ucastnici.priezvisko), asc(ucastnici.meno));
|
||||||
|
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getUcastnikById = async (id) => {
|
||||||
|
const [ucastnik] = await db
|
||||||
|
.select()
|
||||||
|
.from(ucastnici)
|
||||||
|
.where(eq(ucastnici.id, id))
|
||||||
|
.limit(1);
|
||||||
|
|
||||||
|
if (!ucastnik) {
|
||||||
|
throw new NotFoundError('Účastník nenájdený');
|
||||||
|
}
|
||||||
|
|
||||||
|
return ucastnik;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const createUcastnik = async (data) => {
|
||||||
|
const [newUcastnik] = await db
|
||||||
|
.insert(ucastnici)
|
||||||
|
.values({
|
||||||
|
titul: data.titul || null,
|
||||||
|
meno: data.meno,
|
||||||
|
priezvisko: data.priezvisko,
|
||||||
|
email: data.email,
|
||||||
|
telefon: data.telefon || null,
|
||||||
|
firma: data.firma || null,
|
||||||
|
firmaIco: data.firmaIco || null,
|
||||||
|
firmaDic: data.firmaDic || null,
|
||||||
|
firmaIcDph: data.firmaIcDph || null,
|
||||||
|
firmaSidlo: data.firmaSidlo || null,
|
||||||
|
mesto: data.mesto || null,
|
||||||
|
ulica: data.ulica || null,
|
||||||
|
psc: data.psc || null,
|
||||||
|
})
|
||||||
|
.returning();
|
||||||
|
|
||||||
|
return newUcastnik;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const updateUcastnik = async (id, data) => {
|
||||||
|
await getUcastnikById(id);
|
||||||
|
|
||||||
|
const [updated] = await db
|
||||||
|
.update(ucastnici)
|
||||||
|
.set({
|
||||||
|
...data,
|
||||||
|
updatedAt: new Date(),
|
||||||
|
})
|
||||||
|
.where(eq(ucastnici.id, id))
|
||||||
|
.returning();
|
||||||
|
|
||||||
|
return updated;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const deleteUcastnik = async (id) => {
|
||||||
|
await getUcastnikById(id);
|
||||||
|
await db.delete(ucastnici).where(eq(ucastnici.id, id));
|
||||||
|
return { success: true, message: 'Účastník bol odstránený' };
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user