feat(core): Add support for CI workflows and update gitignore
This commit is contained in:
205
ts/spendesk/csv-spendesk.classes.csvspendesk.ts
Normal file
205
ts/spendesk/csv-spendesk.classes.csvspendesk.ts
Normal file
@ -0,0 +1,205 @@
|
||||
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;
|
||||
}
|
||||
}
|
1
ts/spendesk/index.ts
Normal file
1
ts/spendesk/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export * from './csv-spendesk.classes.csvspendesk.js';
|
1
ts/spendesk/interfaces/index.ts
Normal file
1
ts/spendesk/interfaces/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export * from './interface.spendesktransaction';
|
55
ts/spendesk/interfaces/interface.spendesktransaction.ts
Normal file
55
ts/spendesk/interfaces/interface.spendesktransaction.ts
Normal file
@ -0,0 +1,55 @@
|
||||
import * as plugins from '../csv-spendesk.plugins';
|
||||
|
||||
export type TAvailableCurrencies = 'EUR';
|
||||
export type TPaymentState = 'Settled';
|
||||
export type TPaymentType = 'Load' | 'Credit' | 'Payment' | 'FXfee';
|
||||
|
||||
export interface ISpendeskOriginalTransaction {
|
||||
simpleTransaction: plugins.tsclass.ITransaction;
|
||||
original: any;
|
||||
'Payment date': string;
|
||||
'Settlement date': string;
|
||||
Month: string;
|
||||
Payer: string;
|
||||
Team: string;
|
||||
Description: string;
|
||||
Supplier: string;
|
||||
'Expense account': string;
|
||||
'Payment method': string;
|
||||
Type: string;
|
||||
// 'Local amount': number;
|
||||
// 'Local currency': 'EUR';
|
||||
Debit: string;
|
||||
Credit: string;
|
||||
Currency: string;
|
||||
VAT: string;
|
||||
vatPercentage?: string;
|
||||
State: string;
|
||||
'Receipt?': string;
|
||||
'Receipt name(s)': '';
|
||||
}
|
||||
|
||||
export interface ISpendeskTransaction {
|
||||
simpleTransaction?: plugins.tsclass.ITransaction;
|
||||
original: ISpendeskOriginalTransaction;
|
||||
transactionHash: string;
|
||||
paymentDate: Date;
|
||||
settlementDate: Date;
|
||||
month: string;
|
||||
payer: string;
|
||||
team: string;
|
||||
description: string;
|
||||
supplier: string;
|
||||
expenseAccount: string;
|
||||
paymentMethod: string;
|
||||
paymentType: TPaymentType;
|
||||
// 'Local amount': number;
|
||||
// 'Local currency': 'EUR';
|
||||
amount: number;
|
||||
currency: TAvailableCurrencies;
|
||||
vatAmount: number;
|
||||
vatPercentage?: number;
|
||||
paymentState: TPaymentState;
|
||||
receiptAvailable: boolean;
|
||||
receiptNames: string[];
|
||||
}
|
Reference in New Issue
Block a user