1f85277a3b
- neue Seitenleisten-Kategorie accounting (Finanzbuchhaltung / Financial accounting) - CSV (UTF-8, UTF-8 mit BOM, Windows-1252) pruefen, Vorschau mit Zeilenfehlern, DATEV-Lohn-ASCII-Export - Beraternummer, Mandantennummer, Lohnart je Mandant als Admin-Einstellung (KantineDatevConfig mit Zeilenschutz), anfangs leer - hochgeladene Daten werden nicht gespeichert; Download per Blob (auch im Desktop-Client) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
84 lines
3.3 KiB
TypeScript
84 lines
3.3 KiB
TypeScript
import { BadRequestException } from '@nestjs/common';
|
|
import { describe, expect, it, vi } from 'vitest';
|
|
|
|
vi.mock('../prisma/prisma-tenant.extension', () => ({
|
|
forTenant: vi.fn((p: unknown) => p),
|
|
}));
|
|
|
|
import { forTenant } from '../prisma/prisma-tenant.extension';
|
|
import { KantineDatevService } from './kantine-datev.service';
|
|
|
|
const HEADER = 'PersNr;Name;Menge;EK;Netto;ZuAb;MwSt;Zuschuss;Betrag;Von;Bis';
|
|
const GOOD = Buffer.from(
|
|
[HEADER, '100;Max;1;1,00;1,00;0;0;0;7,94;01.03.2026;31.03.2026'].join('\r\n'),
|
|
);
|
|
|
|
function setup(row: Record<string, string | null> | null = null) {
|
|
const kantineDatevConfig = {
|
|
findUnique: vi.fn(async () => row),
|
|
upsert: vi.fn(async ({ create }: any) => create),
|
|
};
|
|
return { prisma: { kantineDatevConfig }, kantineDatevConfig };
|
|
}
|
|
|
|
describe('KantineDatevService — Einstellungen', () => {
|
|
it('liefert ohne Zeile leere Werte und configured=false', async () => {
|
|
const { prisma } = setup(null);
|
|
const res = await new KantineDatevService(prisma as any).getSettings('t1');
|
|
expect(res).toEqual({ beraterNr: null, mandantNr: null, lohnart: null, configured: false });
|
|
expect(forTenant).toHaveBeenCalledWith(prisma, 't1');
|
|
});
|
|
|
|
it('configured=false, solange ein Feld fehlt', async () => {
|
|
const { prisma } = setup({ beraterNr: '1', mandantNr: null, lohnart: '3' });
|
|
expect((await new KantineDatevService(prisma as any).getSettings('t1')).configured).toBe(false);
|
|
});
|
|
|
|
it('speichert per upsert auf tenantId (aus dem Argument)', async () => {
|
|
const { prisma, kantineDatevConfig } = setup();
|
|
const res = await new KantineDatevService(prisma as any).saveSettings('t1', {
|
|
beraterNr: '1234567',
|
|
mandantNr: '12345',
|
|
lohnart: '1111',
|
|
});
|
|
expect(kantineDatevConfig.upsert).toHaveBeenCalledWith(
|
|
expect.objectContaining({ where: { tenantId: 't1' } }),
|
|
);
|
|
expect(res.configured).toBe(true);
|
|
});
|
|
});
|
|
|
|
describe('KantineDatevService — Vorschau und Export', () => {
|
|
it('Vorschau ohne Einstellungen sperrt mit settingsMissing', async () => {
|
|
const { prisma } = setup(null);
|
|
const p = await new KantineDatevService(prisma as any).preview('t1', GOOD);
|
|
expect(p.blockedReason).toBe('settingsMissing');
|
|
expect(p.rowCount).toBe(1);
|
|
});
|
|
|
|
it('Export ohne Einstellungen -> 400 mit code settingsMissing', async () => {
|
|
const { prisma } = setup(null);
|
|
const err: any = await new KantineDatevService(prisma as any)
|
|
.export('t1', GOOD)
|
|
.catch((e) => e);
|
|
expect(err).toBeInstanceOf(BadRequestException);
|
|
expect(err.getResponse().code).toBe('settingsMissing');
|
|
});
|
|
|
|
it('Export mit Fehlern -> 400 mit code hasErrors und Fehlerliste', async () => {
|
|
const { prisma } = setup({ beraterNr: '1', mandantNr: '2', lohnart: '3' });
|
|
const bad = Buffer.from(
|
|
[HEADER, 'x;Max;1;1,00;1,00;0;0;0;7,94;01.03.2026;31.03.2026'].join('\n'),
|
|
);
|
|
const err: any = await new KantineDatevService(prisma as any).export('t1', bad).catch((e) => e);
|
|
expect(err.getResponse().code).toBe('hasErrors');
|
|
expect(err.getResponse().errors).toHaveLength(1);
|
|
});
|
|
|
|
it('Export mit Einstellungen liefert Datei', async () => {
|
|
const { prisma } = setup({ beraterNr: '1234567', mandantNr: '12345', lohnart: '1111' });
|
|
const res = await new KantineDatevService(prisma as any).export('t1', GOOD);
|
|
expect(res.filename).toBe('LuG_1234567_12345_03_2026.sic');
|
|
});
|
|
});
|