@fin.cx/skr
Version:
SKR03 and SKR04 German accounting standards for double-entry bookkeeping
753 lines • 57.1 kB
JavaScript
import * as plugins from './plugins.js';
import * as path from 'path';
import { ChartOfAccounts } from './skr.classes.chartofaccounts.js';
import { Ledger } from './skr.classes.ledger.js';
import { Reports } from './skr.classes.reports.js';
import { Account } from './skr.classes.account.js';
import { Transaction } from './skr.classes.transaction.js';
import { JournalEntry } from './skr.classes.journalentry.js';
import { SkrExport } from './skr.export.js';
import { LedgerExporter } from './skr.export.ledger.js';
import { AccountsExporter } from './skr.export.accounts.js';
import { BalancesExporter } from './skr.export.balances.js';
import { PdfReportGenerator } from './skr.export.pdf.js';
import { SecurityManager } from './skr.security.js';
import { InvoiceAdapter } from './skr.invoice.adapter.js';
import { InvoiceStorage } from './skr.invoice.storage.js';
import { InvoiceBookingEngine } from './skr.invoice.booking.js';
/**
* Main API class for SKR accounting operations
*/
export class SkrApi {
constructor(config) {
this.config = config;
this.ledger = null;
this.reports = null;
this.initialized = false;
this.currentSKRType = null;
this.invoiceAdapter = null;
this.invoiceStorage = null;
this.invoiceBookingEngine = null;
this.chartOfAccounts = new ChartOfAccounts(config);
this.logger = new plugins.smartlog.Smartlog({
logContext: {
company: 'fin.cx',
companyunit: 'skr',
containerName: 'SkrApi',
environment: 'local',
runtime: 'node',
zone: 'local',
},
});
}
/**
* Initialize the API with specified SKR type
*/
async initialize(skrType) {
this.logger.log('info', `Initializing SKR API with ${skrType}`);
// Initialize chart of accounts
if (skrType === 'SKR03') {
await this.chartOfAccounts.initializeSKR03();
}
else if (skrType === 'SKR04') {
await this.chartOfAccounts.initializeSKR04();
}
else {
throw new Error(`Invalid SKR type: ${skrType}`);
}
this.currentSKRType = skrType;
this.ledger = new Ledger(skrType);
this.reports = new Reports(skrType);
// Initialize invoice components
this.invoiceAdapter = new InvoiceAdapter();
const invoicePath = this.config.invoiceExportPath || path.resolve(process.cwd(), 'exports', 'invoices');
this.invoiceStorage = new InvoiceStorage(invoicePath);
this.invoiceBookingEngine = new InvoiceBookingEngine(skrType);
this.initialized = true;
this.logger.log('info', 'SKR API initialized successfully');
}
/**
* Ensure API is initialized
*/
ensureInitialized() {
if (!this.initialized || !this.currentSKRType) {
throw new Error('API not initialized. Call initialize() first.');
}
}
// ========== Account Management ==========
/**
* Create a new account
*/
async createAccount(accountData) {
this.ensureInitialized();
return await this.chartOfAccounts.createCustomAccount(accountData);
}
/**
* Get account by number
*/
async getAccount(accountNumber) {
this.ensureInitialized();
return await this.chartOfAccounts.getAccountByNumber(accountNumber);
}
/**
* Update an account
*/
async updateAccount(accountNumber, updates) {
this.ensureInitialized();
return await this.chartOfAccounts.updateAccount(accountNumber, updates);
}
/**
* Delete an account
*/
async deleteAccount(accountNumber) {
this.ensureInitialized();
await this.chartOfAccounts.deleteAccount(accountNumber);
}
/**
* List accounts with optional filter
*/
async listAccounts(filter) {
this.ensureInitialized();
return await this.chartOfAccounts.getAllAccounts(filter);
}
/**
* Search accounts by term
*/
async searchAccounts(searchTerm) {
this.ensureInitialized();
return await this.chartOfAccounts.searchAccounts(searchTerm);
}
/**
* Get accounts by class
*/
async getAccountsByClass(accountClass) {
this.ensureInitialized();
return await this.chartOfAccounts.getAccountsByClass(accountClass);
}
/**
* Get accounts by type
*/
async getAccountsByType(accountType) {
this.ensureInitialized();
return await this.chartOfAccounts.getAccountsByType(accountType);
}
// ========== Transaction Management ==========
/**
* Post a simple transaction
*/
async postTransaction(transactionData) {
this.ensureInitialized();
if (!this.ledger)
throw new Error('Ledger not initialized');
return await this.ledger.postTransaction(transactionData);
}
/**
* Post a journal entry
*/
async postJournalEntry(journalData) {
this.ensureInitialized();
if (!this.ledger)
throw new Error('Ledger not initialized');
return await this.ledger.postJournalEntry(journalData);
}
/**
* Get transaction by ID
*/
async getTransaction(transactionId) {
this.ensureInitialized();
return await Transaction.getTransactionById(transactionId);
}
/**
* List transactions with optional filter
*/
async listTransactions(filter) {
this.ensureInitialized();
return await this.chartOfAccounts.getTransactions(filter);
}
/**
* Get transactions for specific account
*/
async getAccountTransactions(accountNumber) {
this.ensureInitialized();
return await this.chartOfAccounts.getAccountTransactions(accountNumber);
}
/**
* Reverse a transaction
*/
async reverseTransaction(transactionId) {
this.ensureInitialized();
return await this.chartOfAccounts.reverseTransaction(transactionId);
}
/**
* Reverse a journal entry
*/
async reverseJournalEntry(journalId) {
this.ensureInitialized();
if (!this.ledger)
throw new Error('Ledger not initialized');
return await this.ledger.reverseJournalEntry(journalId);
}
// ========== Reporting ==========
/**
* Generate trial balance
*/
async generateTrialBalance(params) {
this.ensureInitialized();
if (!this.reports)
throw new Error('Reports not initialized');
return await this.reports.getTrialBalance(params);
}
/**
* Generate income statement
*/
async generateIncomeStatement(params) {
this.ensureInitialized();
if (!this.reports)
throw new Error('Reports not initialized');
return await this.reports.getIncomeStatement(params);
}
/**
* Generate balance sheet
*/
async generateBalanceSheet(params) {
this.ensureInitialized();
if (!this.reports)
throw new Error('Reports not initialized');
return await this.reports.getBalanceSheet(params);
}
/**
* Generate general ledger
*/
async generateGeneralLedger(params) {
this.ensureInitialized();
if (!this.reports)
throw new Error('Reports not initialized');
return await this.reports.getGeneralLedger(params);
}
/**
* Generate cash flow statement
*/
async generateCashFlowStatement(params) {
this.ensureInitialized();
if (!this.reports)
throw new Error('Reports not initialized');
return await this.reports.getCashFlowStatement(params);
}
/**
* Export report to CSV
*/
async exportReportToCSV(reportType, params) {
this.ensureInitialized();
if (!this.reports)
throw new Error('Reports not initialized');
return await this.reports.exportToCSV(reportType, params);
}
/**
* Export to DATEV format
*/
async exportToDATEV(params) {
this.ensureInitialized();
if (!this.reports)
throw new Error('Reports not initialized');
return await this.reports.exportToDATEV(params);
}
// ========== Period Management ==========
/**
* Close accounting period
*/
async closePeriod(period, closingAccountNumber) {
this.ensureInitialized();
if (!this.ledger)
throw new Error('Ledger not initialized');
return await this.ledger.closeAccountingPeriod(period, closingAccountNumber);
}
/**
* Get account balance
*/
async getAccountBalance(accountNumber, asOfDate) {
this.ensureInitialized();
if (!this.ledger)
throw new Error('Ledger not initialized');
return await this.ledger.getAccountBalance(accountNumber, asOfDate);
}
/**
* Recalculate all account balances
*/
async recalculateBalances() {
this.ensureInitialized();
if (!this.ledger)
throw new Error('Ledger not initialized');
await this.ledger.recalculateAllBalances();
}
// ========== Import/Export ==========
/**
* Import accounts from CSV
*/
async importAccountsFromCSV(csvContent) {
this.ensureInitialized();
return await this.chartOfAccounts.importAccountsFromCSV(csvContent);
}
/**
* Export accounts to CSV
*/
async exportAccountsToCSV() {
this.ensureInitialized();
return await this.chartOfAccounts.exportAccountsToCSV();
}
/**
* Export Jahresabschluss in GoBD-compliant BagIt format
* Creates a revision-safe export for 10-year archival
*/
async exportJahresabschluss(options) {
this.ensureInitialized();
if (!this.ledger || !this.reports || !this.currentSKRType) {
throw new Error('API not fully initialized');
}
this.logger.log('info', `Starting Jahresabschluss export for fiscal year ${options.fiscalYear}`);
// Create export instance
const exporter = new SkrExport(options);
// Create BagIt structure
await exporter.createBagItStructure();
await exporter.createExportMetadata(this.currentSKRType);
await exporter.createSchemas();
// Export accounting data
await this.exportLedgerData(exporter, options);
await this.exportAccountData(exporter, options);
await this.exportBalanceData(exporter, options);
// Generate PDF reports if requested
if (options.generatePdfReports) {
await this.generatePdfReports(exporter, options);
}
// Sign export if requested
if (options.signExport) {
await this.signExport(exporter, options);
}
// Create manifests and validate
await exporter.writeManifests();
const merkleRoot = await exporter.createMerkleTree();
const isValid = await exporter.validateBagIt();
if (!isValid) {
throw new Error('BagIt validation failed');
}
this.logger.log('ok', `Jahresabschluss export completed. Merkle root: ${merkleRoot}`);
return options.exportPath;
}
/**
* Export ledger data in NDJSON format
*/
async exportLedgerData(exporter, options) {
if (!this.ledger)
throw new Error('Ledger not initialized');
const ledgerExporter = new LedgerExporter(options.exportPath);
await ledgerExporter.initialize();
// Get all transactions for the period
const transactions = await this.chartOfAccounts.getTransactions({
dateFrom: options.dateFrom,
dateTo: options.dateTo
});
// Export each transaction
for (const transaction of transactions) {
const transactionData = transaction;
await ledgerExporter.exportTransaction(transactionData);
}
// Get all journal entries for the period
// Use MongoDB query syntax for date range
const journalEntries = await JournalEntry.getInstances({
date: {
$gte: options.dateFrom,
$lte: options.dateTo
}, // SmartData supports MongoDB query operators
skrType: this.currentSKRType
});
// Export each journal entry
for (const entry of journalEntries) {
const entryData = entry;
await ledgerExporter.exportJournalEntry(entryData);
}
const entryCount = await ledgerExporter.close();
this.logger.log('info', `Exported ${entryCount} ledger entries`);
}
/**
* Export account data in CSV format
*/
async exportAccountData(exporter, options) {
const accountsExporter = new AccountsExporter(options.exportPath);
// Get all accounts
const accounts = await this.chartOfAccounts.getAllAccounts();
// Add each account to export
for (const account of accounts) {
const accountData = account;
accountsExporter.addAccount(accountData);
}
// Export to CSV and JSON
await accountsExporter.exportToCSV();
await accountsExporter.exportToJSON();
this.logger.log('info', `Exported ${accountsExporter.getAccountCount()} accounts`);
}
/**
* Export balance data in CSV format
*/
async exportBalanceData(exporter, options) {
if (!this.ledger)
throw new Error('Ledger not initialized');
const balancesExporter = new BalancesExporter(options.exportPath, options.fiscalYear);
// Get all accounts with balances
const accounts = await this.chartOfAccounts.getAllAccounts();
for (const account of accounts) {
const balance = await this.ledger.getAccountBalance(account.accountNumber, options.dateTo);
if (balance) {
balancesExporter.addBalance(account.accountNumber, account.accountName, balance, `${options.fiscalYear}`);
}
}
// Export balance reports
await balancesExporter.exportToCSV();
await balancesExporter.exportTrialBalance();
await balancesExporter.exportClassSummary();
this.logger.log('info', `Exported ${balancesExporter.getBalanceCount()} account balances`);
}
/**
* Generate PDF reports for the export
*/
async generatePdfReports(exporter, options) {
if (!this.reports)
throw new Error('Reports not initialized');
const pdfOptions = {
companyName: options.companyInfo?.name || 'Unternehmen',
companyAddress: options.companyInfo?.address,
taxId: options.companyInfo?.taxId,
registrationNumber: options.companyInfo?.registrationNumber,
fiscalYear: options.fiscalYear,
dateFrom: options.dateFrom,
dateTo: options.dateTo,
preparedDate: new Date()
};
const pdfGenerator = new PdfReportGenerator(options.exportPath, pdfOptions);
await pdfGenerator.initialize();
try {
// Generate reports
const trialBalance = await this.reports.getTrialBalance({
dateFrom: options.dateFrom,
dateTo: options.dateTo,
skrType: this.currentSKRType
});
const incomeStatement = await this.reports.getIncomeStatement({
dateFrom: options.dateFrom,
dateTo: options.dateTo,
skrType: this.currentSKRType
});
const balanceSheet = await this.reports.getBalanceSheet({
dateFrom: options.dateFrom,
dateTo: options.dateTo,
skrType: this.currentSKRType
});
// Generate PDFs
const jahresabschlussPdf = await pdfGenerator.generateJahresabschlussPdf(trialBalance, incomeStatement, balanceSheet);
// Save PDFs
await pdfGenerator.savePdfReport('jahresabschluss.pdf', jahresabschlussPdf);
// Store in BagIt structure
await exporter.storeDocument(jahresabschlussPdf, 'jahresabschluss.pdf');
this.logger.log('info', 'PDF reports generated successfully');
}
finally {
await pdfGenerator.close();
}
}
/**
* Sign the export with CAdES signature
*/
async signExport(exporter, options) {
const signingOptions = {
certificatePem: options.signExport ? undefined : undefined, // Use provided cert or generate
privateKeyPem: options.signExport ? undefined : undefined,
includeTimestamp: options.timestampExport !== false
};
const security = new SecurityManager(signingOptions);
// Generate self-signed certificate if none provided
let cert, key;
if (!signingOptions.certificatePem) {
const generated = await security.generateSelfSignedCertificate(options.companyInfo?.name || 'SKR Export System');
cert = generated.certificate;
key = generated.privateKey;
}
else {
cert = signingOptions.certificatePem;
key = signingOptions.privateKeyPem;
}
// Sign the manifest
const manifestPath = path.resolve(options.exportPath, `jahresabschluss_${options.fiscalYear}`, 'manifest-sha256.txt');
await security.createDetachedSignature(manifestPath, path.resolve(options.exportPath, `jahresabschluss_${options.fiscalYear}`, 'data', 'metadata', 'signatures', 'manifest.cades'));
this.logger.log('info', 'Export signed with CAdES signature');
}
// ========== Utility Methods ==========
/**
* Get current SKR type
*/
getSKRType() {
return this.currentSKRType;
}
/**
* Get account class description
*/
getAccountClassDescription(accountClass) {
this.ensureInitialized();
return this.chartOfAccounts.getAccountClassDescription(accountClass);
}
/**
* Validate double-entry rules
*/
validateDoubleEntry(debitAmount, creditAmount) {
if (!this.ledger)
throw new Error('Ledger not initialized');
return this.ledger.validateDoubleEntry(debitAmount, creditAmount);
}
/**
* Get unbalanced transactions (for audit)
*/
async getUnbalancedTransactions() {
this.ensureInitialized();
if (!this.ledger)
throw new Error('Ledger not initialized');
return await this.ledger.getUnbalancedTransactions();
}
/**
* Close the API and database connection
*/
async close() {
await this.chartOfAccounts.close();
this.initialized = false;
this.currentSKRType = null;
this.ledger = null;
this.reports = null;
this.logger.log('info', 'SKR API closed');
}
// ========== Batch Operations ==========
/**
* Post multiple transactions
*/
async postBatchTransactions(transactions) {
this.ensureInitialized();
const results = [];
const errors = [];
for (let i = 0; i < transactions.length; i++) {
try {
const transaction = await this.postTransaction(transactions[i]);
results.push(transaction);
}
catch (error) {
errors.push({ index: i, error: error.message });
}
}
if (errors.length > 0) {
this.logger.log('warn', `Batch transaction posting completed with ${errors.length} errors`);
throw new Error(`Batch posting failed for ${errors.length} transactions: ${JSON.stringify(errors)}`);
}
return results;
}
/**
* Create multiple accounts
*/
async createBatchAccounts(accounts) {
this.ensureInitialized();
const results = [];
const errors = [];
for (let i = 0; i < accounts.length; i++) {
try {
const account = await this.createAccount(accounts[i]);
results.push(account);
}
catch (error) {
errors.push({ index: i, error: error.message });
}
}
if (errors.length > 0) {
this.logger.log('warn', `Batch account creation completed with ${errors.length} errors`);
throw new Error(`Batch creation failed for ${errors.length} accounts: ${JSON.stringify(errors)}`);
}
return results;
}
// ========== Pagination Support ==========
/**
* Get paginated accounts
*/
async getAccountsPaginated(page = 1, pageSize = 50, filter) {
this.ensureInitialized();
const allAccounts = await this.listAccounts(filter);
const total = allAccounts.length;
const totalPages = Math.ceil(total / pageSize);
const start = (page - 1) * pageSize;
const end = start + pageSize;
const data = allAccounts.slice(start, end);
return {
data,
total,
page,
pageSize,
totalPages,
};
}
/**
* Get paginated transactions
*/
async getTransactionsPaginated(page = 1, pageSize = 50, filter) {
this.ensureInitialized();
const allTransactions = await this.listTransactions(filter);
const total = allTransactions.length;
const totalPages = Math.ceil(total / pageSize);
const start = (page - 1) * pageSize;
const end = start + pageSize;
const data = allTransactions.slice(start, end);
return {
data,
total,
page,
pageSize,
totalPages,
};
}
// ========== Invoice Management ==========
/**
* Import an invoice from file or buffer
* Parses, validates, and optionally books the invoice
*/
async importInvoice(file, direction, options) {
this.ensureInitialized();
if (!this.invoiceAdapter || !this.invoiceStorage || !this.invoiceBookingEngine) {
throw new Error('Invoice components not initialized');
}
this.logger.log('info', `Importing ${direction} invoice`);
// Parse and validate invoice
const invoice = await this.invoiceAdapter.parseInvoice(file, direction);
// Store invoice
await this.invoiceStorage.initialize();
const contentHash = await this.invoiceStorage.storeInvoice(invoice);
invoice.contentHash = contentHash;
// Auto-book if requested
if (options?.autoBook) {
const bookingResult = await this.bookInvoice(invoice, options.bookingRules, {
autoBook: true,
confidenceThreshold: options.confidenceThreshold || 80,
skipValidation: options.validateOnly
});
if (bookingResult.success && bookingResult.bookingInfo) {
invoice.bookingInfo = bookingResult.bookingInfo;
invoice.status = 'posted';
// Update stored metadata with booking information
await this.invoiceStorage.updateMetadata(invoice.contentHash, {
journalEntryId: bookingResult.bookingInfo.journalEntryId,
transactionIds: bookingResult.bookingInfo.transactionIds
});
}
}
this.logger.log('info', `Invoice imported successfully: ${invoice.invoiceNumber}`);
return invoice;
}
/**
* Book an invoice to the ledger
*/
async bookInvoice(invoice, bookingRules, options) {
this.ensureInitialized();
if (!this.invoiceBookingEngine) {
throw new Error('Invoice booking engine not initialized');
}
this.logger.log('info', `Booking invoice ${invoice.invoiceNumber}`);
const result = await this.invoiceBookingEngine.bookInvoice(invoice, bookingRules, options);
if (result.success) {
this.logger.log('info', `Invoice booked successfully with confidence ${result.confidence}%`);
// Update stored metadata if invoice has a content hash
if (invoice.contentHash && result.bookingInfo && this.invoiceStorage) {
await this.invoiceStorage.updateMetadata(invoice.contentHash, {
journalEntryId: result.bookingInfo.journalEntryId,
transactionIds: result.bookingInfo.transactionIds
});
}
}
else {
this.logger.log('error', `Invoice booking failed: ${result.errors?.join(', ')}`);
}
return result;
}
/**
* Export an invoice in a different format
*/
async exportInvoice(invoice, options) {
this.ensureInitialized();
if (!this.invoiceAdapter) {
throw new Error('Invoice adapter not initialized');
}
this.logger.log('info', `Exporting invoice ${invoice.invoiceNumber} to ${options.format}`);
// Convert format if needed
const xml = await this.invoiceAdapter.convertFormat(invoice, options.format);
// Generate PDF if requested
let pdf;
if (options.embedInPdf) {
const result = await this.invoiceAdapter.generateInvoice(invoice, options.format);
pdf = result.pdf;
}
return { xml, pdf };
}
/**
* Search invoices by filter
*/
async searchInvoices(filter) {
this.ensureInitialized();
if (!this.invoiceStorage) {
throw new Error('Invoice storage not initialized');
}
await this.invoiceStorage.initialize();
const metadata = await this.invoiceStorage.searchInvoices(filter);
const invoices = [];
for (const meta of metadata) {
const invoice = await this.invoiceStorage.retrieveInvoice(meta.contentHash);
if (invoice) {
invoices.push(invoice);
}
}
return invoices;
}
/**
* Get invoice by content hash
*/
async getInvoice(contentHash) {
this.ensureInitialized();
if (!this.invoiceStorage) {
throw new Error('Invoice storage not initialized');
}
await this.invoiceStorage.initialize();
return await this.invoiceStorage.retrieveInvoice(contentHash);
}
/**
* Get invoice storage statistics
*/
async getInvoiceStatistics() {
this.ensureInitialized();
if (!this.invoiceStorage) {
throw new Error('Invoice storage not initialized');
}
await this.invoiceStorage.initialize();
return await this.invoiceStorage.getStatistics();
}
/**
* Create EN16931 compliance report for invoices
*/
async createInvoiceComplianceReport() {
this.ensureInitialized();
if (!this.invoiceStorage) {
throw new Error('Invoice storage not initialized');
}
await this.invoiceStorage.initialize();
await this.invoiceStorage.createComplianceReport();
this.logger.log('info', 'Invoice compliance report created');
}
/**
* Generate an invoice from internal data
*/
async generateInvoice(invoiceData, format) {
this.ensureInitialized();
if (!this.invoiceAdapter) {
throw new Error('Invoice adapter not initialized');
}
this.logger.log('info', `Generating invoice in ${format} format`);
return await this.invoiceAdapter.generateInvoice(invoiceData, format);
}
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2tyLmFwaS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3RzL3Nrci5hcGkudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxLQUFLLE9BQU8sTUFBTSxjQUFjLENBQUM7QUFDeEMsT0FBTyxLQUFLLElBQUksTUFBTSxNQUFNLENBQUM7QUFDN0IsT0FBTyxFQUFFLGVBQWUsRUFBRSxNQUFNLGtDQUFrQyxDQUFDO0FBQ25FLE9BQU8sRUFBRSxNQUFNLEVBQUUsTUFBTSx5QkFBeUIsQ0FBQztBQUNqRCxPQUFPLEVBQUUsT0FBTyxFQUFFLE1BQU0sMEJBQTBCLENBQUM7QUFDbkQsT0FBTyxFQUFFLE9BQU8sRUFBRSxNQUFNLDBCQUEwQixDQUFDO0FBQ25ELE9BQU8sRUFBRSxXQUFXLEVBQUUsTUFBTSw4QkFBOEIsQ0FBQztBQUMzRCxPQUFPLEVBQUUsWUFBWSxFQUFFLE1BQU0sK0JBQStCLENBQUM7QUFDN0QsT0FBTyxFQUFFLFNBQVMsRUFBdUIsTUFBTSxpQkFBaUIsQ0FBQztBQUNqRSxPQUFPLEVBQUUsY0FBYyxFQUFFLE1BQU0sd0JBQXdCLENBQUM7QUFDeEQsT0FBTyxFQUFFLGdCQUFnQixFQUFFLE1BQU0sMEJBQTBCLENBQUM7QUFDNUQsT0FBTyxFQUFFLGdCQUFnQixFQUFFLE1BQU0sMEJBQTBCLENBQUM7QUFDNUQsT0FBTyxFQUFFLGtCQUFrQixFQUEwQixNQUFNLHFCQUFxQixDQUFDO0FBQ2pGLE9BQU8sRUFBRSxlQUFlLEVBQXdCLE1BQU0sbUJBQW1CLENBQUM7QUFDMUUsT0FBTyxFQUFFLGNBQWMsRUFBRSxNQUFNLDBCQUEwQixDQUFDO0FBQzFELE9BQU8sRUFBRSxjQUFjLEVBQUUsTUFBTSwwQkFBMEIsQ0FBQztBQUMxRCxPQUFPLEVBQUUsb0JBQW9CLEVBQTZDLE1BQU0sMEJBQTBCLENBQUM7QUF3QjNHOztHQUVHO0FBQ0gsTUFBTSxPQUFPLE1BQU07SUFXakIsWUFBb0IsTUFBdUI7UUFBdkIsV0FBTSxHQUFOLE1B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