fix(exports): stabilize published types and compatibility with updated dependencies
This commit is contained in:
@@ -3,6 +3,6 @@
|
||||
*/
|
||||
export const commitinfo = {
|
||||
name: '@fin.cx/skr',
|
||||
version: '1.2.1',
|
||||
version: '1.2.2',
|
||||
description: 'SKR03 and SKR04 German accounting standards for double-entry bookkeeping'
|
||||
}
|
||||
|
||||
+43
-15
@@ -1,16 +1,44 @@
|
||||
export * from './skr.types.js';
|
||||
export * from './skr.classes.account.js';
|
||||
export * from './skr.classes.transaction.js';
|
||||
export * from './skr.classes.journalentry.js';
|
||||
export * from './skr.classes.chartofaccounts.js';
|
||||
export * from './skr.classes.ledger.js';
|
||||
export * from './skr.classes.reports.js';
|
||||
export * from './skr.api.js';
|
||||
export * from './skr03.data.js';
|
||||
export * from './skr04.data.js';
|
||||
export * from './skr.export.js';
|
||||
export * from './skr.export.ledger.js';
|
||||
export * from './skr.export.accounts.js';
|
||||
export * from './skr.export.balances.js';
|
||||
export * from './skr.export.pdf.js';
|
||||
export * from './skr.security.js';
|
||||
export { Account } from './skr.classes.account.js';
|
||||
export { Transaction } from './skr.classes.transaction.js';
|
||||
export { JournalEntry } from './skr.classes.journalentry.js';
|
||||
export { ChartOfAccounts } from './skr.classes.chartofaccounts.js';
|
||||
export { Ledger } from './skr.classes.ledger.js';
|
||||
export { Reports } from './skr.classes.reports.js';
|
||||
export { SkrApi } from './skr.api.js';
|
||||
export { SKR03_ACCOUNTS, SKR03_ACCOUNT_CLASSES } from './skr03.data.js';
|
||||
export { SKR04_ACCOUNTS, SKR04_ACCOUNT_CLASSES } from './skr04.data.js';
|
||||
export { SkrExport } from './skr.export.js';
|
||||
export type {
|
||||
IExportOptions,
|
||||
IExportMetadata,
|
||||
IBagItManifest,
|
||||
IDocumentIndex,
|
||||
} from './skr.export.js';
|
||||
export { LedgerExporter } from './skr.export.ledger.js';
|
||||
export type {
|
||||
ITransactionDataExport,
|
||||
IJournalEntryExport,
|
||||
IJournalEntryLineExport,
|
||||
ILedgerEntry,
|
||||
ILedgerLine,
|
||||
IDocumentRef,
|
||||
} from './skr.export.ledger.js';
|
||||
export { AccountsExporter } from './skr.export.accounts.js';
|
||||
export type {
|
||||
IAccountDataExport,
|
||||
IAccountExportRow,
|
||||
} from './skr.export.accounts.js';
|
||||
export { BalancesExporter } from './skr.export.balances.js';
|
||||
export type {
|
||||
IAccountBalanceExport,
|
||||
IBalanceExportRow,
|
||||
} from './skr.export.balances.js';
|
||||
export { PdfReportGenerator } from './skr.export.pdf.js';
|
||||
export type { IPdfReportOptions } from './skr.export.pdf.js';
|
||||
export { SecurityManager } from './skr.security.js';
|
||||
export type {
|
||||
ISigningOptions,
|
||||
ISignatureResult,
|
||||
ITimestampResponse,
|
||||
} from './skr.security.js';
|
||||
|
||||
+53
-16
@@ -3,26 +3,63 @@ import * as smartdata from '@push.rocks/smartdata';
|
||||
import * as smartunique from '@push.rocks/smartunique';
|
||||
import * as smarttime from '@push.rocks/smarttime';
|
||||
import * as smartlog from '@push.rocks/smartlog';
|
||||
import * as smartfile from '@push.rocks/smartfile';
|
||||
import * as smartfsModule from '@push.rocks/smartfs';
|
||||
import * as smarthash from '@push.rocks/smarthash';
|
||||
import * as smartpath from '@push.rocks/smartpath';
|
||||
import * as smartpdf from '@push.rocks/smartpdf';
|
||||
import * as path from 'path';
|
||||
|
||||
// third party
|
||||
import * as nodeForge from 'node-forge';
|
||||
import { MerkleTree } from 'merkletreejs';
|
||||
import * as einvoice from '@fin.cx/einvoice';
|
||||
|
||||
export {
|
||||
smartdata,
|
||||
smartunique,
|
||||
smarttime,
|
||||
smartlog,
|
||||
smartfile,
|
||||
smarthash,
|
||||
smartpath,
|
||||
smartpdf,
|
||||
nodeForge,
|
||||
MerkleTree,
|
||||
einvoice
|
||||
const smartfs = new smartfsModule.SmartFs(
|
||||
new smartfsModule.SmartFsProviderNode(),
|
||||
);
|
||||
|
||||
const smartfile = {
|
||||
fs: {
|
||||
ensureDir: async (dirPath: string): Promise<void> => {
|
||||
await smartfs.directory(dirPath).create();
|
||||
},
|
||||
toBuffer: async (filePath: string): Promise<Buffer> => {
|
||||
return (await smartfs.file(filePath).read()) as Buffer;
|
||||
},
|
||||
toStringSync: async (filePath: string): Promise<string> => {
|
||||
return (await smartfs.file(filePath).encoding('utf8').read()) as string;
|
||||
},
|
||||
fileExists: async (filePath: string): Promise<boolean> => {
|
||||
return await smartfs.file(filePath).exists();
|
||||
},
|
||||
listFileTree: async (dirPath: string, pattern: string): Promise<string[]> => {
|
||||
const suffix = pattern.replace(/^\*\*\/\*/, '');
|
||||
try {
|
||||
const entries = await smartfs.directory(dirPath).recursive().list();
|
||||
return entries
|
||||
.filter((entry) => entry.isFile && entry.path.endsWith(suffix))
|
||||
.map((entry) => path.relative(dirPath, entry.path));
|
||||
} catch (error) {
|
||||
if (error instanceof Error && error.message.includes('ENOENT')) {
|
||||
return [];
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
},
|
||||
memory: {
|
||||
toFs: async (content: string | Buffer, filePath: string): Promise<void> => {
|
||||
await smartfs.directory(path.dirname(filePath)).create();
|
||||
await smartfs.file(filePath).write(content);
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export {
|
||||
smartdata,
|
||||
smartunique,
|
||||
smarttime,
|
||||
smartlog,
|
||||
smartfs,
|
||||
smartfile,
|
||||
smarthash,
|
||||
smartpath,
|
||||
MerkleTree,
|
||||
};
|
||||
|
||||
+9
-5
@@ -534,6 +534,10 @@ export class SkrApi {
|
||||
*/
|
||||
private async generatePdfReports(exporter: SkrExport, options: IExportOptions): Promise<void> {
|
||||
if (!this.reports) throw new Error('Reports not initialized');
|
||||
const skrType = this.currentSKRType;
|
||||
if (!skrType) {
|
||||
throw new Error('API not initialized. Call initialize() first.');
|
||||
}
|
||||
|
||||
const pdfOptions: IPdfReportOptions = {
|
||||
companyName: options.companyInfo?.name || 'Unternehmen',
|
||||
@@ -554,19 +558,19 @@ export class SkrApi {
|
||||
const trialBalance = await this.reports.getTrialBalance({
|
||||
dateFrom: options.dateFrom,
|
||||
dateTo: options.dateTo,
|
||||
skrType: this.currentSKRType
|
||||
skrType,
|
||||
});
|
||||
|
||||
const incomeStatement = await this.reports.getIncomeStatement({
|
||||
dateFrom: options.dateFrom,
|
||||
dateTo: options.dateTo,
|
||||
skrType: this.currentSKRType
|
||||
skrType,
|
||||
});
|
||||
|
||||
const balanceSheet = await this.reports.getBalanceSheet({
|
||||
dateFrom: options.dateFrom,
|
||||
dateTo: options.dateTo,
|
||||
skrType: this.currentSKRType
|
||||
skrType,
|
||||
});
|
||||
|
||||
// Generate PDFs
|
||||
@@ -702,7 +706,7 @@ export class SkrApi {
|
||||
const transaction = await this.postTransaction(transactions[i]);
|
||||
results.push(transaction);
|
||||
} catch (error) {
|
||||
errors.push({ index: i, error: error.message });
|
||||
errors.push({ index: i, error: error instanceof Error ? error.message : String(error) });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -735,7 +739,7 @@ export class SkrApi {
|
||||
const account = await this.createAccount(accounts[i]);
|
||||
results.push(account);
|
||||
} catch (error) {
|
||||
errors.push({ index: i, error: error.message });
|
||||
errors.push({ index: i, error: error instanceof Error ? error.message : String(error) });
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+39
-21
@@ -2,68 +2,86 @@ import * as plugins from './plugins.js';
|
||||
import { getDb, getDbSync } from './skr.database.js';
|
||||
import type { TAccountType, TSKRType, IAccountData } from './skr.types.js';
|
||||
|
||||
const { SmartDataDbDoc, svDb, unI, index, searchable } = plugins.smartdata;
|
||||
declare abstract class SmartDataDbDocBase {
|
||||
public save(): Promise<void>;
|
||||
public delete(): Promise<void>;
|
||||
public static getInstance<T>(
|
||||
this: new (...args: any[]) => T,
|
||||
query: Record<string, any>,
|
||||
): Promise<T | null>;
|
||||
public static getInstances<T>(
|
||||
this: new (...args: any[]) => T,
|
||||
query: Record<string, any>,
|
||||
): Promise<T[]>;
|
||||
}
|
||||
|
||||
@plugins.smartdata.Collection(() => getDbSync())
|
||||
export class Account extends SmartDataDbDoc<Account, Account> {
|
||||
const SmartDataDbDoc = plugins.smartdata.SmartDataDbDoc as unknown as typeof SmartDataDbDocBase;
|
||||
const Collection = plugins.smartdata.Collection as any;
|
||||
const svDb = plugins.smartdata.svDb as any;
|
||||
const unI = plugins.smartdata.unI as any;
|
||||
const index = plugins.smartdata.index as any;
|
||||
const searchable = plugins.smartdata.searchable as any;
|
||||
|
||||
@Collection(() => getDbSync())
|
||||
export class Account extends SmartDataDbDoc {
|
||||
@unI()
|
||||
public id: string;
|
||||
public id!: string;
|
||||
|
||||
@svDb()
|
||||
@index()
|
||||
public accountNumber: string;
|
||||
public accountNumber!: string;
|
||||
|
||||
@svDb()
|
||||
@searchable()
|
||||
public accountName: string;
|
||||
public accountName!: string;
|
||||
|
||||
@svDb()
|
||||
@index()
|
||||
public accountClass: number;
|
||||
public accountClass!: number;
|
||||
|
||||
@svDb()
|
||||
public accountGroup: number;
|
||||
public accountGroup!: number;
|
||||
|
||||
@svDb()
|
||||
public accountSubgroup: number;
|
||||
public accountSubgroup!: number;
|
||||
|
||||
@svDb()
|
||||
public accountType: TAccountType;
|
||||
public accountType!: TAccountType;
|
||||
|
||||
@svDb()
|
||||
@index()
|
||||
public skrType: TSKRType;
|
||||
public skrType!: TSKRType;
|
||||
|
||||
@svDb()
|
||||
@searchable()
|
||||
public description: string;
|
||||
public description!: string;
|
||||
|
||||
@svDb()
|
||||
public vatRate: number;
|
||||
public vatRate!: number;
|
||||
|
||||
@svDb()
|
||||
public balance: number;
|
||||
public balance!: number;
|
||||
|
||||
@svDb()
|
||||
public debitTotal: number;
|
||||
public debitTotal!: number;
|
||||
|
||||
@svDb()
|
||||
public creditTotal: number;
|
||||
public creditTotal!: number;
|
||||
|
||||
@svDb()
|
||||
public isActive: boolean;
|
||||
public isActive!: boolean;
|
||||
|
||||
@svDb()
|
||||
public isSystemAccount: boolean;
|
||||
public isSystemAccount!: boolean;
|
||||
|
||||
@svDb()
|
||||
public isAutomaticAccount: boolean;
|
||||
public isAutomaticAccount!: boolean;
|
||||
|
||||
@svDb()
|
||||
public createdAt: Date;
|
||||
public createdAt!: Date;
|
||||
|
||||
@svDb()
|
||||
public updatedAt: Date;
|
||||
public updatedAt!: Date;
|
||||
|
||||
constructor(data?: Partial<IAccountData>) {
|
||||
super();
|
||||
|
||||
@@ -262,6 +262,9 @@ export class ChartOfAccounts {
|
||||
* Search accounts
|
||||
*/
|
||||
public async searchAccounts(searchTerm: string): Promise<Account[]> {
|
||||
if (!this.skrType) {
|
||||
throw new Error('SKR type not set. Initialize SKR03 or SKR04 first.');
|
||||
}
|
||||
return await Account.searchAccounts(searchTerm, this.skrType);
|
||||
}
|
||||
|
||||
@@ -287,10 +290,11 @@ export class ChartOfAccounts {
|
||||
|
||||
// Apply text search if provided
|
||||
if (filter?.searchTerm) {
|
||||
const lowerSearchTerm = filter.searchTerm.toLowerCase();
|
||||
const searchTerm = filter.searchTerm;
|
||||
const lowerSearchTerm = searchTerm.toLowerCase();
|
||||
return accounts.filter(
|
||||
(account) =>
|
||||
account.accountNumber.includes(filter.searchTerm) ||
|
||||
account.accountNumber.includes(searchTerm) ||
|
||||
account.accountName.toLowerCase().includes(lowerSearchTerm) ||
|
||||
account.description.toLowerCase().includes(lowerSearchTerm),
|
||||
);
|
||||
@@ -468,9 +472,10 @@ export class ChartOfAccounts {
|
||||
await this.createCustomAccount(accountData);
|
||||
importedCount++;
|
||||
} catch (error) {
|
||||
const errorMessage = error instanceof Error ? error.message : String(error);
|
||||
this.logger.log(
|
||||
'warn',
|
||||
`Failed to import account ${parts[0]}: ${error.message}`,
|
||||
`Failed to import account ${parts[0]}: ${errorMessage}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,67 +13,85 @@ import type {
|
||||
IJournalEntryLine,
|
||||
} from './skr.types.js';
|
||||
|
||||
const { SmartDataDbDoc, svDb, unI, index, searchable } = plugins.smartdata;
|
||||
declare abstract class SmartDataDbDocBase {
|
||||
public save(): Promise<void>;
|
||||
public delete(): Promise<void>;
|
||||
public static getInstance<T>(
|
||||
this: new (...args: any[]) => T,
|
||||
query: Record<string, any>,
|
||||
): Promise<T | null>;
|
||||
public static getInstances<T>(
|
||||
this: new (...args: any[]) => T,
|
||||
query: Record<string, any>,
|
||||
): Promise<T[]>;
|
||||
}
|
||||
|
||||
@plugins.smartdata.Collection(() => getDbSync())
|
||||
export class JournalEntry extends SmartDataDbDoc<JournalEntry, JournalEntry> {
|
||||
const SmartDataDbDoc = plugins.smartdata.SmartDataDbDoc as unknown as typeof SmartDataDbDocBase;
|
||||
const Collection = plugins.smartdata.Collection as any;
|
||||
const svDb = plugins.smartdata.svDb as any;
|
||||
const unI = plugins.smartdata.unI as any;
|
||||
const index = plugins.smartdata.index as any;
|
||||
const searchable = plugins.smartdata.searchable as any;
|
||||
|
||||
@Collection(() => getDbSync())
|
||||
export class JournalEntry extends SmartDataDbDoc {
|
||||
@unI()
|
||||
public id: string;
|
||||
public id!: string;
|
||||
|
||||
@svDb()
|
||||
@index()
|
||||
public journalNumber: string;
|
||||
public journalNumber!: string;
|
||||
|
||||
@svDb()
|
||||
@index()
|
||||
public date: Date;
|
||||
public date!: Date;
|
||||
|
||||
@svDb()
|
||||
@searchable()
|
||||
public description: string;
|
||||
public description!: string;
|
||||
|
||||
@svDb()
|
||||
@index()
|
||||
public reference: string;
|
||||
public reference!: string;
|
||||
|
||||
@svDb()
|
||||
public lines: IJournalEntryLine[];
|
||||
public lines!: IJournalEntryLine[];
|
||||
|
||||
@svDb()
|
||||
@index()
|
||||
public skrType: TSKRType;
|
||||
public skrType!: TSKRType;
|
||||
|
||||
@svDb()
|
||||
public totalDebits: number;
|
||||
public totalDebits!: number;
|
||||
|
||||
@svDb()
|
||||
public totalCredits: number;
|
||||
public totalCredits!: number;
|
||||
|
||||
@svDb()
|
||||
public isBalanced: boolean;
|
||||
public isBalanced!: boolean;
|
||||
|
||||
@svDb()
|
||||
@index()
|
||||
public status: 'draft' | 'posted' | 'reversed';
|
||||
public status!: 'draft' | 'posted' | 'reversed';
|
||||
|
||||
@svDb()
|
||||
public transactionIds: string[];
|
||||
public transactionIds!: string[];
|
||||
|
||||
@svDb()
|
||||
@index()
|
||||
public period: string;
|
||||
public period!: string;
|
||||
|
||||
@svDb()
|
||||
public fiscalYear: number;
|
||||
public fiscalYear!: number;
|
||||
|
||||
@svDb()
|
||||
public createdAt: Date;
|
||||
public createdAt!: Date;
|
||||
|
||||
@svDb()
|
||||
public postedAt: Date;
|
||||
public postedAt!: Date | null;
|
||||
|
||||
@svDb()
|
||||
public createdBy: string;
|
||||
public createdBy!: string;
|
||||
|
||||
constructor(data?: Partial<IJournalEntry>) {
|
||||
super();
|
||||
@@ -240,7 +258,7 @@ export class JournalEntry extends SmartDataDbDoc<JournalEntry, JournalEntry> {
|
||||
try {
|
||||
await Account.validateAccountForPosting(line.accountNumber, this.skrType);
|
||||
} catch (error) {
|
||||
validationErrors.push(error.message);
|
||||
validationErrors.push(error instanceof Error ? error.message : String(error));
|
||||
continue; // Skip further validation for this line
|
||||
}
|
||||
|
||||
@@ -329,7 +347,7 @@ export class JournalEntry extends SmartDataDbDoc<JournalEntry, JournalEntry> {
|
||||
date: this.date,
|
||||
debitAccount: debitLines[0].accountNumber,
|
||||
creditAccount: creditLines[0].accountNumber,
|
||||
amount: debitLines[0].debit,
|
||||
amount: debitLines[0].debit || 0,
|
||||
description: this.description,
|
||||
reference: this.reference,
|
||||
skrType: this.skrType,
|
||||
|
||||
@@ -410,7 +410,20 @@ export class Reports {
|
||||
isActive: true,
|
||||
});
|
||||
|
||||
const ledgerEntries = [];
|
||||
const ledgerEntries: Array<{
|
||||
accountNumber: string;
|
||||
accountName: string;
|
||||
accountType: string;
|
||||
entries: Array<{
|
||||
date: Date;
|
||||
reference: string;
|
||||
description: string;
|
||||
debit: number;
|
||||
credit: number;
|
||||
balance: number;
|
||||
}>;
|
||||
finalBalance: number;
|
||||
}> = [];
|
||||
|
||||
for (const account of accounts) {
|
||||
const transactions = await this.getAccountTransactions(
|
||||
@@ -420,7 +433,14 @@ export class Reports {
|
||||
|
||||
if (transactions.length > 0) {
|
||||
let runningBalance = 0;
|
||||
const accountEntries = [];
|
||||
const accountEntries: Array<{
|
||||
date: Date;
|
||||
reference: string;
|
||||
description: string;
|
||||
debit: number;
|
||||
credit: number;
|
||||
balance: number;
|
||||
}> = [];
|
||||
|
||||
for (const transaction of transactions) {
|
||||
const isDebit = transaction.debitAccount === account.accountNumber;
|
||||
|
||||
@@ -7,75 +7,93 @@ import type {
|
||||
ITransactionData,
|
||||
} from './skr.types.js';
|
||||
|
||||
const { SmartDataDbDoc, svDb, unI, index, searchable } = plugins.smartdata;
|
||||
declare abstract class SmartDataDbDocBase {
|
||||
public save(): Promise<void>;
|
||||
public delete(): Promise<void>;
|
||||
public static getInstance<T>(
|
||||
this: new (...args: any[]) => T,
|
||||
query: Record<string, any>,
|
||||
): Promise<T | null>;
|
||||
public static getInstances<T>(
|
||||
this: new (...args: any[]) => T,
|
||||
query: Record<string, any>,
|
||||
): Promise<T[]>;
|
||||
}
|
||||
|
||||
@plugins.smartdata.Collection(() => getDbSync())
|
||||
export class Transaction extends SmartDataDbDoc<Transaction, Transaction> {
|
||||
const SmartDataDbDoc = plugins.smartdata.SmartDataDbDoc as unknown as typeof SmartDataDbDocBase;
|
||||
const Collection = plugins.smartdata.Collection as any;
|
||||
const svDb = plugins.smartdata.svDb as any;
|
||||
const unI = plugins.smartdata.unI as any;
|
||||
const index = plugins.smartdata.index as any;
|
||||
const searchable = plugins.smartdata.searchable as any;
|
||||
|
||||
@Collection(() => getDbSync())
|
||||
export class Transaction extends SmartDataDbDoc {
|
||||
@unI()
|
||||
public id: string;
|
||||
public id!: string;
|
||||
|
||||
@svDb()
|
||||
@index()
|
||||
public transactionNumber: string;
|
||||
public transactionNumber!: string;
|
||||
|
||||
@svDb()
|
||||
@index()
|
||||
public date: Date;
|
||||
public date!: Date;
|
||||
|
||||
@svDb()
|
||||
@index()
|
||||
public debitAccount: string;
|
||||
public debitAccount!: string;
|
||||
|
||||
@svDb()
|
||||
@index()
|
||||
public creditAccount: string;
|
||||
public creditAccount!: string;
|
||||
|
||||
@svDb()
|
||||
public amount: number;
|
||||
public amount!: number;
|
||||
|
||||
@svDb()
|
||||
@searchable()
|
||||
public description: string;
|
||||
public description!: string;
|
||||
|
||||
@svDb()
|
||||
@index()
|
||||
public reference: string;
|
||||
public reference!: string;
|
||||
|
||||
@svDb()
|
||||
@index()
|
||||
public skrType: TSKRType;
|
||||
public skrType!: TSKRType;
|
||||
|
||||
@svDb()
|
||||
public vatAmount: number;
|
||||
public vatAmount!: number;
|
||||
|
||||
@svDb()
|
||||
public costCenter: string;
|
||||
public costCenter!: string;
|
||||
|
||||
@svDb()
|
||||
@index()
|
||||
public status: TTransactionStatus;
|
||||
public status!: TTransactionStatus;
|
||||
|
||||
@svDb()
|
||||
public reversalOf: string;
|
||||
public reversalOf!: string;
|
||||
|
||||
@svDb()
|
||||
public reversedBy: string;
|
||||
public reversedBy!: string;
|
||||
|
||||
@svDb()
|
||||
@index()
|
||||
public period: string; // Format: YYYY-MM
|
||||
public period!: string; // Format: YYYY-MM
|
||||
|
||||
@svDb()
|
||||
public fiscalYear: number;
|
||||
public fiscalYear!: number;
|
||||
|
||||
@svDb()
|
||||
public createdAt: Date;
|
||||
public createdAt!: Date;
|
||||
|
||||
@svDb()
|
||||
public postedAt: Date;
|
||||
public postedAt!: Date | null;
|
||||
|
||||
@svDb()
|
||||
public createdBy: string;
|
||||
public createdBy!: string;
|
||||
|
||||
constructor(data?: Partial<ITransactionData>) {
|
||||
super();
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import * as plugins from './plugins.js';
|
||||
import * as path from 'path';
|
||||
import { SmartPdf } from '@push.rocks/smartpdf';
|
||||
import type { ITrialBalanceReport, IIncomeStatement, IBalanceSheet } from './skr.types.js';
|
||||
|
||||
export interface IPdfReportOptions {
|
||||
@@ -17,7 +18,7 @@ export interface IPdfReportOptions {
|
||||
export class PdfReportGenerator {
|
||||
private exportPath: string;
|
||||
private options: IPdfReportOptions;
|
||||
private pdfInstance: plugins.smartpdf.SmartPdf | null = null;
|
||||
private pdfInstance: SmartPdf | null = null;
|
||||
|
||||
constructor(exportPath: string, options: IPdfReportOptions) {
|
||||
this.exportPath = exportPath;
|
||||
@@ -28,7 +29,7 @@ export class PdfReportGenerator {
|
||||
* Initializes the PDF generator
|
||||
*/
|
||||
public async initialize(): Promise<void> {
|
||||
this.pdfInstance = new plugins.smartpdf.SmartPdf();
|
||||
this.pdfInstance = new SmartPdf();
|
||||
await this.pdfInstance.start();
|
||||
}
|
||||
|
||||
@@ -598,4 +599,4 @@ export class PdfReportGenerator {
|
||||
this.pdfInstance = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+31
-11
@@ -18,11 +18,25 @@ import type {
|
||||
*/
|
||||
export class InvoiceAdapter {
|
||||
private logger: plugins.smartlog.ConsoleLog;
|
||||
private readonly einvoiceModuleName = '@fin.cx/einvoice';
|
||||
|
||||
constructor() {
|
||||
this.logger = new plugins.smartlog.ConsoleLog();
|
||||
}
|
||||
|
||||
private async getEInvoiceClass(): Promise<{
|
||||
new (): any;
|
||||
fromXml(xmlString: string): Promise<any>;
|
||||
}> {
|
||||
const { EInvoice } = (await import(this.einvoiceModuleName)) as {
|
||||
EInvoice: {
|
||||
new (): any;
|
||||
fromXml(xmlString: string): Promise<any>;
|
||||
};
|
||||
};
|
||||
return EInvoice;
|
||||
}
|
||||
|
||||
private readonly MAX_XML_SIZE = 10 * 1024 * 1024; // 10MB max
|
||||
private readonly MAX_PDF_SIZE = 50 * 1024 * 1024; // 50MB max
|
||||
|
||||
@@ -44,13 +58,14 @@ export class InvoiceAdapter {
|
||||
}
|
||||
|
||||
// Parse the invoice using @fin.cx/einvoice
|
||||
let einvoice;
|
||||
const EInvoice = await this.getEInvoiceClass();
|
||||
let einvoice: any;
|
||||
if (typeof file === 'string') {
|
||||
einvoice = await plugins.einvoice.EInvoice.fromXml(file);
|
||||
einvoice = await EInvoice.fromXml(file);
|
||||
} else {
|
||||
// Convert buffer to string first
|
||||
const xmlString = file.toString('utf-8');
|
||||
einvoice = await plugins.einvoice.EInvoice.fromXml(xmlString);
|
||||
einvoice = await EInvoice.fromXml(xmlString);
|
||||
}
|
||||
|
||||
// Get detected format
|
||||
@@ -74,7 +89,7 @@ export class InvoiceAdapter {
|
||||
invoice.xmlContent = einvoice.getXml();
|
||||
|
||||
// Calculate content hash
|
||||
invoice.contentHash = await this.calculateContentHash(invoice.xmlContent);
|
||||
invoice.contentHash = await this.calculateContentHash(invoice.xmlContent!);
|
||||
|
||||
// Classify tax scenario
|
||||
invoice.taxScenario = this.classifyTaxScenario(invoice);
|
||||
@@ -82,7 +97,8 @@ export class InvoiceAdapter {
|
||||
return invoice;
|
||||
} catch (error) {
|
||||
this.logger.log('error', `Failed to parse invoice: ${error}`);
|
||||
throw new Error(`Invoice parsing failed: ${error.message}`);
|
||||
const errorMessage = error instanceof Error ? error.message : String(error);
|
||||
throw new Error(`Invoice parsing failed: ${errorMessage}`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -310,7 +326,7 @@ export class InvoiceAdapter {
|
||||
* Get exemption reason for VAT category
|
||||
*/
|
||||
private getExemptionReason(categoryCode: string): string | undefined {
|
||||
const exemptionReasons: Record<string, string> = {
|
||||
const exemptionReasons: Record<string, string | undefined> = {
|
||||
'E': 'Tax exempt',
|
||||
'Z': 'Zero rated',
|
||||
'AE': 'Reverse charge (§13b UStG)',
|
||||
@@ -516,7 +532,8 @@ export class InvoiceAdapter {
|
||||
): Promise<string> {
|
||||
try {
|
||||
// Load from existing XML
|
||||
const einvoice = await plugins.einvoice.EInvoice.fromXml(invoice.xmlContent!);
|
||||
const EInvoice = await this.getEInvoiceClass();
|
||||
const einvoice: any = await EInvoice.fromXml(invoice.xmlContent!);
|
||||
|
||||
// Convert to target format (takes ~0.6ms)
|
||||
const convertedXml = await einvoice.exportXml(targetFormat as any);
|
||||
@@ -524,7 +541,8 @@ export class InvoiceAdapter {
|
||||
return convertedXml;
|
||||
} catch (error) {
|
||||
this.logger.log('error', `Failed to convert invoice format: ${error}`);
|
||||
throw new Error(`Format conversion failed: ${error.message}`);
|
||||
const errorMessage = error instanceof Error ? error.message : String(error);
|
||||
throw new Error(`Format conversion failed: ${errorMessage}`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -537,7 +555,8 @@ export class InvoiceAdapter {
|
||||
): Promise<{ xml: string; pdf?: Buffer }> {
|
||||
try {
|
||||
// Create a new invoice instance
|
||||
const einvoice = new plugins.einvoice.EInvoice();
|
||||
const EInvoice = await this.getEInvoiceClass();
|
||||
const einvoice: any = new EInvoice();
|
||||
|
||||
// Set invoice data
|
||||
const businessTerms = this.mapToBusinessTerms(invoiceData);
|
||||
@@ -558,7 +577,8 @@ export class InvoiceAdapter {
|
||||
return { xml, pdf };
|
||||
} catch (error) {
|
||||
this.logger.log('error', `Failed to generate invoice: ${error}`);
|
||||
throw new Error(`Invoice generation failed: ${error.message}`);
|
||||
const errorMessage = error instanceof Error ? error.message : String(error);
|
||||
throw new Error(`Invoice generation failed: ${errorMessage}`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -578,4 +598,4 @@ export class InvoiceAdapter {
|
||||
// This would be a comprehensive mapping in production
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -131,10 +131,11 @@ export class InvoiceBookingEngine {
|
||||
};
|
||||
} catch (error) {
|
||||
this.logger.log('error', `Failed to book invoice: ${error}`);
|
||||
const errorMessage = error instanceof Error ? error.message : String(error);
|
||||
return {
|
||||
success: false,
|
||||
confidence: 0,
|
||||
errors: [`Booking failed: ${error.message}`]
|
||||
errors: [`Booking failed: ${errorMessage}`]
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -574,10 +575,11 @@ export class InvoiceBookingEngine {
|
||||
};
|
||||
} catch (error) {
|
||||
this.logger.log('error', `Failed to book payment: ${error}`);
|
||||
const errorMessage = error instanceof Error ? error.message : String(error);
|
||||
return {
|
||||
success: false,
|
||||
confidence: 0,
|
||||
errors: [`Payment booking failed: ${error.message}`]
|
||||
errors: [`Payment booking failed: ${errorMessage}`]
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -757,4 +759,4 @@ export class InvoiceBookingEngine {
|
||||
|
||||
return Array.from(accounts);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -224,7 +224,8 @@ export class InvoiceStorage {
|
||||
return contentHash;
|
||||
} catch (error) {
|
||||
this.logger.log('error', `Failed to store invoice: ${error}`);
|
||||
throw new Error(`Invoice storage failed: ${error.message}`);
|
||||
const errorMessage = error instanceof Error ? error.message : String(error);
|
||||
throw new Error(`Invoice storage failed: ${errorMessage}`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -707,4 +708,4 @@ export class InvoiceStorage {
|
||||
|
||||
this.logger.log('info', `Updated metadata for invoice: ${contentHash}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+23
-22
@@ -2,6 +2,7 @@ import * as plugins from './plugins.js';
|
||||
import * as path from 'path';
|
||||
import * as crypto from 'crypto';
|
||||
import * as https from 'https';
|
||||
import * as nodeForge from 'node-forge';
|
||||
|
||||
export interface ISigningOptions {
|
||||
certificatePem?: string;
|
||||
@@ -57,19 +58,19 @@ export class SecurityManager {
|
||||
|
||||
try {
|
||||
// Parse certificate and key
|
||||
const certificate = plugins.nodeForge.pki.certificateFromPem(cert);
|
||||
const certificate = nodeForge.pki.certificateFromPem(cert);
|
||||
const privateKey = this.options.privateKeyPassphrase
|
||||
? plugins.nodeForge.pki.decryptRsaPrivateKey(key, this.options.privateKeyPassphrase)
|
||||
: plugins.nodeForge.pki.privateKeyFromPem(key);
|
||||
? nodeForge.pki.decryptRsaPrivateKey(key, this.options.privateKeyPassphrase)
|
||||
: nodeForge.pki.privateKeyFromPem(key);
|
||||
|
||||
// Create PKCS#7 signed data (CMS)
|
||||
const p7 = plugins.nodeForge.pkcs7.createSignedData();
|
||||
const p7 = nodeForge.pkcs7.createSignedData();
|
||||
|
||||
// Add content
|
||||
if (typeof data === 'string') {
|
||||
p7.content = plugins.nodeForge.util.createBuffer(data, 'utf8');
|
||||
p7.content = nodeForge.util.createBuffer(data, 'utf8');
|
||||
} else {
|
||||
p7.content = plugins.nodeForge.util.createBuffer(data.toString('latin1'));
|
||||
p7.content = nodeForge.util.createBuffer(data.toString('latin1'));
|
||||
}
|
||||
|
||||
// Add certificate
|
||||
@@ -79,17 +80,17 @@ export class SecurityManager {
|
||||
p7.addSigner({
|
||||
key: privateKey,
|
||||
certificate: certificate,
|
||||
digestAlgorithm: plugins.nodeForge.pki.oids.sha256,
|
||||
digestAlgorithm: nodeForge.pki.oids.sha256,
|
||||
authenticatedAttributes: [
|
||||
{
|
||||
type: plugins.nodeForge.pki.oids.contentType,
|
||||
value: plugins.nodeForge.pki.oids.data
|
||||
type: nodeForge.pki.oids.contentType,
|
||||
value: nodeForge.pki.oids.data
|
||||
},
|
||||
{
|
||||
type: plugins.nodeForge.pki.oids.messageDigest
|
||||
type: nodeForge.pki.oids.messageDigest
|
||||
},
|
||||
{
|
||||
type: plugins.nodeForge.pki.oids.signingTime,
|
||||
type: nodeForge.pki.oids.signingTime,
|
||||
value: new Date().toISOString()
|
||||
}
|
||||
]
|
||||
@@ -99,7 +100,7 @@ export class SecurityManager {
|
||||
p7.sign({ detached: true });
|
||||
|
||||
// Convert to PEM
|
||||
const pem = plugins.nodeForge.pkcs7.messageToPem(p7);
|
||||
const pem = nodeForge.pkcs7.messageToPem(p7);
|
||||
|
||||
// Extract base64 signature
|
||||
const signature = pem
|
||||
@@ -237,14 +238,14 @@ export class SecurityManager {
|
||||
}
|
||||
|
||||
// Parse the PKCS#7 message
|
||||
const p7 = plugins.nodeForge.pkcs7.messageFromPem(pemSignature);
|
||||
const p7 = nodeForge.pkcs7.messageFromPem(pemSignature);
|
||||
|
||||
// Prepare content for verification
|
||||
let content: plugins.nodeForge.util.ByteStringBuffer;
|
||||
let content: nodeForge.util.ByteStringBuffer;
|
||||
if (typeof data === 'string') {
|
||||
content = plugins.nodeForge.util.createBuffer(data, 'utf8');
|
||||
content = nodeForge.util.createBuffer(data, 'utf8');
|
||||
} else {
|
||||
content = plugins.nodeForge.util.createBuffer(data.toString('latin1'));
|
||||
content = nodeForge.util.createBuffer(data.toString('latin1'));
|
||||
}
|
||||
|
||||
// Verify the signature
|
||||
@@ -267,8 +268,8 @@ export class SecurityManager {
|
||||
commonName: string = 'SKR Export System',
|
||||
validDays: number = 365
|
||||
): Promise<{ certificate: string; privateKey: string }> {
|
||||
const keys = plugins.nodeForge.pki.rsa.generateKeyPair(2048);
|
||||
const cert = plugins.nodeForge.pki.createCertificate();
|
||||
const keys = nodeForge.pki.rsa.generateKeyPair(2048);
|
||||
const cert = nodeForge.pki.createCertificate();
|
||||
|
||||
cert.publicKey = keys.publicKey;
|
||||
cert.serialNumber = '01';
|
||||
@@ -326,11 +327,11 @@ export class SecurityManager {
|
||||
]);
|
||||
|
||||
// Self-sign certificate
|
||||
cert.sign(keys.privateKey, plugins.nodeForge.md.sha256.create());
|
||||
cert.sign(keys.privateKey, nodeForge.md.sha256.create());
|
||||
|
||||
// Convert to PEM
|
||||
const certificatePem = plugins.nodeForge.pki.certificateToPem(cert);
|
||||
const privateKeyPem = plugins.nodeForge.pki.privateKeyToPem(keys.privateKey);
|
||||
const certificatePem = nodeForge.pki.certificateToPem(cert);
|
||||
const privateKeyPem = nodeForge.pki.privateKeyToPem(keys.privateKey);
|
||||
|
||||
return {
|
||||
certificate: certificatePem,
|
||||
@@ -402,4 +403,4 @@ export class SecurityManager {
|
||||
|
||||
return ltv;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user