add source code

This commit is contained in:
Michał Chudy
2025-11-14 15:51:00 +01:00
parent df0ef23857
commit b9972746aa
223 changed files with 24651 additions and 46774 deletions

View File

@@ -0,0 +1,122 @@
import { beforeEach, describe, expect, it, vi } from 'vitest';
import { generateAdnotacje } from './Adnotacje';
import { Adnotacje } from '../../types/fa1.types';
vi.mock('../../../shared/PDF-functions', () => ({
createHeader: vi.fn().mockImplementation((label) => ({ text: `HEADER:${label}` })),
createLabelText: vi.fn().mockImplementation((label, value) => [{ text: `LABEL:${label}${value}` }]),
formatText: vi.fn().mockImplementation((text) => ({ text })),
hasValue: vi.fn((val) => Boolean(val && val._text)),
verticalSpacing: vi.fn().mockImplementation((size) => ({ text: `SPACING:${size}` })),
}));
describe(generateAdnotacje.name, () => {
beforeEach(() => {
vi.clearAllMocks();
});
it('returns an empty array when argument is undefined', () => {
const result = generateAdnotacje(undefined);
expect(result).toEqual([]);
});
it('adds annotation P_19 and also P_19A, P_19B, P_19C', () => {
const adnotacje: Adnotacje = {
P_19: { _text: '1' },
P_19A: { _text: 'tekst19A' },
P_19B: { _text: 'tekst19B' },
P_19C: { _text: 'tekst19C' },
};
const result = generateAdnotacje(adnotacje);
expect(result.some((el) => JSON.stringify(el).includes('HEADER:Adnotacje'))).toBeTruthy();
expect(
result.some((el) =>
JSON.stringify(el).includes('LABEL:Przepis ustawy albo aktu wydanego na podstawie ustawy: tekst19A')
)
).toBeTruthy();
expect(
result.some((el) => JSON.stringify(el).includes('LABEL:Przepis dyrektywy: tekst19B'))
).toBeTruthy();
expect(
result.some((el) => JSON.stringify(el).includes('LABEL:Inna podstawa prawna: tekst19C'))
).toBeTruthy();
});
it('adds "Split payment mechanism" annotation when P_18A = 1', () => {
const adnotacje: Adnotacje = { P_18A: { _text: '1' } };
const result = generateAdnotacje(adnotacje);
expect(result.some((el) => JSON.stringify(el).includes('Mechanizm podzielonej płatności'))).toBeTruthy();
});
it('adds correct texts for cash method/reverse charge/simplified procedure', () => {
const adnotacje: Adnotacje = {
P_16: { _text: '1' },
P_18: { _text: '1' },
P_23: { _text: '1' },
};
const result = generateAdnotacje(adnotacje);
expect(JSON.stringify(result)).toContain('Metoda kasowa');
expect(JSON.stringify(result)).toContain('Odwrotne obciążenie');
expect(JSON.stringify(result)).toContain('Procedura trójstronna uproszczona');
});
it('adds "Self-billing" when P_17=1', () => {
const adnotacje: Adnotacje = { P_17: { _text: '1' } };
const result = generateAdnotacje(adnotacje);
expect(JSON.stringify(result)).toContain('Samofakturowanie');
});
it('adds margin annotation based on subtypes', () => {
const adnotacje: Adnotacje = {
P_PMarzy: { _text: '1' },
P_PMarzy_3_2: { _text: '1' }, // works of art
};
const result = generateAdnotacje(adnotacje);
expect(JSON.stringify(result)).toContain('dzieła sztuki');
});
});
describe('generateAdnotacje - additional coverage', () => {
it('adds correct margin annotation for all subtypes', () => {
const variants = [
{ key: 'P_PMarzy_3_1', expected: 'towary używane' },
{ key: 'P_PMarzy_2', expected: 'biura podróży' },
{ key: 'P_PMarzy_3_3', expected: 'przedmioty kolekcjonerskie i antyki' },
];
variants.forEach(({ key, expected }) => {
const adnotacje: any = {
P_PMarzy: { _text: '1' },
[key]: { _text: '1' },
};
const result = generateAdnotacje(adnotacje);
expect(JSON.stringify(result)).toContain(expected);
});
});
it('handles P_22 and calls generateDostawy', () => {
const adnotacje: any = { P_22: { _text: '1' } };
const result = generateAdnotacje(adnotacje);
expect(JSON.stringify(result)).toContain('Wewnątrzwspólnotowe dostawy nowych środków transportu');
});
it('returns empty array if adnotacje has no valid fields', () => {
const adnotacje: any = { P_99: { _text: '' } };
const result = generateAdnotacje(adnotacje);
expect(result).toEqual([]);
});
it('adds header and spacing when result has items', () => {
const adnotacje: any = { P_16: { _text: '1' } };
const result = generateAdnotacje(adnotacje) as any;
const resultString = JSON.stringify(result);
expect(resultString).toContain('HEADER:Adnotacje');
expect(resultString).toContain('SPACING:1');
});
});

View File

@@ -0,0 +1,301 @@
import { Content, ContentTable } from 'pdfmake/interfaces';
import {
createHeader,
createLabelText,
formatText,
hasValue,
verticalSpacing,
} from '../../../shared/PDF-functions';
import { Adnotacje } from '../../types/fa1.types';
import FormatTyp from '../../../shared/enums/common.enum';
import { DEFAULT_TABLE_LAYOUT } from '../../../shared/consts/const';
export function generateAdnotacje(adnotacje?: Adnotacje): Content[] {
const result: Content[] = [];
let firstColumn: Content[] = [];
const secondColumn: Content[] = [];
if (adnotacje) {
if (adnotacje?.P_19?._text === '1') {
addToColumn(
firstColumn,
secondColumn,
{
text: 'Dostawa towarów lub świadczenie usług zwolnionych od podatku na podstawie art. 43 ust. 1, art. 113 ust. 1 i 9 albo przepisów wydanych na podstawie art. 82 ust. 3 lub na podstawie innych przepisów',
},
true
);
if (adnotacje.P_19A?._text) {
addToColumn(
firstColumn,
secondColumn,
createLabelText(
'Podstawa zwolnienia od podatku: ',
'Przepis ustawy albo aktu wydanego na podstawie ustawy, na podstawie którego podatnik stosuje adnotacje od podatku'
),
true
);
addToColumn(
firstColumn,
secondColumn,
createLabelText('Przepis ustawy albo aktu wydanego na podstawie ustawy: ', adnotacje.P_19A._text),
true
);
}
if (adnotacje.P_19B?._text) {
addToColumn(
firstColumn,
secondColumn,
createLabelText(
'Podstawa zwolnienia od podatku: ',
'Przepis dyrektywy 2006/112/WE, który zwalnia od podatku taką dostawę towarów lub takie świadczenie usług'
),
true
);
addToColumn(
firstColumn,
secondColumn,
createLabelText('Przepis dyrektywy: ', adnotacje.P_19B._text),
true
);
}
if (adnotacje.P_19C?._text) {
addToColumn(
firstColumn,
secondColumn,
createLabelText(
'Podstawa zwolnienia od podatku: ',
'Inna podstawa prawna wskazującą na to, że dostawa towarów lub świadczenie usług korzysta ze zwolnienia'
),
true
);
addToColumn(
firstColumn,
secondColumn,
createLabelText('Inna podstawa prawna: ', adnotacje.P_19C._text),
true
);
}
}
if (adnotacje.P_18A?._text === '1') {
addToColumn(firstColumn, secondColumn, { text: 'Mechanizm podzielonej płatności' });
}
if (adnotacje.P_16?._text === '1') {
addToColumn(firstColumn, secondColumn, { text: 'Metoda kasowa' });
}
if (adnotacje.P_18?._text === '1') {
addToColumn(firstColumn, secondColumn, { text: 'Odwrotne obciążenie' });
}
if (adnotacje.P_23?._text === '1') {
addToColumn(firstColumn, secondColumn, { text: 'Procedura trójstronna uproszczona' });
}
if (adnotacje.P_PMarzy?._text === '1') {
let valueMarzy = '';
if (adnotacje.P_PMarzy_3_1?._text === '1') {
valueMarzy = 'towary używane';
} else if (adnotacje.P_PMarzy_3_2?._text === '1') {
valueMarzy = 'dzieła sztuki';
} else if (adnotacje.P_PMarzy_2?._text === '1') {
valueMarzy = 'biura podróży';
} else if (adnotacje.P_PMarzy_3_3?._text === '1') {
valueMarzy = 'przedmioty kolekcjonerskie i antyki';
}
addToColumn(firstColumn, secondColumn, createLabelText('Procedura marży: ', valueMarzy));
}
if (adnotacje.P_17?._text === '1') {
addToColumn(firstColumn, secondColumn, { text: 'Samofakturowanie' });
}
if (adnotacje.P_22?._text === '1') {
let obowiazekVAT: Content[] = [];
obowiazekVAT = [...createLabelText('Wewnątrzwspólnotowe dostawy nowych środków transportu', ' ')];
if (obowiazekVAT) {
firstColumn = [firstColumn, ...obowiazekVAT];
}
}
if (firstColumn.length || secondColumn.length) {
result.push({ columns: [firstColumn, secondColumn], columnGap: 20 });
}
if (result.length) {
result.unshift(verticalSpacing(1));
result.unshift(createHeader('Adnotacje'));
result.unshift(verticalSpacing(1));
result.push(verticalSpacing(1));
}
if (adnotacje.P_22?._text === '1') {
result.push(generateDostawy(adnotacje));
}
}
return result;
}
export function generateDostawy(adnotacje: Adnotacje): Content[] {
const result: Content[] = [];
const table: Content[][] = [];
const anyP22B =
hasValue(adnotacje.P_22B) ||
hasValue(adnotacje.P_22BT) ||
hasValue(adnotacje.P_22B1) ||
hasValue(adnotacje.P_22B2) ||
hasValue(adnotacje.P_22B3) ||
hasValue(adnotacje.P_22B4);
const anyP22C: boolean = hasValue(adnotacje.P_22C) || hasValue(adnotacje.P_22C1);
const anyP22D: boolean = hasValue(adnotacje.P_22D) || hasValue(adnotacje.P_22D1);
if (hasValue(adnotacje.P_22A)) {
table.push([
formatText('Data dopuszczenia nowego środka transportu do użytku', FormatTyp.GrayBoldTitle),
formatText(adnotacje.P_22A?._text, FormatTyp.Default),
]);
}
if (hasValue(adnotacje.P_22BMK)) {
table.push([
formatText('Marka nowego środka transportu', FormatTyp.GrayBoldTitle),
formatText(adnotacje.P_22BMK?._text, FormatTyp.Default),
]);
}
if (hasValue(adnotacje.P_22BMD)) {
table.push([
formatText('Model nowego środka transportu', FormatTyp.GrayBoldTitle),
formatText(adnotacje.P_22BMD?._text, FormatTyp.Default),
]);
}
if (hasValue(adnotacje.P_22BK)) {
table.push([
formatText('Kolor nowego środka transportu', FormatTyp.GrayBoldTitle),
formatText(adnotacje.P_22BK?._text, FormatTyp.Default),
]);
}
if (hasValue(adnotacje.P_22BNR)) {
table.push([
formatText('Numer rejestracyjny nowego środka transportu', FormatTyp.GrayBoldTitle),
formatText(adnotacje.P_22BNR?._text, FormatTyp.Default),
]);
}
if (hasValue(adnotacje.P_22BRP)) {
table.push([
formatText('Rok produkcji nowego środka transportu', FormatTyp.GrayBoldTitle),
formatText(adnotacje.P_22BRP?._text, FormatTyp.Default),
]);
}
if (anyP22B) {
table.push([
formatText('Rodzaj pojazdu', FormatTyp.GrayBoldTitle),
formatText(
'Dostawa dotyczy pojazdów lądowych, o których mowa w art. 2 pkt 10 lit. a ustawy',
FormatTyp.Default
),
]);
if (hasValue(adnotacje.P_22B)) {
table.push([
formatText('Przebieg pojazdu', FormatTyp.GrayBoldTitle),
formatText(adnotacje.P_22B?._text, FormatTyp.Default),
]);
}
if (hasValue(adnotacje.P_22B1)) {
table.push([
formatText('Numer VIN', FormatTyp.GrayBoldTitle),
formatText(adnotacje.P_22B1?._text, FormatTyp.Default),
]);
}
if (hasValue(adnotacje.P_22B2)) {
table.push([
formatText('Numer nadwozia', FormatTyp.GrayBoldTitle),
formatText(adnotacje.P_22B2?._text, FormatTyp.Default),
]);
}
if (hasValue(adnotacje.P_22B3)) {
table.push([
formatText('Numer podwozia', FormatTyp.GrayBoldTitle),
formatText(adnotacje.P_22B3?._text, FormatTyp.Default),
]);
}
if (hasValue(adnotacje.P_22B4)) {
table.push([
formatText('Numer ramy', FormatTyp.GrayBoldTitle),
formatText(adnotacje.P_22B4?._text, FormatTyp.Default),
]);
}
if (hasValue(adnotacje.P_22BT)) {
table.push([
formatText('Typ nowego środka transportu', FormatTyp.GrayBoldTitle),
formatText(adnotacje.P_22BT?._text, FormatTyp.Default),
]);
}
} else if (anyP22C) {
table.push([
formatText('Rodzaj pojazdu', FormatTyp.GrayBoldTitle),
formatText(
'Dostawa dotyczy jednostek pływających, o których mowa w art. 2 pkt 10 lit. b ustawy',
FormatTyp.Default
),
]);
if (hasValue(adnotacje.P_22C)) {
table.push([
formatText('Przebieg pojazdu', FormatTyp.GrayBoldTitle),
formatText(adnotacje.P_22C?._text, FormatTyp.Default),
]);
}
if (hasValue(adnotacje.P_22C1)) {
table.push([
formatText('Numer kadłuba nowego środka transportu', FormatTyp.GrayBoldTitle),
formatText(adnotacje.P_22C1?._text, FormatTyp.Default),
]);
}
} else if (anyP22D) {
table.push([
formatText('Rodzaj pojazdu', FormatTyp.GrayBoldTitle),
formatText(
'Dostawa dotyczy statków powietrznych, o których mowa w art. 2 pkt 10 lit. c ustawy',
FormatTyp.Default
),
]);
if (hasValue(adnotacje.P_22D)) {
table.push([
formatText('Przebieg pojazdu', FormatTyp.GrayBoldTitle),
formatText(adnotacje.P_22D?._text, FormatTyp.Default),
]);
}
if (hasValue(adnotacje.P_22D1)) {
table.push([
formatText('Numer fabryczny nowego środka transportu<', FormatTyp.GrayBoldTitle),
formatText(adnotacje.P_22D1?._text, FormatTyp.Default),
]);
}
}
if (table.length) {
result.push([
{
unbreakable: true,
table: {
body: table,
widths: ['*', '*'],
},
layout: DEFAULT_TABLE_LAYOUT,
} as ContentTable,
]);
}
return result;
}
function addToColumn(
firstColumn: Content[],
secondColumn: Content[],
content: Content,
isFirstColumn?: boolean
): void {
if (firstColumn.length > secondColumn.length && isFirstColumn) {
secondColumn.push(content);
return;
}
firstColumn.push(content);
}

View File

@@ -0,0 +1,43 @@
import { beforeEach, describe, expect, it, vi } from 'vitest';
import { generateDostawy } from './Adnotacje';
import { Adnotacje } from '../../types/fa1.types';
vi.mock('../../../shared/PDF-functions', () => ({
formatText: vi.fn((text, format) => ({ text, format })),
hasValue: vi.fn((val) => Boolean(val && val._text)),
}));
const formatText = vi.fn((text, format) => ({ text, format }));
const hasValue = vi.fn((val) => Boolean(val && val._text));
describe(generateDostawy.name, () => {
beforeEach(() => {
vi.clearAllMocks();
hasValue.mockImplementation((val) => Boolean(val && val._text));
});
it('returns empty array when no values', () => {
const adnotacje: Adnotacje = {};
const result = generateDostawy(adnotacje);
expect(result).toEqual([]);
});
it('creates table for P_22AP_22BRP fields', () => {
const adnotacje: Adnotacje = {
P_22A: { _text: '2024-01-01' },
P_22BMK: { _text: 'Ford' },
P_22BMD: { _text: 'Focus' },
P_22BK: { _text: 'Red' },
P_22BNR: { _text: 'ABC123' },
P_22BRP: { _text: '2023' },
};
const result = generateDostawy(adnotacje);
expect(result).toHaveLength(1);
});
it('does not create table when no valid values found', () => {
const adnotacje: Adnotacje = { P_22D: { _text: '' } };
hasValue.mockReturnValue(false);
const result = generateDostawy(adnotacje);
expect(result).toEqual([]);
});
});

View File

@@ -0,0 +1,105 @@
import { beforeEach, describe, expect, it, vi } from 'vitest';
import { Adres } from '../../types/fa1.types';
import { generateAdres } from './Adres';
vi.mock('../../../shared/PDF-functions', () => ({
createLabelText: vi.fn().mockImplementation((label, value) => [{ text: `LABEL:${label}${value._text}` }]),
}));
describe('generateAdres', () => {
beforeEach(() => {
vi.clearAllMocks();
});
it('returns an empty array when both AdresPol and AdresZagr are undefined', () => {
const adres: Adres = {};
const result = generateAdres(adres);
expect(result).toEqual([]);
});
it('creates correct fields for foreign address (AdresZagr)', () => {
const adres: Adres = {
AdresZagr: {
KodKraju: { _text: 'PL' },
Ulica: { _text: 'Example Street' },
NrDomu: { _text: '123' },
NrLokalu: { _text: '4A' },
KodPocztowy: { _text: '00-123' },
Miejscowosc: { _text: 'Warsaw' },
GLN: { _text: '123456789' },
},
};
const result = generateAdres(adres);
expect(result).toEqual(
expect.arrayContaining([
[{ text: 'LABEL:Kraj: PL' }],
[{ text: 'LABEL:Ulica: Example Street' }],
[{ text: 'LABEL:Numer domu: 123' }],
[{ text: 'LABEL:Numer lokalu: 4A' }],
[{ text: 'LABEL:Kod pocztowy: 00-123' }],
[{ text: 'LABEL:Miejscowość: Warsaw' }],
[{ text: 'LABEL:GLN: 123456789' }],
])
);
});
it('creates correct fields for Polish address (AdresPol)', () => {
const adres: Adres = {
AdresPol: {
Wojewodztwo: { _text: 'Mazowieckie' },
Powiat: { _text: 'Warszawa' },
Gmina: { _text: 'Centrum' },
Ulica: { _text: 'Main' },
NrDomu: { _text: '5' },
NrLokalu: { _text: '12B' },
KodPocztowy: { _text: '01-234' },
Miejscowosc: { _text: 'Warsaw' },
Poczta: { _text: 'Warsaw 1' },
GLN: { _text: '987654321' },
},
};
const result = generateAdres(adres);
expect(result).toEqual(
expect.arrayContaining([
[{ text: 'LABEL:Województwo: Mazowieckie' }],
[{ text: 'LABEL:Powiat: Warszawa' }],
[{ text: 'LABEL:Gmina: Centrum' }],
[{ text: 'LABEL:Ulica: Main' }],
[{ text: 'LABEL:Numer domu: 5' }],
[{ text: 'LABEL:Numer lokalu: 12B' }],
[{ text: 'LABEL:Kod pocztowy: 01-234' }],
[{ text: 'LABEL:Miejscowość: Warsaw' }],
[{ text: 'LABEL:Poczta: Warsaw 1' }],
[{ text: 'LABEL:GLN: 987654321' }],
])
);
});
it('works correctly when both AdresPol and AdresZagr exist', () => {
const adres: Adres = {
AdresPol: { Miejscowosc: { _text: 'Katowice' } },
AdresZagr: { Miejscowosc: { _text: 'Berlin' } },
};
const result = generateAdres(adres);
expect(result).toEqual(
expect.arrayContaining([
[{ text: 'LABEL:Miejscowość: Katowice' }],
[{ text: 'LABEL:Miejscowość: Berlin' }],
])
);
});
it('skips undefined fields in AdresPol and AdresZagr', () => {
const adres: Adres = {
AdresPol: {},
AdresZagr: {},
};
const result = generateAdres(adres);
expect(result).toEqual([]);
});
});

View File

@@ -0,0 +1,69 @@
import { Content } from 'pdfmake/interfaces';
import { createLabelText } from '../../../shared/PDF-functions';
import { Adres, FP } from '../../types/fa1.types';
export function generateAdres(adres: Adres): Content[] {
const result: Content[] = [];
if (adres.AdresZagr) {
const adresZagr: Record<string, FP> = adres.AdresZagr;
if (adresZagr.KodKraju) {
result.push(createLabelText('Kraj: ', adresZagr.KodKraju));
}
if (adresZagr.Ulica) {
result.push(createLabelText('Ulica: ', adresZagr.Ulica));
}
if (adresZagr.NrDomu) {
result.push(createLabelText('Numer domu: ', adresZagr.NrDomu));
}
if (adresZagr.NrLokalu) {
result.push(createLabelText('Numer lokalu: ', adresZagr.NrLokalu));
}
if (adresZagr.KodPocztowy) {
result.push(createLabelText('Kod pocztowy: ', adresZagr.KodPocztowy));
}
if (adresZagr.Miejscowosc) {
result.push(createLabelText('Miejscowość: ', adresZagr.Miejscowosc));
}
if (adresZagr.GLN) {
result.push(createLabelText('GLN: ', adresZagr.GLN));
}
}
if (adres.AdresPol) {
const adresPol: Record<string, FP> = adres.AdresPol;
if (adresPol.Wojewodztwo) {
result.push(createLabelText('Województwo: ', adresPol.Wojewodztwo));
}
if (adresPol.Powiat) {
result.push(createLabelText('Powiat: ', adresPol.Powiat));
}
if (adresPol.Gmina) {
result.push(createLabelText('Gmina: ', adresPol.Gmina));
}
if (adresPol.Ulica) {
result.push(createLabelText('Ulica: ', adresPol.Ulica));
}
if (adresPol.NrDomu) {
result.push(createLabelText('Numer domu: ', adresPol.NrDomu));
}
if (adresPol.NrLokalu) {
result.push(createLabelText('Numer lokalu: ', adresPol.NrLokalu));
}
if (adresPol.KodPocztowy) {
result.push(createLabelText('Kod pocztowy: ', adresPol.KodPocztowy));
}
if (adresPol.Miejscowosc) {
result.push(createLabelText('Miejscowość: ', adresPol.Miejscowosc));
}
if (adresPol.Poczta) {
result.push(createLabelText('Poczta: ', adresPol.Poczta));
}
if (adresPol.GLN) {
result.push(createLabelText('GLN: ', adresPol.GLN));
}
}
return result;
}

View File

@@ -0,0 +1,47 @@
import { beforeEach, describe, expect, it, vi } from 'vitest';
import { DodatkowyOpi, Fa } from '../../types/fa1.types';
import { generateDodatkoweInformacje } from './DodatkoweInformacje';
vi.mock('../../../shared/PDF-functions', () => ({
createHeader: () => [{ text: 'HEADER:Dodatkowe informacje' }],
createSubHeader: () => [{ text: 'SUBHEADER:Dodatkowy opis' }],
formatText: (text: string) => ({ text }),
createSection: (content: any, _: boolean) => content,
getValue: (val: any) => val?._text || '',
getTable: (data: any) => data,
getContentTable: () => ({ content: { table: 'FAKE_TABLE' } }),
}));
describe('generateDodatkoweInformacje', () => {
beforeEach(() => {
vi.clearAllMocks();
});
it('returns an empty array when no triggers present', () => {
const faVat: Fa = {};
const result = generateDodatkoweInformacje(faVat);
expect(result).toEqual([]);
});
it('returns correct table when DodatkowyOpis provided', () => {
const faVat: Fa = {
DodatkowyOpis: [
{ Klucz: { _text: 'RODZAJ' }, Wartosc: { _text: 'TRESC' } },
{ Klucz: { _text: 'INNY' }, Wartosc: { _text: 'OPIS' } },
] as DodatkowyOpi[],
};
const result = generateDodatkoweInformacje(faVat);
expect(result).toContainEqual({ text: 'HEADER:Dodatkowe informacje' });
expect(result).toContainEqual({ text: 'SUBHEADER:Dodatkowy opis' });
expect(result).toContainEqual({ table: 'FAKE_TABLE' });
});
it('includes section when any element is present', () => {
const faVat: Fa = { TP: { _text: '1' }, ZwrotAkcyzy: { _text: '1' } };
const result = generateDodatkoweInformacje(faVat);
expect(result.length).toBeGreaterThan(0);
});
});

View File

@@ -0,0 +1,87 @@
import { Content } from 'pdfmake/interfaces';
import {
createHeader,
createSection,
createSubHeader,
formatText,
getContentTable,
getTable,
getValue,
} from '../../../shared/PDF-functions';
import { HeaderDefine } from '../../../shared/types/pdf-types';
import { DodatkowyOpi, Fa } from '../../types/fa1.types';
import FormatTyp from '../../../shared/enums/common.enum';
import { FakturaZaliczkowa, TableWithFields } from '../../types/fa1-additional-types';
export function generateDodatkoweInformacje(faVat: Fa): Content[] {
const tpLabel1: Content[] = [];
const tpLabel2: Content[] = [];
if (getValue(faVat.TP) === '1') {
tpLabel1.push(
formatText('- Istniejące powiązania między nabywcą a dokonującym dostawy towarów lub usługodawcą')
);
tpLabel2.push(formatText('- Faktura, o której mowa w art. 109 ust. 3d ustawy'));
}
const zwrotAkcyzyLabel: Content[] = [];
if (getValue(faVat.ZwrotAkcyzy) === '1') {
zwrotAkcyzyLabel.push(
formatText(
'- Informacja dodatkowa związana ze zwrotem podatku akcyzowego zawartego w cenie oleju napędowego'
)
);
}
const labels: Content[][] = [tpLabel1, tpLabel2, zwrotAkcyzyLabel].filter((el) => el.length > 0);
const table: Content[] = [
...createHeader('Dodatkowe informacje'),
...labels,
...generateDodatkowyOpis(faVat.DodatkowyOpis),
];
return table.length > 1 ? createSection(table, true) : [];
}
function generateDodatkowyOpis(fakturaZaliczkowaData: DodatkowyOpi[] | undefined): Content[] {
if (!fakturaZaliczkowaData) {
return [];
}
const fakturaZaliczkowa: FakturaZaliczkowa[] = getTable(fakturaZaliczkowaData)?.map((item, index) => ({
...item,
lp: { _text: index + 1 },
}));
const table: Content[] = createSubHeader('Dodatkowy opis');
const fakturaZaliczkowaHeader: HeaderDefine[] = [
{
name: 'lp',
title: 'Lp.',
format: FormatTyp.Default,
width: 'auto',
},
{
name: 'Klucz',
title: 'Rodzaj informacji',
format: FormatTyp.Default,
width: 'auto',
},
{
name: 'Wartosc',
title: 'Treść informacji',
format: FormatTyp.Default,
width: '*',
},
];
const tableFakturaZaliczkowa: TableWithFields = getContentTable<(typeof fakturaZaliczkowa)[0]>(
fakturaZaliczkowaHeader,
fakturaZaliczkowa,
'*',
[0, 0, 0, 0]
);
if (tableFakturaZaliczkowa.content) {
table.push(tableFakturaZaliczkowa.content);
}
return table;
}

View File

@@ -0,0 +1,122 @@
import { beforeEach, describe, expect, it, vi } from 'vitest';
import { generatePlatnosc } from './Platnosc';
import { Platnosc } from '../../types/fa1.types';
vi.mock('../../../shared/PDF-functions', () => ({
createHeader: vi.fn((label: string, _pad?: any) => [{ text: `HEADER:${label}` }]),
createLabelText: vi.fn((label: string, value: any) => ({
text: `LABEL:${label}${typeof value === 'object' && value?._text ? value._text : value}`,
})),
generateLine: vi.fn(() => ({ text: 'LINE' })),
generateTwoColumns: vi.fn((left, right, pad?) => ({ left, right, pad, type: '2COL' })),
getContentTable: vi.fn((_header, data, _width) => ({
content: Array.isArray(data) && data.length ? { data } : undefined,
})),
getTable: vi.fn((data) => data || []),
hasValue: vi.fn((fp: any) => Boolean(fp && fp._text && fp._text !== '')),
}));
vi.mock('../../../shared/generators/common/functions', () => ({
getFormaPlatnosciString: vi.fn((fp: any) => (fp?._text ? 'Przelew' : '')),
}));
vi.mock('./RachunekBankowy', () => ({
generujRachunekBankowy: vi.fn((table, label) => [{ text: `ACCOUNT:${label}` }]),
}));
describe('generatePlatnosc', () => {
beforeEach(() => vi.clearAllMocks());
it('returns empty array when no payment info given', () => {
expect(generatePlatnosc(undefined)).toEqual([]);
});
it('handles case: Platnosc.Zaplacono = 1', () => {
const p: Platnosc = { Zaplacono: { _text: '1' }, DataZaplaty: { _text: '2025-10-10' } };
const result = generatePlatnosc(p);
expect(result).toEqual(
expect.arrayContaining([
{ text: 'LINE' },
{ text: 'HEADER:Płatność' },
{ text: 'LABEL:Informacja o płatności: Zapłacono' },
{ text: 'LABEL:Data zapłaty: 2025-10-10' },
])
);
});
it('handles case: Platnosc.ZaplataCzesciowa = 1', () => {
const p: Platnosc = { ZaplataCzesciowa: { _text: '1' } };
const result = generatePlatnosc(p);
expect(result).toEqual(
expect.arrayContaining([{ text: 'LABEL:Informacja o płatności: Zapłata częściowa' }])
);
});
it('handles case: not paid', () => {
const p: Platnosc = {};
const result = generatePlatnosc(p);
expect(result).toEqual(expect.arrayContaining([{ text: 'LABEL:Informacja o płatności: Brak zapłaty' }]));
});
it('adds "Forma płatności" with getFormaPlatnosciString when present', () => {
const p: Platnosc = { FormaPlatnosci: { _text: '1' } };
const result = generatePlatnosc(p);
expect(result).toEqual(expect.arrayContaining([{ text: 'LABEL:Forma płatności: Przelew' }]));
});
it('falls back on "Brak zapłaty" and inna/opis if FormaPlatnosci undefined but OpisPlatnosci present', () => {
const p: Platnosc = { OpisPlatnosci: { _text: 'Gotówka przy odbiorze' } };
const result = generatePlatnosc(p);
expect(result).toEqual(
expect.arrayContaining([
{ text: 'LABEL:Informacja o płatności: Brak zapłaty' },
{ text: 'LABEL:Forma płatności: Płatność inna' },
{ text: 'LABEL:Opis płatności innej: Gotówka przy odbiorze' },
])
);
});
it('renders tables for partials and terms if present', () => {
const p: Platnosc = {
PlatnosciCzesciowe: [
{ DataZaplatyCzesciowej: { _text: '2025-01-01' }, KwotaZaplatyCzesciowej: { _text: '100' } },
],
TerminyPlatnosci: [
{ TerminPlatnosci: { _text: '2025-02-01' }, TerminPlatnosciOpis: { _text: 'Do miesiąca' } },
],
};
const result: any = generatePlatnosc(p);
expect(result.some((el: any) => el.type === '2COL')).toBe(true);
});
it('renders Skonto info if present', () => {
const p: Platnosc = { Skonto: { WarunkiSkonta: { _text: '30 dni' }, WysokoscSkonta: { _text: '5%' } } };
const result = generatePlatnosc(p);
expect(result).toEqual(
expect.arrayContaining([
[{ text: 'HEADER:Skonto' }],
{ text: 'LABEL:Warunki skonta: 30 dni' },
{ text: 'LABEL:Wysokość skonta: 5%' },
])
);
});
it('always outputs column with bank accounts', () => {
const p: Platnosc = { RachunekBankowy: [{}], RachunekBankowyFaktora: [{}] };
const result: any = generatePlatnosc(p);
const accCol = result.find(
(el: any) =>
Array.isArray(el.left) &&
Array.isArray(el.right) &&
el.left[0]?.text?.startsWith('ACCOUNT:') &&
el.right[0]?.text?.startsWith('ACCOUNT:')
);
expect(accCol).toBeTruthy();
});
});

View File

@@ -0,0 +1,128 @@
import { Content } from 'pdfmake/interfaces';
import {
createHeader,
createLabelText,
generateLine,
generateTwoColumns,
getContentTable,
getTable,
hasValue,
} from '../../../shared/PDF-functions';
import { HeaderDefine } from '../../../shared/types/pdf-types';
import { FP, Platnosc, PlatnosciCzesciowe, TerminyPlatnosci } from '../../types/fa1.types';
import { getFormaPlatnosciString } from '../../../shared/generators/common/functions';
import { generujRachunekBankowy } from './RachunekBankowy';
import FormatTyp from '../../../shared/enums/common.enum';
import { TableWithFields, TerminPlatnosciContent } from '../../types/fa1-additional-types';
export function generatePlatnosc(platnosc: Platnosc | undefined): Content {
if (!platnosc) {
return [];
}
const terminPlatnosci: TerminyPlatnosci[] = getTable(platnosc.TerminyPlatnosci);
const zaplataCzesciowaHeader: HeaderDefine[] = [
{
name: 'TerminPlatnosci',
title: 'Termin płatności',
format: FormatTyp.Default,
},
];
if (terminPlatnosci.some((termin: TerminyPlatnosci): FP | undefined => termin.TerminPlatnosciOpis)) {
zaplataCzesciowaHeader.push({
name: 'TerminPlatnosciOpis',
title: 'Opis płatności',
format: FormatTyp.Default,
});
}
const zaplataCzesciowaNaglowek: HeaderDefine[] = [
{
name: 'DataZaplatyCzesciowej',
title: 'Data zapłaty częściowej',
format: FormatTyp.Default,
},
{ name: 'KwotaZaplatyCzesciowej', title: 'Kwota zapłaty częściowej', format: FormatTyp.Currency },
{ name: 'FormaPlatnosci', title: 'Forma płatności', format: FormatTyp.FormOfPayment },
];
const table: Content[] = [generateLine(), ...createHeader('Płatność')];
if (platnosc.Zaplacono?._text === '1') {
table.push(createLabelText('Informacja o płatności: ', 'Zapłacono'));
table.push(createLabelText('Data zapłaty: ', platnosc.DataZaplaty));
} else if (platnosc.ZaplataCzesciowa?._text === '1') {
table.push(createLabelText('Informacja o płatności: ', 'Zapłata częściowa'));
} else {
table.push(createLabelText('Informacja o płatności: ', 'Brak zapłaty'));
}
if (hasValue(platnosc.FormaPlatnosci)) {
table.push(createLabelText('Forma płatności: ', getFormaPlatnosciString(platnosc.FormaPlatnosci)));
} else {
if (platnosc.OpisPlatnosci?._text) {
table.push(createLabelText('Forma płatności: ', 'Płatność inna'));
table.push(createLabelText('Opis płatności innej: ', platnosc.OpisPlatnosci));
}
}
const zaplataCzesciowa: PlatnosciCzesciowe[] = getTable(platnosc.PlatnosciCzesciowe);
const tableZaplataCzesciowa: TableWithFields = getContentTable<(typeof zaplataCzesciowa)[0]>(
zaplataCzesciowaNaglowek,
zaplataCzesciowa,
'*'
);
const terminPatnosciContent: (TerminyPlatnosci | TerminPlatnosciContent)[] = terminPlatnosci.map(
(platnosc: TerminyPlatnosci): TerminyPlatnosci | TerminPlatnosciContent => {
if (!terminPlatnosci.some((termin: TerminyPlatnosci): FP | undefined => termin.TerminPlatnosciOpis)) {
return platnosc;
} else {
return {
...platnosc,
TerminPlatnosciOpis: {
_text: `${platnosc.TerminPlatnosciOpis?._text ?? ''}`,
} as any,
};
}
}
);
const tableTerminPlatnosci = getContentTable<(typeof terminPlatnosci)[0]>(
zaplataCzesciowaHeader,
terminPatnosciContent as TerminyPlatnosci[],
'*'
);
if (zaplataCzesciowa.length > 0 && terminPlatnosci.length > 0) {
table.push(
generateTwoColumns(
tableZaplataCzesciowa.content ?? [],
tableTerminPlatnosci.content ?? [],
[0, 4, 0, 0]
)
);
} else if (terminPlatnosci.length > 0) {
if (tableTerminPlatnosci.content) {
table.push(generateTwoColumns([], tableTerminPlatnosci.content));
}
} else if (zaplataCzesciowa.length > 0 && tableZaplataCzesciowa.content) {
table.push(tableZaplataCzesciowa.content);
}
table.push(
generateTwoColumns(
generujRachunekBankowy(getTable(platnosc.RachunekBankowy), 'Numer rachunku bankowego'),
generujRachunekBankowy(getTable(platnosc.RachunekBankowyFaktora), 'Numer rachunku bankowego faktora')
)
);
if (platnosc.Skonto) {
table.push(createHeader('Skonto', [0, 0]));
table.push(createLabelText('Warunki skonta: ', platnosc.Skonto.WarunkiSkonta));
table.push(createLabelText('Wysokość skonta: ', platnosc.Skonto.WysokoscSkonta));
}
return table;
}

View File

@@ -0,0 +1,92 @@
import { beforeEach, describe, expect, it, vi } from 'vitest';
import { Podmiot1 } from '../../types/fa1.types';
import { generatePodmiot1 } from './Podmiot1';
vi.mock('../../../shared/PDF-functions', () => ({
createHeader: vi.fn((label: string) => [{ text: `HEADER:${label}` }]),
createLabelText: vi.fn((label: string, value: any) => ({
text: `LABEL:${label}${typeof value === 'object' && value?._text ? value._text : value}`,
})),
formatText: vi.fn((text: string, _args?: any) => ({ text: `FMT:${text}` })),
getTable: vi.fn((data) => data || []),
}));
vi.mock('./PodmiotAdres', () => ({
generatePodmiotAdres: vi.fn((adres: any, label: string) => ({ adr: `${label}` })),
}));
vi.mock('./PodmiotDaneIdentyfikacyjne', () => ({
generateDaneIdentyfikacyjne: vi.fn((daneId: any) => [{ id: 'ID' }]),
}));
vi.mock('./PodmiotDaneKontaktowe', () => ({
generateDaneKontaktowe: vi.fn((email: any, tel: any) => [{ contact: 'KONTAKT' }]),
}));
describe('generatePodmiot1', () => {
beforeEach(() => vi.clearAllMocks());
it('renders header and base labels', () => {
const podmiot1: Podmiot1 = {
NrEORI: { _text: 'EORI123' },
PrefiksPodatnika: { _text: 'PL' },
};
const result = generatePodmiot1(podmiot1);
expect(result).toEqual(
expect.arrayContaining([
{ text: 'HEADER:Sprzedawca' },
{ text: 'LABEL:NrEORI: EORI123' },
{ text: 'LABEL:Prefiks VAT: PL' },
])
);
});
it('calls generateDaneIdentyfikacyjne when present', () => {
const podmiot1: Podmiot1 = {
DaneIdentyfikacyjne: { NIP: { _text: '1234' } },
NrEORI: { _text: 'xxx' },
PrefiksPodatnika: { _text: 'PL' },
};
const result = generatePodmiot1(podmiot1);
expect(result).toEqual(expect.arrayContaining([{ id: 'ID' }]));
});
it('renders PodmiotAdres if adres present', () => {
const podmiot1: Podmiot1 = {
NrEORI: { _text: 'xxx' },
PrefiksPodatnika: { _text: 'PL' },
Adres: { AdresPol: { Miasto: { _text: 'Katowice' } } },
};
const result = generatePodmiot1(podmiot1);
expect(result).toEqual(expect.arrayContaining([{ adr: 'Adres' }]));
});
it('renders contact section and status if Email/Telefon present', () => {
const podmiot1: Podmiot1 = {
NrEORI: { _text: 'xxx' },
PrefiksPodatnika: { _text: 'PL' },
Email: { _text: 'a@b.pl' },
StatusInfoPodatnika: { _text: 'zarejestrowany' },
};
const result = generatePodmiot1(podmiot1);
expect(result).toEqual(
expect.arrayContaining([
{ text: 'FMT:Dane kontaktowe' },
{ contact: 'KONTAKT' },
{ text: 'LABEL:Status podatnika: zarejestrowany' },
])
);
});
it('renders only status if no Email/Telefon but StatusInfoPodatnika present', () => {
const podmiot1: Podmiot1 = {
NrEORI: { _text: 'xxx' },
PrefiksPodatnika: { _text: 'PL' },
StatusInfoPodatnika: { _text: 'SAMO' },
};
const result = generatePodmiot1(podmiot1);
expect(result).toEqual(expect.arrayContaining([{ text: 'LABEL:Status podatnika: SAMO' }]));
});
});

View File

@@ -0,0 +1,39 @@
import { Content } from 'pdfmake/interfaces';
import { createHeader, createLabelText, formatText, getTable } from '../../../shared/PDF-functions';
import FormatTyp from '../../../shared/enums/common.enum';
import { Podmiot1 } from '../../types/fa1.types';
import { generatePodmiotAdres } from './PodmiotAdres';
import { generateDaneIdentyfikacyjne } from './PodmiotDaneIdentyfikacyjne';
import { generateDaneKontaktowe } from './PodmiotDaneKontaktowe';
export function generatePodmiot1(podmiot1: Podmiot1): Content[] {
const result: Content[] = createHeader('Sprzedawca');
result.push(
createLabelText('NrEORI: ', podmiot1.NrEORI),
createLabelText('Prefiks VAT: ', podmiot1.PrefiksPodatnika)
);
if (podmiot1.DaneIdentyfikacyjne) {
result.push(...generateDaneIdentyfikacyjne(podmiot1.DaneIdentyfikacyjne));
}
if (podmiot1.Adres) {
result.push(generatePodmiotAdres(podmiot1.Adres, 'Adres', true, [0, 12, 0, 1.3]));
}
if (podmiot1.AdresKoresp) {
result.push(
...generatePodmiotAdres(podmiot1.AdresKoresp, 'Adres do korespondencji', true, [0, 12, 0, 1.3])
);
}
if (podmiot1.Email || podmiot1.Telefon) {
result.push(
formatText('Dane kontaktowe', [FormatTyp.Label, FormatTyp.LabelMargin]),
...generateDaneKontaktowe(podmiot1.Email, getTable(podmiot1.Telefon))
);
result.push(createLabelText('Status podatnika: ', podmiot1.StatusInfoPodatnika));
} else if (podmiot1.StatusInfoPodatnika) {
result.push(createLabelText('Status podatnika: ', podmiot1.StatusInfoPodatnika));
}
return result;
}

View File

@@ -0,0 +1,67 @@
import { beforeEach, describe, expect, it, vi } from 'vitest';
import { Podmiot1, Podmiot1K } from '../../types/fa1.types';
import { generatePodmiot1Podmiot1K } from './Podmiot1Podmiot1K';
import { Content } from 'pdfmake/interfaces';
vi.mock('../../../shared/PDF-functions', () => ({
createHeader: vi.fn((label: string) => [{ text: `HEADER:${label}` }]),
createLabelText: vi.fn((label: string, value: any) => ({
text: `LABEL:${label}${value && value._text ? value._text : value}`,
})),
createSubHeader: vi.fn((label: string) => ({ text: `SUBHEADER:${label}` })),
verticalSpacing: vi.fn((v: number) => ({ text: `SPACING:${v}` })),
getTable: vi.fn((data) => data || []),
}));
vi.mock('./PodmiotAdres', () => ({
generatePodmiotAdres: vi.fn((adres: any, label: string) => ({ adr: label })),
}));
vi.mock('./PodmiotDaneIdentyfikacyjne', () => ({
generateDaneIdentyfikacyjne: vi.fn(() => [{ id: 'ID' }]),
}));
vi.mock('./PodmiotDaneKontaktowe', () => ({
generateDaneKontaktowe: vi.fn(() => ({ contact: 'KONTAKT' })),
}));
describe('generatePodmiot1Podmiot1K', () => {
beforeEach(() => vi.clearAllMocks());
it('puts DaneIdentyfikacyjne & status in firstColumn', () => {
const podmiot1: Podmiot1 = {
NrEORI: { _text: 'EORI' },
DaneIdentyfikacyjne: { NIP: { _text: '777' } },
StatusInfoPodatnika: { _text: 'AKTYWNY' },
};
const podmiot1K: Podmiot1K = {};
const result: any = generatePodmiot1Podmiot1K(podmiot1, podmiot1K);
const firstCol: Content = result.find((r: any) => r.columns)?.columns[0];
expect(firstCol).toEqual(
expect.arrayContaining([
{ text: 'SUBHEADER:Dane identyfikacyjne' },
{ text: 'LABEL:Numer EORI: EORI' },
{ id: 'ID' },
{ text: 'LABEL:Status podatnika: AKTYWNY' },
])
);
});
it('adds contact if Email present', () => {
const podmiot1: Podmiot1 = {
NrEORI: { _text: 'EORI' },
Email: { _text: 'mail@ex.pl' },
};
const podmiot1K: Podmiot1K = {};
const result: any = generatePodmiot1Podmiot1K(podmiot1, podmiot1K);
const firstCol: Content = result.find((r: any) => r.columns)?.columns[0];
expect(firstCol).toEqual(expect.arrayContaining([{ contact: 'KONTAKT' }]));
});
it('adds verticalSpacing at the end', () => {
const podmiot1: Podmiot1 = { NrEORI: { _text: 'EORI' } };
const podmiot1K: Podmiot1K = {};
const result: Content[] = generatePodmiot1Podmiot1K(podmiot1, podmiot1K);
expect(result[result.length - 1]).toEqual({ text: 'SPACING:1' });
});
});

View File

@@ -0,0 +1,71 @@
import { Content } from 'pdfmake/interfaces';
import {
createHeader,
createLabelText,
createSubHeader,
getTable,
verticalSpacing,
} from '../../../shared/PDF-functions';
import { Podmiot1, Podmiot1K } from '../../types/fa1.types';
import { generatePodmiotAdres } from './PodmiotAdres';
import { generateDaneIdentyfikacyjne } from './PodmiotDaneIdentyfikacyjne';
import { generateDaneKontaktowe } from './PodmiotDaneKontaktowe';
export function generatePodmiot1Podmiot1K(podmiot1: Podmiot1, podmiot1K: Podmiot1K): Content[] {
const result: Content[] = createHeader('Sprzedawca');
let firstColumn: Content[] = [];
let secondColumn: Content[] = [];
firstColumn.push(createSubHeader('Dane identyfikacyjne'), createLabelText('Numer EORI: ', podmiot1.NrEORI));
if (podmiot1.DaneIdentyfikacyjne) {
firstColumn.push(...generateDaneIdentyfikacyjne(podmiot1.DaneIdentyfikacyjne));
}
if (podmiot1.Email || podmiot1.Telefon) {
firstColumn.push(generateDaneKontaktowe(podmiot1.Email, getTable(podmiot1.Telefon)));
}
if (podmiot1.StatusInfoPodatnika) {
firstColumn.push(createLabelText('Status podatnika: ', podmiot1.StatusInfoPodatnika));
}
if (firstColumn.length) {
result.push({
columns: [firstColumn, []],
columnGap: 20,
});
}
firstColumn = generateCorrectedContent(podmiot1K, 'Treść korygowana');
secondColumn = generateCorrectedContent(podmiot1, 'Treść korygująca');
if (podmiot1.AdresKoresp) {
secondColumn.push(
generatePodmiotAdres(podmiot1.AdresKoresp, 'Adres do korespondencji', true, [0, 12, 0, 1.3])
);
}
if (firstColumn.length || secondColumn.length) {
result.push({
columns: [firstColumn, secondColumn],
columnGap: 20,
});
}
if (result.length) {
result.push(verticalSpacing(1));
}
return result;
}
export function generateCorrectedContent(podmiot: Podmiot1 | Podmiot1K, headerText: string): Content[] {
const result: Content[] = [];
result.push(createSubHeader(headerText));
if (podmiot.PrefiksPodatnika?._text) {
result.push(createLabelText('Prefiks VAT: ', podmiot.PrefiksPodatnika));
}
if (podmiot.DaneIdentyfikacyjne) {
result.push(...generateDaneIdentyfikacyjne(podmiot.DaneIdentyfikacyjne));
}
if (podmiot.Adres) {
result.push(generatePodmiotAdres(podmiot.Adres, 'Adres', true, [0, 12, 0, 1.3]));
}
return result;
}

View File

@@ -0,0 +1,68 @@
import { beforeEach, describe, expect, it, vi } from 'vitest';
import { Podmiot2 } from '../../types/fa1.types';
import { generatePodmiot2 } from './Podmiot2';
vi.mock('../../../shared/PDF-functions', () => ({
createHeader: vi.fn((label: string) => [{ text: `HEADER:${label}` }]),
createLabelText: vi.fn((label: string, value: any) => ({
text: `LABEL:${label}${typeof value === 'object' && value?._text ? value._text : value}`,
})),
formatText: vi.fn((text: string, _args?: any) => ({ text: `FMT:${text}` })),
getTable: vi.fn((data) => data || []),
getValue: vi.fn((val) => val?._text || ''),
hasValue: vi.fn((val) => Boolean(val && val._text)),
}));
vi.mock('./PodmiotAdres', () => ({
generatePodmiotAdres: vi.fn((adres: any, label: string) => ({ adr: label })),
}));
vi.mock('./PodmiotDaneIdentyfikacyjne', () => ({
generateDaneIdentyfikacyjne: vi.fn(() => [{ id: 'ID' }]),
}));
vi.mock('./PodmiotDaneKontaktowe', () => ({
generateDaneKontaktowe: vi.fn(() => [{ contact: 'KONTAKT' }]),
}));
describe('generatePodmiot2', () => {
beforeEach(() => vi.clearAllMocks());
it('renders header and base fields', () => {
const podmiot2: Podmiot2 = {
NrEORI: { _text: 'EORI777' },
};
const result = generatePodmiot2(podmiot2);
expect(result).toEqual(
expect.arrayContaining([{ text: 'HEADER:Nabywca' }, { text: 'LABEL:NrEORI: EORI777' }])
);
});
it('adds PrefiksNabywcy if hasValue true', () => {
const podmiot2: Podmiot2 = {
NrEORI: { _text: 'EORI1' },
PrefiksNabywcy: { _text: 'PN1' },
};
const result = generatePodmiot2(podmiot2);
expect(result).toEqual(expect.arrayContaining([{ text: 'LABEL:Prefiks VAT: PN1' }]));
});
it('includes contact info when Email or Telefon present', () => {
const podmiot2: Podmiot2 = {
NrEORI: { _text: 'EORI4' },
Email: { _text: 'test@mail.com' },
};
const result = generatePodmiot2(podmiot2);
expect(result).toEqual(expect.arrayContaining([{ text: 'FMT:Dane kontaktowe' }, { contact: 'KONTAKT' }]));
});
it('includes NrKlienta label if present', () => {
const podmiot2: Podmiot2 = {
NrEORI: { _text: 'EORI5' },
NrKlienta: { _text: 'NR1234' },
};
const result = generatePodmiot2(podmiot2);
expect(result).toEqual(expect.arrayContaining([{ text: 'LABEL:Numer klienta: NR1234' }]));
});
});

View File

@@ -0,0 +1,54 @@
import { Content } from 'pdfmake/interfaces';
import {
createHeader,
createLabelText,
formatText,
getTable,
getValue,
hasValue,
} from '../../../shared/PDF-functions';
import FormatTyp from '../../../shared/enums/common.enum';
import { Podmiot2 } from '../../types/fa1.types';
import { generatePodmiotAdres } from './PodmiotAdres';
import { generateDaneIdentyfikacyjne } from './PodmiotDaneIdentyfikacyjne';
import { generateDaneKontaktowe } from './PodmiotDaneKontaktowe';
import { DaneIdentyfikacyjneTPodmiot2Dto } from '../../types/fa2-additional-types';
export function generatePodmiot2(podmiot2: Podmiot2): Content[] {
const result: Content[] = createHeader('Nabywca');
result.push(createLabelText('NrEORI: ', podmiot2.NrEORI));
if (hasValue(podmiot2.PrefiksNabywcy)) {
result.push(createLabelText('Prefiks VAT: ', podmiot2.PrefiksNabywcy));
}
if (podmiot2.DaneIdentyfikacyjne) {
if (hasValue(podmiot2.DaneIdentyfikacyjne.NrID)) {
result.push(createLabelText('Identyfikator podatkowy inny: ', podmiot2.DaneIdentyfikacyjne.NrID));
}
if (getValue(podmiot2.DaneIdentyfikacyjne.BrakID) === '1') {
result.push(createLabelText('Brak identyfikatora ', ' '));
}
result.push(
...generateDaneIdentyfikacyjne(podmiot2.DaneIdentyfikacyjne as DaneIdentyfikacyjneTPodmiot2Dto)
);
}
if (podmiot2.Adres) {
result.push(generatePodmiotAdres(podmiot2.Adres, 'Adres', true, [0, 12, 0, 1.3]));
}
if (podmiot2.AdresKoresp) {
result.push(
...generatePodmiotAdres(podmiot2.AdresKoresp, 'Adres do korespondencji', true, [0, 12, 0, 1.3])
);
}
if (podmiot2.Email || podmiot2.Telefon) {
result.push(
formatText('Dane kontaktowe', [FormatTyp.Label, FormatTyp.LabelMargin]),
...generateDaneKontaktowe(podmiot2.Email, getTable(podmiot2.Telefon))
);
}
if (podmiot2.NrKlienta) {
result.push(createLabelText('Numer klienta: ', podmiot2.NrKlienta));
}
return result;
}

View File

@@ -0,0 +1,103 @@
import { beforeEach, describe, expect, it, vi } from 'vitest';
import { Podmiot2, Podmiot2K } from '../../types/fa1.types';
import { generatePodmiot2Podmiot2K } from './Podmiot2Podmiot2k';
import { Content } from 'pdfmake/interfaces';
vi.mock('../../../shared/PDF-functions', () => ({
createHeader: vi.fn((label) => [{ text: `HEADER:${label}` }]),
createLabelText: vi.fn((label, value) => ({
text: `LABEL:${label}${value && value._text ? value._text : ''}`,
})),
createSubHeader: vi.fn((label) => ({ text: `SUBHEADER:${label}` })),
verticalSpacing: vi.fn((num) => ({ text: `SPACING:${num}` })),
getTable: vi.fn((arr) => arr || []),
getValue: vi.fn((val) => (val && val._text ? val._text : '')),
hasValue: vi.fn((val) => Boolean(val && val._text)),
}));
vi.mock('./PodmiotAdres', () => ({
generatePodmiotAdres: vi.fn((adres, label) => ({ adr: label })),
}));
vi.mock('./PodmiotDaneIdentyfikacyjne', () => ({
generateDaneIdentyfikacyjne: vi.fn(() => [{ id: 'ID' }]),
}));
vi.mock('./PodmiotDaneKontaktowe', () => ({
generateDaneKontaktowe: vi.fn(() => ({ contact: 'KONTAKT' })),
}));
describe('generatePodmiot2Podmiot2K', () => {
beforeEach(() => vi.clearAllMocks());
function hasColumns(r: unknown): r is { columns: unknown[] } {
return (
typeof r === 'object' &&
r !== null &&
'columns' in r &&
Array.isArray((r as { columns: unknown[] }).columns)
);
}
it('renders HEADER and at least one columns object', () => {
const podmiot2: Podmiot2 = { NrEORI: { _text: 'A' } };
const podmiot2K: Podmiot2K = {};
const result: Content[] = generatePodmiot2Podmiot2K(podmiot2, podmiot2K);
expect(result[0]).toEqual({ text: 'HEADER:Nabywca' });
expect(
result.some((r: Content) => {
if (hasColumns(r)) {
return Array.isArray(r.columns);
}
return false;
})
).toBeTruthy();
});
it('builds firstColumn with full data', () => {
const podmiot2: Podmiot2 = {
NrEORI: { _text: 'EORI-X' },
DaneIdentyfikacyjne: { NrID: { _text: 'FOO' } },
Email: { _text: 'xx@a.pl' },
NrKlienta: { _text: 'CUSTX' },
Telefon: [{ _text: '600100200' }],
};
const podmiot2K: Podmiot2K = {};
const result: any = generatePodmiot2Podmiot2K(podmiot2, podmiot2K);
const firstCol: Content = result.find(hasColumns)?.columns[0];
expect(firstCol).toEqual(
expect.arrayContaining([
{ text: 'SUBHEADER:Dane identyfikacyjne' },
{ text: 'LABEL:Numer EORI: EORI-X' },
{ id: 'ID' },
{ contact: 'KONTAKT' },
{ text: 'LABEL:Numer klienta: CUSTX' },
])
);
});
it('renders "corrected content" for both cols', () => {
const podmiot2: Podmiot2 = {
PrefiksNabywcy: { _text: 'PN2' },
DaneIdentyfikacyjne: { BrakID: { _text: '1' }, NrID: { _text: '123' } },
Adres: { AdresPol: { Miasto: { _text: 'CITY' } } },
};
const podmiot2K: Podmiot2K = {
PrefiksNabywcy: { _text: 'NNK' },
DaneIdentyfikacyjne: { NrID: { _text: 'XYZ' } },
Adres: { AdresZagr: { Kraj: { _text: 'UK' } } },
};
const result: any = generatePodmiot2Podmiot2K(podmiot2, podmiot2K);
const cols: Content[] = result.find(hasColumns)?.columns;
expect(cols[1]).toEqual([]);
});
it('ends with verticalSpacing', () => {
const podmiot2: Podmiot2 = { NrEORI: { _text: 'END' } };
const podmiot2K: Podmiot2K = {};
const result: Content[] = generatePodmiot2Podmiot2K(podmiot2, podmiot2K);
expect(result[result.length - 1]).toEqual({ text: 'SPACING:1' });
});
});

View File

@@ -0,0 +1,79 @@
import { Content } from 'pdfmake/interfaces';
import {
createHeader,
createLabelText,
createSubHeader,
getTable,
getValue,
hasValue,
verticalSpacing,
} from '../../../shared/PDF-functions';
import { Podmiot2, Podmiot2K } from '../../types/fa1.types';
import { generatePodmiotAdres } from './PodmiotAdres';
import { generateDaneIdentyfikacyjne } from './PodmiotDaneIdentyfikacyjne';
import { generateDaneKontaktowe } from './PodmiotDaneKontaktowe';
export function generatePodmiot2Podmiot2K(podmiot2: Podmiot2, podmiot2K: Podmiot2K): Content[] {
const result: Content[] = createHeader('Nabywca');
let firstColumn: Content[] = [];
let secondColumn: Content[] = [];
firstColumn.push(createSubHeader('Dane identyfikacyjne'), createLabelText('Numer EORI: ', podmiot2.NrEORI));
if (podmiot2.DaneIdentyfikacyjne) {
firstColumn.push(...generateDaneIdentyfikacyjne(podmiot2.DaneIdentyfikacyjne));
}
if (podmiot2.Email || podmiot2.Telefon) {
firstColumn.push(generateDaneKontaktowe(podmiot2.Email, getTable(podmiot2.Telefon)));
}
if (podmiot2.NrKlienta) {
firstColumn.push(createLabelText('Numer klienta: ', podmiot2.NrKlienta));
}
if (firstColumn.length) {
result.push({
columns: [firstColumn, []],
columnGap: 20,
});
}
firstColumn = generateCorrectedContent(podmiot2K, 'Treść korygowana');
secondColumn = generateCorrectedContent(podmiot2, 'Treść korygująca');
if (podmiot2.AdresKoresp) {
secondColumn.push(
generatePodmiotAdres(podmiot2.AdresKoresp, 'Adres do korespondencji', true, [0, 12, 0, 1.3])
);
}
if (firstColumn.length || secondColumn.length) {
result.push({
columns: [firstColumn, secondColumn],
columnGap: 20,
});
}
if (result.length) {
result.push(verticalSpacing(1));
}
return result;
}
export function generateCorrectedContent(podmiot: Podmiot2 | Podmiot2K, headerText: string): Content[] {
const result: Content[] = [];
result.push(createSubHeader(headerText));
if (hasValue(podmiot.PrefiksNabywcy)) {
result.push(createLabelText('Prefiks VAT: ', podmiot.PrefiksNabywcy));
}
if (podmiot.DaneIdentyfikacyjne) {
if (hasValue(podmiot.DaneIdentyfikacyjne.NrID)) {
result.push(createLabelText('Identyfikator podatkowy inny: ', podmiot.DaneIdentyfikacyjne.NrID));
}
if (getValue(podmiot.DaneIdentyfikacyjne.BrakID) === '1') {
result.push(createLabelText('Brak identyfikatora ', ' '));
}
result.push(...generateDaneIdentyfikacyjne(podmiot.DaneIdentyfikacyjne));
}
if (podmiot.Adres) {
result.push(generatePodmiotAdres(podmiot.Adres, 'Adres', true, [0, 12, 0, 1.3]));
}
return result;
}

View File

@@ -0,0 +1,95 @@
import { beforeEach, describe, expect, it, vi } from 'vitest';
import { Podmiot3 } from '../../types/fa1.types';
import { generatePodmiot3 } from './Podmiot3';
import { Content } from 'pdfmake/interfaces';
vi.mock('../../../shared/PDF-functions', () => ({
createHeader: vi.fn((label) => [{ text: `HEADER:${label}` }]),
createLabelText: vi.fn((label, value) => ({
text: `LABEL:${label}${value && value._text ? value._text : (value ?? '')}`,
})),
createSection: vi.fn((arr) => arr),
formatText: vi.fn((txt, _args) => ({ text: `FMT:${txt}` })),
generateTwoColumns: vi.fn((left, right) => ({ type: '2COL', left, right })),
getTable: vi.fn((arr) => arr || []),
getValue: vi.fn((val) => (val && val._text ? val._text : '')),
hasValue: vi.fn((val) => !!(val && val._text)),
}));
vi.mock('../../../shared/generators/common/functions', () => ({
getRolaString: vi.fn((rola, idx) => (rola && rola._text ? 'SPRZEDAWCA' : '')),
}));
vi.mock('./PodmiotAdres', () => ({
generatePodmiotAdres: vi.fn((adres, label) => ({ adr: label })),
}));
vi.mock('./PodmiotDaneIdentyfikacyjne', () => ({
generateDaneIdentyfikacyjne: vi.fn(() => [{ id: 'ID' }]),
}));
vi.mock('./PodmiotDaneKontaktowe', () => ({
generateDaneKontaktowe: vi.fn(() => [{ contact: 'KONTAKT' }]),
}));
describe('generatePodmiot3', () => {
beforeEach(() => vi.clearAllMocks());
it('renders minimal column structure', () => {
const podmiot: Podmiot3 = { NrEORI: { _text: '999' }, Rola: { _text: 'X' } };
const result: Content[] = generatePodmiot3(podmiot, 0);
const last: any = result[result.length - 1] || {};
expect(last.type).toBe('2COL');
expect(last.left).toEqual(
expect.arrayContaining([
{ text: 'HEADER:Podmiot inny 1' },
{ text: 'LABEL:NrEORI: 999' },
[{ text: 'LABEL:Rola: SPRZEDAWCA' }, { text: 'LABEL:Rola inna: ' }, { text: 'LABEL:Udział: ' }],
])
);
});
it('renders column1 with all tax ID and role info', () => {
const podmiot: Podmiot3 = {
NrEORI: { _text: '1000' },
DaneIdentyfikacyjne: { NrID: { _text: 'TAXX' }, BrakID: { _text: '1' } },
Rola: { _text: 'S' },
OpisRoli: { _text: 'operator' },
Udzial: { _text: '50%' },
};
const result: any = generatePodmiot3(podmiot, 1);
const col1: Content = result[result.length - 1].left;
expect(col1).toEqual(
expect.arrayContaining([
{ text: 'HEADER:Podmiot inny 2' },
{ text: 'LABEL:NrEORI: 1000' },
{ text: 'LABEL:Identyfikator podatkowy inny: TAXX' },
{ text: 'LABEL:Brak identyfikatora ' },
{ id: 'ID' },
[
{ text: 'LABEL:Rola: SPRZEDAWCA' },
{ text: 'LABEL:Rola inna: operator' },
{ text: 'LABEL:Udział: 50%' },
],
])
);
});
it('adds Numer klienta in right column if present', () => {
const podmiot: Podmiot3 = {
NrEORI: { _text: 'foo' },
Rola: { _text: 'ROLA' },
NrKlienta: { _text: 'KLIENTX' },
};
const result: any = generatePodmiot3(podmiot, 2);
const col2: Content = result[result.length - 1].right;
expect(col2).toEqual(expect.arrayContaining([{ text: 'LABEL:Numer klienta: KLIENTX' }]));
});
it('always returns results wrapped in sections', () => {
const podmiot: Podmiot3 = { NrEORI: { _text: 'A' }, Rola: { _text: 'B' } };
const result: any = generatePodmiot3(podmiot, 1);
expect(Array.isArray(result)).toBe(true);
expect(result.some((r: any) => r.type === '2COL')).toBeTruthy();
});
});

View File

@@ -0,0 +1,62 @@
import { Content } from 'pdfmake/interfaces';
import {
createHeader,
createLabelText,
createSection,
formatText,
generateTwoColumns,
getTable,
getValue,
hasValue,
} from '../../../shared/PDF-functions';
import FormatTyp from '../../../shared/enums/common.enum';
import { Podmiot3 } from '../../types/fa1.types';
import { getRolaString } from '../../../shared/generators/common/functions';
import { generatePodmiotAdres } from './PodmiotAdres';
import { generateDaneIdentyfikacyjne } from './PodmiotDaneIdentyfikacyjne';
import { generateDaneKontaktowe } from './PodmiotDaneKontaktowe';
export function generatePodmiot3(podmiot: Podmiot3, index: number): Content[] {
const result: Content[] = [];
const column1: Content[] = [
...createHeader(`Podmiot inny ${index + 1}`),
createLabelText('NrEORI: ', podmiot.NrEORI),
];
if (hasValue(podmiot.DaneIdentyfikacyjne?.NrID)) {
column1.push(createLabelText('Identyfikator podatkowy inny: ', podmiot.DaneIdentyfikacyjne?.NrID));
}
if (getValue(podmiot.DaneIdentyfikacyjne?.BrakID) === '1') {
column1.push(createLabelText('Brak identyfikatora ', ' '));
}
if (podmiot.DaneIdentyfikacyjne) {
column1.push(...generateDaneIdentyfikacyjne(podmiot.DaneIdentyfikacyjne));
}
column1.push([
createLabelText('Rola: ', getRolaString(podmiot.Rola, 1)),
createLabelText('Rola inna: ', podmiot.OpisRoli),
createLabelText('Udział: ', podmiot.Udzial, [FormatTyp.Percentage]),
]);
const column2: Content[] = [];
if (podmiot.Adres) {
column2.push(generatePodmiotAdres(podmiot.Adres, 'Adres', true, [0, 12, 0, 1.3]));
}
if (podmiot.AdresKoresp) {
column2.push(
...generatePodmiotAdres(podmiot.AdresKoresp, 'Adres do korespondencji', true, [0, 12, 0, 1.3])
);
}
if (podmiot.Email || podmiot.Telefon) {
column2.push(
formatText('Dane kontaktowe', [FormatTyp.Label, FormatTyp.LabelMargin]),
...generateDaneKontaktowe(podmiot.Email, getTable(podmiot.Telefon))
);
}
if (podmiot.NrKlienta) {
column2.push(createLabelText('Numer klienta: ', podmiot.NrKlienta));
}
result.push(generateTwoColumns(column1, column2));
return createSection(result, true);
}

View File

@@ -0,0 +1,83 @@
import { beforeEach, describe, expect, it, vi } from 'vitest';
import { generateDaneIdentyfikacyjneTPodmiot3Dto } from './Podmiot3Podmiot2k';
import { Podmiot3Podmiot2KDto } from '../../types/fa1-additional-types';
import { Content } from 'pdfmake/interfaces';
vi.mock('../../../shared/PDF-functions', () => ({
createHeader: vi.fn((label) => [{ text: `HEADER:${label}` }]),
createLabelText: vi.fn((label, value) => ({
text: `LABEL:${label}${value && value._text ? value._text : (value ?? '')}`,
})),
createSubHeader: vi.fn((label) => [{ text: `SUBHEADER:${label}` }]),
generateTwoColumns: vi.fn((left, right) => ({ type: '2COL', left, right })),
getTable: vi.fn((arr) => arr || []),
getValue: vi.fn((val) => (val && val._text ? val._text : '')),
hasValue: vi.fn((val) => !!(val && val._text)),
}));
vi.mock('../../../shared/generators/common/functions', () => ({
getRolaString: vi.fn((rola) => (rola && rola._text ? 'SPRZEDAWCA' : '')),
}));
vi.mock('./PodmiotAdres', () => ({
generatePodmiotAdres: vi.fn((adres, label) => ({ adr: label })),
}));
vi.mock('./PodmiotDaneIdentyfikacyjne', () => ({
generateDaneIdentyfikacyjne: vi.fn(() => [{ id: 'ID' }]),
}));
vi.mock('./PodmiotDaneKontaktowe', () => ({
generateDaneKontaktowe: vi.fn(() => ({ contact: 'KONTAKT' })),
}));
describe('generateDaneIdentyfikacyjneTPodmiot3Dto', () => {
beforeEach(() => vi.clearAllMocks());
it('returns empty array for undefined input', () => {
expect(generateDaneIdentyfikacyjneTPodmiot3Dto(undefined, 0)).toEqual([]);
});
it('creates header and main fields for full identification', () => {
const podmiot2KDto: Podmiot3Podmiot2KDto = {
fakturaPodmiotNDto: {
NrEORI: { _text: 'EXX' },
Rola: { _text: 'SPRZEDAWCA' },
OpisRoli: { _text: 'rola inna' },
Udzial: { _text: '51%' },
Email: { _text: 'a@b.pl' },
NrKlienta: { _text: 'KLIENTX' },
DaneIdentyfikacyjne: { NrID: { _text: 'TID' } },
Adres: { AdresPol: { Miasto: { _text: 'Warsaw' } } },
AdresKoresp: { AdresPol: { Miasto: { _text: 'Gdansk' } } },
},
podmiot2KDto: {
DaneIdentyfikacyjne: { NrID: { _text: 'IDK' } },
Adres: { AdresZagr: { Kraj: { _text: 'PL' } } },
},
};
const result = generateDaneIdentyfikacyjneTPodmiot3Dto(podmiot2KDto, 1);
expect(result).toEqual(
expect.arrayContaining([
{ text: 'HEADER:Podmiot inny 2' },
{ text: 'SUBHEADER:Dane identyfikacyjne' },
{ text: 'LABEL:Numer EORI: EXX' },
{ text: 'LABEL:Rola: SPRZEDAWCA' },
{ text: 'LABEL:Rola inna: rola inna' },
{ text: 'LABEL:Udział: 51%' },
{ contact: 'KONTAKT' },
{ text: 'LABEL:Numer klienta: KLIENTX' },
])
);
});
it('renders Adres korespondencyjny last in right column', () => {
const podmiot2KDto: Podmiot3Podmiot2KDto = {
fakturaPodmiotNDto: {
AdresKoresp: { AdresPol: { Miasto: { _text: 'Gdynia' } } },
},
podmiot2KDto: {},
};
const result: Content[] = generateDaneIdentyfikacyjneTPodmiot3Dto(podmiot2KDto, 2);
const twoCol: any = result.find((r: any): boolean => r.type === '2COL');
expect(twoCol.right[twoCol.right.length - 1]).toEqual({ adr: 'Adres korespondencyjny' });
});
});

View File

@@ -0,0 +1,87 @@
import { Content } from 'pdfmake/interfaces';
import {
createHeader,
createLabelText,
createSubHeader,
generateTwoColumns,
getTable,
getValue,
hasValue,
} from '../../../shared/PDF-functions';
import FormatTyp from '../../../shared/enums/common.enum';
import { Podmiot3Podmiot2KDto } from '../../types/fa1-additional-types';
import { getRolaString } from '../../../shared/generators/common/functions';
import { generatePodmiotAdres } from './PodmiotAdres';
import { generateDaneIdentyfikacyjne } from './PodmiotDaneIdentyfikacyjne';
import { generateDaneKontaktowe } from './PodmiotDaneKontaktowe';
import { Podmiot2K, Podmiot3 } from '../../types/fa1.types';
export function generateDaneIdentyfikacyjneTPodmiot3Dto(
podmiot2KDto: Podmiot3Podmiot2KDto | undefined,
index: number
): Content[] {
if (!podmiot2KDto) {
return [];
}
const podmiot1: Podmiot3 = podmiot2KDto.fakturaPodmiotNDto;
const podmiot1K: Podmiot2K | undefined = podmiot2KDto.podmiot2KDto;
const result: Content[] = createHeader(`Podmiot inny ${index + 1}`);
if (
hasValue(podmiot1.NrEORI) ||
hasValue(podmiot1.Rola) ||
hasValue(podmiot1.OpisRoli) ||
hasValue(podmiot1?.Udzial)
) {
result.push(
...createSubHeader('Dane identyfikacyjne'),
createLabelText('Numer EORI: ', podmiot1.NrEORI),
createLabelText('Rola: ', getRolaString(podmiot1.Rola, 1)),
createLabelText('Rola inna: ', podmiot1.OpisRoli),
createLabelText('Udział: ', podmiot1.Udzial, FormatTyp.Currency6)
);
}
if (podmiot1.Email || podmiot1.Telefon) {
result.push(generateDaneKontaktowe(podmiot1.Email, getTable(podmiot1.Telefon)));
}
if (hasValue(podmiot1.NrKlienta)) {
result.push(createLabelText('Numer klienta: ', podmiot1.NrKlienta));
}
const columns1: Content[] = [...createSubHeader('Treść korygowana')];
if (hasValue(podmiot1K?.DaneIdentyfikacyjne?.NrID)) {
columns1.push(createLabelText('Identyfikator podatkowy inny: ', podmiot1K?.DaneIdentyfikacyjne?.NrID));
}
if (getValue(podmiot1K?.DaneIdentyfikacyjne?.BrakID) === '1') {
columns1.push(createLabelText('Brak identyfikatora ', ' '));
}
if (podmiot1K?.DaneIdentyfikacyjne) {
columns1.push(generateDaneIdentyfikacyjne(podmiot1K.DaneIdentyfikacyjne));
}
if (podmiot1K?.Adres) {
columns1.push(generatePodmiotAdres(podmiot1K.Adres, 'Adres', true));
}
const columns2: Content[] = [...createSubHeader('Treść korygująca')];
if (hasValue(podmiot1.DaneIdentyfikacyjne?.NrID)) {
columns2.push(createLabelText('Identyfikator podatkowy inny: ', podmiot1.DaneIdentyfikacyjne?.NrID));
}
if (getValue(podmiot1.DaneIdentyfikacyjne?.BrakID) === '1') {
columns2.push(createLabelText('Brak identyfikatora ', ' '));
}
if (podmiot1?.DaneIdentyfikacyjne) {
columns2.push(generateDaneIdentyfikacyjne(podmiot1.DaneIdentyfikacyjne));
}
if (podmiot1?.Adres) {
columns2.push(generatePodmiotAdres(podmiot1.Adres, 'Adres', true));
}
if (podmiot1.AdresKoresp != null) {
columns2.push(generatePodmiotAdres(podmiot1.AdresKoresp, 'Adres korespondencyjny', true));
}
result.push(generateTwoColumns(columns1, columns2));
return result;
}

View File

@@ -0,0 +1,48 @@
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { generatePodmiotAdres } from './PodmiotAdres';
import { Adres } from '../../types/fa1.types';
import { Margins } from 'pdfmake/interfaces';
vi.mock('../../../shared/PDF-functions', () => ({
createHeader: vi.fn((title, margin) => [{ text: `HEADER:${title}`, margin }]),
createSubHeader: vi.fn((title, margin) => [{ text: `SUBHEADER:${title}`, margin }]),
}));
vi.mock('./Adres', () => ({
generateAdres: vi.fn((adres) => [{ text: `ADRES:${adres?.AdresPol?.Miasto?._text ?? ''}` }]),
}));
describe('generatePodmiotAdres', () => {
beforeEach(() => vi.clearAllMocks());
it('returns empty array for undefined address', () => {
expect(generatePodmiotAdres(undefined)).toEqual([]);
});
it('renders with HEADER when isSubheader is false (default)', () => {
const adres: Adres = { AdresPol: { Miasto: { _text: 'Katowice' } } };
const result = generatePodmiotAdres(adres, 'Adres główny');
expect(result[0]).toEqual({ text: 'HEADER:Adres główny', margin: undefined });
expect(result[1]).toEqual({ text: 'ADRES:Katowice' });
});
it('renders with SUBHEADER and propagates headerMargin', () => {
const adres: Adres = { AdresPol: { Miasto: { _text: 'Wrocław' } } };
const margin: Margins = [2, 4, 6, 8];
const result = generatePodmiotAdres(adres, 'Nagłówek podrzędny', true, margin);
expect(result[0]).toEqual({ text: 'SUBHEADER:Nagłówek podrzędny', margin });
expect(result[1]).toEqual({ text: 'ADRES:Wrocław' });
});
it('returns both header/subheader content and address output', () => {
const adres: Adres = { AdresPol: { Miasto: { _text: 'Warszawa' } } };
const resultHeader = generatePodmiotAdres(adres, 'Adres', false);
const resultSub = generatePodmiotAdres(adres, 'Adres', true);
expect(resultHeader[0]).toEqual({ text: 'HEADER:Adres', margin: undefined });
expect(resultSub[0]).toEqual({ text: 'SUBHEADER:Adres', margin: undefined });
expect(resultHeader[1]).toEqual({ text: 'ADRES:Warszawa' });
expect(resultSub[1]).toEqual({ text: 'ADRES:Warszawa' });
});
});

View File

@@ -0,0 +1,19 @@
import { Content, Margins } from 'pdfmake/interfaces';
import { createHeader, createSubHeader } from '../../../shared/PDF-functions';
import { Adres } from '../../types/fa1.types';
import { generateAdres } from './Adres';
export function generatePodmiotAdres(
podmiotAdres: Adres | undefined,
headerTitle = 'Adres',
isSubheader = false,
headerMargin?: Margins
): Content[] {
if (!podmiotAdres) {
return [];
}
return [
...(isSubheader ? createSubHeader(headerTitle, headerMargin) : createHeader(headerTitle, headerMargin)),
...generateAdres(podmiotAdres),
];
}

View File

@@ -0,0 +1,62 @@
import { beforeEach, describe, expect, it, vi } from 'vitest';
import { generateDaneIdentyfikacyjne } from './PodmiotDaneIdentyfikacyjne';
import { Content } from 'pdfmake/interfaces';
import { DaneIdentyfikacyjneTPodmiot2Dto } from '../../types/fa2-additional-types';
vi.mock('../../../shared/PDF-functions', () => ({
createLabelText: vi.fn((label, value) => ({
text: `LABEL:${label}${typeof value === 'object' && value?._text ? value._text : (value ?? '')}`,
})),
getValue: vi.fn((val) => (val && val._text ? val._text : '')),
hasValue: vi.fn((val) => !!(val && val._text)),
}));
describe('generateDaneIdentyfikacyjne', () => {
beforeEach(() => vi.clearAllMocks());
it('adds NIP label always', () => {
const dane: DaneIdentyfikacyjneTPodmiot2Dto = { NIP: { _text: '1111111111' } };
const result = generateDaneIdentyfikacyjne(dane);
expect(result[0]).toEqual({ text: 'LABEL:NIP: 1111111111' });
});
it('skips name/last name label for empty values', () => {
const dane: DaneIdentyfikacyjneTPodmiot2Dto = { NIP: { _text: 'xx' } };
const result: Content[] = generateDaneIdentyfikacyjne(dane);
expect(result.some((r: any): boolean => r.text === 'LABEL: ')).toBeFalsy();
});
it('adds Pełna nazwa label when present', () => {
const dane: DaneIdentyfikacyjneTPodmiot2Dto = {
NIP: { _text: '99' },
PelnaNazwa: { _text: 'INSTRUMENTS INC.' },
};
const result = generateDaneIdentyfikacyjne(dane);
expect(result).toEqual(expect.arrayContaining([{ text: 'LABEL:Pełna nazwa: INSTRUMENTS INC.' }]));
});
it('adds Nazwa handlowa when surname is present', () => {
const dane: DaneIdentyfikacyjneTPodmiot2Dto = {
NIP: { _text: '43210' },
Nazwisko: { _text: 'Smith' },
NazwaHandlowa: { _text: 'Smithy PLC' },
};
const result = generateDaneIdentyfikacyjne(dane);
expect(result).toEqual(expect.arrayContaining([{ text: 'LABEL:Nazwa handlowa: Smithy PLC' }]));
});
it('returns array including only expected fields', () => {
const dane: DaneIdentyfikacyjneTPodmiot2Dto = {
NIP: { _text: '11' },
PelnaNazwa: { _text: 'Best Corp' },
};
const result = generateDaneIdentyfikacyjne(dane);
expect(result.length).toBe(2);
expect(result).toEqual([{ text: 'LABEL:NIP: 11' }, { text: 'LABEL:Pełna nazwa: Best Corp' }]);
});
});

View File

@@ -0,0 +1,24 @@
import { Content } from 'pdfmake/interfaces';
import { createLabelText, getValue, hasValue } from '../../../shared/PDF-functions';
import { DaneIdentyfikacyjneTPodmiot2Dto } from '../../types/fa2-additional-types';
export function generateDaneIdentyfikacyjne(daneIdentyfikacyjne: DaneIdentyfikacyjneTPodmiot2Dto): Content[] {
const result: Content[] = [];
result.push(createLabelText('NIP: ', daneIdentyfikacyjne.NIP));
if (hasValue(daneIdentyfikacyjne.ImiePierwsze) || hasValue(daneIdentyfikacyjne.Nazwisko)) {
result.push(
createLabelText(
'',
`${getValue(daneIdentyfikacyjne.ImiePierwsze)} ${getValue(daneIdentyfikacyjne.Nazwisko)}`
)
);
}
if (daneIdentyfikacyjne.PelnaNazwa) {
result.push(createLabelText('Pełna nazwa: ', daneIdentyfikacyjne.PelnaNazwa));
}
if (daneIdentyfikacyjne.Nazwisko) {
result.push(createLabelText('Nazwa handlowa: ', daneIdentyfikacyjne.NazwaHandlowa));
}
return result;
}

View File

@@ -0,0 +1,49 @@
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { generateDaneKontaktowe } from './PodmiotDaneKontaktowe';
import { FP } from '../../types/fa1.types';
vi.mock('../../../shared/PDF-functions', () => ({
createLabelText: vi.fn((label: string, value: any) => ({
text: `LABEL:${label}${typeof value === 'object' && value?._text ? value._text : (value ?? '')}`,
})),
}));
describe('generateDaneKontaktowe', () => {
beforeEach(() => vi.clearAllMocks());
it('returns empty array for no input', () => {
expect(generateDaneKontaktowe()).toEqual([]);
});
it('returns only email entry when only email is present', () => {
const email: FP = { _text: 'aa@bb.cc' };
const result = generateDaneKontaktowe(email, undefined);
expect(result).toEqual([{ text: 'LABEL:Email: aa@bb.cc' }]);
});
it('returns only tel entry when only single phone is present', () => {
const tel = [{ _text: '123456789' }];
const result = generateDaneKontaktowe(undefined, tel);
expect(result).toEqual([{ text: 'LABEL:Tel.: [object Object]\n' }]);
});
it('returns email and all telephones in order', () => {
const email: FP = { _text: 'z@x.io' };
const telo = [{ _text: '101' }, { _text: '202' }];
const result = generateDaneKontaktowe(email, telo);
expect(result[0]).toEqual({ text: 'LABEL:Email: z@x.io' });
expect(result.length).toBe(3);
expect(result[1]).toEqual({ text: 'LABEL:Tel.: [object Object]\n' });
expect(result[2]).toEqual({ text: 'LABEL:Tel.: [object Object]\n' });
});
it('handles multiple telephones', () => {
const tel = [{ _text: '666111222' }, { _text: '555222111' }];
const result = generateDaneKontaktowe(undefined, tel);
expect(result.length).toBe(2);
});
});

View File

@@ -0,0 +1,17 @@
import { Content } from 'pdfmake/interfaces';
import { createLabelText } from '../../../shared/PDF-functions';
import { FP } from '../../types/fa1.types';
export function generateDaneKontaktowe(email?: FP, telefon?: FP[]): Content[] {
const result: Content[] = [];
if (email) {
result.push(createLabelText('Email: ', email));
}
if (telefon) {
telefon.forEach((item) => {
result.push(createLabelText('Tel.: ', `${item}\n`));
});
}
return result;
}

View File

@@ -0,0 +1,46 @@
vi.mock('../../../shared/PDF-functions', () => ({
createHeader: vi.fn((label) => [{ text: `HEADER:${label}` }]),
createLabelText: vi.fn((label, value) => ({
text: `LABEL:${label}${value && value._text ? value._text : (value ?? '')}`,
})),
formatText: vi.fn((txt, _args) => ({ text: `FMT:${txt}` })),
getTable: vi.fn((arr) => arr || []),
getValue: vi.fn((val) => (val && val._text ? val._text : '')),
hasValue: vi.fn((val) => Boolean(val && val._text)),
generateTwoColumns: vi.fn((left, right) => ({ type: '2COL', left, right })),
}));
vi.mock('../../../shared/generators/common/functions', () => ({
getRolaUpowaznionegoString: vi.fn(() => 'Rola string'),
}));
vi.mock('./PodmiotAdres', () => ({
generatePodmiotAdres: vi.fn((adres, label) => ({ adr: label })),
}));
vi.mock('./PodmiotDaneIdentyfikacyjne', () => ({
generateDaneIdentyfikacyjne: vi.fn(() => [{ id: 'ID' }]),
}));
vi.mock('./PodmiotDaneKontaktowe', () => ({
generateDaneKontaktowe: vi.fn(() => ({ contact: 'KONTAKT' })),
}));
import { beforeEach, describe, expect, it, vi } from 'vitest';
import { generatePodmiotUpowazniony } from './PodmiotUpowazniony';
import { PodmiotUpowazniony } from '../../types/fa1.types';
describe(generatePodmiotUpowazniony.name, () => {
beforeEach(() => vi.clearAllMocks());
it('returns empty if input undefined', () => {
expect(generatePodmiotUpowazniony(undefined)).toEqual([]);
});
it('renders header and basic role and EORI if present', () => {
const podmiot: PodmiotUpowazniony = {
RolaPU: { _text: 'X' },
NrEORI: { _text: 'EORI123' },
};
const res = generatePodmiotUpowazniony(podmiot);
expect(res[0]).toEqual({ text: 'HEADER:Podmiot upoważniony' });
expect(res.some((r: any) => r.type === '2COL')).toBe(true);
});
});

View File

@@ -0,0 +1,56 @@
import { Content } from 'pdfmake/interfaces';
import {
createHeader,
createLabelText,
formatText,
generateTwoColumns,
getTable,
getValue,
hasValue,
} from '../../../shared/PDF-functions';
import FormatTyp from '../../../shared/enums/common.enum';
import { PodmiotUpowazniony } from '../../types/fa1.types';
import { generatePodmiotAdres } from './PodmiotAdres';
import { generateDaneIdentyfikacyjne } from './PodmiotDaneIdentyfikacyjne';
import { generateDaneKontaktowe } from './PodmiotDaneKontaktowe';
import { getRolaUpowaznionegoString } from '../../../shared/generators/common/functions';
export function generatePodmiotUpowazniony(podmiot: PodmiotUpowazniony | undefined): Content[] {
if (!podmiot) {
return [];
}
const result: Content[] = createHeader('Podmiot upoważniony');
const columnLeft: Content[] = [];
const columnRight: Content[] = [];
if (hasValue(podmiot.RolaPU)) {
columnLeft.push(createLabelText('Rola: ', getRolaUpowaznionegoString(podmiot.RolaPU, 1)));
}
if (hasValue(podmiot.NrEORI)) {
columnLeft.push(createLabelText('NrEORI: ', podmiot.NrEORI));
}
if (podmiot.DaneIdentyfikacyjne) {
if (hasValue(podmiot.DaneIdentyfikacyjne.NrID)) {
columnLeft.push(createLabelText('Identyfikator podatkowy inny: ', podmiot.DaneIdentyfikacyjne.NrID));
}
if (getValue(podmiot.DaneIdentyfikacyjne.BrakID) === '1') {
columnLeft.push(createLabelText('Brak identyfikatora ', ' '));
}
columnLeft.push(generateDaneIdentyfikacyjne(podmiot.DaneIdentyfikacyjne));
}
if (podmiot.Adres) {
columnRight.push(generatePodmiotAdres(podmiot.Adres, 'Adres', true));
}
if (podmiot.AdresKoresp) {
columnRight.push(generatePodmiotAdres(podmiot.AdresKoresp, 'Adres do korespondencji', true));
}
if (podmiot.EmailPU || podmiot.TelefonPU) {
columnRight.push(
formatText('Dane kontaktowe', [FormatTyp.Label]),
...generateDaneKontaktowe(podmiot.EmailPU, getTable(podmiot.TelefonPU))
);
}
result.push(generateTwoColumns(columnLeft, columnRight));
return result;
}

View File

@@ -0,0 +1,118 @@
import { beforeEach, describe, expect, it, vi } from 'vitest';
import { generatePodmioty } from './Podmioty';
import { Faktura } from '../../types/fa1.types';
vi.mock('../../../shared/PDF-functions', () => ({
createSection: vi.fn((arr) => arr),
getTable: vi.fn((obj) => (obj ? (Array.isArray(obj) ? obj : [obj]) : [])),
getValue: vi.fn((val) => (val && val._text ? val._text : '')),
hasValue: vi.fn((val) => Boolean(val && val._text)),
}));
vi.mock('./Podmiot1', () => ({
generatePodmiot1: vi.fn(() => [{ podmiot1: true }]),
}));
vi.mock('./Podmiot1Podmiot1K', () => ({
generatePodmiot1Podmiot1K: vi.fn(() => [{ podmiot1K: true }]),
}));
vi.mock('./Podmiot2', () => ({
generatePodmiot2: vi.fn(() => [{ podmiot2: true }]),
}));
vi.mock('./Podmiot2Podmiot2k', () => ({
generatePodmiot2Podmiot2K: vi.fn(() => [{ podmiot2K: true }]),
}));
vi.mock('./Podmiot3', () => ({
generatePodmiot3: vi.fn(() => [{ podmiot3: true }]),
}));
vi.mock('./Podmiot3Podmiot2k', () => ({
generateDaneIdentyfikacyjneTPodmiot3Dto: vi.fn(() => [{ daneIdentyfikacyjne: true }]),
}));
vi.mock('./PodmiotUpowazniony', () => ({
generatePodmiotUpowazniony: vi.fn(() => [{ upowazniony: true }]),
}));
vi.mock('../../../shared/generators/common/functions', () => ({
getValue: vi.fn((val) => (val && val._text ? val._text : '')),
}));
describe(generatePodmioty.name, () => {
beforeEach(() => {
vi.clearAllMocks();
});
it('calls generatePodmiot1Podmiot1K when Podmiot1K exists', () => {
const invoice: Faktura = {
Podmiot1: {},
Podmiot2: {},
Fa: {
Podmiot1K: {},
Podmiot2K: [{}],
},
};
const result = generatePodmioty(invoice);
expect(result).toContainEqual(expect.arrayContaining([{ podmiot1K: true }]));
});
it('calls generatePodmiot1 when Podmiot1K missing', () => {
const invoice: Faktura = {
Podmiot1: {},
Podmiot2: {},
Fa: {
Podmiot2K: [{}],
},
};
const result = generatePodmioty(invoice);
expect(result).toContainEqual(expect.arrayContaining([{ podmiot1: true }]));
});
it('calls generatePodmiot2Podmiot2K when Podmiot2K available', () => {
const invoice: Faktura = {
Podmiot2: {},
Fa: {
Podmiot2K: [{}, {}],
},
};
const result = generatePodmioty(invoice);
expect(result).toContainEqual(expect.arrayContaining([{ podmiot2K: true }]));
});
it('renders Podmiot1 and Podmiot2 side by side if no Fa Podmiot1K or Podmiot2K', () => {
const invoice: Faktura = {
Podmiot1: {},
Podmiot2: {},
};
const result = generatePodmioty(invoice);
expect(result).toEqual(
expect.arrayContaining([
expect.arrayContaining([
expect.objectContaining({
columns: expect.any(Array),
}),
]),
])
);
});
it('calls generateDaneIdentyfikacyjneTPodmiot3Dto or generatePodmiot3 accordingly', () => {
const invoice: Faktura = {
Podmiot3: [{ Rola: { _text: '4' } }],
Fa: {
Podmiot2K: [{}, {}],
},
};
const result = generatePodmioty(invoice);
expect(result.some((e: any) => e.some((o: any) => o.daneIdentyfikacyjne || o.podmiot3))).toBe(true);
});
it('calls generatePodmiotUpowazniony last', () => {
const invoice: Faktura = {
PodmiotUpowazniony: {},
};
const result = generatePodmioty(invoice);
expect(result).toContainEqual(expect.arrayContaining([{ upowazniony: true }]));
});
});

View File

@@ -0,0 +1,95 @@
import { Content } from 'pdfmake/interfaces';
import { createSection, getTable, getValue } from '../../../shared/PDF-functions';
import { Faktura, Podmiot2K, Podmiot3 } from '../../types/fa1.types';
import { Podmiot3Podmiot2KDto } from '../../types/fa1-additional-types';
import { generatePodmiot1 } from './Podmiot1';
import { generatePodmiot1Podmiot1K } from './Podmiot1Podmiot1K';
import { generatePodmiot2 } from './Podmiot2';
import { generatePodmiot2Podmiot2K } from './Podmiot2Podmiot2k';
import { generatePodmiot3 } from './Podmiot3';
import { generateDaneIdentyfikacyjneTPodmiot3Dto } from './Podmiot3Podmiot2k';
import { generatePodmiotUpowazniony } from './PodmiotUpowazniony';
export function generatePodmioty(invoice: Faktura): Content[] {
const result: Content[] = [];
const podmiot2K: Podmiot2K[] = getTable(invoice.Fa?.Podmiot2K);
const podmiot3: Podmiot3[] = getTable(invoice.Podmiot3);
if (invoice.Fa?.Podmiot1K || podmiot2K.length > 0) {
if (invoice.Fa?.Podmiot1K) {
result.push(generatePodmiot1Podmiot1K(invoice.Podmiot1 ?? {}, invoice.Fa?.Podmiot1K));
} else if (invoice.Podmiot1 != null) {
result.push(generatePodmiot1(invoice.Podmiot1));
}
if (podmiot2K.length > 0) {
podmiot2K.forEach((podmiot2K: Podmiot2K): void => {
result.push(generatePodmiot2Podmiot2K(invoice.Podmiot2 ?? {}, podmiot2K));
});
} else if (invoice.Podmiot2) {
result.push(createSection(generatePodmiot2(invoice.Podmiot2!), true));
}
} else {
result.push([
{
columns: [generatePodmiot1(invoice.Podmiot1!), generatePodmiot2(invoice.Podmiot2!)],
margin: [0, 0, 0, 8],
},
]);
}
if (podmiot3.length > 0) {
const podmiot3Podmiot2KDto: Podmiot3Podmiot2KDto[] = getPodmiot3Podmiot2KDto(podmiot2K, podmiot3);
if (podmiot3Podmiot2KDto.length > 0) {
podmiot3Podmiot2KDto.forEach((pdm2KDto: Podmiot3Podmiot2KDto, i: number): void => {
if (pdm2KDto.podmiot2KDto) {
result.push(generateDaneIdentyfikacyjneTPodmiot3Dto(pdm2KDto, i));
} else {
result.push(generatePodmiot3(pdm2KDto.fakturaPodmiotNDto, i));
}
if (i < podmiot3Podmiot2KDto.length - 1) {
result.push({ margin: [0, 8, 0, 0], text: '' });
}
});
} else {
podmiot3.forEach((podmiot: Podmiot3, index: number): void => {
result.push(generatePodmiot3(podmiot, index));
if (index < podmiot3.length - 1) {
result.push({ margin: [0, 8, 0, 0], text: '' });
}
});
}
}
result.push(generatePodmiotUpowazniony(invoice.PodmiotUpowazniony));
return createSection(result, true);
}
function getPodmiot3Podmiot2KDto(podmioty2K: Podmiot2K[], podmioty3: Podmiot3[]): Podmiot3Podmiot2KDto[] {
const result: Podmiot3Podmiot2KDto[] = [];
if (
podmioty2K.length > 1 &&
podmioty3.filter((p: Podmiot3): boolean => getValue(p.Rola) === '4').length > 0
) {
let idx: number = 1;
podmioty3.forEach((podmiot3) => {
if (getValue(podmiot3.Rola) === '4') {
if (podmioty2K.length > idx) {
result.push({
fakturaPodmiotNDto: podmiot3,
podmiot2KDto: podmioty2K[idx],
});
}
idx++;
} else {
result.push({
fakturaPodmiotNDto: podmiot3,
});
}
});
}
return result;
}

View File

@@ -0,0 +1,95 @@
import { beforeEach, describe, expect, it, vi } from 'vitest';
import { Fa, Faktura, FP } from '../../types/fa1.types';
import { generatePodsumowanieStawekPodatkuVat, getSummaryTaxRate } from './PodsumowanieStawekPodatkuVat';
vi.mock('../../../shared/PDF-functions', () => ({
createHeader: vi.fn((label) => [{ text: `HEADER:${label}` }]),
createSection: vi.fn((input) => input),
formatText: vi.fn((text, style) => ({ text, style })),
getValue: vi.fn((val) =>
val && val._text ? (typeof val._text === 'string' ? val._text : val._text.toString()) : ''
),
hasValue: vi.fn((val) => !!(val && val._text)),
getNumberRounded: vi.fn((val) => (typeof val === 'number' ? val : parseFloat(val?._text ?? '0') || 0)),
}));
describe('generatePodsumowanieStawekPodatkuVat', () => {
beforeEach(() => vi.clearAllMocks());
it('returns empty array if no relevant Fa data', () => {
const faktura: Faktura = { Fa: {} as Fa };
expect(generatePodsumowanieStawekPodatkuVat(faktura)).toEqual([]);
});
it('creates table with header and data rows', () => {
const fa: Fa = {
P_13_1: { _text: '100' },
P_14_1: { _text: '23' },
P_14_1W: { _text: '23' },
};
const faktura: Faktura = { Fa: fa };
const result: any = generatePodsumowanieStawekPodatkuVat(faktura);
expect(result.some((r: any) => r.table && r.table.body)).toBeTruthy();
expect(result[0].text).toContain('Podsumowanie stawek podatku'); // header present
});
});
describe('getSummaryTaxRate', () => {
beforeEach(() => vi.clearAllMocks());
it('returns empty summarization if no values', () => {
const fa: Fa = {};
expect(getSummaryTaxRate(fa)).toEqual([]);
});
it('returns correct summary entries for each tax rate', () => {
const floatText = (num: number): FP => ({ _text: num.toString() });
const fa: Fa = {
P_13_1: floatText(100),
P_14_1: floatText(23),
P_14_1W: floatText(23),
P_13_2: floatText(200),
P_14_2: floatText(16),
P_14_2W: floatText(16),
P_13_3: floatText(300),
P_14_3: floatText(15),
P_14_3W: floatText(15),
P_13_4: floatText(400),
P_14_4: floatText(12),
P_14_4W: floatText(12),
P_13_5: floatText(500),
P_14_5: floatText(0),
P_13_7: floatText(600),
};
const summary = getSummaryTaxRate(fa);
expect(summary.length).toBe(6);
expect(summary[0]).toMatchObject({
no: 1,
taxRateString: '23% lub 22%',
net: '100.00',
tax: '23.00',
taxPLN: '23.00',
gross: '123.00',
});
expect(summary[1].taxRateString).toBe('8% lub 7%');
expect(summary[4].taxRateString).toBe('');
expect(summary[5].taxRateString).toBe('zwolnione z opodatkowania');
});
it('includes only tax rates with non-zero values', () => {
const fa: Fa = {
P_13_1: { _text: '0' },
P_14_1: { _text: '0' },
P_14_1W: { _text: '0' },
};
expect(getSummaryTaxRate(fa)).toHaveLength(0);
});
});

View File

@@ -0,0 +1,189 @@
import { Content, ContentTable, ContentText, TableCell } from 'pdfmake/interfaces';
import {
createHeader,
createSection,
formatText,
getNumberRounded,
getValue,
hasValue,
} from '../../../shared/PDF-functions';
import FormatTyp from '../../../shared/enums/common.enum';
import { Fa, Faktura, FP } from '../../types/fa1.types';
import { TaxSummaryTypes } from '../../types/tax-summary.types';
import { DEFAULT_TABLE_LAYOUT } from '../../../shared/consts/const';
export function generatePodsumowanieStawekPodatkuVat(faktura: Faktura): Content[] {
const AnyP13P14_5Diff0: boolean =
hasValue(faktura.Fa?.P_13_1) ||
hasValue(faktura.Fa?.P_13_2) ||
hasValue(faktura.Fa?.P_13_3) ||
hasValue(faktura.Fa?.P_13_4) ||
(hasValue(faktura.Fa?.P_13_5) && (!hasValue(faktura.Fa?.P_14_5) || getValue(faktura.Fa?.P_14_5) == 0)) ||
hasValue(faktura.Fa?.P_13_6) ||
hasValue(faktura.Fa?.P_13_7);
const AnyP13: boolean =
hasValue(faktura.Fa?.P_13_1) ||
hasValue(faktura.Fa?.P_13_2) ||
hasValue(faktura.Fa?.P_13_3) ||
hasValue(faktura.Fa?.P_13_4) ||
hasValue(faktura.Fa?.P_13_5) ||
hasValue(faktura.Fa?.P_13_7);
const AnyP_14xW: boolean =
hasValue(faktura.Fa?.P_14_1W) ||
hasValue(faktura.Fa?.P_14_2W) ||
hasValue(faktura.Fa?.P_14_3W) ||
hasValue(faktura.Fa?.P_14_4W);
let tableBody: TableCell[] = [];
const table: ContentTable = {
table: {
headerRows: 1,
widths: [],
body: [] as TableCell[][],
},
layout: DEFAULT_TABLE_LAYOUT,
};
const definedHeader: Content[] = [
...[{ text: 'Lp.', style: FormatTyp.GrayBoldTitle }],
...(AnyP13P14_5Diff0 ? [{ text: 'Stawka podatku', style: FormatTyp.GrayBoldTitle }] : []),
...(AnyP13 ? [{ text: 'Kwota netto', style: FormatTyp.GrayBoldTitle }] : []),
...(AnyP13P14_5Diff0 ? [{ text: 'Kwota podatku', style: FormatTyp.GrayBoldTitle }] : []),
...(AnyP13 ? [{ text: 'Kwota brutto', style: FormatTyp.GrayBoldTitle }] : []),
...(AnyP_14xW ? [{ text: 'Kwota podatku PLN', style: FormatTyp.GrayBoldTitle }] : []),
];
const widths: Content[] = [
...['auto'],
...(AnyP13P14_5Diff0 ? ['*'] : []),
...(AnyP13 ? ['*'] : []),
...(AnyP13P14_5Diff0 ? ['*'] : []),
...(AnyP13 ? ['*'] : []),
...(AnyP_14xW ? ['*'] : []),
];
if (faktura?.Fa) {
const summary: TaxSummaryTypes[] = getSummaryTaxRate(faktura.Fa);
tableBody = summary.map((item: TaxSummaryTypes): (string | number | ContentText)[] => {
const data: (string | number | ContentText)[] = [];
data.push(item.no);
if (AnyP13P14_5Diff0) {
data.push(item.taxRateString);
}
if (AnyP13) {
data.push(formatText(item.net, FormatTyp.Currency));
}
if (AnyP13P14_5Diff0) {
data.push(formatText(item.tax, FormatTyp.Currency));
}
if (AnyP13) {
data.push(formatText(item.gross, FormatTyp.Currency));
}
if (AnyP_14xW) {
data.push(formatText(item.taxPLN, FormatTyp.Currency));
}
return data;
});
}
table.table.body = [[...definedHeader], ...tableBody] as TableCell[][];
table.table.widths = [...widths] as never[];
return tableBody.length
? createSection([...createHeader('Podsumowanie stawek podatku', [0, 0, 0, 8]), table], false)
: [];
}
export function getSummaryTaxRate(fa: Fa): TaxSummaryTypes[] {
const summary: TaxSummaryTypes[] = [];
const AnyP13_1P14_1P14_1WDiff0 =
hasValueAndDiff0(fa?.P_13_1) || hasValueAndDiff0(fa?.P_14_1) || hasValueAndDiff0(fa?.P_14_1W);
const AnyP13_2P14_2P14_2WDiff0 =
hasValueAndDiff0(fa?.P_13_2) || hasValueAndDiff0(fa?.P_14_2) || hasValueAndDiff0(fa?.P_14_2W);
const AnyP13_3P14_3P14_3WDiff0 =
hasValueAndDiff0(fa?.P_13_3) || hasValueAndDiff0(fa?.P_14_3) || hasValueAndDiff0(fa?.P_14_3W);
const AnyP13_4P14_4P14_4WDiff0 =
hasValueAndDiff0(fa?.P_13_4) || hasValueAndDiff0(fa?.P_14_4) || hasValueAndDiff0(fa?.P_14_4W);
const AnyP13_5P14_5Diff0 = hasValueAndDiff0(fa?.P_13_5) || hasValueAndDiff0(fa?.P_14_5);
const AnyP13_7Diff0 = hasValueAndDiff0(fa?.P_13_7);
let no = 1;
if (AnyP13_1P14_1P14_1WDiff0) {
summary.push({
no,
net: getNumberRounded(fa.P_13_1).toFixed(2),
gross: (getNumberRounded(fa.P_13_1) + getNumberRounded(fa.P_14_1)).toFixed(2),
tax: getNumberRounded(fa.P_14_1).toFixed(2),
taxPLN: getNumberRounded(fa.P_14_1W).toFixed(2),
taxRateString: '23% lub 22%',
});
no++;
}
if (AnyP13_2P14_2P14_2WDiff0) {
summary.push({
no,
net: getNumberRounded(fa.P_13_2).toFixed(2),
gross: (getNumberRounded(fa.P_13_2) + getNumberRounded(fa.P_14_2)).toFixed(2),
tax: getNumberRounded(fa.P_14_2).toFixed(2),
taxPLN: getNumberRounded(fa.P_14_2W).toFixed(2),
taxRateString: '8% lub 7%',
});
no++;
}
if (AnyP13_3P14_3P14_3WDiff0) {
summary.push({
no,
net: getNumberRounded(fa.P_13_3).toFixed(2),
gross: (getNumberRounded(fa.P_13_3) + getNumberRounded(fa.P_14_3)).toFixed(2),
tax: getNumberRounded(fa.P_14_3).toFixed(2),
taxPLN: getNumberRounded(fa.P_14_3W).toFixed(2),
taxRateString: '5%',
});
no++;
}
if (AnyP13_4P14_4P14_4WDiff0) {
summary.push({
no,
net: getNumberRounded(fa.P_13_4).toFixed(2),
gross: (getNumberRounded(fa.P_13_4) + getNumberRounded(fa.P_14_4)).toFixed(2),
tax: getNumberRounded(fa.P_14_4).toFixed(2),
taxPLN: getNumberRounded(fa.P_14_4W).toFixed(2),
taxRateString: '4% lub 3% lub oo',
});
no++;
}
if (AnyP13_5P14_5Diff0) {
summary.push({
no,
net: getNumberRounded(fa.P_13_5).toFixed(2),
gross: getNumberRounded(fa.P_13_5).toFixed(2),
tax: getNumberRounded(fa.P_14_5).toFixed(2),
taxPLN: '',
taxRateString: getValue(fa.P_14_5) != 0 ? 'niepodlegające opodatkowaniu' : '',
});
no++;
}
if (AnyP13_7Diff0) {
summary.push({
no,
net: getNumberRounded(fa.P_13_7).toFixed(2),
gross: getNumberRounded(fa.P_13_7).toFixed(2),
tax: '0.00',
taxPLN: '',
taxRateString: 'zwolnione z opodatkowania',
});
no++;
}
return summary;
}
function hasValueAndDiff0(value: FP | string | number | undefined): boolean {
return hasValue(value) && getValue(value) != 0;
}

View File

@@ -0,0 +1,34 @@
import { beforeEach, describe, expect, it, vi } from 'vitest';
import { generatePrzewoznik } from './Przewoznik';
import { Przewoznik } from '../../types/fa1.types';
vi.mock('../../../shared/PDF-functions', () => ({
createHeader: vi.fn((label) => [{ text: `HEADER:${label}` }]),
generateTwoColumns: vi.fn((left, right, margin) => ({ left, right, margin, type: '2COL' })),
}));
vi.mock('./PodmiotAdres', () => ({
generatePodmiotAdres: vi.fn(() => ({ adr: 'AdresPrzewoznika' })),
}));
vi.mock('./PodmiotDaneIdentyfikacyjne', () => ({
generateDaneIdentyfikacyjne: vi.fn(() => [{ id: 'DaneIdentyfikacyjne' }]),
}));
describe('generatePrzewoznik', () => {
beforeEach(() => vi.clearAllMocks());
it('returns empty array if przewoznik is undefined', () => {
expect(generatePrzewoznik(undefined)).toEqual([]);
});
it('returns header and twoColumns for valid przewoznik', () => {
const przewoznik: Przewoznik = {
DaneIdentyfikacyjne: { NIP: { _text: '12345' } },
AdresPrzewoznika: { AdresPol: { Miasto: { _text: 'Warsaw' } } },
};
const result: any = generatePrzewoznik(przewoznik);
expect(result[0]).toEqual({ text: 'HEADER:Przewoźnik' });
});
});

View File

@@ -0,0 +1,21 @@
import { Content } from 'pdfmake/interfaces';
import { createHeader, generateTwoColumns } from '../../../shared/PDF-functions';
import { Przewoznik } from '../../types/fa1.types';
import { generatePodmiotAdres } from './PodmiotAdres';
import { generateDaneIdentyfikacyjne } from './PodmiotDaneIdentyfikacyjne';
export function generatePrzewoznik(przewoznik: Przewoznik | undefined): Content {
if (!przewoznik) {
return [];
}
return [
...createHeader('Przewoźnik'),
[
generateTwoColumns(
generateDaneIdentyfikacyjne(przewoznik.DaneIdentyfikacyjne as any),
generatePodmiotAdres(przewoznik.AdresPrzewoznika, 'Adres przewoźnika', true, [0, 0, 0, 0]),
[0, 0, 0, 8]
),
],
];
}

View File

@@ -0,0 +1,239 @@
import { beforeEach, describe, expect, it, vi } from 'vitest';
import { generateRabat } from './Rabat';
import * as PDFFunctions from '../../../shared/PDF-functions';
import { Fa } from '../../types/fa1.types';
import FormatTyp, { Position } from '../../../shared/enums/common.enum';
vi.mock('../../../shared/PDF-functions', () => ({
createHeader: vi.fn(),
createLabelText: vi.fn(),
createSection: vi.fn(),
formatText: vi.fn(),
generateTwoColumns: vi.fn(),
getContentTable: vi.fn(),
getTable: vi.fn(),
}));
describe(generateRabat.name, () => {
beforeEach(() => {
vi.clearAllMocks();
});
const mockInvoice: Fa = {
FaWiersz: [
{
NrWierszaFa: { _text: '1' },
P_7: { _text: 'Product 1' },
P_8B: { _text: '5' },
P_8A: { _text: 'szt' },
},
],
P_15: { _text: '100.00' },
} as any;
beforeEach(() => {
vi.mocked(PDFFunctions.getTable).mockReturnValue([]);
vi.mocked(PDFFunctions.createHeader).mockReturnValue(['header'] as any);
vi.mocked(PDFFunctions.createLabelText).mockReturnValue(['label'] as any);
vi.mocked(PDFFunctions.createSection).mockReturnValue('section' as any);
vi.mocked(PDFFunctions.formatText).mockReturnValue('formatted' as any);
vi.mocked(PDFFunctions.generateTwoColumns).mockReturnValue('columns' as any);
vi.mocked(PDFFunctions.getContentTable).mockReturnValue({
content: null,
fieldsWithValue: [],
});
});
it('should call createHeader with "Rabat"', () => {
generateRabat(mockInvoice);
expect(PDFFunctions.createHeader).toHaveBeenCalledWith('Rabat');
});
it('should call createSection and return result', () => {
const mockSection = 'section';
vi.mocked(PDFFunctions.createSection).mockReturnValue(mockSection as any);
const result = generateRabat(mockInvoice);
expect(PDFFunctions.createSection).toHaveBeenCalledWith(expect.any(Array), true);
expect(result).toEqual(mockSection);
});
it('should call getTable with FaWiersz', () => {
generateRabat(mockInvoice);
expect(PDFFunctions.getTable).toHaveBeenCalledWith(mockInvoice.FaWiersze);
});
it('should call createLabelText with discount value', () => {
generateRabat(mockInvoice);
expect(PDFFunctions.createLabelText).toHaveBeenCalledWith(
'Wartość rabatu ogółem: ',
mockInvoice.P_15,
FormatTyp.Currency,
{ alignment: Position.RIGHT }
);
});
it('should call getContentTable with correct headers', () => {
generateRabat(mockInvoice);
expect(PDFFunctions.getContentTable).toHaveBeenCalledWith(
expect.arrayContaining([
expect.objectContaining({ name: 'NrWierszaFay', title: 'Lp.' }),
expect.objectContaining({ name: 'P_7', title: 'Nazwa towaru lub usługi' }),
expect.objectContaining({ name: 'P_8B', title: 'Ilość' }),
expect.objectContaining({ name: 'P_8A', title: 'Miara' }),
]),
[],
'*'
);
});
describe('discount text generation', () => {
it('should generate text "nie dotyczy" when NrWierszaFa is in fieldsWithValue', () => {
vi.mocked(PDFFunctions.getContentTable).mockReturnValue({
content: null,
fieldsWithValue: ['NrWierszaFa', 'P_7'],
});
generateRabat(mockInvoice);
expect(PDFFunctions.formatText).toHaveBeenCalledWith(
'Rabat nie dotyczy wszystkich dostaw towarów i wykonanych usług na rzecz tego nabywcy w danym okresie.',
FormatTyp.Default
);
});
it('should generate text "dotyczy" when NrWierszaFa is not in fieldsWithValue', () => {
vi.mocked(PDFFunctions.getContentTable).mockReturnValue({
content: null,
fieldsWithValue: ['P_7', 'P_8B'],
});
generateRabat(mockInvoice);
expect(PDFFunctions.formatText).toHaveBeenCalledWith(
'Rabat dotyczy wszystkich dostaw towarów i wykonanych usług na rzecz tego nabywcy w danym okresie.',
FormatTyp.Default
);
});
it('should call generateTwoColumns with formatted text and empty string', () => {
vi.mocked(PDFFunctions.formatText).mockReturnValue('formatted-text' as any);
generateRabat(mockInvoice);
expect(PDFFunctions.generateTwoColumns).toHaveBeenCalledWith('formatted-text', '');
});
});
describe('table content', () => {
it('should add table content when fieldsWithValue is not empty and content exists', () => {
const mockTable = { table: 'mock-table' };
vi.mocked(PDFFunctions.getContentTable).mockReturnValue({
content: mockTable as any,
fieldsWithValue: ['P_7', 'P_8B'],
});
generateRabat(mockInvoice);
const sectionCall = vi.mocked(PDFFunctions.createSection).mock.calls[0][0] as any[];
expect(sectionCall).toContain(mockTable);
});
it('should not add table content when fieldsWithValue is empty', () => {
const mockTable = { table: 'mock-table' };
vi.mocked(PDFFunctions.getContentTable).mockReturnValue({
content: mockTable as any,
fieldsWithValue: [],
});
generateRabat(mockInvoice);
const sectionCall = vi.mocked(PDFFunctions.createSection).mock.calls[0][0] as any[];
expect(sectionCall).not.toContain(mockTable);
});
it('should not add table content when content is null', () => {
vi.mocked(PDFFunctions.getContentTable).mockReturnValue({
content: null,
fieldsWithValue: ['P_7', 'P_8B'],
});
generateRabat(mockInvoice);
const sectionCall = vi.mocked(PDFFunctions.createSection).mock.calls[0][0] as any[];
expect(sectionCall.every((item: any) => item !== null)).toBe(true);
});
it('should not add table content when both fieldsWithValue is empty and content is null', () => {
vi.mocked(PDFFunctions.getContentTable).mockReturnValue({
content: null,
fieldsWithValue: [],
});
generateRabat(mockInvoice);
const sectionCall = vi.mocked(PDFFunctions.createSection).mock.calls[0][0] as any[];
expect(sectionCall.length).toBeGreaterThan(0);
});
});
describe('result structure', () => {
it('should include header in result', () => {
vi.mocked(PDFFunctions.createHeader).mockReturnValue(['header-content'] as any);
generateRabat(mockInvoice);
const sectionCall = vi.mocked(PDFFunctions.createSection).mock.calls[0][0] as any[];
expect(sectionCall).toContain('header-content');
});
it('should include label text in result', () => {
vi.mocked(PDFFunctions.createLabelText).mockReturnValue(['label-content'] as any);
generateRabat(mockInvoice);
const sectionCall = vi.mocked(PDFFunctions.createSection).mock.calls[0][0] as any[];
expect(sectionCall).toContain('label-content');
});
it('should include two columns in result', () => {
vi.mocked(PDFFunctions.generateTwoColumns).mockReturnValue('columns-content' as any);
generateRabat(mockInvoice);
const sectionCall = vi.mocked(PDFFunctions.createSection).mock.calls[0][0] as any[];
expect(sectionCall).toContain('columns-content');
});
});
describe('complete integration', () => {
it('should generate complete structure with all elements', () => {
const mockTable = { table: 'mock-table' };
vi.mocked(PDFFunctions.getContentTable).mockReturnValue({
content: mockTable as any,
fieldsWithValue: ['NrWierszaFa', 'P_7'],
});
vi.mocked(PDFFunctions.createHeader).mockReturnValue(['header'] as any);
vi.mocked(PDFFunctions.createLabelText).mockReturnValue(['label'] as any);
vi.mocked(PDFFunctions.generateTwoColumns).mockReturnValue('columns' as any);
generateRabat(mockInvoice);
expect(PDFFunctions.createHeader).toHaveBeenCalled();
expect(PDFFunctions.createLabelText).toHaveBeenCalled();
expect(PDFFunctions.formatText).toHaveBeenCalled();
expect(PDFFunctions.generateTwoColumns).toHaveBeenCalled();
expect(PDFFunctions.getContentTable).toHaveBeenCalled();
expect(PDFFunctions.createSection).toHaveBeenCalled();
const sectionCall = vi.mocked(PDFFunctions.createSection).mock.calls[0][0] as any[];
expect(sectionCall.length).toBeGreaterThan(3);
});
});
});

View File

@@ -0,0 +1,46 @@
import { Content } from 'pdfmake/interfaces';
import {
createHeader,
createLabelText,
createSection,
formatText,
generateTwoColumns,
getContentTable,
getTable,
} from '../../../shared/PDF-functions';
import { HeaderDefine } from '../../../shared/types/pdf-types';
import { Fa, FP } from '../../types/fa1.types';
import FormatTyp, { Position } from '../../../shared/enums/common.enum';
import { TableWithFields } from '../../types/fa1-additional-types';
export function generateRabat(invoice: Fa): Content[] {
const faRows: Record<string, FP>[] = getTable(invoice!.FaWiersze?.FaWiersz);
const result: Content[] = [];
const definedHeader: HeaderDefine[] = [
{ name: 'NrWierszaFay', title: 'Lp.', format: FormatTyp.Default },
{ name: 'P_7', title: 'Nazwa towaru lub usługi', format: FormatTyp.Default },
{ name: 'P_8B', title: 'Ilość', format: FormatTyp.Default },
{ name: 'P_8A', title: 'Miara', format: FormatTyp.Default },
];
const tabRabat: TableWithFields = getContentTable<(typeof faRows)[0]>(definedHeader, faRows, '*');
const isNrWierszaFa: boolean = tabRabat.fieldsWithValue.includes('NrWierszaFa');
result.push(
...createHeader('Rabat'),
...createLabelText('Wartość rabatu ogółem: ', invoice.P_15, FormatTyp.Currency, {
alignment: Position.RIGHT,
}),
generateTwoColumns(
formatText(
`Rabat ${isNrWierszaFa ? 'nie ' : ''}dotyczy wszystkich dostaw towarów i wykonanych usług na rzecz tego nabywcy w danym okresie.`,
FormatTyp.Default
),
''
)
);
if (tabRabat.fieldsWithValue.length > 0 && tabRabat.content) {
result.push(tabRabat.content);
}
return createSection(result, true);
}

View File

@@ -0,0 +1,235 @@
import { beforeEach, describe, expect, it, vi } from 'vitest';
import { generujRachunekBankowy } from './RachunekBankowy';
import * as PDFFunctions from '../../../shared/PDF-functions';
import FormatTyp from '../../../shared/enums/common.enum';
import * as CommonFunctions from '../../../shared/generators/common/functions';
vi.mock('../../../shared/PDF-functions', () => ({
createHeader: vi.fn(),
createSection: vi.fn(),
formatText: vi.fn(),
}));
vi.mock('../../../shared/generators/common/functions', () => ({
getTypRachunkowWlasnych: vi.fn(),
}));
describe(generujRachunekBankowy.name, () => {
beforeEach(() => {
vi.clearAllMocks();
});
const mockAccount: any = {
NrRB: { _text: '12345678901234567890123456' },
SWIFT: { _text: 'BPKOPLPW' },
RachunekWlasnyBanku: { _text: '1' },
NazwaBanku: { _text: 'PKO Bank Polski' },
OpisRachunku: { _text: 'Rachunek główny' },
} as any;
beforeEach(() => {
vi.mocked(PDFFunctions.createHeader).mockReturnValue(['header'] as any);
vi.mocked(PDFFunctions.createSection).mockReturnValue('section' as any);
vi.mocked(PDFFunctions.formatText).mockReturnValue('formatted' as any);
vi.mocked(CommonFunctions.getTypRachunkowWlasnych).mockReturnValue('Tak');
});
describe('when accounts is undefined or empty', () => {
it('should return empty array when accounts is undefined', () => {
const result = generujRachunekBankowy(undefined, 'Rachunek bankowy');
expect(result).toEqual([]);
});
it('should return empty array when accounts is empty array', () => {
const result = generujRachunekBankowy([], 'Rachunek bankowy');
expect(result).toEqual([]);
});
});
describe('when accounts exist', () => {
it('should call createHeader with provided title', () => {
generujRachunekBankowy([mockAccount], 'Rachunek bankowy');
expect(PDFFunctions.createHeader).toHaveBeenCalledWith('Rachunek bankowy ', [0, 12, 0, 8]);
});
it('should call createHeader with empty string when title is not provided', () => {
generujRachunekBankowy([mockAccount]);
expect(PDFFunctions.createHeader).toHaveBeenCalledWith('', [0, 12, 0, 8]);
});
it('should call createSection and return result', () => {
const mockSection = 'section';
vi.mocked(PDFFunctions.createSection).mockReturnValue(mockSection as any);
const result = generujRachunekBankowy([mockAccount], 'Rachunek bankowy');
expect(PDFFunctions.createSection).toHaveBeenCalledWith(expect.any(Array), false);
expect(result).toEqual(mockSection);
});
it('should format "Kod SWIFT" field', () => {
generujRachunekBankowy([mockAccount], 'Rachunek bankowy');
expect(PDFFunctions.formatText).toHaveBeenCalledWith('Kod SWIFT', FormatTyp.GrayBoldTitle);
expect(PDFFunctions.formatText).toHaveBeenCalledWith(mockAccount.SWIFT?._text, FormatTyp.Default);
});
it('should format "Rachunek własny banku" field', () => {
vi.mocked(CommonFunctions.getTypRachunkowWlasnych).mockReturnValue('Tak');
generujRachunekBankowy([mockAccount], 'Rachunek bankowy');
expect(PDFFunctions.formatText).toHaveBeenCalledWith('Rachunek własny banku', FormatTyp.GrayBoldTitle);
expect(CommonFunctions.getTypRachunkowWlasnych).toHaveBeenCalledWith(mockAccount.RachunekWlasnyBanku);
expect(PDFFunctions.formatText).toHaveBeenCalledWith('Tak', FormatTyp.Default);
});
it('should format "Nazwa banku" field', () => {
generujRachunekBankowy([mockAccount], 'Rachunek bankowy');
expect(PDFFunctions.formatText).toHaveBeenCalledWith('Nazwa banku', FormatTyp.GrayBoldTitle);
expect(PDFFunctions.formatText).toHaveBeenCalledWith(mockAccount.NazwaBanku?._text, FormatTyp.Default);
});
it('should create table structure with correct widths', () => {
generujRachunekBankowy([mockAccount], 'Rachunek bankowy');
const sectionCall = vi.mocked(PDFFunctions.createSection).mock.calls[0][0] as any[];
const tableContent = sectionCall[0][1];
expect(tableContent).toHaveProperty('table');
expect(tableContent.table).toHaveProperty('widths', ['*', 'auto']);
});
it('should create table structure with unbreakable property', () => {
generujRachunekBankowy([mockAccount], 'Rachunek bankowy');
const sectionCall = vi.mocked(PDFFunctions.createSection).mock.calls[0][0] as any[];
const tableContent = sectionCall[0][1];
expect(tableContent).toHaveProperty('unbreakable', true);
});
it('should create table with correct layout', () => {
generujRachunekBankowy([mockAccount], 'Rachunek bankowy');
const sectionCall = vi.mocked(PDFFunctions.createSection).mock.calls[0][0] as any[];
const tableContent = sectionCall[0][1];
expect(tableContent).toHaveProperty('layout');
expect(tableContent.layout).toHaveProperty('hLineWidth');
expect(tableContent.layout).toHaveProperty('hLineColor');
expect(tableContent.layout).toHaveProperty('vLineWidth');
expect(tableContent.layout).toHaveProperty('vLineColor');
});
it('should create table with correct line colors', () => {
generujRachunekBankowy([mockAccount], 'Rachunek bankowy');
const sectionCall = vi.mocked(PDFFunctions.createSection).mock.calls[0][0] as any[];
const tableContent = sectionCall[0][1];
expect(tableContent.layout.hLineColor()).toBe('#BABABA');
expect(tableContent.layout.vLineColor()).toBe('#BABABA');
});
it('should create table with correct line widths', () => {
generujRachunekBankowy([mockAccount], 'Rachunek bankowy');
const sectionCall = vi.mocked(PDFFunctions.createSection).mock.calls[0][0] as any[];
const tableContent = sectionCall[0][1];
expect(tableContent.layout.hLineWidth()).toBe(1);
expect(tableContent.layout.vLineWidth()).toBe(1);
});
it('should handle account with undefined fields', () => {
const accountWithUndefined: any = {
NrRB: { _text: undefined },
SWIFT: { _text: undefined },
RachunekWlasnyBanku: { _text: undefined },
NazwaBanku: { _text: undefined },
OpisRachunku: { _text: undefined },
} as any;
generujRachunekBankowy([accountWithUndefined], 'Rachunek bankowy');
expect(PDFFunctions.formatText).toHaveBeenCalledWith(undefined, FormatTyp.Default);
});
});
describe('when multiple accounts exist', () => {
it('should create multiple tables for multiple accounts', () => {
const account2: any = {
NrRB: { _text: '98765432109876543210987654' },
SWIFT: { _text: 'PKOPPLPW' },
RachunekWlasnyBanku: { _text: '0' },
NazwaBanku: { _text: 'mBank' },
OpisRachunku: { _text: 'Rachunek pomocniczy' },
} as any;
generujRachunekBankowy([mockAccount, account2], 'Rachunek bankowy');
const sectionCall = vi.mocked(PDFFunctions.createSection).mock.calls[0][0] as any[];
expect(sectionCall.length).toBe(2);
});
it('should call getTypRachunkowWlasnych for each account', () => {
const account2: any = {
NrRB: { _text: '98765432109876543210987654' },
SWIFT: { _text: 'PKOPPLPW' },
RachunekWlasnyBanku: { _text: '0' },
NazwaBanku: { _text: 'mBank' },
OpisRachunku: { _text: 'Rachunek pomocniczy' },
} as any;
generujRachunekBankowy([mockAccount, account2], 'Rachunek bankowy');
expect(CommonFunctions.getTypRachunkowWlasnych).toHaveBeenCalledTimes(2);
expect(CommonFunctions.getTypRachunkowWlasnych).toHaveBeenCalledWith(mockAccount.RachunekWlasnyBanku);
expect(CommonFunctions.getTypRachunkowWlasnych).toHaveBeenCalledWith(account2.RachunekWlasnyBanku);
});
it('should format all fields for all accounts', () => {
const account2: any = {
NrRB: { _text: '98765432109876543210987654' },
SWIFT: { _text: 'PKOPPLPW' },
RachunekWlasnyBanku: { _text: '0' },
NazwaBanku: { _text: 'mBank' },
OpisRachunku: { _text: 'Rachunek pomocniczy' },
} as any;
generujRachunekBankowy([mockAccount, account2], 'Rachunek bankowy');
expect(PDFFunctions.formatText).toHaveBeenCalledWith('Kod SWIFT', FormatTyp.GrayBoldTitle);
expect(PDFFunctions.formatText).toHaveBeenCalledWith('Kod SWIFT', FormatTyp.GrayBoldTitle);
});
});
describe('table structure', () => {
it('should create table with 5 rows', () => {
generujRachunekBankowy([mockAccount], 'Rachunek bankowy');
const sectionCall = vi.mocked(PDFFunctions.createSection).mock.calls[0][0] as any[];
const tableContent = sectionCall[0][1];
expect(tableContent.table.body.length).toBe(3);
});
it('should include header in result array', () => {
vi.mocked(PDFFunctions.createHeader).mockReturnValue(['header-content'] as any);
generujRachunekBankowy([mockAccount], 'Rachunek bankowy');
const sectionCall = vi.mocked(PDFFunctions.createSection).mock.calls[0][0] as any[];
expect(sectionCall[0][0]).toBe('header-content');
});
});
});

View File

@@ -0,0 +1,74 @@
import { Content, ContentTable } from 'pdfmake/interfaces';
import { createHeader, createSection, formatText } from '../../../shared/PDF-functions';
import FormatTyp from '../../../shared/enums/common.enum';
import { FP } from '../../types/fa1.types';
import { getTypRachunkowWlasnych } from '../../../shared/generators/common/functions';
import { DEFAULT_TABLE_LAYOUT } from '../../../shared/consts/const';
export const generujRachunekBankowy: (accounts?: Record<string, FP>[], title?: string) => Content[] = (
accounts?: Record<string, FP>[],
title?: string
): Content[] => {
const result: Content[] = [];
if (!accounts?.length) {
return [];
}
accounts.forEach((account: Record<string, FP>, index: number): void => {
const table: Content[][] = [];
const base: Content[] = createHeader(
title ? `${title} ${accounts?.length > 1 ? ++index : ''}` : '',
[0, 12, 0, 8]
);
if (account.NrRBZagr?._text) {
table.push([
formatText('Format rachunku', FormatTyp.GrayBoldTitle),
formatText('Zagraniczny', FormatTyp.Default),
]);
} else if (account.NrRBPL?._text) {
table.push([
formatText('Format rachunku', FormatTyp.GrayBoldTitle),
formatText('Polski', FormatTyp.Default),
]);
}
if (account.NrRBPL?._text) {
table.push([
formatText('Pełny numer rachunku w standardzie NRB', FormatTyp.GrayBoldTitle),
formatText(account.NrRBPL?._text, FormatTyp.Default),
]);
}
if (account.NrRBZagr?._text) {
table.push([
formatText('Pełny numer rachunku zagranicznego', FormatTyp.GrayBoldTitle),
formatText(account.NrRBZagr?._text, FormatTyp.Default),
]);
}
table.push([
formatText('Kod SWIFT', FormatTyp.GrayBoldTitle),
formatText(account.SWIFT?._text, FormatTyp.Default),
]);
table.push([
formatText('Rachunek własny banku', FormatTyp.GrayBoldTitle),
formatText(getTypRachunkowWlasnych(account.RachunekWlasnyBanku), FormatTyp.Default),
]);
table.push([
formatText('Nazwa banku', FormatTyp.GrayBoldTitle),
formatText(account.NazwaBanku?._text, FormatTyp.Default),
]);
result.push([
...base,
{
unbreakable: true,
table: {
body: table,
widths: ['*', 'auto'],
},
layout: DEFAULT_TABLE_LAYOUT,
} as ContentTable,
]);
});
return createSection(result, false);
};

View File

@@ -0,0 +1,170 @@
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { generateSzczegoly } from './Szczegoly';
import * as PDFFunctions from '../../../shared/PDF-functions';
import FormatTyp from '../../../shared/enums/common.enum';
import { TRodzajFaktury } from '../../../shared/consts/const';
import { Fa } from '../../types/fa1.types';
vi.mock('../../../shared/PDF-functions', () => ({
createHeader: vi.fn(),
createLabelText: vi.fn(),
createLabelTextArray: vi.fn(),
createSection: vi.fn(),
generateTwoColumns: vi.fn(),
getContentTable: vi.fn(),
getDifferentColumnsValue: vi.fn(),
getTable: vi.fn(),
getValue: vi.fn(),
hasColumnsValue: vi.fn(),
hasValue: vi.fn(),
}));
describe(generateSzczegoly.name, () => {
let mockFaVat: Fa;
beforeEach(() => {
vi.clearAllMocks();
mockFaVat = {
FaWiersze: [],
Zamowienie: { ZamowienieWiersz: [] },
RodzajFaktury: TRodzajFaktury.VAT,
OkresFa: { P_6_Od: { _text: '' }, P_6_Do: { _text: '' } },
P_1: { _text: '2024-01-01' },
P_1M: { _text: 'Warsaw' },
OkresFaKorygowanej: { _text: '' },
P_6: { _text: '2024-01-15' },
KodWaluty: { _text: 'PLN' },
} as any;
vi.mocked(PDFFunctions.getTable).mockReturnValue([]);
vi.mocked(PDFFunctions.createHeader).mockReturnValue(['header'] as any);
vi.mocked(PDFFunctions.createLabelText).mockReturnValue('label' as any);
vi.mocked(PDFFunctions.createLabelTextArray).mockReturnValue('labelArray' as any);
vi.mocked(PDFFunctions.createSection).mockReturnValue('section' as any);
vi.mocked(PDFFunctions.generateTwoColumns).mockReturnValue('columns' as any);
vi.mocked(PDFFunctions.getContentTable).mockReturnValue({ content: null, fieldsWithValue: [] });
vi.mocked(PDFFunctions.getDifferentColumnsValue).mockReturnValue([]);
vi.mocked(PDFFunctions.getValue).mockReturnValue('PLN');
vi.mocked(PDFFunctions.hasColumnsValue).mockReturnValue(false);
vi.mocked(PDFFunctions.hasValue).mockReturnValue(false);
});
it('should create header and section', () => {
const result = generateSzczegoly(mockFaVat);
expect(PDFFunctions.createHeader).toHaveBeenCalledWith('Szczegóły');
expect(PDFFunctions.createSection).toHaveBeenCalledWith(expect.any(Array), true);
expect(result).toEqual('section');
});
it('should call getTable for FaWiersze and ZamowienieWiersz', () => {
generateSzczegoly(mockFaVat);
expect(PDFFunctions.getTable).toHaveBeenCalledWith(mockFaVat.FaWiersze?.FaWiersz);
expect(PDFFunctions.getTable).toHaveBeenCalledWith(mockFaVat.Zamowienie?.ZamowienieWiersz);
});
describe('P_6 label', () => {
it('uses "Data otrzymania zapłaty" for ZAL', () => {
generateSzczegoly({ ...mockFaVat, RodzajFaktury: TRodzajFaktury.ZAL } as any);
expect(PDFFunctions.createLabelText).toHaveBeenCalledWith('Data otrzymania zapłaty: ', mockFaVat.P_6);
});
it('uses "Data dokonania lub zakończenia dostawy" for VAT', () => {
generateSzczegoly(mockFaVat);
expect(PDFFunctions.createLabelText).toHaveBeenCalledWith(
'Data dokonania lub zakończenia dostawy towarów lub wykonania usługi: ',
mockFaVat.P_6
);
});
});
describe('P_6 scope', () => {
it('creates scope array when P_6_Od and P_6_Do exist', () => {
const data = {
...mockFaVat,
OkresFa: { P_6_Od: { _text: '2024-01-01' }, P_6_Do: { _text: '2024-01-31' } },
} as any;
vi.mocked(PDFFunctions.hasValue).mockImplementation(
(value: any) => value === data.OkresFa.P_6_Od || value === data.OkresFa.P_6_Do
);
generateSzczegoly(data);
expect(PDFFunctions.createLabelTextArray).toHaveBeenCalledWith([
{ value: 'Data dokonania lub zakończenia dostawy towarów lub wykonania usługi: od ' },
{ value: data.OkresFa.P_6_Od, formatTyp: FormatTyp.Value },
{ value: ' do ' },
{ value: data.OkresFa.P_6_Do, formatTyp: FormatTyp.Value },
]);
});
});
describe('ceny labels', () => {
it('adds netto label when P_11 exists', () => {
vi.mocked(PDFFunctions.hasColumnsValue).mockImplementation((col) => col === 'P_11');
generateSzczegoly(mockFaVat);
expect(PDFFunctions.createLabelText).toHaveBeenCalledWith('Faktura wystawiona w cenach: ', 'netto');
});
it('adds brutto label when P_11 does not exist', () => {
vi.mocked(PDFFunctions.hasColumnsValue).mockReturnValue(false);
generateSzczegoly(mockFaVat);
expect(PDFFunctions.createLabelText).toHaveBeenCalledWith('Faktura wystawiona w cenach: ', 'brutto');
});
it('adds currency label', () => {
generateSzczegoly(mockFaVat);
expect(PDFFunctions.createLabelText).toHaveBeenCalledWith('Kod waluty: ', mockFaVat.KodWaluty);
});
});
describe('P_12_XII label', () => {
it('adds OSS label when P_12_XII exists', () => {
vi.mocked(PDFFunctions.hasColumnsValue).mockImplementation((col) => col === 'P_12_XII');
generateSzczegoly(mockFaVat);
expect(PDFFunctions.createLabelText).toHaveBeenCalledWith('Procedura One Stop Shop', ' ');
});
});
describe('kurs waluty', () => {
it('adds currency rate when currency is not PLN and common rate exists', () => {
const data = { ...mockFaVat, KodWaluty: { _text: 'EUR' }, Zamowienie: { ZamowienieWiersz: [] } } as any;
vi.mocked(PDFFunctions.hasValue).mockReturnValue(true);
vi.mocked(PDFFunctions.getValue).mockReturnValue('EUR');
vi.mocked(PDFFunctions.getDifferentColumnsValue).mockReturnValue([{ value: '4.50' }]);
generateSzczegoly(data);
expect(PDFFunctions.createLabelText).toHaveBeenCalledWith(
'Kurs waluty wspólny dla wszystkich wierszy faktury',
' '
);
expect(PDFFunctions.createLabelText).toHaveBeenCalledWith('Kurs waluty: ', '4.50', FormatTyp.Currency6);
});
it('does not add currency rate when PLN', () => {
vi.mocked(PDFFunctions.getValue).mockReturnValue('PLN');
generateSzczegoly(mockFaVat);
const calls = vi.mocked(PDFFunctions.createLabelText).mock.calls;
const currencyCall = calls.find((call) => call[0] === 'Kurs waluty: ');
expect(currencyCall).toBeUndefined();
});
});
describe('columns distribution', () => {
it('calls generateTwoColumns with labels split', () => {
generateSzczegoly(mockFaVat);
expect(PDFFunctions.generateTwoColumns).toHaveBeenCalledWith(expect.any(Array), expect.any(Array));
});
});
describe('faktura zaliczkowa', () => {
it('generates table when data exists', () => {
const data = { ...mockFaVat, NrFaZaliczkowej: [{ _text: 'FA001' }] } as any;
vi.mocked(PDFFunctions.getTable).mockReturnValueOnce([{ _text: 'FA001' }] as any);
generateSzczegoly(data);
expect(PDFFunctions.getContentTable).toHaveBeenCalled();
});
});
});

View File

@@ -0,0 +1,168 @@
import { Content } from 'pdfmake/interfaces';
import {
createHeader,
createLabelText,
createLabelTextArray,
createSection,
generateTwoColumns,
getContentTable,
getDifferentColumnsValue,
getTable,
getValue,
hasColumnsValue,
hasValue,
} from '../../../shared/PDF-functions';
import { HeaderDefine } from '../../../shared/types/pdf-types';
import { TRodzajFaktury } from '../../../shared/consts/const';
import { Fa, FP } from '../../types/fa1.types';
import { DifferentValues, TypesOfValues } from '../../../shared/types/universal.types';
import FormatTyp from '../../../shared/enums/common.enum';
import { TableWithFields } from '../../types/fa1-additional-types';
export function generateSzczegoly(faVat: Fa): Content[] {
const faWiersze: Record<string, FP>[] = getTable(faVat.FaWiersze?.FaWiersz);
const zamowieniaWiersze: Record<string, FP>[] = getTable(faVat.Zamowienie?.ZamowienieWiersz);
const LabelP_6: string =
faVat.RodzajFaktury == TRodzajFaktury.ZAL || faVat.RodzajFaktury == TRodzajFaktury.KOR_ZAL
? 'Data otrzymania zapłaty: '
: 'Data dokonania lub zakończenia dostawy towarów lub wykonania usługi: ';
const P_6Scope: Content[] = generateP_6Scope(faVat.OkresFa?.P_6_Od, faVat.OkresFa?.P_6_Do);
const cenyLabel1: Content[] = [];
const cenyLabel2: Content[] = [];
if (!(faWiersze.length > 0 || zamowieniaWiersze.length > 0)) {
const Any_P_11: boolean =
hasColumnsValue('P_11', faWiersze) || hasColumnsValue('P_11', zamowieniaWiersze);
if (Any_P_11) {
cenyLabel1.push(createLabelText('Faktura wystawiona w cenach: ', 'netto'));
} else {
cenyLabel1.push(createLabelText('Faktura wystawiona w cenach: ', 'brutto'));
}
cenyLabel2.push(createLabelText('Kod waluty: ', faVat.KodWaluty));
}
const P_12_XIILabel: Content[] = [];
if (hasColumnsValue('P_12_XII', faWiersze) || hasColumnsValue('P_12_XII', zamowieniaWiersze)) {
P_12_XIILabel.push(createLabelText('Procedura One Stop Shop', ' '));
}
const kodWalutyLabel1: Content[] = [];
const kodWalutyLabel2: Content[] = [];
if (hasValue(faVat.KodWaluty) && getValue(faVat.KodWaluty) != 'PLN') {
if (faVat.Zamowienie?.ZamowienieWiersz?.length) {
const Common_KursWaluty: DifferentValues[] = getDifferentColumnsValue(
'KursWalutyZ',
faVat.Zamowienie?.ZamowienieWiersz
);
if (Common_KursWaluty.length === 1) {
kodWalutyLabel1.push(createLabelText('Kurs waluty wspólny dla wszystkich wierszy faktury', ' '));
kodWalutyLabel2.push(
createLabelText('Kurs waluty: ', Common_KursWaluty[0].value, FormatTyp.Currency6)
);
}
} else {
const Common_KursWaluty: DifferentValues[] = getDifferentColumnsValue('KursWaluty', faWiersze);
if (Common_KursWaluty.length === 1) {
kodWalutyLabel1.push(createLabelText('Kurs waluty wspólny dla wszystkich wierszy faktury', ' '));
kodWalutyLabel2.push(
createLabelText('Kurs waluty: ', Common_KursWaluty[0].value, FormatTyp.Currency6)
);
}
}
}
const tpLabel1: Content[] = [];
const tpLabel2: Content[] = [];
const forColumns: Content[][] = [
createLabelText('Numer faktury: ', faVat.P_2),
createLabelText('Data wystawienia, z zastrzeżeniem art. 106na ust. 1 ustawy: ', faVat.P_1),
createLabelText('Miejsce wystawienia: ', faVat.P_1M),
createLabelText('Okres, którego dotyczy rabat: ', faVat.OkresFaKorygowanej),
createLabelText(LabelP_6, faVat.P_6),
P_6Scope,
cenyLabel1,
cenyLabel2,
P_12_XIILabel,
kodWalutyLabel1,
kodWalutyLabel2,
tpLabel1,
tpLabel2,
].filter((el: Content[]): boolean => el.length > 0);
const columns1: Content[] = [];
const columns2: Content[] = [];
forColumns.forEach((tab: Content[], index: number): void => {
if (index % 2) {
columns2.push(tab);
} else {
columns1.push(tab);
}
});
const table: Content[] = [
...createHeader('Szczegóły'),
generateTwoColumns(columns1, columns2),
...generateFakturaZaliczkowa(getTable(faVat.NrFaZaliczkowej)),
];
return createSection(table, true);
}
function generateP_6Scope(P_6_Od: TypesOfValues, P_6_Do: TypesOfValues): Content[] {
const table: Content[] = [];
if (hasValue(P_6_Od) && hasValue(P_6_Do)) {
table.push(
createLabelTextArray([
{
value: 'Data dokonania lub zakończenia dostawy towarów lub wykonania usługi: od ',
},
{ value: P_6_Od, formatTyp: FormatTyp.Value },
{ value: ' do ' },
{ value: P_6_Do, formatTyp: FormatTyp.Value },
])
);
} else if (hasValue(P_6_Od)) {
table.push(
createLabelText('Data dokonania lub zakończenia dostawy towarów lub wykonania usługi: od ', P_6_Od)
);
} else if (hasValue(P_6_Do)) {
table.push(
createLabelText('Data dokonania lub zakończenia dostawy towarów lub wykonania usługi: do ', P_6_Do)
);
}
return table;
}
function generateFakturaZaliczkowa(fakturaZaliczkowa: FP[] | undefined): Content[] {
if (!fakturaZaliczkowa) {
return [];
}
const table: Content[] = [];
const fakturaZaliczkowaHeader: HeaderDefine[] = [
{
name: '',
title: 'Numery wcześniejszych faktur zaliczkowych',
format: FormatTyp.Default,
},
];
const tableFakturaZaliczkowa: TableWithFields = getContentTable<(typeof fakturaZaliczkowa)[0]>(
fakturaZaliczkowaHeader,
fakturaZaliczkowa,
'50%',
[0, 4, 0, 0]
);
if (tableFakturaZaliczkowa.content) {
table.push(tableFakturaZaliczkowa.content);
}
return table;
}

View File

@@ -0,0 +1,170 @@
import { beforeEach, describe, expect, it, vi } from 'vitest';
import { generateTransport } from './Transport';
import * as PDFFunctions from '../../../shared/PDF-functions';
import * as PrzewoznikModule from './Przewoznik';
import * as CommonFunctions from '../../../shared/generators/common/functions';
vi.mock('../../../shared/PDF-functions', () => ({
createHeader: vi.fn(),
createLabelText: vi.fn(),
createSection: vi.fn(),
createSubHeader: vi.fn(),
generateTwoColumns: vi.fn(),
getTable: vi.fn(),
hasValue: vi.fn(),
}));
vi.mock('./Przewoznik', () => ({
generatePrzewoznik: vi.fn(),
}));
vi.mock('../../../shared/generators/common/functions', () => ({
getDateTimeWithoutSeconds: vi.fn(),
getRodzajTransportuString: vi.fn(),
getOpisTransportuString: vi.fn(),
}));
describe(generateTransport.name, () => {
let mockTransport: any;
beforeEach(() => {
vi.clearAllMocks();
mockTransport = {
RodzajTransportu: { _text: '1' },
TransportInny: { _text: '0' },
OpisInnegoTransportu: { _text: '' },
NrZleceniaTransportu: { _text: 'TR001' },
OpisLadunku: { _text: 'Goods' },
LadunekInny: { _text: '0' },
OpisInnegoLadunku: { _text: '' },
JednostkaOpakowania: { _text: 'Box' },
DataGodzRozpTransportu: { _text: '2024-01-01T10:00:00' },
DataGodzZakTransportu: { _text: '2024-01-02T15:00:00' },
Przewoznik: {},
WysylkaZ: {
AdresL1: { _text: 'Street 1' },
AdresL2: { _text: 'City 1' },
KodKraju: { _text: 'PL' },
GLN: { _text: '123456' },
},
WysylkaDo: {
AdresL1: { _text: 'Street 2' },
AdresL2: { _text: 'City 2' },
KodKraju: { _text: 'DE' },
GLN: { _text: '789012' },
},
WysylkaPrzez: [],
};
vi.mocked(PDFFunctions.createHeader).mockReturnValue('header' as any);
vi.mocked(PDFFunctions.createLabelText).mockReturnValue('label' as any);
vi.mocked(PDFFunctions.createSection).mockReturnValue('section' as any);
vi.mocked(PDFFunctions.createSubHeader).mockReturnValue('subheader' as any);
vi.mocked(PDFFunctions.generateTwoColumns).mockReturnValue('columns' as any);
vi.mocked(PDFFunctions.getTable).mockReturnValue([]);
vi.mocked(PDFFunctions.hasValue).mockReturnValue(true);
vi.mocked(PrzewoznikModule.generatePrzewoznik).mockReturnValue('przewoznik' as any);
vi.mocked(CommonFunctions.getDateTimeWithoutSeconds).mockReturnValue('2024-01-01 10:00');
vi.mocked(CommonFunctions.getRodzajTransportuString).mockReturnValue('Road');
vi.mocked(CommonFunctions.getOpisTransportuString).mockReturnValue('Opis');
});
it('should return a section', () => {
const result = generateTransport(mockTransport);
expect(PDFFunctions.createSection).toHaveBeenCalledWith(expect.any(Array), true);
expect(result).toBe('section');
});
it('should handle RodzajTransportu', () => {
generateTransport(mockTransport);
expect(CommonFunctions.getRodzajTransportuString).toHaveBeenCalledWith(mockTransport.RodzajTransportu);
expect(PDFFunctions.createLabelText).toHaveBeenCalledWith('Rodzaj transportu: ', 'Road');
});
it('should handle TransportInny', () => {
const data = {
...mockTransport,
RodzajTransportu: { _text: '' },
TransportInny: { _text: '1' },
OpisInnegoTransportu: { _text: 'Custom transport' },
};
generateTransport(data);
expect(PDFFunctions.createLabelText).toHaveBeenCalledWith('Rodzaj transportu: ', 'Transport inny');
expect(PDFFunctions.createLabelText).toHaveBeenCalledWith('Rodzaj transportu: ', 'Transport inny');
});
it('should handle Dane transportu', () => {
generateTransport(mockTransport);
expect(PDFFunctions.createLabelText).toHaveBeenCalledWith(
'Numer zlecenia transportu: ',
mockTransport.NrZleceniaTransportu
);
expect(PDFFunctions.createLabelText).toHaveBeenCalledWith('Opis ładunku: ', 'Opis');
expect(PDFFunctions.createLabelText).toHaveBeenCalledWith(
'Jednostka opakowania: ',
mockTransport.JednostkaOpakowania
);
expect(PDFFunctions.createLabelText).toHaveBeenCalledWith(
'Data i godzina rozpoczęcia transportu: ',
'2024-01-01 10:00'
);
expect(PDFFunctions.createLabelText).toHaveBeenCalledWith(
'Data i godzina zakończenia transportu: ',
'2024-01-01 10:00'
);
expect(PDFFunctions.createSubHeader).toHaveBeenCalledWith('Dane transportu', [0, 0, 0, 0]);
});
it('should call generatePrzewoznik', () => {
generateTransport(mockTransport);
expect(PrzewoznikModule.generatePrzewoznik).toHaveBeenCalledWith(mockTransport.Przewoznik);
});
it('should handle WysylkaZ and WysylkaDo', () => {
generateTransport(mockTransport);
expect(PDFFunctions.createSubHeader).toHaveBeenCalledWith('Adres miejsca wysyłki', [0, 0, 0, 0]);
expect(PDFFunctions.createSubHeader).toHaveBeenCalledWith(
'Adres miejsca docelowego, do którego został zlecony transport',
[0, 0, 0, 0]
);
});
it('should handle WysylkaPrzez', () => {
const data = {
...mockTransport,
WysylkaPrzez: [
{
AdresL1: { _text: 'Street 3' },
AdresL2: { _text: 'City 3' },
KodKraju: { _text: 'FR' },
GLN: { _text: '345678' },
},
],
};
vi.mocked(PDFFunctions.getTable).mockReturnValue(data.WysylkaPrzez as any);
generateTransport(data);
expect(PDFFunctions.createSubHeader).toHaveBeenCalledWith('Adres pośredni wysyłki', [0, 4, 0, 0]);
});
it('should call generateTwoColumns for main sections and wysylka', () => {
generateTransport(mockTransport);
expect(PDFFunctions.generateTwoColumns).toHaveBeenCalled();
});
it('should add header for Transport and Wysyłka', () => {
generateTransport(mockTransport);
expect(PDFFunctions.createHeader).toHaveBeenCalledWith('Transport');
expect(PDFFunctions.createHeader).toHaveBeenCalledWith('Wysyłka');
});
it('should handle full integration with all fields', () => {
const data = {
...mockTransport,
LadunekInny: { _text: '1' },
OpisInnegoLadunku: { _text: 'Custom cargo' },
};
generateTransport(data);
expect(PDFFunctions.createLabelText).toHaveBeenCalledWith('Opis ładunku: ', 'Ładunek inny');
expect(PDFFunctions.createLabelText).toHaveBeenCalledWith('Rodzaj transportu: ', 'Road');
});
});

View File

@@ -0,0 +1,97 @@
import { Content } from 'pdfmake/interfaces';
import {
createHeader,
createLabelText,
createSection,
createSubHeader,
generateTwoColumns,
getTable,
hasValue,
} from '../../../shared/PDF-functions';
import { Transport } from '../../types/fa1.types';
import {
getDateTimeWithoutSeconds,
getOpisTransportuString,
getRodzajTransportuString,
} from '../../../shared/generators/common/functions';
import { generateAdres } from './Adres';
import { generatePrzewoznik } from './Przewoznik';
export function generateTransport(transport: Transport, index?: number | null): Content {
const table: Content[] = [];
const columns = {
transport: [] as Content[],
dane: [] as Content[],
wysylkaZ: [] as Content[],
wysylkaDo: [] as Content[],
wysylkaPrzez: [] as Content[],
};
table.push(createHeader(index ? `Transport ${index}` : 'Transport'));
if (transport.RodzajTransportu?._text) {
columns.transport.push(
createLabelText('Rodzaj transportu: ', getRodzajTransportuString(transport.RodzajTransportu))
);
} else if (transport.TransportInny?._text == '1' && transport.OpisInnegoTransportu?._text) {
columns.transport.push(createLabelText('Rodzaj transportu: ', 'Transport inny'));
columns.transport.push(
createLabelText('Opis innego rodzaju transportu: ', transport.OpisInnegoTransportu)
);
}
columns.dane.push(createLabelText('Numer zlecenia transportu: ', transport.NrZleceniaTransportu));
if (hasValue(transport.OpisLadunku)) {
columns.dane.push(createLabelText('Opis ładunku: ', getOpisTransportuString(transport.OpisLadunku)));
if (transport.LadunekInny?._text === '1' && transport.OpisInnegoLadunku?._text) {
columns.dane.push(createLabelText('Opis ładunku: ', 'Ładunek inny'));
columns.dane.push(createLabelText('Opis innego ładunku: ', transport.OpisInnegoLadunku));
}
}
columns.dane.push(createLabelText('Jednostka opakowania: ', transport.JednostkaOpakowania));
columns.dane.push(
createLabelText(
'Data i godzina rozpoczęcia transportu: ',
getDateTimeWithoutSeconds(transport.DataGodzRozpTransportu)
)
);
columns.dane.push(
createLabelText(
'Data i godzina zakończenia transportu: ',
getDateTimeWithoutSeconds(transport.DataGodzZakTransportu)
)
);
if (columns.dane.length > 0) {
columns.dane.unshift(createSubHeader('Dane transportu', [0, 0, 0, 0]));
}
table.push(generateTwoColumns(columns.transport, columns.dane));
table.push(generatePrzewoznik(transport.Przewoznik));
if (transport.WysylkaZ) {
columns.wysylkaZ.push(createSubHeader('Adres miejsca wysyłki', [0, 0, 0, 0]));
columns.wysylkaZ.push(generateAdres(transport.WysylkaZ));
}
if (transport.WysylkaDo) {
columns.wysylkaDo.push(
createSubHeader('Adres miejsca docelowego, do którego został zlecony transport', [0, 0, 0, 0])
);
columns.wysylkaDo.push(generateAdres(transport.WysylkaDo));
}
const wysylkaPrzez = getTable(transport.WysylkaPrzez);
wysylkaPrzez.forEach((adres, index) => {
if (index) {
columns.wysylkaPrzez.push('\n');
}
columns.wysylkaPrzez.push(createSubHeader('Adres pośredni wysyłki', [0, 4, 0, 0]));
columns.wysylkaPrzez.push(generateAdres(adres));
});
if (transport.WysylkaZ || transport.WysylkaDo || transport.WysylkaPrzez?.length) {
table.push(createHeader('Wysyłka'));
table.push(generateTwoColumns(columns.wysylkaZ, columns.wysylkaDo));
table.push(generateTwoColumns(columns.wysylkaPrzez, []));
}
return createSection(table, true);
}

View File

@@ -0,0 +1,517 @@
import { beforeEach, describe, expect, it, vi } from 'vitest';
import { generateWarunkiTransakcji } from './WarunkiTransakcji';
import * as PDFFunctions from '../../../shared/PDF-functions';
import FormatTyp from '../../../shared/enums/common.enum';
import { WarunkiTransakcji } from '../../types/fa1.types';
import * as TransportModule from './Transport';
vi.mock('../../../shared/PDF-functions', () => ({
createHeader: vi.fn(),
createLabelText: vi.fn(),
createSection: vi.fn(),
createSubHeader: vi.fn(),
formatText: vi.fn(),
generateTwoColumns: vi.fn(),
getContentTable: vi.fn(),
getTable: vi.fn(),
}));
vi.mock('./Transport', () => ({
generateTransport: vi.fn(),
}));
describe(generateWarunkiTransakcji.name, () => {
beforeEach(() => {
vi.clearAllMocks();
});
describe('when warunkiTransakcji is undefined', () => {
it('should return empty array', () => {
const result = generateWarunkiTransakcji(undefined);
expect(result).toEqual([]);
});
});
describe('when warunkiTransakcji is defined', () => {
const mockWarunkiTransakcji: WarunkiTransakcji = {
Umowy: [],
Zamowienia: [],
NrPartiiTowaru: [],
WalutaUmowna: { _text: '' },
KursUmowny: { _text: '' },
WarunkiDostawy: { _text: '' },
PodmiotPosredniczacy: { _text: '0' },
} as any;
beforeEach(() => {
vi.mocked(PDFFunctions.getTable).mockReturnValue([]);
vi.mocked(PDFFunctions.createHeader).mockReturnValue('header' as any);
vi.mocked(PDFFunctions.createSection).mockReturnValue('section' as any);
vi.mocked(PDFFunctions.createLabelText).mockReturnValue('label' as any);
vi.mocked(PDFFunctions.getContentTable).mockReturnValue({
content: null,
fieldsWithValue: [],
});
});
it('should call createHeader with "Warunki transakcji"', () => {
generateWarunkiTransakcji(mockWarunkiTransakcji);
expect(PDFFunctions.createHeader).toHaveBeenCalledWith('Warunki transakcji', [0, 8, 0, 4]);
});
it('should call getTable for Umowy, Zamowienia and NrPartiiTowaru', () => {
const data = {
...mockWarunkiTransakcji,
Umowy: [{ DataUmowy: { _text: '2024-01-01' } }],
Zamowienia: [{ DataZamowienia: { _text: '2024-01-02' } }],
NrPartiiTowaru: [{ _text: 'BATCH001' }],
} as any;
generateWarunkiTransakcji(data);
expect(PDFFunctions.getTable).toHaveBeenCalledWith(data.Umowy);
expect(PDFFunctions.getTable).toHaveBeenCalledWith(data.Zamowienia);
expect(PDFFunctions.getTable).toHaveBeenCalledWith(data.NrPartiiTowaru);
});
it('should call createSection with table array', () => {
generateWarunkiTransakcji(mockWarunkiTransakcji);
expect(PDFFunctions.createSection).toHaveBeenCalledWith(expect.any(Array), true);
});
it('should return result from createSection', () => {
const mockSection = { section: 'test' };
vi.mocked(PDFFunctions.createSection).mockReturnValue(mockSection as any);
const result = generateWarunkiTransakcji(mockWarunkiTransakcji);
expect(result).toEqual(mockSection);
});
describe('umowy section', () => {
it('should create umowy section when umowy exist', () => {
const data = {
...mockWarunkiTransakcji,
Umowy: [{ DataUmowy: { _text: '2024-01-01' }, NrUmowy: { _text: 'U001' } }],
} as any;
vi.mocked(PDFFunctions.getTable).mockImplementation((field: any) => {
if (field === data.Umowy)
return [
{
DataUmowy: { _text: '2024-01-01' },
NrUmowy: { _text: 'U001' },
},
] as any;
return [];
});
vi.mocked(PDFFunctions.getContentTable).mockReturnValueOnce({
content: { table: {} } as any,
fieldsWithValue: ['DataUmowy', 'NrUmowy'],
});
vi.mocked(PDFFunctions.createSubHeader).mockReturnValue('subheader' as any);
generateWarunkiTransakcji(data);
expect(PDFFunctions.createSubHeader).toHaveBeenCalledWith('Umowa');
expect(PDFFunctions.getContentTable).toHaveBeenCalledWith(
expect.arrayContaining([
expect.objectContaining({ name: 'DataUmowy', title: 'Data umowy' }),
expect.objectContaining({ name: 'NrUmowy', title: 'Numer umowy' }),
]),
expect.any(Array),
'*'
);
});
it('should not create umowy section when umowy are empty', () => {
vi.mocked(PDFFunctions.getTable).mockReturnValue([]);
vi.mocked(PDFFunctions.createSubHeader).mockClear();
generateWarunkiTransakcji(mockWarunkiTransakcji);
expect(PDFFunctions.createSubHeader).not.toHaveBeenCalledWith('Umowa');
});
});
describe('zamowienia section', () => {
it('should create zamowienia section when zamowienia exist', () => {
const data = {
...mockWarunkiTransakcji,
Zamowienia: [{ DataZamowienia: { _text: '2024-01-02' }, NrZamowienia: { _text: 'Z001' } }],
} as any;
vi.mocked(PDFFunctions.getTable).mockImplementation((field: any) => {
if (field === data.Zamowienia)
return [
{
DataZamowienia: { _text: '2024-01-02' },
NrZamowienia: { _text: 'Z001' },
},
] as any;
return [];
});
vi.mocked(PDFFunctions.getContentTable).mockReturnValueOnce({
content: { table: {} } as any,
fieldsWithValue: ['DataZamowienia', 'NrZamowienia'],
});
vi.mocked(PDFFunctions.createSubHeader).mockReturnValue('subheader' as any);
generateWarunkiTransakcji(data);
expect(PDFFunctions.createSubHeader).toHaveBeenCalledWith('Zamówienie');
expect(PDFFunctions.getContentTable).toHaveBeenCalledWith(
expect.arrayContaining([
expect.objectContaining({ name: 'DataZamowienia', title: 'Data zamówienia' }),
expect.objectContaining({ name: 'NrZamowienia', title: 'Numer zamówienia' }),
]),
expect.any(Array),
'*'
);
});
it('should not create zamowienia section when zamowienia are empty', () => {
vi.mocked(PDFFunctions.getTable).mockReturnValue([]);
vi.mocked(PDFFunctions.createSubHeader).mockClear();
generateWarunkiTransakcji(mockWarunkiTransakcji);
expect(PDFFunctions.createSubHeader).not.toHaveBeenCalledWith('Zamówienie');
});
it('should not create zamowienia section when fieldsWithValue is empty', () => {
const data = {
...mockWarunkiTransakcji,
Zamowienia: [{ DataZamowienia: { _text: '' } }],
} as any;
vi.mocked(PDFFunctions.getTable).mockImplementation((field: any) => {
if (field === data.Zamowienia) return [{ DataZamowienia: { _text: '' } }] as any;
return [];
});
vi.mocked(PDFFunctions.getContentTable).mockReturnValueOnce({
content: { table: {} } as any,
fieldsWithValue: [],
});
generateWarunkiTransakcji(data);
expect(PDFFunctions.generateTwoColumns).not.toHaveBeenCalled();
});
});
describe('two columns generation', () => {
it('should generate two columns when umowy exist', () => {
const data = {
...mockWarunkiTransakcji,
Umowy: [{ DataUmowy: { _text: '2024-01-01' } }],
} as any;
vi.mocked(PDFFunctions.getTable).mockImplementation((field: any) => {
if (field === data.Umowy) return [{ DataUmowy: { _text: '2024-01-01' } }] as any;
return [];
});
vi.mocked(PDFFunctions.getContentTable).mockReturnValueOnce({
content: { table: 'umowy' } as any,
fieldsWithValue: ['DataUmowy'],
});
vi.mocked(PDFFunctions.createSubHeader).mockReturnValue('subheader' as any);
vi.mocked(PDFFunctions.generateTwoColumns).mockReturnValue('twoColumns' as any);
generateWarunkiTransakcji(data);
expect(PDFFunctions.generateTwoColumns).toHaveBeenCalledWith(
expect.arrayContaining(['subheader', { table: 'umowy' }]),
[]
);
});
it('should generate two columns when zamowienia exist', () => {
const data = {
...mockWarunkiTransakcji,
Zamowienia: [{ DataZamowienia: { _text: '2024-01-02' } }],
} as any;
vi.mocked(PDFFunctions.getTable).mockImplementation((field: any) => {
if (field === data.Zamowienia) return [{ DataZamowienia: { _text: '2024-01-02' } }] as any;
return [];
});
vi.mocked(PDFFunctions.getContentTable).mockReturnValueOnce({
content: { table: 'zamowienia' } as any,
fieldsWithValue: ['DataZamowienia'],
});
vi.mocked(PDFFunctions.createSubHeader).mockReturnValue('subheader' as any);
vi.mocked(PDFFunctions.generateTwoColumns).mockReturnValue('twoColumns' as any);
generateWarunkiTransakcji(data);
expect(PDFFunctions.generateTwoColumns).toHaveBeenCalledWith(
[],
expect.arrayContaining(['subheader', { table: 'zamowienia' }])
);
});
it('should not generate two columns when both umowy and zamowienia are empty', () => {
vi.mocked(PDFFunctions.getTable).mockReturnValue([]);
generateWarunkiTransakcji(mockWarunkiTransakcji);
expect(PDFFunctions.generateTwoColumns).not.toHaveBeenCalled();
});
});
describe('waluta umowna section', () => {
it('should create waluta umowna section when WalutaUmowna exists', () => {
const data = {
...mockWarunkiTransakcji,
WalutaUmowna: { _text: 'EUR' },
KursUmowny: { _text: '' },
} as any;
generateWarunkiTransakcji(data);
expect(PDFFunctions.createHeader).toHaveBeenCalledWith('Waluta umowna i kurs umowny', [0, 8, 0, 4]);
expect(PDFFunctions.createLabelText).toHaveBeenCalledWith('Waluta umowna: ', data.WalutaUmowna);
expect(PDFFunctions.createLabelText).toHaveBeenCalledWith('Kurs umowny: ', data.KursUmowny);
});
it('should create waluta umowna section when KursUmowny exists', () => {
const data = {
...mockWarunkiTransakcji,
WalutaUmowna: { _text: '' },
KursUmowny: { _text: '4.50' },
} as any;
generateWarunkiTransakcji(data);
expect(PDFFunctions.createHeader).toHaveBeenCalledWith('Waluta umowna i kurs umowny', [0, 8, 0, 4]);
});
it('should not create waluta umowna section when both are empty', () => {
const data = {
...mockWarunkiTransakcji,
WalutaUmowna: { _text: '' },
KursUmowny: { _text: '' },
} as any;
vi.mocked(PDFFunctions.createHeader).mockClear();
generateWarunkiTransakcji(data);
expect(PDFFunctions.createHeader).toHaveBeenCalledTimes(1);
expect(PDFFunctions.createHeader).toHaveBeenCalledWith('Warunki transakcji', [0, 8, 0, 4]);
});
});
describe('partia towaru section', () => {
it('should create partia towaru section when NrPartiiTowaru exists', () => {
const data = {
...mockWarunkiTransakcji,
NrPartiiTowaru: [{ _text: 'BATCH001' }],
} as any;
vi.mocked(PDFFunctions.getTable).mockImplementation((field: any) => {
if (field === data.NrPartiiTowaru) return [{ _text: 'BATCH001' }] as any;
return [];
});
vi.mocked(PDFFunctions.getContentTable).mockReturnValueOnce({
content: { table: 'partia' } as any,
fieldsWithValue: ['_text'],
});
vi.mocked(PDFFunctions.generateTwoColumns).mockReturnValue('twoColumns' as any);
generateWarunkiTransakcji(data);
expect(PDFFunctions.getContentTable).toHaveBeenCalledWith(
expect.arrayContaining([expect.objectContaining({ name: '', title: 'Numer partii towaru' })]),
expect.any(Array),
'*',
[0, 4]
);
expect(PDFFunctions.generateTwoColumns).toHaveBeenCalledWith({ table: 'partia' }, ' ');
});
it('should not create partia towaru section when NrPartiiTowaru is empty', () => {
vi.mocked(PDFFunctions.getTable).mockReturnValue([]);
const callCountBefore = vi.mocked(PDFFunctions.generateTwoColumns).mock.calls.length;
generateWarunkiTransakcji(mockWarunkiTransakcji);
expect(vi.mocked(PDFFunctions.generateTwoColumns).mock.calls.length).toBe(callCountBefore);
});
});
describe('warunki dostawy', () => {
it('should add warunki dostawy label', () => {
const data = {
...mockWarunkiTransakcji,
WarunkiDostawy: { _text: 'FOB' },
} as any;
generateWarunkiTransakcji(data);
expect(PDFFunctions.createLabelText).toHaveBeenCalledWith(
'Warunki dostawy towarów: ',
data.WarunkiDostawy,
FormatTyp.MarginTop4
);
});
});
describe('podmiot posredniczacy', () => {
it('should add podmiot posredniczacy info when value is "1"', () => {
const data = {
...mockWarunkiTransakcji,
PodmiotPosredniczacy: { _text: '1' },
} as any;
generateWarunkiTransakcji(data);
expect(PDFFunctions.formatText).toHaveBeenCalledWith(
expect.stringContaining('Dostawa dokonana przez podmiot, o którym mowa w art. 22 ust. 2d ustawy'),
[FormatTyp.Label, FormatTyp.MarginTop4]
);
});
it('should not add podmiot posredniczacy info when value is not "1"', () => {
const data = {
...mockWarunkiTransakcji,
PodmiotPosredniczacy: { _text: '0' },
} as any;
vi.mocked(PDFFunctions.formatText).mockClear();
generateWarunkiTransakcji(data);
expect(PDFFunctions.formatText).not.toHaveBeenCalled();
});
it('should not add podmiot posredniczacy info when value is empty', () => {
const data = {
...mockWarunkiTransakcji,
PodmiotPosredniczacy: { _text: '' },
} as any;
vi.mocked(PDFFunctions.formatText).mockClear();
generateWarunkiTransakcji(data);
expect(PDFFunctions.formatText).not.toHaveBeenCalled();
});
});
describe('transport section', () => {
it('should call generateTransport for each transport item', () => {
const data = {
...mockWarunkiTransakcji,
Transport: [{ RodzajTransportu: { _text: 'Road' } }, { RodzajTransportu: { _text: 'Air' } }],
} as any;
vi.mocked(PDFFunctions.getTable).mockImplementation((field: any) => {
if (field === data.Transport) {
return [{ RodzajTransportu: { _text: 'Road' } }, { RodzajTransportu: { _text: 'Air' } }] as any;
}
return [];
});
vi.mocked(TransportModule.generateTransport).mockReturnValue('transport' as any);
generateWarunkiTransakcji(data);
expect(TransportModule.generateTransport).toHaveBeenCalledTimes(2);
expect(TransportModule.generateTransport).toHaveBeenCalledWith(
{
RodzajTransportu: { _text: 'Road' },
},
1
);
expect(TransportModule.generateTransport).toHaveBeenCalledWith(
{
RodzajTransportu: { _text: 'Air' },
},
2
);
});
it('should not call generateTransport when Transport is undefined', () => {
const data = {
...mockWarunkiTransakcji,
Transport: undefined,
} as any;
generateWarunkiTransakcji(data);
expect(TransportModule.generateTransport).not.toHaveBeenCalled();
});
it('should not call generateTransport when Transport is empty', () => {
const data = {
...mockWarunkiTransakcji,
Transport: [],
} as any;
vi.mocked(PDFFunctions.getTable).mockReturnValue([]);
generateWarunkiTransakcji(data);
expect(TransportModule.generateTransport).not.toHaveBeenCalled();
});
});
describe('complete integration', () => {
it('should handle all sections when fully populated', () => {
const data = {
Umowy: [{ DataUmowy: { _text: '2024-01-01' }, NrUmowy: { _text: 'U001' } }],
Zamowienia: [{ DataZamowienia: { _text: '2024-01-02' }, NrZamowienia: { _text: 'Z001' } }],
NrPartiiTowaru: [{ _text: 'BATCH001' }],
WalutaUmowna: { _text: 'EUR' },
KursUmowny: { _text: '4.50' },
WarunkiDostawy: { _text: 'FOB' },
PodmiotPosredniczacy: { _text: '1' },
Transport: [{ RodzajTransportu: { _text: 'Road' } }],
} as any;
vi.mocked(PDFFunctions.getTable).mockImplementation((field: any) => {
if (field === data.Umowy) return [{ DataUmowy: { _text: '2024-01-01' } }] as any;
if (field === data.Zamowienia) return [{ DataZamowienia: { _text: '2024-01-02' } }] as any;
if (field === data.NrPartiiTowaru) return [{ _text: 'BATCH001' }] as any;
if (field === data.Transport) return [{ RodzajTransportu: { _text: 'Road' } }] as any;
return [];
});
vi.mocked(PDFFunctions.getContentTable)
.mockReturnValueOnce({ content: { table: 'umowy' } as any, fieldsWithValue: ['DataUmowy'] })
.mockReturnValueOnce({
content: { table: 'zamowienia' } as any,
fieldsWithValue: ['DataZamowienia'],
})
.mockReturnValueOnce({ content: { table: 'partia' } as any, fieldsWithValue: ['_text'] });
generateWarunkiTransakcji(data);
expect(PDFFunctions.createHeader).toHaveBeenCalledWith('Warunki transakcji', [0, 8, 0, 4]);
expect(PDFFunctions.createHeader).toHaveBeenCalledWith('Waluta umowna i kurs umowny', [0, 8, 0, 4]);
expect(PDFFunctions.createSubHeader).toHaveBeenCalledWith('Umowa');
expect(PDFFunctions.createSubHeader).toHaveBeenCalledWith('Zamówienie');
expect(PDFFunctions.formatText).toHaveBeenCalled();
expect(TransportModule.generateTransport).toHaveBeenCalled();
expect(PDFFunctions.createSection).toHaveBeenCalled();
});
});
});
});

View File

@@ -0,0 +1,105 @@
import { Content } from 'pdfmake/interfaces';
import {
createHeader,
createLabelText,
createSection,
createSubHeader,
formatText,
generateTwoColumns,
getContentTable,
getTable,
} from '../../../shared/PDF-functions';
import { HeaderDefine } from '../../../shared/types/pdf-types';
import { FP, Umowy, WarunkiTransakcji, Zamowienia } from '../../types/fa1.types';
import { generateTransport } from './Transport';
import FormatTyp from '../../../shared/enums/common.enum';
import { FormContentState } from '../../../shared/types/additional-data.types';
export function generateWarunkiTransakcji(warunkiTransakcji: WarunkiTransakcji | undefined): Content {
if (!warunkiTransakcji) {
return [];
}
const table: Content[] = [];
const Kolumny = { umowy: [] as Content[], zamowienia: [] as Content[] };
const umowy: Umowy[] = getTable(warunkiTransakcji?.Umowy);
const zamowienia: Zamowienia[] = getTable(warunkiTransakcji?.Zamowienia);
const partiaTowaru: FP[] = getTable(warunkiTransakcji?.NrPartiiTowaru);
const definedHeaderUmowy: HeaderDefine[] = [
{ name: 'DataUmowy', title: 'Data umowy', format: FormatTyp.Default },
{ name: 'NrUmowy', title: 'Numer umowy', format: FormatTyp.Default },
];
const definedHeaderZamowienia: HeaderDefine[] = [
{ name: 'DataZamowienia', title: 'Data zamówienia', format: FormatTyp.Default },
{ name: 'NrZamowienia', title: 'Numer zamówienia', format: FormatTyp.Default },
];
const definedHeaderPartiaTowaru: HeaderDefine[] = [
{ name: '', title: 'Numer partii towaru', format: FormatTyp.Default },
];
table.push(createHeader('Warunki transakcji', [0, 8, 0, 4]));
if (umowy.length > 0) {
const tabUmowy: FormContentState = getContentTable<(typeof umowy)[0]>(definedHeaderUmowy, umowy, '*');
if (tabUmowy.content) {
Kolumny.umowy = [createSubHeader('Umowa'), tabUmowy.content];
}
}
if (zamowienia.length > 0) {
const tabZamowienia: FormContentState = getContentTable<(typeof zamowienia)[0]>(
definedHeaderZamowienia,
zamowienia,
'*'
);
if (tabZamowienia.content && tabZamowienia.fieldsWithValue.length > 0) {
Kolumny.zamowienia = [createSubHeader('Zamówienie'), tabZamowienia.content];
}
}
if (Kolumny.zamowienia.length > 0 || Kolumny.umowy.length > 0) {
table.push(generateTwoColumns(Kolumny.umowy, Kolumny.zamowienia));
}
if (warunkiTransakcji.WalutaUmowna?._text || warunkiTransakcji.KursUmowny?._text) {
table.push(createHeader('Waluta umowna i kurs umowny', [0, 8, 0, 4]));
table.push(createLabelText('Waluta umowna: ', warunkiTransakcji.WalutaUmowna));
table.push(createLabelText('Kurs umowny: ', warunkiTransakcji.KursUmowny));
}
if (partiaTowaru.length > 0) {
const tabPartiaTowaru: FormContentState = getContentTable<(typeof partiaTowaru)[0]>(
definedHeaderPartiaTowaru,
partiaTowaru,
'*',
[0, 4]
);
if (tabPartiaTowaru.content) {
table.push(generateTwoColumns(tabPartiaTowaru.content, ' '));
}
}
table.push(
createLabelText('Warunki dostawy towarów: ', warunkiTransakcji.WarunkiDostawy, FormatTyp.MarginTop4)
);
if (warunkiTransakcji.PodmiotPosredniczacy?._text === '1') {
table.push(
formatText(
'Dostawa dokonana przez podmiot, o którym mowa w art. 22 ust. 2d ustawy. Pole dotyczy sytuacji, w której podmiot uczestniczy w transakcji łańcuchowej innej niż procedura trójstronna uproszczona, o której mowa w art. 135 ust. 1 pkt 4 ustawy',
[FormatTyp.Label, FormatTyp.MarginTop4]
)
);
}
if (warunkiTransakcji.Transport) {
getTable(warunkiTransakcji.Transport).forEach((transport, index) => {
table.push(
generateTransport(transport, getTable(warunkiTransakcji.Transport).length !== 0 ? index + 1 : null)
);
});
}
return createSection(table, true);
}

View File

@@ -0,0 +1,379 @@
import { describe, it, expect, vi, beforeEach } from 'vitest';
import * as PDFFunctions from '../../../shared/PDF-functions';
import FormatTyp from '../../../shared/enums/common.enum';
import { TRodzajFaktury } from '../../../shared/consts/const';
import { Fa } from '../../types/fa1.types';
import { generateWiersze } from './Wiersze';
vi.mock('../../../shared/PDF-functions', () => ({
createHeader: vi.fn(),
createLabelTextArray: vi.fn(),
createSection: vi.fn(),
formatText: vi.fn(),
getContentTable: vi.fn(),
getTable: vi.fn(),
getValue: vi.fn(),
}));
describe(generateWiersze.name, () => {
beforeEach(() => {
vi.clearAllMocks();
});
const mockFaVat: Fa = {
FaWiersz: [
{
NrWierszaFa: { _text: '1' },
P_7: { _text: 'Product 1' },
P_9A: { _text: '100' },
P_8B: { _text: '2' },
},
],
KodWaluty: { _text: 'PLN' },
P_15: { _text: '200' },
RodzajFaktury: { _text: TRodzajFaktury.VAT },
} as any;
const setupBasicMocks = (p15Value: string, rodzajFakturyValue: string, currencyValue: string = 'PLN') => {
vi.mocked(PDFFunctions.getTable).mockReturnValue([
{
NrWierszaFa: { _text: '1' },
P_7: { _text: 'Product 1' },
P_9A: { _text: '100' },
},
] as any);
vi.mocked(PDFFunctions.getContentTable).mockReturnValue({
content: { table: {} } as any,
fieldsWithValue: ['P_11', 'P_7'],
});
vi.mocked(PDFFunctions.getValue).mockImplementation((field: any) => {
if (field === mockFaVat.P_15 || field?._text === p15Value) return p15Value;
if (field === mockFaVat.RodzajFaktury || field?._text === rodzajFakturyValue) return rodzajFakturyValue;
if (field === mockFaVat.KodWaluty || field?._text === currencyValue) return currencyValue;
return undefined;
});
vi.mocked(PDFFunctions.formatText).mockReturnValue('formatted text' as any);
vi.mocked(PDFFunctions.createHeader).mockReturnValue(['Header'] as any);
vi.mocked(PDFFunctions.createSection).mockReturnValue({ section: 'content' } as any);
vi.mocked(PDFFunctions.createLabelTextArray).mockReturnValue(['Label', 'Value'] as any);
};
describe('when no invoice lines exist', () => {
it('should return empty array when table is empty', () => {
vi.mocked(PDFFunctions.getTable).mockReturnValue([]);
vi.mocked(PDFFunctions.getContentTable).mockReturnValue({
content: null,
fieldsWithValue: [],
});
vi.mocked(PDFFunctions.getValue).mockReturnValue('0');
const result = generateWiersze(mockFaVat);
expect(result).toEqual([]);
});
});
describe('when invoice lines exist', () => {
it('should call getTable with FaWiersz', () => {
setupBasicMocks('200', TRodzajFaktury.VAT);
generateWiersze(mockFaVat);
expect(PDFFunctions.getTable).toHaveBeenCalledWith(mockFaVat.FaWiersze);
});
it('should call createHeader with "Pozycje"', () => {
setupBasicMocks('200', TRodzajFaktury.VAT);
generateWiersze(mockFaVat);
expect(PDFFunctions.createHeader).toHaveBeenCalledWith('Pozycje');
});
describe('price text formatting', () => {
it('should display "netto" when P_11 is in fieldsWithValue', () => {
setupBasicMocks('200', TRodzajFaktury.VAT);
generateWiersze(mockFaVat);
expect(PDFFunctions.formatText).toHaveBeenCalledWith(expect.stringContaining('netto'), [
FormatTyp.Label,
FormatTyp.MarginBottom8,
]);
});
it('should display "brutto" when P_11 is not in fieldsWithValue', () => {
vi.mocked(PDFFunctions.getTable).mockReturnValue([{ NrWierszaFa: { _text: '1' } }] as any);
vi.mocked(PDFFunctions.getContentTable).mockReturnValue({
content: { table: {} } as any,
fieldsWithValue: ['P_11A', 'P_7'],
});
vi.mocked(PDFFunctions.getValue).mockReturnValue('0');
vi.mocked(PDFFunctions.formatText).mockReturnValue('formatted text' as any);
vi.mocked(PDFFunctions.createHeader).mockReturnValue(['Header'] as any);
vi.mocked(PDFFunctions.createSection).mockReturnValue({ section: 'content' } as any);
generateWiersze(mockFaVat);
expect(PDFFunctions.formatText).toHaveBeenCalledWith(expect.stringContaining('brutto'), [
FormatTyp.Label,
FormatTyp.MarginBottom8,
]);
});
it('should include currency code in price text', () => {
setupBasicMocks('200', TRodzajFaktury.VAT);
generateWiersze(mockFaVat);
expect(PDFFunctions.formatText).toHaveBeenCalledWith(expect.stringContaining('PLN'), [
FormatTyp.Label,
FormatTyp.MarginBottom8,
]);
});
});
describe('table generation', () => {
it('should generate single table when fieldsWithValue.length <= 8', () => {
setupBasicMocks('200', TRodzajFaktury.VAT);
generateWiersze(mockFaVat);
expect(PDFFunctions.getContentTable).toHaveBeenCalledTimes(1);
});
it('should generate two tables when fieldsWithValue.length > 8', () => {
vi.mocked(PDFFunctions.getTable).mockReturnValue([{ NrWierszaFa: { _text: '1' } }] as any);
vi.mocked(PDFFunctions.getContentTable)
.mockReturnValueOnce({
content: null,
fieldsWithValue: Array(9).fill('field'),
})
.mockReturnValueOnce({
content: { table: 'table1' } as any,
fieldsWithValue: ['field1', 'field2'],
})
.mockReturnValueOnce({
content: { table: 'table2' } as any,
fieldsWithValue: ['field3', 'field4'],
});
vi.mocked(PDFFunctions.getValue).mockReturnValue('0');
vi.mocked(PDFFunctions.formatText).mockReturnValue('formatted text' as any);
vi.mocked(PDFFunctions.createHeader).mockReturnValue(['Header'] as any);
vi.mocked(PDFFunctions.createSection).mockReturnValue({ section: 'content' } as any);
generateWiersze(mockFaVat);
expect(PDFFunctions.getContentTable).toHaveBeenCalledTimes(3);
});
it('should not add second table if it has only 1 field with value', () => {
vi.mocked(PDFFunctions.getTable).mockReturnValue([{ NrWierszaFa: { _text: '1' } }] as any);
vi.mocked(PDFFunctions.getContentTable)
.mockReturnValueOnce({
content: null,
fieldsWithValue: Array(9).fill('field'),
})
.mockReturnValueOnce({
content: { table: 'table1' } as any,
fieldsWithValue: ['field1', 'field2'],
})
.mockReturnValueOnce({
content: { table: 'table2' } as any,
fieldsWithValue: ['field1'],
});
vi.mocked(PDFFunctions.getValue).mockReturnValue('0');
vi.mocked(PDFFunctions.formatText).mockReturnValue('formatted text' as any);
vi.mocked(PDFFunctions.createHeader).mockReturnValue(['Header'] as any);
vi.mocked(PDFFunctions.createSection).mockReturnValue({ section: 'content' } as any);
generateWiersze(mockFaVat);
const sectionCall = vi.mocked(PDFFunctions.createSection).mock.calls[0][0];
expect(sectionCall).not.toContain('\n');
expect(sectionCall.filter((item: any) => item?.table === 'table2')).toHaveLength(0);
});
});
describe('payment amount description', () => {
it('should add "Kwota pozostała do zapłaty" for ROZ invoice type when P_15 > 0', () => {
setupBasicMocks('150', TRodzajFaktury.ROZ, 'EUR');
generateWiersze(mockFaVat);
expect(PDFFunctions.createLabelTextArray).toHaveBeenCalledWith([
{ value: 'Kwota pozostała do zapłaty: ', formatTyp: FormatTyp.LabelGreater },
{
value: '150',
formatTyp: FormatTyp.CurrencyGreater,
currency: 'EUR',
},
]);
});
it('should not add description for ROZ invoice when P_15 = 0', () => {
setupBasicMocks('0', TRodzajFaktury.ROZ, 'EUR');
vi.mocked(PDFFunctions.createLabelTextArray).mockClear();
generateWiersze(mockFaVat);
expect(PDFFunctions.createLabelTextArray).not.toHaveBeenCalled();
});
it('should add "Kwota należności ogółem" for VAT invoice when P_15 > 0', () => {
setupBasicMocks('200', TRodzajFaktury.VAT, 'PLN');
generateWiersze(mockFaVat);
expect(PDFFunctions.createLabelTextArray).toHaveBeenCalledWith([
{ value: 'Kwota należności ogółem: ', formatTyp: FormatTyp.LabelGreater },
{
value: '200',
formatTyp: [FormatTyp.CurrencyGreater],
currency: 'PLN',
},
]);
});
it('should add description for KOR invoice when P_15 > 0', () => {
setupBasicMocks('300', TRodzajFaktury.KOR, 'USD');
generateWiersze(mockFaVat);
expect(PDFFunctions.createLabelTextArray).toHaveBeenCalledWith([
{ value: 'Kwota należności ogółem: ', formatTyp: FormatTyp.LabelGreater },
{
value: '300',
formatTyp: [FormatTyp.CurrencyGreater],
currency: 'USD',
},
]);
});
it('should add description for KOR_ROZ invoice when P_15 > 0', () => {
setupBasicMocks('250', TRodzajFaktury.KOR_ROZ, 'PLN');
generateWiersze(mockFaVat);
expect(PDFFunctions.createLabelTextArray).toHaveBeenCalled();
});
it('should add description for UPR invoice when P_15 > 0', () => {
setupBasicMocks('180', TRodzajFaktury.UPR, 'PLN');
generateWiersze(mockFaVat);
expect(PDFFunctions.createLabelTextArray).toHaveBeenCalled();
});
it('should not add description for VAT invoice when P_15 = 0', () => {
setupBasicMocks('0', TRodzajFaktury.VAT, 'PLN');
vi.mocked(PDFFunctions.createLabelTextArray).mockClear();
generateWiersze(mockFaVat);
expect(PDFFunctions.createLabelTextArray).not.toHaveBeenCalled();
});
it('should use empty string for currency if KodWaluty is undefined', () => {
vi.mocked(PDFFunctions.getTable).mockReturnValue([{ NrWierszaFa: { _text: '1' } }] as any);
vi.mocked(PDFFunctions.getContentTable).mockReturnValue({
content: { table: {} } as any,
fieldsWithValue: ['P_11', 'P_7'],
});
vi.mocked(PDFFunctions.getValue).mockImplementation((field: any) => {
if (field === mockFaVat.P_15) return '200';
if (field === mockFaVat.RodzajFaktury) return TRodzajFaktury.VAT;
if (field === mockFaVat.KodWaluty) return undefined;
return undefined;
});
vi.mocked(PDFFunctions.formatText).mockReturnValue('formatted text' as any);
vi.mocked(PDFFunctions.createHeader).mockReturnValue(['Header'] as any);
vi.mocked(PDFFunctions.createSection).mockReturnValue({ section: 'content' } as any);
vi.mocked(PDFFunctions.createLabelTextArray).mockReturnValue(['Label', 'Value'] as any);
const faVatNoCurrency = { ...mockFaVat, KodWaluty: undefined } as any;
generateWiersze(faVatNoCurrency);
expect(PDFFunctions.createLabelTextArray).toHaveBeenCalledWith(
expect.arrayContaining([
expect.objectContaining({
currency: '',
}),
])
);
});
});
describe('createSection call', () => {
it('should call createSection with correct parameters', () => {
setupBasicMocks('200', TRodzajFaktury.VAT);
generateWiersze(mockFaVat);
expect(PDFFunctions.createSection).toHaveBeenCalledWith(
expect.arrayContaining(['Header', 'formatted text']),
true
);
});
it('should return the result of createSection', () => {
const mockSection = { section: 'test' };
setupBasicMocks('200', TRodzajFaktury.VAT);
vi.mocked(PDFFunctions.createSection).mockReturnValue(mockSection as any);
const result = generateWiersze(mockFaVat);
expect(result).toEqual(mockSection);
});
});
describe('header definitions', () => {
it('should include all required headers in first table', () => {
setupBasicMocks('200', TRodzajFaktury.VAT);
generateWiersze(mockFaVat);
const firstCall = vi.mocked(PDFFunctions.getContentTable).mock.calls[0];
const headers = firstCall[0];
expect(headers).toEqual(
expect.arrayContaining([
expect.objectContaining({ name: 'NrWierszaFa', title: 'Lp.' }),
expect.objectContaining({ name: 'P_7', title: 'Nazwa towaru lub usługi' }),
expect.objectContaining({ name: 'P_9A', title: 'Cena jedn. netto' }),
expect.objectContaining({ name: 'P_11', title: 'Wartość sprzedaży netto' }),
])
);
});
it('should use correct format types for currency fields', () => {
setupBasicMocks('200', TRodzajFaktury.VAT);
generateWiersze(mockFaVat);
const firstCall = vi.mocked(PDFFunctions.getContentTable).mock.calls[0];
const headers = firstCall[0];
const currencyHeaders = headers.filter(
(h: any) => h.format === FormatTyp.Currency || h.format === FormatTyp.Currency6
);
expect(currencyHeaders.length).toBeGreaterThan(0);
});
});
});
});

View File

@@ -0,0 +1,130 @@
import { Content, ContentStack, ContentText } from 'pdfmake/interfaces';
import {
createHeader,
createLabelTextArray,
createSection,
formatText,
getContentTable,
getTable,
getValue,
} from '../../../shared/PDF-functions';
import { HeaderDefine } from '../../../shared/types/pdf-types';
import { Procedura, TRodzajFaktury } from '../../../shared/consts/const';
import { Fa, FP } from '../../types/fa1.types';
import FormatTyp, { Position } from '../../../shared/enums/common.enum';
import { FormContentState } from '../../../shared/types/additional-data.types';
import { shouldAddMarza } from '../common/Wiersze';
export function generateWiersze(faVat: Fa): Content {
const table: Content[] = [];
const rodzajFaktury: string | number | undefined = getValue(faVat.RodzajFaktury);
const isP_PMarzy: boolean = Boolean(Number(getValue(faVat.Adnotacje?.P_PMarzy)));
const faWiersze: Record<string, FP>[] = getTable(faVat.FaWiersze?.FaWiersz).map(
(wiersz: Record<string, FP>): Record<string, FP> => {
const marza: Record<string, FP> = shouldAddMarza(rodzajFaktury, isP_PMarzy, wiersz)!;
return marza ? { ...wiersz, ...marza } : wiersz;
}
);
const definedHeaderLp: HeaderDefine[] = [
{ name: 'NrWierszaFa', title: 'Lp.', format: FormatTyp.Default, width: 'auto' },
];
const definedHeader1: HeaderDefine[] = [
{ name: 'UU_ID', title: 'Unikalny numer wiersza', format: FormatTyp.Default, width: 'auto' },
{ name: 'P_7', title: 'Nazwa towaru lub usługi', format: FormatTyp.Default, width: '*' },
{ name: 'P_9A', title: 'Cena jedn. netto', format: FormatTyp.Currency, width: 'auto' },
{ name: 'P_9B', title: 'Cena jedn. brutto', format: FormatTyp.Currency, width: 'auto' },
{ name: 'P_8B', title: 'Ilość', format: FormatTyp.Right, width: 'auto' },
{ name: 'P_8A', title: 'Miara', format: FormatTyp.Default, width: 'auto' },
{ name: 'P_10', title: 'Rabat', format: FormatTyp.Currency, width: 'auto' },
{ name: 'P_12', title: 'Stawka podatku', format: FormatTyp.Default, width: 'auto' },
{ name: 'P_12_XII', title: 'Stawka podatku OSS', format: FormatTyp.Default, width: 'auto' },
{ name: 'P_11', title: 'Wartość sprzedaży netto', format: FormatTyp.Currency, width: 'auto' },
{ name: 'P_11A', title: 'Wartość sprzedaży brutto', format: FormatTyp.Currency, width: 'auto' },
{ name: 'KursWaluty', title: 'Kurs waluty', format: FormatTyp.Currency6, width: 'auto' },
];
const definedHeader2: HeaderDefine[] = [
{ name: 'GTIN', title: 'GTIN', format: FormatTyp.Default, width: 'auto' },
{ name: 'PKWiU', title: 'PKWiU', format: FormatTyp.Default, width: 'auto' },
{ name: 'CN', title: 'CN', format: FormatTyp.Default, width: 'auto' },
{ name: 'PKOB', title: 'PKOB', format: FormatTyp.Default, width: 'auto' },
{ name: 'DodatkoweInfo', title: 'Dodatkowe informacje', format: FormatTyp.Default, width: 'auto' },
{
name: 'P_12_Procedura',
title: 'Procedura',
format: FormatTyp.Default,
mappingData: Procedura,
width: '*',
},
{ name: 'KwotaAkcyzy', title: 'KwotaAkcyzy', format: FormatTyp.Default, width: 'auto' },
{ name: 'GTU', title: 'GTU', format: FormatTyp.Default, width: 'auto' },
{ name: 'Procedura', title: 'Oznaczenia dotyczące procedur', format: FormatTyp.Default, width: '*' },
{ name: 'P_6A', title: 'Data dostawy / wykonania', format: FormatTyp.Default, width: 'auto' },
];
let content: FormContentState = getContentTable<(typeof faWiersze)[0]>(
[...definedHeaderLp, ...definedHeader1, ...definedHeader2],
faWiersze,
'*'
);
const ceny: string | ContentText = formatText(
`Faktura wystawiona w cenach ${content.fieldsWithValue.includes('P_11') ? 'netto' : 'brutto'} w walucie ${faVat.KodWaluty?._text}`,
[FormatTyp.Label, FormatTyp.MarginBottom8]
);
const p_15: string | number | undefined = getValue(faVat.P_15);
let opis: ContentStack[] = [];
if (rodzajFaktury == TRodzajFaktury.ROZ && Number(p_15) !== 0) {
opis = [
{
stack: createLabelTextArray([
{ value: 'Kwota pozostała do zapłaty: ', formatTyp: FormatTyp.LabelGreater },
{
value: p_15,
formatTyp: FormatTyp.CurrencyGreater,
currency: getValue(faVat.KodWaluty)?.toString() ?? '',
},
]),
alignment: Position.RIGHT,
margin: [0, 8, 0, 0],
},
];
} else if (
(rodzajFaktury == TRodzajFaktury.VAT ||
rodzajFaktury == TRodzajFaktury.KOR ||
rodzajFaktury == TRodzajFaktury.KOR_ROZ ||
rodzajFaktury == TRodzajFaktury.UPR) &&
Number(p_15) !== 0
) {
opis = [
{
stack: createLabelTextArray([
{ value: 'Kwota należności ogółem: ', formatTyp: FormatTyp.LabelGreater },
{
value: p_15,
formatTyp: [FormatTyp.CurrencyGreater],
currency: getValue(faVat.KodWaluty)?.toString() ?? '',
},
]),
alignment: Position.RIGHT,
margin: [0, 8, 0, 0],
},
];
}
if (content.fieldsWithValue.length <= 9 && content.content) {
table.push(content.content);
} else {
content = getContentTable<(typeof faWiersze)[0]>([...definedHeaderLp, ...definedHeader1], faWiersze, '*');
if (content.content) {
table.push(content.content);
}
content = getContentTable<(typeof faWiersze)[0]>([...definedHeaderLp, ...definedHeader2], faWiersze, '*');
if (content.content && content.fieldsWithValue.length > 1) {
table.push('\n');
table.push(content.content);
}
}
if (table.length < 1) {
return [];
}
return createSection([...createHeader('Pozycje'), ceny, ...table, ...opis], true);
}

View File

@@ -0,0 +1,288 @@
import { beforeEach, describe, expect, it, vi } from 'vitest';
import * as PDFFunctions from '../../../shared/PDF-functions';
import FormatTyp from '../../../shared/enums/common.enum';
import { TRodzajFaktury } from '../../../shared/consts/const';
import { Zamowienie } from '../../types/fa1.types';
import { generateZamowienie } from './Zamowienie';
import { ZamowienieKorekta } from '../../enums/invoice.enums';
vi.mock('../../../shared/PDF-functions', () => ({
createHeader: vi.fn(),
createLabelTextArray: vi.fn(),
formatText: vi.fn(),
getContentTable: vi.fn(),
getTable: vi.fn(),
}));
describe(generateZamowienie.name, () => {
beforeEach(() => {
vi.clearAllMocks();
});
describe('when orderData is undefined', () => {
it('should return an empty array', () => {
const result = generateZamowienie(
undefined,
ZamowienieKorekta.BeforeCorrection,
'100',
TRodzajFaktury.ZAL,
'PLN'
);
expect(result).toEqual([]);
});
});
describe('when orderData is defined', () => {
const mockOrderData: Zamowienie = {
ZamowienieWiersz: [
{
NrWierszaZam: { _text: '1' },
P_7Z: { _text: 'Towar 1' },
P_9AZ: { _text: '100' },
P_8BZ: { _text: '2' },
},
],
WartoscZamowienia: { _text: '200' },
} as any;
beforeEach(() => {
vi.mocked(PDFFunctions.getTable).mockReturnValue([
{
NrWierszaZam: { _text: '1' },
P_7Z: { _text: 'Towar 1' },
P_9AZ: { _text: '100' },
P_8BZ: { _text: '2' },
},
] as any);
vi.mocked(PDFFunctions.getContentTable).mockReturnValue({
content: { table: {} } as any,
fieldsWithValue: ['P_11'],
});
vi.mocked(PDFFunctions.createHeader).mockReturnValue('Header' as any);
vi.mocked(PDFFunctions.formatText).mockReturnValue('200 PLN' as any);
});
it('should call getTable with ZamowienieWiersz', () => {
generateZamowienie(mockOrderData, ZamowienieKorekta.BeforeCorrection, '100', TRodzajFaktury.ZAL, 'PLN');
expect(PDFFunctions.getTable).toHaveBeenCalledWith(mockOrderData.ZamowienieWiersz);
});
it('should fill NrWierszaZam if it is empty', () => {
const dataWithEmptyNr = {
...mockOrderData,
ZamowienieWiersz: [
{
NrWierszaZam: { _text: '' },
P_7Z: { _text: 'Towar 1' },
},
],
} as any;
vi.mocked(PDFFunctions.getTable).mockReturnValue([
{
NrWierszaZam: { _text: '' },
P_7Z: { _text: 'Towar 1' },
},
] as any);
generateZamowienie(
dataWithEmptyNr,
ZamowienieKorekta.BeforeCorrection,
'100',
TRodzajFaktury.ZAL,
'PLN'
);
const tableCall = vi.mocked(PDFFunctions.getTable).mock.results[0].value;
expect(tableCall[0].NrWierszaZam._text).toBe('1');
});
describe('price formatting', () => {
it('should use FormatTyp.CurrencyAbs for BeforeCorrection', () => {
vi.mocked(PDFFunctions.getContentTable).mockReturnValue({
content: { table: {} } as any,
fieldsWithValue: ['field1', 'field2', 'field3'],
});
generateZamowienie(
mockOrderData,
ZamowienieKorekta.BeforeCorrection,
'100',
TRodzajFaktury.ZAL,
'PLN'
);
const calls = vi.mocked(PDFFunctions.getContentTable).mock.calls;
const header = calls[0][0];
const cenaNetto = header.find((h: any) => h.name === 'P_9AZ');
expect(cenaNetto?.format).toBe(FormatTyp.CurrencyAbs);
});
it('should use FormatTyp.Currency when not BeforeCorrection', () => {
vi.mocked(PDFFunctions.getContentTable).mockReturnValue({
content: { table: {} } as any,
fieldsWithValue: ['field1', 'field2', 'field3'],
});
generateZamowienie(
mockOrderData,
ZamowienieKorekta.AfterCorrection,
'100',
TRodzajFaktury.KOR_ZAL,
'PLN'
);
const calls = vi.mocked(PDFFunctions.getContentTable).mock.calls;
const header = calls[0][0];
const cenaNetto = header.find((h: any) => h.name === 'P_9AZ');
expect(cenaNetto?.format).toBe(FormatTyp.Currency);
});
});
describe('table generation', () => {
it('should generate one table when fieldsWithValue.length <= 8', () => {
vi.mocked(PDFFunctions.getContentTable).mockReturnValue({
content: { table: {} } as any,
fieldsWithValue: ['field1', 'field2', 'field3'],
});
const result = generateZamowienie(
mockOrderData,
ZamowienieKorekta.BeforeCorrection,
'100',
TRodzajFaktury.ZAL,
'PLN'
);
expect(PDFFunctions.getContentTable).toHaveBeenCalledTimes(1);
expect(result[0]).toBeDefined();
});
it('should generate two tables when fieldsWithValue.length > 8', () => {
vi.mocked(PDFFunctions.getContentTable)
.mockReturnValueOnce({
content: null,
fieldsWithValue: Array(9).fill('field'),
})
.mockReturnValueOnce({
content: { table: {} } as any,
fieldsWithValue: ['field1', 'field2'],
})
.mockReturnValueOnce({
content: { table: {} } as any,
fieldsWithValue: ['field3', 'field4'],
});
generateZamowienie(
mockOrderData,
ZamowienieKorekta.BeforeCorrection,
'100',
TRodzajFaktury.ZAL,
'PLN'
);
expect(PDFFunctions.getContentTable).toHaveBeenCalledTimes(1);
});
});
describe('payment amount description', () => {
it('should add description for advance invoice (ZAL) when p_15 > 0', () => {
vi.mocked(PDFFunctions.createLabelTextArray).mockReturnValue(['Label', 'Value'] as any);
const result = generateZamowienie(
mockOrderData,
ZamowienieKorekta.BeforeCorrection,
'100',
TRodzajFaktury.ZAL,
'PLN'
);
expect(PDFFunctions.createLabelTextArray).toHaveBeenCalledWith([
{ value: 'Otrzymana kwota zapłaty (zaliczki): ', formatTyp: FormatTyp.LabelGreater },
{ value: '100', formatTyp: FormatTyp.CurrencyGreater },
]);
});
it('should not add description for ZAL invoice when p_15 = 0', () => {
vi.mocked(PDFFunctions.createLabelTextArray).mockClear();
generateZamowienie(mockOrderData, ZamowienieKorekta.BeforeCorrection, '0', TRodzajFaktury.ZAL, 'PLN');
expect(PDFFunctions.createLabelTextArray).not.toHaveBeenCalled();
});
it('should add description for advance correction (KOR_ZAL) when not BeforeCorrection', () => {
vi.mocked(PDFFunctions.createLabelTextArray).mockReturnValue(['Label', 'Value'] as any);
generateZamowienie(
mockOrderData,
ZamowienieKorekta.AfterCorrection,
'150',
TRodzajFaktury.KOR_ZAL,
'PLN'
);
expect(PDFFunctions.createLabelTextArray).toHaveBeenCalledWith([
{ value: 'Kwota należności ogółem: ', formatTyp: FormatTyp.LabelGreater },
{ value: '150', formatTyp: FormatTyp.CurrencyGreater },
]);
});
});
describe('price text', () => {
it('should display "netto" when P_11 is in fieldsWithValue', () => {
vi.mocked(PDFFunctions.getContentTable).mockReturnValue({
content: { table: {} } as any,
fieldsWithValue: ['P_11', 'P_7Z'],
});
const result = generateZamowienie(
mockOrderData,
ZamowienieKorekta.BeforeCorrection,
'100',
TRodzajFaktury.ZAL,
'EUR'
);
const stack = (result[0] as any).stack;
expect(stack[1]).toContain('netto');
expect(stack[1]).toContain('EUR');
});
it('should display "brutto" when P_11 is not in fieldsWithValue', () => {
vi.mocked(PDFFunctions.getContentTable).mockReturnValue({
content: { table: {} } as any,
fieldsWithValue: ['P_7Z', 'P_9AZ'],
});
const result = generateZamowienie(
mockOrderData,
ZamowienieKorekta.BeforeCorrection,
'100',
TRodzajFaktury.ZAL,
'USD'
);
const stack = (result[0] as any).stack;
expect(stack[1]).toContain('brutto');
expect(stack[1]).toContain('USD');
});
});
it('should call createHeader with correct parameter', () => {
generateZamowienie(mockOrderData, ZamowienieKorekta.BeforeCorrection, '100', TRodzajFaktury.ZAL, 'PLN');
expect(PDFFunctions.createHeader).toHaveBeenCalledWith(ZamowienieKorekta.BeforeCorrection);
});
it('should format order value', () => {
generateZamowienie(mockOrderData, ZamowienieKorekta.BeforeCorrection, '100', TRodzajFaktury.ZAL, 'PLN');
expect(PDFFunctions.formatText).toHaveBeenCalledWith('200', FormatTyp.Currency);
});
});
});

View File

@@ -0,0 +1,143 @@
import { Content } from 'pdfmake/interfaces';
import {
createHeader,
createLabelTextArray,
formatText,
getContentTable,
getTable,
} from '../../../shared/PDF-functions';
import { HeaderDefine } from '../../../shared/types/pdf-types';
import { Procedura, TRodzajFaktury } from '../../../shared/consts/const';
import { FP, Zamowienie } from '../../types/fa1.types';
import FormatTyp, { Position } from '../../../shared/enums/common.enum';
import { ZamowienieKorekta } from '../../enums/invoice.enums';
import { FormContentState } from '../../../shared/types/additional-data.types';
export function generateZamowienie(
orderData: Zamowienie | undefined,
zamowienieKorekta: ZamowienieKorekta,
p_15: string,
rodzajFaktury: string,
KodWaluty: string
): Content[] {
if (!orderData) {
return [];
}
const formatAbs: FormatTyp.Currency | FormatTyp.CurrencyAbs =
zamowienieKorekta === ZamowienieKorekta.BeforeCorrection ? FormatTyp.CurrencyAbs : FormatTyp.Currency;
const orderTable: Record<string, FP>[] = getTable(orderData?.ZamowienieWiersz).map((el, index) => {
if (!el.NrWierszaZam._text) {
el.NrWierszaZam._text = (index + 1).toString();
}
return el;
});
const definedHeaderLp: HeaderDefine[] = [
{ name: 'NrWierszaZam', title: 'Lp.', format: FormatTyp.Default, width: 'auto' },
];
const definedHeader1: HeaderDefine[] = [
{ name: 'UU_IDZ', title: 'Unikalny numer wiersza', format: FormatTyp.Default, width: 'auto' },
{ name: 'P_7Z', title: 'Nazwa towaru lub usługi', format: FormatTyp.Default, width: '*' },
{
name: 'P_9AZ',
title: 'Cena jedn. netto',
format: formatAbs,
},
{ name: 'P_8BZ', title: 'Ilość', format: FormatTyp.Right, width: 'auto' },
{ name: 'P_8AZ', title: 'Miara', format: FormatTyp.Default, width: 'auto' },
{ name: 'P_12Z', title: 'Stawka podatku', format: FormatTyp.Default, width: 'auto' },
{ name: 'P_12Z_XII', title: 'Stawka podatku OSS', format: FormatTyp.Default, width: 'auto' },
{ name: 'P_11NettoZ', title: 'Wartość sprzedaży netto', format: formatAbs, width: 'auto' },
{ name: 'P_11VatZ', title: 'Kwota podatku', format: formatAbs, width: 'auto' },
{ name: 'KursWalutyZ', title: 'Kwota podatku', format: formatAbs, width: 'auto' },
];
const definedHeader2: HeaderDefine[] = [
{ name: 'GTINZ', title: 'GTIN', format: FormatTyp.Default, width: 'auto' },
{ name: 'PKWiUZ', title: 'PKWiU', format: FormatTyp.Default, width: 'auto' },
{ name: 'CNZ', title: 'CN', format: FormatTyp.Default, width: 'auto' },
{ name: 'PKOBZ', title: 'PKOB', format: FormatTyp.Default, width: 'auto' },
{ name: 'DodatkoweInfoZ', title: 'Dodatkowe informacje', format: FormatTyp.Default, width: '*' },
{
name: 'P_12Z_Procedura',
title: 'Procedura',
format: FormatTyp.Default,
mappingData: Procedura,
width: '*',
},
{ name: 'KwotaAkcyzyZ', title: 'Kwota podatku akcyzowego', format: FormatTyp.Currency, width: 'auto' },
{ name: 'GTUZ', title: 'GTU', format: FormatTyp.Default, width: 'auto' },
{ name: 'ProceduraZ', title: 'Oznaczenia dotyczące procedur', format: FormatTyp.Default, width: '*' },
];
let content: FormContentState = getContentTable<(typeof orderTable)[0]>(
[...definedHeaderLp, ...definedHeader1, ...definedHeader2],
orderTable,
'*'
);
const table: Content[] = [];
if (content.fieldsWithValue.length <= 9) {
if (content.content) {
table.push(content.content);
}
} else {
content = getContentTable<(typeof orderTable)[0]>(
[...definedHeaderLp, ...definedHeader1],
orderTable,
'*'
);
if (content.content) {
table.push(content.content);
}
content = getContentTable<(typeof orderTable)[0]>(
[...definedHeaderLp, ...definedHeader2],
orderTable,
'*'
);
if (content.content && content.fieldsWithValue.length > 1) {
table.push(content.content);
}
}
const ceny = `Faktura wystawiona w cenach ${content.fieldsWithValue.includes('P_11') ? 'netto' : 'brutto'} w walucie ${KodWaluty}`;
let opis: Content = '';
if (Number(p_15) > 0 && rodzajFaktury == TRodzajFaktury.ZAL) {
opis = {
stack: createLabelTextArray([
{ value: 'Otrzymana kwota zapłaty (zaliczki): ', formatTyp: FormatTyp.LabelGreater },
{ value: p_15, formatTyp: FormatTyp.CurrencyGreater },
]),
alignment: Position.RIGHT,
margin: [0, 8, 0, 0],
};
} else if (
zamowienieKorekta !== ZamowienieKorekta.BeforeCorrection &&
rodzajFaktury == TRodzajFaktury.KOR_ZAL &&
Number(p_15) >= 0
) {
opis = {
stack: createLabelTextArray([
{ value: 'Kwota należności ogółem: ', formatTyp: FormatTyp.LabelGreater },
{ value: p_15, formatTyp: FormatTyp.CurrencyGreater },
]),
alignment: Position.RIGHT,
margin: [0, 8, 0, 0],
};
}
return [
{
stack: [
createHeader(zamowienieKorekta),
ceny,
{
text: [
'Wartość zamówienia lub umowy z uwzględnieniem kwoty podatku: ',
formatText(orderData.WartoscZamowienia?._text, FormatTyp.Currency),
],
marginBottom: 4,
},
...table,
opis,
],
},
];
}