494 lines
19 KiB
TypeScript
494 lines
19 KiB
TypeScript
import { tap, expect } from '@git.zone/tstest/tapbundle';
|
|
import * as skr from '../ts/index.js';
|
|
import { getTestConfig } from './helpers/setup.js';
|
|
|
|
let api: skr.SkrApi;
|
|
let testConfig: Awaited<ReturnType<typeof getTestConfig>>;
|
|
|
|
tap.test('should demonstrate complete Jahresabschluss (Annual Financial Statement) for SKR04', async () => {
|
|
testConfig = await getTestConfig();
|
|
|
|
// Use timestamp to ensure unique database for each test run
|
|
const timestamp = Date.now();
|
|
api = new skr.SkrApi({
|
|
mongoDbUrl: testConfig.mongoDbUrl,
|
|
dbName: `${testConfig.mongoDbName}_jahresabschluss_skr04_${timestamp}`,
|
|
});
|
|
|
|
await api.initialize('SKR04');
|
|
expect(api.getSKRType()).toEqual('SKR04');
|
|
});
|
|
|
|
tap.test('should set up opening balances (Eröffnungsbilanz) for SKR04', async () => {
|
|
// Opening balances from previous year's closing
|
|
// SKR04 uses different account structure than SKR03
|
|
|
|
// Post opening journal entry (Eröffnungsbuchung)
|
|
const openingEntry = await api.postJournalEntry({
|
|
date: new Date('2024-01-01'),
|
|
description: 'Eröffnungsbilanz 2024',
|
|
reference: 'EB-2024',
|
|
lines: [
|
|
// Debit all asset accounts
|
|
{ accountNumber: '0200', debit: 45000, description: 'Grundstücke' },
|
|
{ accountNumber: '0210', debit: 120000, description: 'Gebäude' },
|
|
{ accountNumber: '0500', debit: 35000, description: 'BGA' },
|
|
{ accountNumber: '0400', debit: 8000, description: 'Fuhrpark' },
|
|
{ accountNumber: '1200', debit: 25000, description: 'Bank' },
|
|
{ accountNumber: '1000', debit: 2500, description: 'Kasse' },
|
|
{ accountNumber: '1400', debit: 18000, description: 'Forderungen' },
|
|
|
|
// Credit all liability and equity accounts
|
|
{ accountNumber: '9000', credit: 150000, description: 'Eigenkapital' },
|
|
{ accountNumber: '9300', credit: 35000, description: 'Gewinnrücklagen' },
|
|
{ accountNumber: '1600', credit: 40500, description: 'Verbindlichkeiten L+L' },
|
|
{ accountNumber: '1700', credit: 28000, description: 'Sonstige Verbindlichkeiten' },
|
|
],
|
|
skrType: 'SKR04',
|
|
});
|
|
|
|
expect(openingEntry.isBalanced).toBeTrue();
|
|
expect(openingEntry.totalDebits).toEqual(253500);
|
|
expect(openingEntry.totalCredits).toEqual(253500);
|
|
});
|
|
|
|
tap.test('should record Q1 business transactions for SKR04', async () => {
|
|
// January - March transactions using SKR04 accounts
|
|
|
|
// Sale of goods with 19% VAT - SKR04 uses 4300 for revenue with 19% VAT
|
|
await api.postJournalEntry({
|
|
date: new Date('2024-01-15'),
|
|
description: 'Verkauf Waren auf Rechnung',
|
|
reference: 'RE-2024-001',
|
|
lines: [
|
|
{ accountNumber: '1400', debit: 11900, description: 'Forderungen inkl. USt' },
|
|
{ accountNumber: '4300', credit: 10000, description: 'Erlöse 19% USt' },
|
|
{ accountNumber: '1771', credit: 1900, description: 'Umsatzsteuer 19%' },
|
|
],
|
|
skrType: 'SKR04',
|
|
});
|
|
|
|
// Purchase of materials with 19% VAT - SKR04 uses 2100 for goods purchases
|
|
await api.postJournalEntry({
|
|
date: new Date('2024-01-20'),
|
|
description: 'Einkauf Material auf Rechnung',
|
|
reference: 'ER-2024-001',
|
|
lines: [
|
|
{ accountNumber: '2100', debit: 5000, description: 'Bezogene Waren' },
|
|
{ accountNumber: '1571', debit: 950, description: 'Vorsteuer 19%' },
|
|
{ accountNumber: '1600', credit: 5950, description: 'Verbindlichkeiten' },
|
|
],
|
|
skrType: 'SKR04',
|
|
});
|
|
|
|
// Salary payment - SKR04 uses 2300 for wages
|
|
await api.postJournalEntry({
|
|
date: new Date('2024-01-31'),
|
|
description: 'Gehaltszahlung Januar',
|
|
reference: 'GH-2024-01',
|
|
lines: [
|
|
{ accountNumber: '2300', debit: 8000, description: 'Löhne' },
|
|
{ accountNumber: '2400', debit: 1600, description: 'Gehälter' },
|
|
{ accountNumber: '1200', credit: 9600, description: 'Banküberweisung' },
|
|
],
|
|
skrType: 'SKR04',
|
|
});
|
|
|
|
// Customer payment received
|
|
await api.postJournalEntry({
|
|
date: new Date('2024-02-10'),
|
|
description: 'Zahlungseingang Kunde',
|
|
reference: 'ZE-2024-001',
|
|
lines: [
|
|
{ accountNumber: '1200', debit: 11900, description: 'Bankgutschrift' },
|
|
{ accountNumber: '1400', credit: 11900, description: 'Forderungsausgleich' },
|
|
],
|
|
skrType: 'SKR04',
|
|
});
|
|
|
|
// Rent payment - SKR04 uses 3000 for rent
|
|
await api.postJournalEntry({
|
|
date: new Date('2024-02-01'),
|
|
description: 'Miete Februar',
|
|
reference: 'MI-2024-02',
|
|
lines: [
|
|
{ accountNumber: '3000', debit: 2000, description: 'Miete' },
|
|
{ accountNumber: '1200', credit: 2000, description: 'Banküberweisung' },
|
|
],
|
|
skrType: 'SKR04',
|
|
});
|
|
|
|
// Office supplies purchase - SKR04 uses 3100 for office supplies
|
|
await api.postJournalEntry({
|
|
date: new Date('2024-02-15'),
|
|
description: 'Büromaterial',
|
|
reference: 'BM-2024-001',
|
|
lines: [
|
|
{ accountNumber: '3100', debit: 200, description: 'Bürobedarf' },
|
|
{ accountNumber: '1571', debit: 38, description: 'Vorsteuer 19%' },
|
|
{ accountNumber: '1200', credit: 238, description: 'Bankzahlung' },
|
|
],
|
|
skrType: 'SKR04',
|
|
});
|
|
|
|
// Vehicle expenses - SKR04 uses 3300 for vehicle costs
|
|
await api.postJournalEntry({
|
|
date: new Date('2024-03-05'),
|
|
description: 'Tankrechnung Firmenfahrzeug',
|
|
reference: 'KFZ-2024-001',
|
|
lines: [
|
|
{ accountNumber: '3300', debit: 150, description: 'Kfz-Kosten' },
|
|
{ accountNumber: '1571', debit: 28.50, description: 'Vorsteuer 19%' },
|
|
{ accountNumber: '1200', credit: 178.50, description: 'Bankzahlung' },
|
|
],
|
|
skrType: 'SKR04',
|
|
});
|
|
|
|
// Another sale
|
|
await api.postJournalEntry({
|
|
date: new Date('2024-03-20'),
|
|
description: 'Verkauf Dienstleistung',
|
|
reference: 'RE-2024-002',
|
|
lines: [
|
|
{ accountNumber: '1400', debit: 7140, description: 'Forderungen inkl. USt' },
|
|
{ accountNumber: '4300', credit: 6000, description: 'Erlöse 19% USt' },
|
|
{ accountNumber: '1771', credit: 1140, description: 'Umsatzsteuer 19%' },
|
|
],
|
|
skrType: 'SKR04',
|
|
});
|
|
});
|
|
|
|
tap.test('should record Q2-Q4 business transactions for SKR04', async () => {
|
|
// More transactions throughout the year
|
|
|
|
// Q2: Investment in new equipment
|
|
await api.postJournalEntry({
|
|
date: new Date('2024-04-15'),
|
|
description: 'Kauf neue Produktionsmaschine',
|
|
reference: 'INV-2024-001',
|
|
lines: [
|
|
{ accountNumber: '0500', debit: 25000, description: 'Neue Maschine' },
|
|
{ accountNumber: '1571', debit: 4750, description: 'Vorsteuer 19%' },
|
|
{ accountNumber: '1200', credit: 29750, description: 'Banküberweisung' },
|
|
],
|
|
skrType: 'SKR04',
|
|
});
|
|
|
|
// Q2: Large sale
|
|
await api.postJournalEntry({
|
|
date: new Date('2024-05-10'),
|
|
description: 'Großauftrag Kunde ABC',
|
|
reference: 'RE-2024-003',
|
|
lines: [
|
|
{ accountNumber: '1400', debit: 35700, description: 'Forderungen inkl. USt' },
|
|
{ accountNumber: '4300', credit: 30000, description: 'Erlöse 19% USt' },
|
|
{ accountNumber: '1771', credit: 5700, description: 'Umsatzsteuer 19%' },
|
|
],
|
|
skrType: 'SKR04',
|
|
});
|
|
|
|
// Q3: Marketing expenses - SKR04 uses 3400 for advertising
|
|
await api.postJournalEntry({
|
|
date: new Date('2024-07-10'),
|
|
description: 'Werbekampagne',
|
|
reference: 'WK-2024-001',
|
|
lines: [
|
|
{ accountNumber: '3400', debit: 5000, description: 'Werbekosten' },
|
|
{ accountNumber: '1571', debit: 950, description: 'Vorsteuer 19%' },
|
|
{ accountNumber: '1600', credit: 5950, description: 'Verbindlichkeiten' },
|
|
],
|
|
skrType: 'SKR04',
|
|
});
|
|
|
|
// Q3: Professional services - SKR04 uses 3500 for legal/consulting
|
|
await api.postJournalEntry({
|
|
date: new Date('2024-08-15'),
|
|
description: 'Steuerberatung',
|
|
reference: 'STB-2024-001',
|
|
lines: [
|
|
{ accountNumber: '3500', debit: 2500, description: 'Steuerberatungskosten' },
|
|
{ accountNumber: '1571', debit: 475, description: 'Vorsteuer 19%' },
|
|
{ accountNumber: '1200', credit: 2975, description: 'Banküberweisung' },
|
|
],
|
|
skrType: 'SKR04',
|
|
});
|
|
|
|
// Q4: Year-end bonus payment
|
|
await api.postJournalEntry({
|
|
date: new Date('2024-11-30'),
|
|
description: 'Jahresbonus Mitarbeiter',
|
|
reference: 'BON-2024',
|
|
lines: [
|
|
{ accountNumber: '2300', debit: 10000, description: 'Tantieme' },
|
|
{ accountNumber: '2400', debit: 2000, description: 'Gehälter Bonus' },
|
|
{ accountNumber: '1200', credit: 12000, description: 'Banküberweisung' },
|
|
],
|
|
skrType: 'SKR04',
|
|
});
|
|
|
|
// Q4: Collection of outstanding receivables
|
|
await api.postJournalEntry({
|
|
date: new Date('2024-12-15'),
|
|
description: 'Zahlungseingang Großauftrag',
|
|
reference: 'ZE-2024-003',
|
|
lines: [
|
|
{ accountNumber: '1200', debit: 35700, description: 'Bankgutschrift' },
|
|
{ accountNumber: '1400', credit: 35700, description: 'Forderungsausgleich' },
|
|
],
|
|
skrType: 'SKR04',
|
|
});
|
|
});
|
|
|
|
tap.test('should perform year-end adjustments (Jahresabschlussbuchungen) for SKR04', async () => {
|
|
// 1. Depreciation (Abschreibungen) - SKR04 uses 3700 for depreciation
|
|
await api.postJournalEntry({
|
|
date: new Date('2024-12-31'),
|
|
description: 'Abschreibung Gebäude (linear 2%)',
|
|
reference: 'AFA-2024-001',
|
|
lines: [
|
|
{ accountNumber: '3700', debit: 2400, description: 'AfA auf Gebäude' },
|
|
{ accountNumber: '0210', credit: 2400, description: 'Wertberichtigung Gebäude' },
|
|
],
|
|
skrType: 'SKR04',
|
|
});
|
|
|
|
await api.postJournalEntry({
|
|
date: new Date('2024-12-31'),
|
|
description: 'Abschreibung BGA (linear 10%)',
|
|
reference: 'AFA-2024-002',
|
|
lines: [
|
|
{ accountNumber: '3700', debit: 6000, description: 'AfA auf BGA' }, // (35000 + 25000) * 10%
|
|
{ accountNumber: '0500', credit: 6000, description: 'Wertberichtigung BGA' },
|
|
],
|
|
skrType: 'SKR04',
|
|
});
|
|
|
|
await api.postJournalEntry({
|
|
date: new Date('2024-12-31'),
|
|
description: 'Abschreibung Fuhrpark (linear 20%)',
|
|
reference: 'AFA-2024-003',
|
|
lines: [
|
|
{ accountNumber: '3700', debit: 1600, description: 'AfA auf Fuhrpark' },
|
|
{ accountNumber: '0400', credit: 1600, description: 'Wertberichtigung Fuhrpark' },
|
|
],
|
|
skrType: 'SKR04',
|
|
});
|
|
|
|
// 2. Accruals (Rechnungsabgrenzung) - SKR04 uses 1900 for prepaid expenses
|
|
await api.postJournalEntry({
|
|
date: new Date('2024-12-31'),
|
|
description: 'Aktive Rechnungsabgrenzung - Vorausbezahlte Versicherung',
|
|
reference: 'ARA-2024-001',
|
|
lines: [
|
|
{ accountNumber: '1900', debit: 1000, description: 'Aktive Rechnungsabgrenzung' },
|
|
{ accountNumber: '3200', credit: 1000, description: 'Versicherungen' },
|
|
],
|
|
skrType: 'SKR04',
|
|
});
|
|
|
|
// 3. Provisions (Rückstellungen) - SKR04 uses 0800 for provisions
|
|
await api.postJournalEntry({
|
|
date: new Date('2024-12-31'),
|
|
description: 'Rückstellung für Jahresabschlusskosten',
|
|
reference: 'RS-2024-001',
|
|
lines: [
|
|
{ accountNumber: '3500', debit: 3000, description: 'Rechts- und Beratungskosten' },
|
|
{ accountNumber: '0800', credit: 3000, description: 'Rückstellungen' },
|
|
],
|
|
skrType: 'SKR04',
|
|
});
|
|
|
|
// 4. VAT clearing (Umsatzsteuer-Vorauszahlung)
|
|
await api.postJournalEntry({
|
|
date: new Date('2024-12-31'),
|
|
description: 'USt-Abschluss Q4',
|
|
reference: 'UST-2024-Q4',
|
|
lines: [
|
|
{ accountNumber: '1771', debit: 8740, description: 'USt-Saldo' }, // Total collected VAT
|
|
{ accountNumber: '1571', credit: 7191.50, description: 'Vorsteuer-Saldo' }, // Total input VAT
|
|
{ accountNumber: '1700', credit: 1548.50, description: 'USt-Zahllast' },
|
|
],
|
|
skrType: 'SKR04',
|
|
});
|
|
|
|
// Assert VAT accounts are cleared
|
|
const ust19 = await api.getAccountBalance('1771');
|
|
const vorst19 = await api.getAccountBalance('1571');
|
|
const ustZahllast = await api.getAccountBalance('1700');
|
|
|
|
expect(Math.abs(ust19.balance)).toBeLessThan(0.01);
|
|
expect(Math.abs(vorst19.balance)).toBeLessThan(0.01);
|
|
// Account 1700 started with 28000 from opening balance, plus 1548.50 from VAT clearing
|
|
expect(Math.abs(ustZahllast.balance - 29548.50)).toBeLessThan(0.01);
|
|
});
|
|
|
|
tap.test('should calculate income statement (GuV) before closing for SKR04', async () => {
|
|
const incomeStatement = await api.generateIncomeStatement({
|
|
dateFrom: new Date('2024-01-01'),
|
|
dateTo: new Date('2024-12-31'),
|
|
skrType: 'SKR04',
|
|
});
|
|
|
|
expect(incomeStatement).toBeDefined();
|
|
expect(incomeStatement.totalRevenue).toBeGreaterThan(0);
|
|
expect(incomeStatement.totalExpenses).toBeGreaterThan(0);
|
|
|
|
// Assert the exact expected values based on actual bookings
|
|
// Revenue: 46000 (4300 account)
|
|
// Expenses: 5000 + 18000 + 3600 + 10000 + 2000 + 150 + 5000 + 5500 + 200 = 49450
|
|
// Less credit balances: -1000 (insurance accrual) = -1000
|
|
// Net expenses: 49450 - 1000 = 48450
|
|
// Net income: 46000 - 48450 = -2450 (loss)
|
|
|
|
expect(Math.round(incomeStatement.totalRevenue)).toEqual(46000);
|
|
expect(Math.round(incomeStatement.totalExpenses)).toEqual(48450);
|
|
expect(Math.round(incomeStatement.netIncome)).toEqual(-2450);
|
|
|
|
console.log('Income Statement Summary (SKR04):');
|
|
console.log('Revenue:', incomeStatement.totalRevenue);
|
|
console.log('Expenses:', incomeStatement.totalExpenses);
|
|
console.log('Net Income:', incomeStatement.netIncome);
|
|
});
|
|
|
|
tap.test('should perform closing entries (Abschlussbuchungen) for SKR04', async () => {
|
|
// Close all income and expense accounts to the profit/loss account
|
|
// SKR04 uses 9500 for annual P&L account
|
|
|
|
// Close revenue accounts
|
|
await api.postJournalEntry({
|
|
date: new Date('2024-12-31'),
|
|
description: 'Abschluss Ertragskonten',
|
|
reference: 'AB-2024-001',
|
|
lines: [
|
|
{ accountNumber: '4300', debit: 46000, description: 'Erlöse abschließen' },
|
|
{ accountNumber: '9500', credit: 46000, description: 'GuV-Konto' },
|
|
],
|
|
skrType: 'SKR04',
|
|
});
|
|
|
|
// Close expense accounts
|
|
await api.postJournalEntry({
|
|
date: new Date('2024-12-31'),
|
|
description: 'Abschluss Aufwandskonten',
|
|
reference: 'AB-2024-002',
|
|
lines: [
|
|
{ accountNumber: '9500', debit: 48450, description: 'GuV-Konto' },
|
|
{ accountNumber: '3200', debit: 1000, description: 'Versicherung abschließen (credit balance)' },
|
|
{ accountNumber: '2100', credit: 5000, description: 'Bezogene Waren abschließen' },
|
|
{ accountNumber: '2300', credit: 18000, description: 'Löhne abschließen' },
|
|
{ accountNumber: '2400', credit: 3600, description: 'Gehälter abschließen' },
|
|
{ accountNumber: '3700', credit: 10000, description: 'AfA abschließen' },
|
|
{ accountNumber: '3000', credit: 2000, description: 'Miete abschließen' },
|
|
{ accountNumber: '3300', credit: 150, description: 'Kfz abschließen' },
|
|
{ accountNumber: '3400', credit: 5000, description: 'Werbung abschließen' },
|
|
{ accountNumber: '3500', credit: 5500, description: 'Beratung abschließen' },
|
|
{ accountNumber: '3100', credit: 200, description: 'Bürobedarf abschließen' },
|
|
],
|
|
skrType: 'SKR04',
|
|
});
|
|
|
|
// Transfer profit/loss to equity
|
|
const guv_result = 46000 - 48450; // Loss of 2450
|
|
if (guv_result > 0) {
|
|
await api.postJournalEntry({
|
|
date: new Date('2024-12-31'),
|
|
description: 'Jahresgewinn auf Eigenkapital',
|
|
reference: 'AB-2024-003',
|
|
lines: [
|
|
{ accountNumber: '9500', debit: guv_result, description: 'GuV-Konto ausgleichen' },
|
|
{ accountNumber: '9300', credit: guv_result, description: 'Gewinnrücklagen' },
|
|
],
|
|
skrType: 'SKR04',
|
|
});
|
|
} else if (guv_result < 0) {
|
|
await api.postJournalEntry({
|
|
date: new Date('2024-12-31'),
|
|
description: 'Jahresverlust auf Eigenkapital',
|
|
reference: 'AB-2024-003',
|
|
lines: [
|
|
{ accountNumber: '9400', debit: Math.abs(guv_result), description: 'Verlustvortrag' },
|
|
{ accountNumber: '9500', credit: Math.abs(guv_result), description: 'GuV-Konto ausgleichen' },
|
|
],
|
|
skrType: 'SKR04',
|
|
});
|
|
}
|
|
|
|
// Assert GuV account is closed and equity is updated
|
|
const guv = await api.getAccountBalance('9500');
|
|
const verlustvortrag = await api.getAccountBalance('9400');
|
|
|
|
expect(Math.abs(guv.balance)).toBeLessThan(0.01);
|
|
expect(Math.round(verlustvortrag.balance)).toEqual(-2450); // Loss of 2450 (debit balance is negative)
|
|
|
|
// Assert all P&L accounts are closed (zero balance)
|
|
const plAccounts = ['4300', '2100', '2300', '2400', '3400', '3500', '3100', '3700', '3000', '3200', '3300'];
|
|
for (const accNum of plAccounts) {
|
|
const balance = await api.getAccountBalance(accNum);
|
|
expect(Math.abs(balance.balance)).toBeLessThan(0.01);
|
|
}
|
|
});
|
|
|
|
tap.test('should generate final balance sheet (Schlussbilanz) for SKR04', async () => {
|
|
const balanceSheet = await api.generateBalanceSheet({
|
|
dateTo: new Date('2024-12-31'),
|
|
skrType: 'SKR04',
|
|
});
|
|
|
|
expect(balanceSheet).toBeDefined();
|
|
expect(balanceSheet.assets).toBeDefined();
|
|
expect(balanceSheet.liabilities).toBeDefined();
|
|
expect(balanceSheet.equity).toBeDefined();
|
|
|
|
console.log('\n=== JAHRESABSCHLUSS 2024 (SKR04) ===\n');
|
|
console.log('BILANZ zum 31.12.2024\n');
|
|
|
|
// Verify balance sheet balances
|
|
const totalAssets = balanceSheet.assets.totalAssets;
|
|
const totalLiabilitiesAndEquity = balanceSheet.liabilities.totalLiabilities + balanceSheet.equity.totalEquity;
|
|
|
|
console.log('Balance Sheet Check (SKR04):');
|
|
console.log(' Total Assets:', totalAssets);
|
|
console.log(' Total Liabilities + Equity:', totalLiabilitiesAndEquity);
|
|
console.log(' Difference:', Math.abs(totalAssets - totalLiabilitiesAndEquity));
|
|
|
|
expect(Math.abs(totalAssets - totalLiabilitiesAndEquity)).toBeLessThan(0.01);
|
|
console.log('✓ Balance Sheet is balanced!');
|
|
});
|
|
|
|
tap.test('should generate trial balance (Summen- und Saldenliste) for SKR04', async () => {
|
|
const trialBalance = await api.generateTrialBalance({
|
|
dateFrom: new Date('2024-01-01'),
|
|
dateTo: new Date('2024-12-31'),
|
|
skrType: 'SKR04',
|
|
});
|
|
|
|
expect(trialBalance).toBeDefined();
|
|
expect(trialBalance.isBalanced).toBeTrue();
|
|
|
|
console.log('\nSUMMEN- UND SALDENLISTE 2024 (SKR04)');
|
|
console.log('=====================================');
|
|
console.log('Konto | Bezeichnung | Soll | Haben | Saldo');
|
|
console.log('------|-------------|------|-------|-------');
|
|
|
|
// Display key accounts
|
|
const keyAccounts = [
|
|
'0200', '0210', '0400', '0500', // Fixed assets
|
|
'1000', '1200', '1400', '1900', // Current assets
|
|
'9000', '9400', '9300', // Equity
|
|
'1600', '1700', '0800', // Liabilities
|
|
];
|
|
|
|
for (const accountNumber of keyAccounts) {
|
|
const account = await api.getAccount(accountNumber);
|
|
if (account) {
|
|
const balance = await api.getAccountBalance(accountNumber);
|
|
console.log(`${accountNumber} | ${account.accountName.substring(0, 30).padEnd(30)} | ${balance.debitTotal.toFixed(2).padStart(12)} | ${balance.creditTotal.toFixed(2).padStart(12)} | ${balance.balance.toFixed(2).padStart(12)}`);
|
|
}
|
|
}
|
|
});
|
|
|
|
tap.test('should close API connection', async () => {
|
|
await api.close();
|
|
});
|
|
|
|
export default tap.start(); |