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:
richardtekula
2026-01-28 07:43:32 +01:00
parent 37dbf1b177
commit 57e6a2ea45
4 changed files with 434 additions and 445 deletions

View 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 };
};