import * as plugins from '../csvparser.plugins.js'; import * as interfaces from './interfaces/index.js'; export class CsvSpendesk extends plugins.portablefinance.AcCsvParser { // ========= STATIC ================ /** * get the SpendeskData from an extracted direcotory * @param dirPath */ public static async fromFile(filePath: string): Promise { const reresolvedPath = plugins.path.resolve(filePath); const fileString = plugins.smartfile.fs.toStringSync(reresolvedPath); const csvSpendesk = await CsvSpendesk.fromCsvString(fileString); return csvSpendesk; } /** * get the SpendeskData from an extracted direcotory * @param dirPath */ public static async fromDir(dirPath: string): Promise { const foundFiles: string[] = await plugins.smartfile.fs.listFileTree( dirPath, '**/Spendesk*', true ); if (foundFiles.length === 0) { throw new Error('no files found!'); } const csvSpendesks: CsvSpendesk[] = []; for (const foundFile of foundFiles) { const fileString = plugins.smartfile.fs.toStringSync(plugins.path.resolve(foundFile)); plugins.path.join(dirPath, foundFile); csvSpendesks.push(await this.fromFile(foundFile)); } let returnCsvSpendesk: CsvSpendesk; for (const csvSpendeskInstance of csvSpendesks) { if (!returnCsvSpendesk) { returnCsvSpendesk = csvSpendeskInstance; } else { await returnCsvSpendesk.concat(csvSpendeskInstance); } } return returnCsvSpendesk; } public static async fromCsvString(csvStringArg: string): Promise { // lets parse the data from the directory const csvInstance = await plugins.smartcsv.Csv.createCsvFromString(csvStringArg, { headers: true }); // lets differentiate between payments and credits const originalTransactionArray: interfaces.ISpendeskOriginalTransaction[] = (await csvInstance.exportAsObject()) as interfaces.ISpendeskOriginalTransaction[]; const paymentsArray: interfaces.ISpendeskTransaction[] = []; for (const originalTransaction of originalTransactionArray) { const finalTransaction: interfaces.ISpendeskTransaction = { // the original transaction original: originalTransaction, // assigned later paymentType: null, amount: null, simpleTransaction: null, transactionHash: null, // assigned now currency: originalTransaction.Currency as interfaces.TAvailableCurrencies, description: originalTransaction.Description, expenseAccount: originalTransaction['Expense account'], month: originalTransaction.Month, payer: originalTransaction.Payer, paymentDate: new Date(originalTransaction['Payment date']), paymentMethod: originalTransaction['Payment method'], paymentState: originalTransaction.State as interfaces.TPaymentState, settlementDate: new Date(originalTransaction['Settlement date']), receiptAvailable: (() => { if ((originalTransaction['Receipt?'] as any) === 'Yes') { return true; } else { return false; } })(), receiptNames: [], supplier: originalTransaction.Supplier, team: originalTransaction.Team, vatAmount: parseFloat(originalTransaction.VAT), vatPercentage: ((): number => { if (!originalTransaction.VAT || originalTransaction.VAT === '0') { return 0; } else { const vatAmount = parseFloat(originalTransaction.VAT); const debitAmount = parseFloat(originalTransaction.Debit); return Math.round((vatAmount / (debitAmount - vatAmount)) * 100); } })() }; // type finalTransaction.paymentType = (() => { let paymentType: interfaces.TPaymentType; if (parseFloat(finalTransaction.original.Credit) !== 0) { paymentType = 'Load'; } else if (parseFloat(originalTransaction.Debit) !== 0) { paymentType = 'Payment'; } if (originalTransaction.Description.startsWith('FX fee')) { paymentType = 'FXfee'; } return paymentType; })(); // amount finalTransaction.amount = (() => { switch (parseFloat(originalTransaction.Credit)) { case 0: return -parseFloat(originalTransaction.Debit); default: return parseFloat(originalTransaction.Credit); } })(); // transaction hash finalTransaction.transactionHash = await plugins.smarthash.sha265FromObject({ amount: finalTransaction.amount, transactionDate: finalTransaction.paymentDate, settlementDate: finalTransaction.settlementDate, supplier: finalTransaction.supplier }); // simple transaction finalTransaction.simpleTransaction = { accountId: null, id: finalTransaction.transactionHash, amount: finalTransaction.amount, date: finalTransaction.settlementDate, description: finalTransaction.description, name: finalTransaction.supplier }; paymentsArray.push(finalTransaction); } const csvSpendeskInstance = new CsvSpendesk(paymentsArray); return csvSpendeskInstance; } /** * get the SpendeskData from Spendesk.com * @param dirPath */ public static async fromSpendeskCom(dirPath: string) { // TODO: implement spendesk API throw new Error(`method is not yet implemented`); } // ========= INSTANCE ================ public paymentProviderName: string = 'Spendesk'; public origin: 'api' | 'file' | 'dir'; public updateFunction: ( fromTimeStamp: plugins.smarttime.TimeStamp, untilTimeStamp: plugins.smarttime.TimeStamp ) => interfaces.ISpendeskTransaction[]; public transactionArray: interfaces.ISpendeskTransaction[]; constructor(transactionArrayArg: interfaces.ISpendeskTransaction[]) { super(); this.transactionArray = transactionArrayArg; } /** * gets all transactions */ public async getTransactions() { return this.transactionArray; } /** * gets all loads */ public async getLoads() { return this.transactionArray.filter(payment => { return payment.paymentType === 'Load'; }); } public async getDebits() { return this.transactionArray.filter(payment => { return payment.paymentType === 'Payment'; }); } /** * concat this instance's transactions with those of another one */ public async concat(csvSpendeskInstance: CsvSpendesk): Promise { this.transactionArray = this.transactionArray.concat(csvSpendeskInstance.transactionArray); return this; } }