206 lines
6.7 KiB
TypeScript
206 lines
6.7 KiB
TypeScript
|
import * as plugins from '../csvparser.plugins.js';
|
||
|
|
||
|
import * as interfaces from './interfaces/index.js';
|
||
|
|
||
|
export class CsvSpendesk extends plugins.portablefinance.AcCsvParser<any> {
|
||
|
// ========= STATIC ================
|
||
|
/**
|
||
|
* get the SpendeskData from an extracted direcotory
|
||
|
* @param dirPath
|
||
|
*/
|
||
|
public static async fromFile(filePath: string): Promise<CsvSpendesk> {
|
||
|
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<CsvSpendesk> {
|
||
|
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<CsvSpendesk> {
|
||
|
// 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<CsvSpendesk> {
|
||
|
this.transactionArray = this.transactionArray.concat(csvSpendeskInstance.transactionArray);
|
||
|
return this;
|
||
|
}
|
||
|
}
|