feat(core): Add support for CI workflows and update gitignore
This commit is contained in:
commit
7fc8f04be9
66
.gitea/workflows/default_nottags.yaml
Normal file
66
.gitea/workflows/default_nottags.yaml
Normal file
@ -0,0 +1,66 @@
|
||||
name: Default (not tags)
|
||||
|
||||
on:
|
||||
push:
|
||||
tags-ignore:
|
||||
- '**'
|
||||
|
||||
env:
|
||||
IMAGE: registry.gitlab.com/hosttoday/ht-docker-node:npmci
|
||||
NPMCI_COMPUTED_REPOURL: https://${{gitea.repository_owner}}:${{secrets.GITEA_TOKEN}}@gitea.lossless.digital/${{gitea.repository}}.git
|
||||
NPMCI_TOKEN_NPM: ${{secrets.NPMCI_TOKEN_NPM}}
|
||||
NPMCI_TOKEN_NPM2: ${{secrets.NPMCI_TOKEN_NPM2}}
|
||||
NPMCI_GIT_GITHUBTOKEN: ${{secrets.NPMCI_GIT_GITHUBTOKEN}}
|
||||
NPMCI_URL_CLOUDLY: ${{secrets.NPMCI_URL_CLOUDLY}}
|
||||
|
||||
jobs:
|
||||
security:
|
||||
runs-on: ubuntu-latest
|
||||
continue-on-error: true
|
||||
container:
|
||||
image: ${{ env.IMAGE }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Install pnpm and npmci
|
||||
run: |
|
||||
pnpm install -g pnpm
|
||||
pnpm install -g @shipzone/npmci
|
||||
|
||||
- name: Run npm prepare
|
||||
run: npmci npm prepare
|
||||
|
||||
- name: Audit production dependencies
|
||||
run: |
|
||||
npmci command npm config set registry https://registry.npmjs.org
|
||||
npmci command pnpm audit --audit-level=high --prod
|
||||
continue-on-error: true
|
||||
|
||||
- name: Audit development dependencies
|
||||
run: |
|
||||
npmci command npm config set registry https://registry.npmjs.org
|
||||
npmci command pnpm audit --audit-level=high --dev
|
||||
continue-on-error: true
|
||||
|
||||
test:
|
||||
if: ${{ always() }}
|
||||
needs: security
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: ${{ env.IMAGE }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Test stable
|
||||
run: |
|
||||
npmci node install stable
|
||||
npmci npm install
|
||||
npmci npm test
|
||||
|
||||
- name: Test build
|
||||
run: |
|
||||
npmci node install stable
|
||||
npmci npm install
|
||||
npmci npm build
|
124
.gitea/workflows/default_tags.yaml
Normal file
124
.gitea/workflows/default_tags.yaml
Normal file
@ -0,0 +1,124 @@
|
||||
name: Default (tags)
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- '*'
|
||||
|
||||
env:
|
||||
IMAGE: registry.gitlab.com/hosttoday/ht-docker-node:npmci
|
||||
NPMCI_COMPUTED_REPOURL: https://${{gitea.repository_owner}}:${{secrets.GITEA_TOKEN}}@gitea.lossless.digital/${{gitea.repository}}.git
|
||||
NPMCI_TOKEN_NPM: ${{secrets.NPMCI_TOKEN_NPM}}
|
||||
NPMCI_TOKEN_NPM2: ${{secrets.NPMCI_TOKEN_NPM2}}
|
||||
NPMCI_GIT_GITHUBTOKEN: ${{secrets.NPMCI_GIT_GITHUBTOKEN}}
|
||||
NPMCI_URL_CLOUDLY: ${{secrets.NPMCI_URL_CLOUDLY}}
|
||||
|
||||
jobs:
|
||||
security:
|
||||
runs-on: ubuntu-latest
|
||||
continue-on-error: true
|
||||
container:
|
||||
image: ${{ env.IMAGE }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Prepare
|
||||
run: |
|
||||
pnpm install -g pnpm
|
||||
pnpm install -g @shipzone/npmci
|
||||
npmci npm prepare
|
||||
|
||||
- name: Audit production dependencies
|
||||
run: |
|
||||
npmci command npm config set registry https://registry.npmjs.org
|
||||
npmci command pnpm audit --audit-level=high --prod
|
||||
continue-on-error: true
|
||||
|
||||
- name: Audit development dependencies
|
||||
run: |
|
||||
npmci command npm config set registry https://registry.npmjs.org
|
||||
npmci command pnpm audit --audit-level=high --dev
|
||||
continue-on-error: true
|
||||
|
||||
test:
|
||||
if: ${{ always() }}
|
||||
needs: security
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: ${{ env.IMAGE }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Prepare
|
||||
run: |
|
||||
pnpm install -g pnpm
|
||||
pnpm install -g @shipzone/npmci
|
||||
npmci npm prepare
|
||||
|
||||
- name: Test stable
|
||||
run: |
|
||||
npmci node install stable
|
||||
npmci npm install
|
||||
npmci npm test
|
||||
|
||||
- name: Test build
|
||||
run: |
|
||||
npmci node install stable
|
||||
npmci npm install
|
||||
npmci npm build
|
||||
|
||||
release:
|
||||
needs: test
|
||||
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/')
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: ${{ env.IMAGE }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Prepare
|
||||
run: |
|
||||
pnpm install -g pnpm
|
||||
pnpm install -g @shipzone/npmci
|
||||
npmci npm prepare
|
||||
|
||||
- name: Release
|
||||
run: |
|
||||
npmci node install stable
|
||||
npmci npm publish
|
||||
|
||||
metadata:
|
||||
needs: test
|
||||
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/')
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: ${{ env.IMAGE }}
|
||||
continue-on-error: true
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Prepare
|
||||
run: |
|
||||
pnpm install -g pnpm
|
||||
pnpm install -g @shipzone/npmci
|
||||
npmci npm prepare
|
||||
|
||||
- name: Code quality
|
||||
run: |
|
||||
npmci command npm install -g typescript
|
||||
npmci npm install
|
||||
|
||||
- name: Trigger
|
||||
run: npmci trigger
|
||||
|
||||
- name: Build docs and upload artifacts
|
||||
run: |
|
||||
npmci node install stable
|
||||
npmci npm install
|
||||
pnpm install -g @git.zone/tsdoc
|
||||
npmci command tsdoc
|
||||
continue-on-error: true
|
20
.gitignore
vendored
Normal file
20
.gitignore
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
.nogit/
|
||||
|
||||
# artifacts
|
||||
coverage/
|
||||
public/
|
||||
pages/
|
||||
|
||||
# installs
|
||||
node_modules/
|
||||
|
||||
# caches
|
||||
.yarn/
|
||||
.cache/
|
||||
.rpt2_cache
|
||||
|
||||
# builds
|
||||
dist/
|
||||
dist_*/
|
||||
|
||||
# custom
|
138
.gitlab-ci.yml
Normal file
138
.gitlab-ci.yml
Normal file
@ -0,0 +1,138 @@
|
||||
# gitzone ci_default_private
|
||||
image: registry.gitlab.com/hosttoday/ht-docker-node:npmci
|
||||
|
||||
cache:
|
||||
paths:
|
||||
- .npmci_cache/
|
||||
key: '$CI_BUILD_STAGE'
|
||||
|
||||
stages:
|
||||
- security
|
||||
- test
|
||||
- release
|
||||
- metadata
|
||||
|
||||
before_script:
|
||||
- npm install -g @shipzone/npmci
|
||||
|
||||
# ====================
|
||||
# security stage
|
||||
# ====================
|
||||
|
||||
auditProductionDependencies:
|
||||
image: registry.gitlab.com/hosttoday/ht-docker-node:npmci
|
||||
stage: security
|
||||
script:
|
||||
- npmci npm prepare
|
||||
- npmci command npm install --production --ignore-scripts
|
||||
- npmci command npm config set registry https://registry.npmjs.org
|
||||
- npmci command npm audit --audit-level=high --only=prod --production
|
||||
tags:
|
||||
- metadatacompany
|
||||
- docker
|
||||
allow_failure: true
|
||||
|
||||
auditDevDependencies:
|
||||
image: registry.gitlab.com/hosttoday/ht-docker-node:npmci
|
||||
stage: security
|
||||
script:
|
||||
- npmci npm prepare
|
||||
- npmci command npm install --ignore-scripts
|
||||
- npmci command npm config set registry https://registry.npmjs.org
|
||||
- npmci command npm audit --audit-level=high --only=dev
|
||||
tags:
|
||||
- metadatacompany
|
||||
- docker
|
||||
allow_failure: true
|
||||
|
||||
# ====================
|
||||
# test stage
|
||||
# ====================
|
||||
|
||||
testStable:
|
||||
stage: test
|
||||
script:
|
||||
- npmci npm prepare
|
||||
- npmci node install stable
|
||||
- npmci npm install
|
||||
- npmci npm test
|
||||
coverage: /\d+.?\d+?\%\s*coverage/
|
||||
tags:
|
||||
- metadatacompany
|
||||
- docker
|
||||
|
||||
|
||||
testBuild:
|
||||
stage: test
|
||||
script:
|
||||
- npmci npm prepare
|
||||
- npmci node install stable
|
||||
- npmci npm install
|
||||
- npmci command npm run build
|
||||
coverage: /\d+.?\d+?\%\s*coverage/
|
||||
tags:
|
||||
- metadatacompany
|
||||
- docker
|
||||
|
||||
|
||||
release:
|
||||
stage: release
|
||||
script:
|
||||
- npmci npm prepare
|
||||
- npmci node install stable
|
||||
- npmci npm publish
|
||||
only:
|
||||
- tags
|
||||
tags:
|
||||
- metadatacompany
|
||||
- docker
|
||||
|
||||
|
||||
# ====================
|
||||
# metadata stage
|
||||
# ====================
|
||||
codequality:
|
||||
stage: metadata
|
||||
allow_failure: true
|
||||
only:
|
||||
- tags
|
||||
script:
|
||||
- npmci command npm install -g tslint typescript
|
||||
- npmci npm prepare
|
||||
- npmci npm install
|
||||
- npmci command "tslint -c tslint.json ./ts/**/*.ts"
|
||||
tags:
|
||||
- metadatacompany
|
||||
- docker
|
||||
- priv
|
||||
|
||||
trigger:
|
||||
stage: metadata
|
||||
script:
|
||||
- npmci trigger
|
||||
only:
|
||||
- tags
|
||||
tags:
|
||||
- metadatacompany
|
||||
- docker
|
||||
|
||||
|
||||
pages:
|
||||
stage: metadata
|
||||
script:
|
||||
- npmci node install lts
|
||||
- npmci command npm install -g @gitzone/tsdoc
|
||||
- npmci npm prepare
|
||||
- npmci npm install
|
||||
- npmci command tsdoc
|
||||
tags:
|
||||
- metadatacompany
|
||||
- docker
|
||||
|
||||
only:
|
||||
- tags
|
||||
artifacts:
|
||||
expire_in: 1 week
|
||||
paths:
|
||||
- public
|
||||
allow_failure: true
|
11
.vscode/launch.json
vendored
Normal file
11
.vscode/launch.json
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"command": "npm test",
|
||||
"name": "Run npm test",
|
||||
"request": "launch",
|
||||
"type": "node-terminal"
|
||||
}
|
||||
]
|
||||
}
|
26
.vscode/settings.json
vendored
Normal file
26
.vscode/settings.json
vendored
Normal file
@ -0,0 +1,26 @@
|
||||
{
|
||||
"json.schemas": [
|
||||
{
|
||||
"fileMatch": ["/npmextra.json"],
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"npmci": {
|
||||
"type": "object",
|
||||
"description": "settings for npmci"
|
||||
},
|
||||
"gitzone": {
|
||||
"type": "object",
|
||||
"description": "settings for gitzone",
|
||||
"properties": {
|
||||
"projectType": {
|
||||
"type": "string",
|
||||
"enum": ["website", "element", "service", "npm", "wcc"]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
51
changelog.md
Normal file
51
changelog.md
Normal file
@ -0,0 +1,51 @@
|
||||
# Changelog
|
||||
|
||||
## 2024-07-05 - 1.1.0 - feat(core)
|
||||
Add support for CI workflows and update gitignore
|
||||
|
||||
- Added `.gitea/workflows/default_nottags.yaml` for non-tag workflows.
|
||||
- Added `.gitea/workflows/default_tags.yaml` for tag workflows.
|
||||
- Updated `.gitignore` to include standard and custom ignore rules.
|
||||
- Added `.gitlab-ci.yml` for GitLab CI/CD integration.
|
||||
- Added `.npmrc` with a custom registry URL.
|
||||
- Added VSCode configuration files for debugging and settings.
|
||||
- Added `changelog.md` to document project changes.
|
||||
- Added `license` file with MIT License.
|
||||
- Updated scripts in `package.json` for building, testing, and documentation.
|
||||
- Added test file `test/test.ts`.
|
||||
- Added core CSV parsing files and structures for Spendesk, PayPal, Fidor, and Commerzbank.
|
||||
|
||||
## 2024-07-02 - 1.0.9 - fix(core)
|
||||
No changes detected
|
||||
|
||||
|
||||
## 2023-11-17 - 1.0.8 - Core
|
||||
Main version update
|
||||
|
||||
- Miscellaneous fixes
|
||||
|
||||
## 2023-11-17 - 1.0.7 - Core
|
||||
Minor fixes
|
||||
|
||||
- Core update
|
||||
|
||||
## 2023-11-17 - 1.0.6 - Core
|
||||
Minor fixes
|
||||
|
||||
- Core update
|
||||
|
||||
## 2022-01-03 - 1.0.5 to 1.0.6 - Core
|
||||
Multiple minor fixes and updates
|
||||
|
||||
- Core updates
|
||||
|
||||
## 2022-01-03 - 1.0.4 - Core
|
||||
Minor fixes
|
||||
|
||||
- Core update
|
||||
|
||||
## 2022-01-03 - 1.0.3 - Commerzbank
|
||||
Commerzbank specific fix
|
||||
|
||||
- Fixed Commerzbank to comply with spec
|
||||
|
19
license
Normal file
19
license
Normal file
@ -0,0 +1,19 @@
|
||||
Copyright (c) 2022 Lossless GmbH (hello@lossless.com)
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
32
npmextra.json
Normal file
32
npmextra.json
Normal file
@ -0,0 +1,32 @@
|
||||
{
|
||||
"gitzone": {
|
||||
"projectType": "npm",
|
||||
"module": {
|
||||
"githost": "gitlab.com",
|
||||
"gitscope": "fin.cx",
|
||||
"gitrepo": "csvparser",
|
||||
"description": "A TypeScript-based parser for CSV files from various financial service providers.",
|
||||
"npmPackagename": "@fin.cx/csvparser",
|
||||
"license": "MIT",
|
||||
"projectDomain": "fin.cx",
|
||||
"keywords": [
|
||||
"CSV",
|
||||
"parser",
|
||||
"finance",
|
||||
"TypeScript",
|
||||
"PayPal",
|
||||
"Spendesk",
|
||||
"Commerzbank",
|
||||
"Fidor",
|
||||
"financial data",
|
||||
"data processing",
|
||||
"import",
|
||||
"export"
|
||||
]
|
||||
}
|
||||
},
|
||||
"npmci": {
|
||||
"npmGlobalTools": [],
|
||||
"npmAccessLevel": "private"
|
||||
}
|
||||
}
|
70
package.json
Normal file
70
package.json
Normal file
@ -0,0 +1,70 @@
|
||||
{
|
||||
"name": "@fin.cx/csvparser",
|
||||
"version": "1.0.9",
|
||||
"private": false,
|
||||
"description": "A TypeScript-based parser for CSV files from various financial service providers.",
|
||||
"main": "dist_ts/index.js",
|
||||
"typings": "dist_ts/index.d.ts",
|
||||
"author": "Lossless GmbH",
|
||||
"license": "UNLICENSED",
|
||||
"scripts": {
|
||||
"test": "(tstest test/ --web)",
|
||||
"build": "(tsbuild --web)",
|
||||
"buildDocs": "tsdoc"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@gitzone/tsbuild": "^2.1.25",
|
||||
"@gitzone/tsbundle": "^2.0.8",
|
||||
"@gitzone/tstest": "^1.0.44",
|
||||
"@push.rocks/tapbundle": "^5.0.8",
|
||||
"@types/node": "^20.9.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@fin.cx/portablefinance": "^1.0.25",
|
||||
"@push.rocks/smartcsv": "^2.0.2",
|
||||
"@push.rocks/smartfile": "^11.0.0",
|
||||
"@push.rocks/smarthash": "^3.0.2",
|
||||
"@push.rocks/smartmoney": "^1.0.6",
|
||||
"@push.rocks/smartstring": "^4.0.7",
|
||||
"@push.rocks/smarttime": "^4.0.1",
|
||||
"@tsclass/tsclass": "^4.0.46"
|
||||
},
|
||||
"browserslist": [
|
||||
"last 1 chrome versions"
|
||||
],
|
||||
"files": [
|
||||
"ts/**/*",
|
||||
"ts_web/**/*",
|
||||
"dist/**/*",
|
||||
"dist_*/**/*",
|
||||
"dist_ts/**/*",
|
||||
"dist_ts_web/**/*",
|
||||
"assets/**/*",
|
||||
"cli.js",
|
||||
"npmextra.json",
|
||||
"readme.md"
|
||||
],
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://gitlab.com/fin.cx/csvparser.git"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://gitlab.com/fin.cx/csvparser/issues"
|
||||
},
|
||||
"homepage": "https://gitlab.com/fin.cx/csvparser#readme",
|
||||
"type": "module",
|
||||
"keywords": [
|
||||
"CSV",
|
||||
"parser",
|
||||
"finance",
|
||||
"TypeScript",
|
||||
"PayPal",
|
||||
"Spendesk",
|
||||
"Commerzbank",
|
||||
"Fidor",
|
||||
"financial data",
|
||||
"data processing",
|
||||
"import",
|
||||
"export"
|
||||
]
|
||||
}
|
5756
pnpm-lock.yaml
generated
Normal file
5756
pnpm-lock.yaml
generated
Normal file
File diff suppressed because it is too large
Load Diff
0
readme.hints.md
Normal file
0
readme.hints.md
Normal file
258
readme.md
Normal file
258
readme.md
Normal file
@ -0,0 +1,258 @@
|
||||
```markdown
|
||||
# @fin.cx/csvparser
|
||||
a csvparser for fin2021
|
||||
|
||||
## Install
|
||||
|
||||
To install the `@fin.cx/csvparser` module, you can use npm. Simply run the following command:
|
||||
|
||||
```bash
|
||||
npm install @fin.cx/csvparser
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
The `@fin.cx/csvparser` module provides tools to parse CSV files from various financial institutions such as Commerzbank, Fidor Bank, PayPal, and Spendesk. This helps in extracting and managing financial transaction data efficiently.
|
||||
|
||||
Here's a guide to showcase various functionalities along with comprehensive TypeScript code examples.
|
||||
|
||||
### Getting Started
|
||||
|
||||
First, make sure to import the necessary components from the `@fin.cx/csvparser` module.
|
||||
|
||||
```typescript
|
||||
import { CsvSpendesk, CsvPayPal, CsvFidor, CsvCommerzbank } from '@fin.cx/csvparser';
|
||||
```
|
||||
|
||||
### Spendesk CSV Parsing
|
||||
|
||||
#### Parsing Spendesk CSV from File
|
||||
|
||||
The `CsvSpendesk` class provides methods to parse Spendesk CSV files.
|
||||
|
||||
```typescript
|
||||
import { CsvSpendesk } from '@fin.cx/csvparser';
|
||||
|
||||
async function parseSpendeskFile(filePath: string) {
|
||||
try {
|
||||
const spendeskData = await CsvSpendesk.fromFile(filePath);
|
||||
const transactions = await spendeskData.getTransactions();
|
||||
console.log(transactions);
|
||||
} catch (error) {
|
||||
console.error('Error parsing Spendesk file:', error);
|
||||
}
|
||||
}
|
||||
|
||||
// Usage example
|
||||
parseSpendeskFile('./path/to/spendesk.csv');
|
||||
```
|
||||
|
||||
#### Parsing Spendesk CSV from Directory
|
||||
|
||||
You might have multiple Spendesk CSV files in a directory. The `fromDir` method helps in parsing all of them.
|
||||
|
||||
```typescript
|
||||
import { CsvSpendesk } from '@fin.cx/csvparser';
|
||||
|
||||
async function parseSpendeskDirectory(dirPath: string) {
|
||||
try {
|
||||
const spendeskData = await CsvSpendesk.fromDir(dirPath);
|
||||
const transactions = await spendeskData.getTransactions();
|
||||
console.log(transactions);
|
||||
} catch (error) {
|
||||
console.error('Error parsing Spendesk directory:', error);
|
||||
}
|
||||
}
|
||||
|
||||
// Usage example
|
||||
parseSpendeskDirectory('./path/to/spendesk_dir');
|
||||
```
|
||||
|
||||
### PayPal CSV Parsing
|
||||
|
||||
The `CsvPayPal` class is used to parse PayPal CSV files.
|
||||
|
||||
```typescript
|
||||
import { CsvPayPal } from '@fin.cx/csvparser';
|
||||
|
||||
async function parsePayPalFile(filePath: string) {
|
||||
try {
|
||||
const payPalData = new CsvPayPal();
|
||||
const csvDescriptor = {
|
||||
filename: 'paypal.csv',
|
||||
contentString: await fs.promises.readFile(filePath, 'utf8')
|
||||
}
|
||||
payPalData.addCsvDecriptor(csvDescriptor);
|
||||
const transactions = await payPalData.getTransactions();
|
||||
console.log(transactions);
|
||||
} catch (error) {
|
||||
console.error('Error parsing PayPal file:', error);
|
||||
}
|
||||
}
|
||||
|
||||
// Usage example
|
||||
parsePayPalFile('./path/to/paypal.csv');
|
||||
```
|
||||
|
||||
### Fidor Bank CSV Parsing
|
||||
|
||||
The `CsvFidor` class is used to parse Fidor Bank CSV files.
|
||||
|
||||
```typescript
|
||||
import { CsvFidor } from '@fin.cx/csvparser';
|
||||
|
||||
async function parseFidorFile(filePath: string) {
|
||||
try {
|
||||
const fidorData = new CsvFidor();
|
||||
const csvDescriptor = {
|
||||
filename: 'fidor.csv',
|
||||
contentString: await fs.promises.readFile(filePath, 'utf8')
|
||||
}
|
||||
fidorData.addCsvDecriptor(csvDescriptor);
|
||||
const transactions = await fidorData.getTransactions();
|
||||
console.log(transactions);
|
||||
} catch (error) {
|
||||
console.error('Error parsing Fidor file:', error);
|
||||
}
|
||||
}
|
||||
|
||||
// Usage example
|
||||
parseFidorFile('./path/to/fidor.csv');
|
||||
```
|
||||
|
||||
### Commerzbank CSV Parsing
|
||||
|
||||
The `CsvCommerzbank` class is used to parse Commerzbank CSV files.
|
||||
|
||||
```typescript
|
||||
import { CsvCommerzbank } from '@fin.cx/csvparser';
|
||||
|
||||
async function parseCommerzbankFile(filePath: string) {
|
||||
try {
|
||||
const commerzbankData = new CsvCommerzbank();
|
||||
const csvDescriptor = {
|
||||
filename: 'commerzbank.csv',
|
||||
contentString: await fs.promises.readFile(filePath, 'utf8')
|
||||
}
|
||||
commerzbankData.addCsvDecriptor(csvDescriptor);
|
||||
const transactions = await commerzbankData.getTransactions();
|
||||
console.log(transactions);
|
||||
} catch (error) {
|
||||
console.error('Error parsing Commerzbank file:', error);
|
||||
}
|
||||
}
|
||||
|
||||
// Usage example
|
||||
parseCommerzbankFile('./path/to/commerzbank.csv');
|
||||
```
|
||||
|
||||
### Advanced Usage
|
||||
|
||||
#### Merging Spendesk Data
|
||||
|
||||
You may need to merge multiple Spendesk CSV data instances.
|
||||
|
||||
```typescript
|
||||
import { CsvSpendesk } from '@fin.cx/csvparser';
|
||||
|
||||
async function mergeSpendeskData(filePaths: string[]) {
|
||||
try {
|
||||
const spendeskInstances = await Promise.all(filePaths.map(CsvSpendesk.fromFile));
|
||||
const mergedInstance = spendeskInstances.reduce(
|
||||
(merged, currentInstance) => merged.concat(currentInstance),
|
||||
spendeskInstances[0]
|
||||
);
|
||||
console.log(await mergedInstance.getTransactions());
|
||||
} catch (error) {
|
||||
console.error('Error merging Spendesk data:', error);
|
||||
}
|
||||
}
|
||||
|
||||
// Usage example
|
||||
mergeSpendeskData([
|
||||
'./path/to/spendesk1.csv',
|
||||
'./path/to/spendesk2.csv'
|
||||
]);
|
||||
```
|
||||
|
||||
#### Working with PayPal Linked Transactions
|
||||
|
||||
PayPal CSV data contains linked transactions, which you may need to handle specifically.
|
||||
|
||||
```typescript
|
||||
import { CsvPayPal } from '@fin.cx/csvparser';
|
||||
|
||||
async function handlePayPalLinkedTransactions(filePath: string) {
|
||||
try {
|
||||
const payPalData = new CsvPayPal();
|
||||
const csvDescriptor = {
|
||||
filename: 'paypal.csv',
|
||||
contentString: await fs.promises.readFile(filePath, 'utf8')
|
||||
}
|
||||
payPalData.addCsvDecriptor(csvDescriptor);
|
||||
const transactions = await payPalData.getTransactions();
|
||||
|
||||
const linkedTransactions = transactions.filter(tx => tx.linkedTransactionCode);
|
||||
console.log(linkedTransactions);
|
||||
} catch (error) {
|
||||
console.error('Error handling linked transactions:', error);
|
||||
}
|
||||
}
|
||||
|
||||
// Usage example
|
||||
handlePayPalLinkedTransactions('./path/to/paypal.csv');
|
||||
```
|
||||
|
||||
#### Parsing Multiple File Types
|
||||
|
||||
You may need to parse CSV files from different financial service providers.
|
||||
|
||||
```typescript
|
||||
import { CsvSpendesk, CsvPayPal, CsvFidor, CsvCommerzbank } from '@fin.cx/csvparser';
|
||||
|
||||
async function parseMultipleFiles(filePaths: {spendesk: string, paypal: string, fidor: string, commerzbank: string}) {
|
||||
try {
|
||||
const spendeskData = await CsvSpendesk.fromFile(filePaths.spendesk);
|
||||
const payPalData = new CsvPayPal();
|
||||
const csvDescriptorPayPal = {
|
||||
filename: 'paypal.csv',
|
||||
contentString: await fs.promises.readFile(filePaths.paypal, 'utf8')
|
||||
}
|
||||
payPalData.addCsvDecriptor(csvDescriptorPayPal);
|
||||
|
||||
const fidorData = new CsvFidor();
|
||||
const csvDescriptorFidor = {
|
||||
filename: 'fidor.csv',
|
||||
contentString: await fs.promises.readFile(filePaths.fidor, 'utf8')
|
||||
}
|
||||
fidorData.addCsvDecriptor(csvDescriptorFidor);
|
||||
|
||||
const commerzbankData = new CsvCommerzbank();
|
||||
const csvDescriptorCommerzbank = {
|
||||
filename: 'commerzbank.csv',
|
||||
contentString: await fs.promises.readFile(filePaths.commerzbank, 'utf8')
|
||||
}
|
||||
commerzbankData.addCsvDecriptor(csvDescriptorCommerzbank);
|
||||
|
||||
console.log(await spendeskData.getTransactions());
|
||||
console.log(await payPalData.getTransactions());
|
||||
console.log(await fidorData.getTransactions());
|
||||
console.log(await commerzbankData.getTransactions());
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error parsing multiple files:', error);
|
||||
}
|
||||
}
|
||||
|
||||
// Usage example
|
||||
parseMultipleFiles({
|
||||
spendesk: './path/to/spendesk.csv',
|
||||
paypal: './path/to/paypal.csv',
|
||||
fidor: './path/to/fidor.csv',
|
||||
commerzbank: './path/to/commerzbank.csv'
|
||||
});
|
||||
```
|
||||
|
||||
By covering multiple use cases and providing comprehensive documentation and code samples, this readme should help developers integrate and utilize the `@fin.cx/csvparser` module effectively.
|
||||
```
|
||||
undefined
|
8
test/test.ts
Normal file
8
test/test.ts
Normal file
@ -0,0 +1,8 @@
|
||||
import { expect, tap } from '@push.rocks/tapbundle';
|
||||
import * as csvparser from '../ts/index';
|
||||
|
||||
tap.test('first test', async () => {
|
||||
console.log(csvparser.standardExport);
|
||||
});
|
||||
|
||||
tap.start();
|
8
ts/00_commitinfo_data.ts
Normal file
8
ts/00_commitinfo_data.ts
Normal file
@ -0,0 +1,8 @@
|
||||
/**
|
||||
* autocreated commitinfo by @push.rocks/commitinfo
|
||||
*/
|
||||
export const commitinfo = {
|
||||
name: '@fin.cx/csvparser',
|
||||
version: '1.1.0',
|
||||
description: 'A TypeScript-based parser for CSV files from various financial service providers.'
|
||||
}
|
98
ts/commerzbank/csv-commerzbank.classes.csvcommerzbank.ts
Normal file
98
ts/commerzbank/csv-commerzbank.classes.csvcommerzbank.ts
Normal file
@ -0,0 +1,98 @@
|
||||
import * as plugins from '../csvparser.plugins.js';
|
||||
import * as interfaces from './interfaces/index.js';
|
||||
|
||||
export class CsvCommerzbank extends plugins.portablefinance
|
||||
.AcCsvParser<interfaces.ICommerzbankTransaction> {
|
||||
// INSTANCE
|
||||
public paymentProviderName = 'Commerzbank';
|
||||
public description: string = `a csv parser for parsing downloaded csv transaction files from Commerzbank`;
|
||||
private csvDescriptorArray: plugins.portablefinance.ICsvDescriptor[] = [];
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
|
||||
addCsvDecriptor(csvDescriptorArg: plugins.portablefinance.ICsvDescriptor): void {
|
||||
this.csvDescriptorArray.push(csvDescriptorArg);
|
||||
}
|
||||
|
||||
public async getTransactions(): Promise<plugins.portablefinance.IMonetaryTransaction[]> {
|
||||
const payments: interfaces.ICommerzbankOriginalTransaction[] = [];
|
||||
|
||||
for (const csvDescriptor of this.csvDescriptorArray) {
|
||||
const csvInstance = await plugins.smartcsv.Csv.createCsvFromString(csvDescriptor.contentString, {
|
||||
headers: true,
|
||||
unquote: true,
|
||||
});
|
||||
|
||||
payments.push(
|
||||
...(await csvInstance.exportAsObject())
|
||||
);
|
||||
}
|
||||
|
||||
const finalTransactionArray: interfaces.ICommerzbankTransaction[] = [];
|
||||
for (const transaction of payments) {
|
||||
// transaction.Buchungstag = transaction.Wertstellung;
|
||||
console.log(transaction);
|
||||
const finalTransaction: interfaces.ICommerzbankTransaction = {
|
||||
simpleTransaction: null,
|
||||
transactionHash: null,
|
||||
original: transaction,
|
||||
amount: plugins.smartmoney.parseEuropeanNumberString(transaction.Betrag),
|
||||
currency: transaction.Währung,
|
||||
description: transaction.Buchungstext,
|
||||
transactionDate: plugins.smarttime.ExtendedDate.fromEuropeanDate(transaction.Buchungstag),
|
||||
valuationDate: plugins.smarttime.ExtendedDate.fromEuropeanDate(transaction.Wertstellung),
|
||||
transactionType: ((): interfaces.TTransactionType => {
|
||||
switch (transaction.Umsatzart) {
|
||||
case 'Gutschrift':
|
||||
return 'Credit';
|
||||
case 'Lastschrift':
|
||||
return 'Debit';
|
||||
case 'Zinsen/Entgelte':
|
||||
return 'BankFees';
|
||||
case 'Überweisung':
|
||||
return 'ActiveTransfer';
|
||||
default:
|
||||
throw new Error(`unknown transactiontype ${transaction.Umsatzart}`);
|
||||
}
|
||||
})(),
|
||||
};
|
||||
|
||||
// lets assign the transactionHash
|
||||
finalTransaction.transactionHash = await plugins.smarthash.sha265FromObject({
|
||||
description: finalTransaction.description,
|
||||
amount: finalTransaction.amount,
|
||||
date: finalTransaction.valuationDate,
|
||||
});
|
||||
|
||||
finalTransaction.simpleTransaction = {
|
||||
id: finalTransaction.transactionHash,
|
||||
accountId: null,
|
||||
name: finalTransaction.description,
|
||||
amount: finalTransaction.amount,
|
||||
description: finalTransaction.description,
|
||||
date: finalTransaction.transactionDate,
|
||||
};
|
||||
finalTransactionArray.push(finalTransaction);
|
||||
}
|
||||
|
||||
return finalTransactionArray.map((commerzbankTransaction) => {
|
||||
const fin2021Transaction: plugins.portablefinance.IMonetaryTransaction = {
|
||||
id: null,
|
||||
data: {
|
||||
additionalIds: [],
|
||||
amount: commerzbankTransaction.amount,
|
||||
date: commerzbankTransaction.transactionDate.getTime(),
|
||||
description: commerzbankTransaction.description,
|
||||
name: commerzbankTransaction.description,
|
||||
originAccountId: null,
|
||||
originTransactionId: commerzbankTransaction.transactionHash,
|
||||
paymentAccountId: null,
|
||||
justForLooks: null,
|
||||
},
|
||||
};
|
||||
return fin2021Transaction;
|
||||
});
|
||||
}
|
||||
}
|
1
ts/commerzbank/index.ts
Normal file
1
ts/commerzbank/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export * from './csv-commerzbank.classes.csvcommerzbank.js';
|
1
ts/commerzbank/interfaces/index.ts
Normal file
1
ts/commerzbank/interfaces/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export * from './interfaces.commerzbanktransaction.js';
|
@ -0,0 +1,30 @@
|
||||
import * as plugins from '../../csvparser.plugins.js';
|
||||
|
||||
export interface ICommerzbankOriginalTransaction {
|
||||
Buchungstag: string;
|
||||
Wertstellung: string;
|
||||
Umsatzart: 'Überweisung' | 'Gutschrift' | 'Lastschrift' | 'Zinsen/Entgelte';
|
||||
Buchungstext: string;
|
||||
Betrag: string;
|
||||
Währung: string;
|
||||
Auftraggeberkonto: string;
|
||||
'Bankleitzahl Auftraggeberkonto': string;
|
||||
'IBAN Auftraggeberkonto': string;
|
||||
Kategorie: string;
|
||||
}
|
||||
|
||||
export type TTransactionType = 'Credit' | 'Debit' | 'ActiveTransfer' | 'BankFees';
|
||||
|
||||
export interface ICommerzbankTransaction {
|
||||
simpleTransaction: plugins.tsclass.finance.ITransaction;
|
||||
transactionHash: string;
|
||||
original: ICommerzbankOriginalTransaction;
|
||||
|
||||
// translated to English
|
||||
transactionDate: plugins.smarttime.ExtendedDate;
|
||||
valuationDate: plugins.smarttime.ExtendedDate;
|
||||
transactionType: TTransactionType;
|
||||
description: string;
|
||||
amount: number;
|
||||
currency: string;
|
||||
}
|
26
ts/csvparser.plugins.ts
Normal file
26
ts/csvparser.plugins.ts
Normal file
@ -0,0 +1,26 @@
|
||||
// node native scope
|
||||
import * as path from 'path';
|
||||
|
||||
export { path };
|
||||
|
||||
// fin.cx scope
|
||||
import * as portablefinance from '@fin.cx/portablefinance';
|
||||
|
||||
export {
|
||||
portablefinance
|
||||
}
|
||||
|
||||
// pushrocks scope
|
||||
import * as smartcsv from '@push.rocks/smartcsv';
|
||||
import * as smartfile from '@push.rocks/smartfile';
|
||||
import * as smarthash from '@push.rocks/smarthash';
|
||||
import * as smartmoney from '@push.rocks/smartmoney';
|
||||
import * as smartstring from '@push.rocks/smartstring';
|
||||
import * as smarttime from '@push.rocks/smarttime';
|
||||
|
||||
export { smartcsv, smartfile, smarthash, smartmoney, smartstring, smarttime };
|
||||
|
||||
// tsclass scope
|
||||
import * as tsclass from '@tsclass/tsclass';
|
||||
|
||||
export { tsclass };
|
55
ts/fidor/csv-fidor.classes.csvfidor.ts
Normal file
55
ts/fidor/csv-fidor.classes.csvfidor.ts
Normal file
@ -0,0 +1,55 @@
|
||||
import * as plugins from '../csvparser.plugins.js';
|
||||
|
||||
import { ExtendedDate } from '@push.rocks/smarttime';
|
||||
|
||||
import * as interfaces from './interfaces/index.js';
|
||||
|
||||
export class CsvFidor extends plugins.portablefinance.AcCsvParser<plugins.portablefinance.IMonetaryTransaction> {
|
||||
|
||||
// INSTANCE
|
||||
public paymentProviderName: string = 'Fidor Bank AG';
|
||||
public description: string = 'a csv parser optimized for csv files from Fidor.';
|
||||
public csvDescriptorArray: plugins.portablefinance.ICsvDescriptor[] = [];
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
|
||||
addCsvDecriptor (csvDescriptorArg: plugins.portablefinance.ICsvDescriptor): void {
|
||||
this.csvDescriptorArray.push(csvDescriptorArg);
|
||||
}
|
||||
|
||||
/**
|
||||
* returns the transactions
|
||||
*/
|
||||
public async getTransactions(): Promise<plugins.portablefinance.IMonetaryTransaction[]> {
|
||||
const payments: plugins.portablefinance.IMonetaryTransaction[] = [];
|
||||
|
||||
for (const csvDescriptor of this.csvDescriptorArray) {
|
||||
const csvInstance = new plugins.smartcsv.Csv(csvDescriptor.contentString, {
|
||||
headers: true
|
||||
});
|
||||
|
||||
const fidorTransactionArray: interfaces.IFidorOriginalTransaction[] = await csvInstance.exportAsObject();
|
||||
for (const transaction of fidorTransactionArray) {
|
||||
payments.push({
|
||||
id: await plugins.smarthash.sha265FromObject(transaction),
|
||||
data: {
|
||||
additionalIds: [],
|
||||
amount: parseFloat(transaction.Wert.replace('.', '').replace(',', '.')),
|
||||
date: plugins.smarttime.ExtendedDate.fromEuropeanDate(transaction.Datum).getTime(),
|
||||
name: transaction.Beschreibung,
|
||||
description: transaction.Beschreibung2,
|
||||
originAccountId: null,
|
||||
originTransactionId: null,
|
||||
paymentAccountId: null,
|
||||
justForLooks: null,
|
||||
voucherData: null,
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return payments;
|
||||
}
|
||||
}
|
1
ts/fidor/index.ts
Normal file
1
ts/fidor/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export * from './csv-fidor.classes.csvfidor.js';
|
1
ts/fidor/interfaces/index.ts
Normal file
1
ts/fidor/interfaces/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export * from './interfaces.fidortransaction.js';
|
6
ts/fidor/interfaces/interfaces.fidortransaction.ts
Normal file
6
ts/fidor/interfaces/interfaces.fidortransaction.ts
Normal file
@ -0,0 +1,6 @@
|
||||
export interface IFidorOriginalTransaction {
|
||||
Datum: string;
|
||||
Beschreibung: string;
|
||||
Beschreibung2: string;
|
||||
Wert: string;
|
||||
}
|
4
ts/index.ts
Normal file
4
ts/index.ts
Normal file
@ -0,0 +1,4 @@
|
||||
export * from './commerzbank/index.js';
|
||||
export * from './fidor/index.js';
|
||||
export * from './paypal/index.js';
|
||||
export * from './spendesk/index.js';
|
147
ts/paypal/csv-paypal.classes.csvpaypal.ts
Normal file
147
ts/paypal/csv-paypal.classes.csvpaypal.ts
Normal file
@ -0,0 +1,147 @@
|
||||
import * as plugins from '../csvparser.plugins.js';
|
||||
|
||||
import * as interfaces from './interfaces/index.js';
|
||||
|
||||
export class CsvPayPal extends plugins.portablefinance
|
||||
.AcCsvParser<plugins.portablefinance.IMonetaryTransaction> {
|
||||
// INSTANCE
|
||||
public paymentProviderName = 'PayPal';
|
||||
public description: string = 'a csv parser optimized for PayPal obtained csv files.';
|
||||
public csvDescriptorArray: plugins.portablefinance.ICsvDescriptor[] = [];
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* gets transactions
|
||||
*/
|
||||
public async getTransactions() {
|
||||
const payments: plugins.portablefinance.IMonetaryTransaction[] = [];
|
||||
for (const csvDescriptor of this.csvDescriptorArray) {
|
||||
let stringToParse = csvDescriptor.contentString;
|
||||
stringToParse = stringToParse.replace(/"(.*?)"/gi, (match, p1, offset, originalString) => {
|
||||
return plugins.smartstring.base64.encodeUri(match);
|
||||
});
|
||||
const smartCsvInstance = new plugins.smartcsv.Csv(stringToParse, {
|
||||
headers: true,
|
||||
});
|
||||
const payPalTransactions: interfaces.IPayPalCsvOriginalTransaction[] = (
|
||||
await smartCsvInstance.exportAsObject()
|
||||
).map((originalTransaction) => {
|
||||
// tslint:disable-next-line: no-object-literal-type-assertion
|
||||
const decodedTransaction = {} as interfaces.IPayPalCsvOriginalTransaction;
|
||||
for (const key in originalTransaction) {
|
||||
if (originalTransaction[key]) {
|
||||
let finalKey = plugins.smartstring.base64.decode(key);
|
||||
finalKey = finalKey.replace(/['"]+/g, '');
|
||||
let finalValue = plugins.smartstring.base64.decode(originalTransaction[key]);
|
||||
finalValue = finalValue.replace(/['"]+/g, '');
|
||||
decodedTransaction[finalKey] = finalValue;
|
||||
}
|
||||
}
|
||||
// pushing the ready transaction
|
||||
return decodedTransaction;
|
||||
});
|
||||
|
||||
// adjust numberFormat
|
||||
const anf = (numberString: string): number => {
|
||||
return parseFloat(numberString.replace(/\,/g, '.'));
|
||||
};
|
||||
|
||||
const monetaryTransactions: interfaces.IPayPalTransaction[] = [];
|
||||
for (const originalTransaction of payPalTransactions) {
|
||||
const paypalTransaction: interfaces.IPayPalTransaction = {
|
||||
// assigned later
|
||||
transactionHash: null,
|
||||
simpleTransaction: null,
|
||||
|
||||
// assigned now
|
||||
originalTransaction,
|
||||
transactionDate: plugins.smarttime.ExtendedDate.fromEuropeanDateAndTime(
|
||||
originalTransaction.Datum,
|
||||
originalTransaction.Uhrzeit,
|
||||
'Europe/Berlin'
|
||||
),
|
||||
transactionCode: originalTransaction.Transaktionscode,
|
||||
linkedTransactionCode: originalTransaction['Zugehöriger Transaktionscode'],
|
||||
timezone: originalTransaction.Zeitzone,
|
||||
bankAccount: originalTransaction.Bankkonto,
|
||||
bankName: originalTransaction['Name der Bank'],
|
||||
brutto: anf(originalTransaction.Brutto),
|
||||
netto: anf(originalTransaction.Netto),
|
||||
credit: anf(originalTransaction.Guthaben),
|
||||
fee: anf(originalTransaction.Gebühr),
|
||||
processingAndShippingFee: anf(originalTransaction['Versand- und Bearbeitungsgebühr']),
|
||||
currency: originalTransaction.Währung,
|
||||
description: originalTransaction.Beschreibung,
|
||||
invoiceNumber: originalTransaction.Rechnungsnummer,
|
||||
name: originalTransaction.Name,
|
||||
payeeEmail: originalTransaction['Absender E-Mail-Adresse'],
|
||||
transactionTime: originalTransaction.Uhrzeit,
|
||||
vatAmount: anf(originalTransaction.Umsatzsteuer),
|
||||
};
|
||||
|
||||
monetaryTransactions.push(paypalTransaction);
|
||||
}
|
||||
|
||||
const foreignTransactions: interfaces.IPayPalTransaction[] = [];
|
||||
const eurTransactions: interfaces.IPayPalTransaction[] = monetaryTransactions.filter(
|
||||
(payPalTransaction: interfaces.IPayPalTransaction) => {
|
||||
const isEur = payPalTransaction.currency === 'EUR';
|
||||
if (isEur) {
|
||||
return true;
|
||||
} else {
|
||||
foreignTransactions.push(payPalTransaction);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
);
|
||||
let finalReturnTransactions = eurTransactions.map((transaction) => {
|
||||
if (transaction.brutto > 0) {
|
||||
return transaction; // lets don't bother with payments from the bank
|
||||
}
|
||||
const eurTime = transaction.transactionDate.getTime();
|
||||
const foreignCandidates: interfaces.IPayPalTransaction[] = [];
|
||||
for (const foreignTransaction of foreignTransactions) {
|
||||
const foreignTime = foreignTransaction.transactionDate.getTime();
|
||||
if (eurTime === foreignTime) {
|
||||
foreignCandidates.push(foreignTransaction);
|
||||
}
|
||||
}
|
||||
|
||||
if (foreignCandidates.length !== 2 && foreignCandidates.length !== 0) {
|
||||
console.log('error! Found a weird amoun of corresponding foreign transactions');
|
||||
}
|
||||
|
||||
if (foreignCandidates.length === 2) {
|
||||
const wantedForeignTransaction = foreignCandidates.find((foreignTransaction) => {
|
||||
return foreignTransaction.brutto < 0;
|
||||
});
|
||||
transaction.description = wantedForeignTransaction.description;
|
||||
transaction.payeeEmail = wantedForeignTransaction.payeeEmail;
|
||||
transaction.name = wantedForeignTransaction.name;
|
||||
}
|
||||
|
||||
return transaction;
|
||||
});
|
||||
|
||||
// lets assign simple transactions at last
|
||||
finalReturnTransactions = finalReturnTransactions.map((transaction) => {
|
||||
transaction.simpleTransaction = {
|
||||
accountId: null,
|
||||
id: transaction.transactionCode,
|
||||
amount: transaction.brutto,
|
||||
date: transaction.transactionDate,
|
||||
description: transaction.description,
|
||||
name: transaction.name,
|
||||
};
|
||||
return transaction;
|
||||
});
|
||||
|
||||
const csvPayPalInstance = new CsvPayPal(finalReturnTransactions);
|
||||
return csvPayPalInstance;
|
||||
}
|
||||
return payments;
|
||||
}
|
||||
}
|
1
ts/paypal/index.ts
Normal file
1
ts/paypal/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export * from './csv-paypal.classes.csvpaypal';
|
1
ts/paypal/interfaces/index.ts
Normal file
1
ts/paypal/interfaces/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export * from './interfaces.paypaltransaction.js';
|
49
ts/paypal/interfaces/interfaces.paypaltransaction.ts
Normal file
49
ts/paypal/interfaces/interfaces.paypaltransaction.ts
Normal file
@ -0,0 +1,49 @@
|
||||
import * as plugins from '../../csvparser.plugins.js';
|
||||
|
||||
export interface IPayPalCsvOriginalTransaction {
|
||||
Datum: string;
|
||||
Uhrzeit: string;
|
||||
Zeitzone: string;
|
||||
Beschreibung: string;
|
||||
Währung: string;
|
||||
Brutto: string;
|
||||
Gebühr: string;
|
||||
Netto: string;
|
||||
Guthaben: string;
|
||||
Transaktionscode: string;
|
||||
'Absender E-Mail-Adresse': string;
|
||||
Name: string;
|
||||
'Name der Bank': string;
|
||||
Bankkonto: string;
|
||||
'Versand- und Bearbeitungsgebühr': string;
|
||||
Umsatzsteuer: string;
|
||||
Rechnungsnummer: string;
|
||||
'Zugehöriger Transaktionscode': string;
|
||||
}
|
||||
|
||||
export interface IPayPalTransaction {
|
||||
// standardised
|
||||
simpleTransaction: plugins.tsclass.finance.ITransaction;
|
||||
originalTransaction;
|
||||
transactionHash: string;
|
||||
|
||||
// specific
|
||||
transactionCode: string;
|
||||
transactionDate: Date;
|
||||
transactionTime: string;
|
||||
timezone: string;
|
||||
description: string;
|
||||
currency: string;
|
||||
brutto: number;
|
||||
fee: number;
|
||||
netto: number;
|
||||
credit: number;
|
||||
payeeEmail: string;
|
||||
name: string;
|
||||
bankName: string;
|
||||
bankAccount: string;
|
||||
processingAndShippingFee: number;
|
||||
vatAmount: number;
|
||||
invoiceNumber: string;
|
||||
linkedTransactionCode: string;
|
||||
}
|
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[];
|
||||
}
|
14
tsconfig.json
Normal file
14
tsconfig.json
Normal file
@ -0,0 +1,14 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"experimentalDecorators": true,
|
||||
"useDefineForClassFields": false,
|
||||
"target": "ES2022",
|
||||
"module": "NodeNext",
|
||||
"moduleResolution": "NodeNext",
|
||||
"esModuleInterop": true,
|
||||
"verbatimModuleSyntax": true
|
||||
},
|
||||
"exclude": [
|
||||
"dist_*/**/*.d.ts"
|
||||
]
|
||||
}
|
Loading…
Reference in New Issue
Block a user