Compare commits
333 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 422f18443a | |||
| 0fbf185223 | |||
| 91cdffa9a6 | |||
| 89199eed8b | |||
| 1a65bb9db6 | |||
| 9772d9ca62 | |||
| 5ee93a5db1 | |||
| 85c4f686da | |||
| 1f066b0d1e | |||
| 38ddb7aaa3 | |||
| a22931f96b | |||
| dcee067aea | |||
| 302d118ae0 | |||
| 09eea96cdc | |||
| 205dea9e58 | |||
| 089eabb806 | |||
| dd63500202 | |||
| 13488efdaf | |||
| edcf33f49c | |||
| d601e01029 | |||
| 4d7c3650b5 | |||
| a0fd468309 | |||
| 0b7471879d | |||
| 282b74c95e | |||
| 5ce1dc973c | |||
| 7ac1e0b69f | |||
| 127bed1026 | |||
| d517a1862b | |||
| 8e5202b375 | |||
| 301fb58917 | |||
| aedebb1461 | |||
| 1336377598 | |||
| 3b58dcbc4d | |||
| 23a5f0a96f | |||
| b81d2ec63c | |||
| cabe365907 | |||
| 54f61ecb18 | |||
| 404cd62d7b | |||
| f0f3143605 | |||
| b729fdedca | |||
| 973cec2c6a | |||
| 6e61aba050 | |||
| 40a8deba12 | |||
| 0ba762ba6e | |||
| 732c256db2 | |||
| d2ce801277 | |||
| 4845fdedfd | |||
| f5a7e2e2d6 | |||
| a84f48ae8a | |||
| c4c9503e31 | |||
| 8c1f499ed8 | |||
| c6eb3cfb74 | |||
| d7a0d253c4 | |||
| 9d275a3051 | |||
| 8192a48bc5 | |||
| 247181830c | |||
| d5dfdc8c05 | |||
| d95fcd8b00 | |||
| 40a366e68d | |||
| 593ae10783 | |||
| 75d9e11bab | |||
| 6d37d42e50 | |||
| f9e9c9285f | |||
| 314bf876f2 | |||
| 61c52cc888 | |||
| b42f226aba | |||
| 767b841866 | |||
| fd08666f49 | |||
| eb662681a1 | |||
| ef15eccc33 | |||
| e0286ff133 | |||
| 2baffe3f11 | |||
| 196657ee86 | |||
| b4c4aafc99 | |||
| b907a79223 | |||
| 0d213de580 | |||
| 2e97d699e7 | |||
| 22e4738b7a | |||
| 4b68641043 | |||
| 3a66a3d655 | |||
| 76d1d3aef3 | |||
| fe2aa5d28b | |||
| f474bbf09a | |||
| c4d02db879 | |||
| 75b36ec547 | |||
| 43b7aea76e | |||
| 13a4a47d40 | |||
| fd9f380922 | |||
| a5fdb9d6b7 | |||
| 983c65e4f8 | |||
| fa568056d3 | |||
| ea8b2812d4 | |||
| b6a2aea8fd | |||
| fa047bf303 | |||
| 4177ac3d46 | |||
| 7647f4f5b9 | |||
| bab03dbde1 | |||
| 85db6e96af | |||
| 548461ade0 | |||
| ecbf182173 | |||
| ab38d33e31 | |||
| 0020f4ede9 | |||
| b470cb63b7 | |||
| 32f2eaef3c | |||
| a7fc3c78eb | |||
| d42b3ecb5e | |||
| 2d4a603d11 | |||
| 7a369328b6 | |||
| 545667e502 | |||
| 8387a81a59 | |||
| 1e3087ccf0 | |||
| bee7772bfd | |||
| a8b6f72ee6 | |||
| 9484cf514d | |||
| 70958c00d3 | |||
| 9467335536 | |||
| f916fdff06 | |||
| ced346506e | |||
| e0cd96f87e | |||
| ed7e906903 | |||
| 3bb7f5abf4 | |||
| 5d801a2343 | |||
| 0d9e59dad9 | |||
| 5fd1396b5c | |||
| b3b9d9293b | |||
| 8b405e513f | |||
| 2bfcfbf03d | |||
| 10388a5ffa | |||
| a127a381cc | |||
| 4aa0dc20af | |||
| 012cc04107 | |||
| 25a84ad3af | |||
| c0036d230a | |||
| 869970a4ab | |||
| 42f8aa410c | |||
| 80e1223505 | |||
| fc9581580c | |||
| b0e6764bfe | |||
| 03fef81414 | |||
| 8dcb8648a5 | |||
| 50b4c96a99 | |||
| c9b894fdbe | |||
| a2f1d944ad | |||
| be4ec2bcce | |||
| c1a728c391 | |||
| 46ff0ecd3b | |||
| 8db69f64c8 | |||
| 8447dd7ae6 | |||
| 543cc5f656 | |||
| 9664bac47f | |||
| 42ae323568 | |||
| a357fb8136 | |||
| 3b487ca0d9 | |||
| 91e98f3126 | |||
| 7ecacaeb05 | |||
| 598ae9fa06 | |||
| 0803a5930f | |||
| 9aaf3284c0 | |||
| b27f9c12de | |||
| a730ebab8f | |||
| f7d0e2279a | |||
| e41dd1c1f8 | |||
| 98274ab864 | |||
| a3261acc82 | |||
| 7d9cfc4ced | |||
| b556efa510 | |||
| 4b72bfd76d | |||
| 0f532094ca | |||
| 7e48cca4ab | |||
| 98aa535193 | |||
| 48ef9acc19 | |||
| e304f4d3fa | |||
| 83a34ae322 | |||
| 43a6d1be0f | |||
| 89fb8a099e | |||
| 853b0d430e | |||
| 88b63d0222 | |||
| 618ad4cac2 | |||
| 9b4dd5600a | |||
| ca959fb9ce | |||
| ee9b281919 | |||
| 1a0630846d | |||
| 9585cbc8a9 | |||
| 19c0ca8191 | |||
| 3b0b95ac4a | |||
| 1691c320cc | |||
| caf88a9488 | |||
| b295b99d3d | |||
| 3cf1276fa7 | |||
| 5ae763273a | |||
| e39965e7b5 | |||
| af36fe9212 | |||
| 6eb7fa27f6 | |||
| 0dd0597c3b | |||
| f0a74a6108 | |||
| 6829eddde5 | |||
| 1c596c4a15 | |||
| ab88b0bf44 | |||
| d462d0164c | |||
| d4d1342c70 | |||
| a157c1961a | |||
| 9a037ace5a | |||
| c64b4502cb | |||
| dc41bf8e10 | |||
| 0ce66d9070 | |||
| 6e369f39a4 | |||
| fb25f589fb | |||
| 8651755d7a | |||
| 277da30339 | |||
| 2fb509beb2 | |||
| 6634d5b791 | |||
| 41739d97e7 | |||
| 43bc04012d | |||
| 43154832b6 | |||
| 91a00cb5b3 | |||
| 526d7e50ec | |||
| cc0996e0d2 | |||
| 8be5e8aa1d | |||
| 022dd3303b | |||
| 2865635013 | |||
| c276f261f9 | |||
| ee7e98bb00 | |||
| 554ce37475 | |||
| 1938d972ff | |||
| 630859bc25 | |||
| 8ea8a9fe2a | |||
| f5e4d82efc | |||
| 958515b9e0 | |||
| 5131e3d6e3 | |||
| b5a18c86dc | |||
| 2f3e26dbe5 | |||
| 3313ccf051 | |||
| 2ada077b38 | |||
| 31c36f0edf | |||
| e74d290016 | |||
| 28337ae228 | |||
| e252378898 | |||
| 1cc0cd7ae6 | |||
| 088e9a339d | |||
| b009c7b6e5 | |||
| 6bb69b0c27 | |||
| 842683da25 | |||
| d39816bb9f | |||
| e856aefd7b | |||
| f54c4998ef | |||
| 59a138d417 | |||
| 0dc2825e5d | |||
| 76af5d946a | |||
| c35cbbda15 | |||
| ece58b60ec | |||
| d95e34a597 | |||
| a09d7b57f9 | |||
| a535fbcef1 | |||
| 931d5e8395 | |||
| b37450db15 | |||
| 2dd7fd30de | |||
| fb55cd1b33 | |||
| e9b4392163 | |||
| 861e4c036b | |||
| e825323bb0 | |||
| aebd65449b | |||
| 0a8f62741a | |||
| b1cefa5a34 | |||
| a12038e40c | |||
| b2fab42170 | |||
| e9c3001c28 | |||
| 44039438e0 | |||
| 372ea29edd | |||
| 1eb958a21b | |||
| 89dd306bb4 | |||
| c170cb42e6 | |||
| 78f3beaf2f | |||
| 482d025c90 | |||
| 11c943efef | |||
| 6debea6dbb | |||
| a7a8b9a2fb | |||
| 47cc046e60 | |||
| 5a9f4ec3b4 | |||
| 70aa19c623 | |||
| d2771f6fa9 | |||
| 5e4637c6ad | |||
| a9a7d28082 | |||
| ffce01c612 | |||
| 7a3ec9468f | |||
| 5850e0e298 | |||
| fdf6548cc9 | |||
| ee8aa2bb8e | |||
| eccea273e6 | |||
| e143c8f098 | |||
| 81226c3bb2 | |||
| c3db8cee2d | |||
| 9061fc3188 | |||
| b9539c4aba | |||
| deabe178df | |||
| a5320cf929 | |||
| 79842d9171 | |||
| 3daff44155 | |||
| cd2cce4268 | |||
| ad132d5637 | |||
| c8b3daa915 | |||
| 96561ec2be | |||
| 608411feab | |||
| 516e3a5613 | |||
| 2431152cec | |||
| 17f604b6aa | |||
| 3fe51dce63 | |||
| 2c454f001e | |||
| 4781cb34eb | |||
| 9faea14e36 | |||
| bd704a8c15 | |||
| bb9a19bcb2 | |||
| 9ff1334584 | |||
| de27c8e6c5 | |||
| ba278e47ff | |||
| 7d70859107 | |||
| 6430a52027 | |||
| 45be96cf68 | |||
| 837a62a534 | |||
| 707283fd66 | |||
| 10b9c09192 | |||
| ed8c5c96ac | |||
| 3ba91c590e | |||
| 44dc45de51 | |||
| 83bd68e7f4 | |||
| dafbc115c4 | |||
| 5de1e32cd8 | |||
| 8ae5c1ea99 | |||
| 29651f674a | |||
| b1dff5ef51 | |||
| bb0971ea17 | |||
| 8a020b666c | |||
| 8b34750426 | |||
| 32cb2b2354 |
@@ -3,13 +3,13 @@ name: Build backend file for windows
|
|||||||
inputs:
|
inputs:
|
||||||
go-version:
|
go-version:
|
||||||
required: false
|
required: false
|
||||||
default: "1.25.3"
|
default: "1.25.7"
|
||||||
mingw-version:
|
mingw-version:
|
||||||
required: false
|
required: false
|
||||||
default: "14.2.0"
|
default: "15.2.0"
|
||||||
mingw-revison:
|
mingw-revison:
|
||||||
required: false
|
required: false
|
||||||
default: "v12-rev2"
|
default: "v13-rev1"
|
||||||
release-build:
|
release-build:
|
||||||
required: false
|
required: false
|
||||||
type: string
|
type: string
|
||||||
|
|||||||
+14
@@ -147,3 +147,17 @@ dist/
|
|||||||
|
|
||||||
# Roo Code
|
# Roo Code
|
||||||
.roo/
|
.roo/
|
||||||
|
|
||||||
|
# Binary and build files
|
||||||
|
ezbookkeeping
|
||||||
|
!**/ezbookkeeping/
|
||||||
|
package/
|
||||||
|
|
||||||
|
# Environment variable files
|
||||||
|
.env
|
||||||
|
**/.env
|
||||||
|
|
||||||
|
# Other directories
|
||||||
|
data/
|
||||||
|
storage/
|
||||||
|
log/
|
||||||
|
|||||||
+3
-3
@@ -1,5 +1,5 @@
|
|||||||
# Build backend binary file
|
# Build backend binary file
|
||||||
FROM golang:1.25.3-alpine3.22 AS be-builder
|
FROM golang:1.25.7-alpine3.23 AS be-builder
|
||||||
ARG RELEASE_BUILD
|
ARG RELEASE_BUILD
|
||||||
ARG BUILD_PIPELINE
|
ARG BUILD_PIPELINE
|
||||||
ARG BUILD_UNIXTIME
|
ARG BUILD_UNIXTIME
|
||||||
@@ -19,7 +19,7 @@ RUN apk add git gcc g++ libc-dev
|
|||||||
RUN ./build.sh backend
|
RUN ./build.sh backend
|
||||||
|
|
||||||
# Build frontend files
|
# Build frontend files
|
||||||
FROM --platform=$BUILDPLATFORM node:24.10.0-alpine3.22 AS fe-builder
|
FROM --platform=$BUILDPLATFORM node:24.14.0-alpine3.23 AS fe-builder
|
||||||
ARG RELEASE_BUILD
|
ARG RELEASE_BUILD
|
||||||
ARG BUILD_PIPELINE
|
ARG BUILD_PIPELINE
|
||||||
ARG BUILD_UNIXTIME
|
ARG BUILD_UNIXTIME
|
||||||
@@ -35,7 +35,7 @@ RUN apk add git
|
|||||||
RUN ./build.sh frontend
|
RUN ./build.sh frontend
|
||||||
|
|
||||||
# Package docker image
|
# Package docker image
|
||||||
FROM alpine:3.22.2
|
FROM alpine:3.23.3
|
||||||
LABEL maintainer="MaysWind <i@mayswind.net>"
|
LABEL maintainer="MaysWind <i@mayswind.net>"
|
||||||
RUN addgroup -S -g 1000 ezbookkeeping && adduser -S -G ezbookkeeping -u 1000 ezbookkeeping
|
RUN addgroup -S -g 1000 ezbookkeeping && adduser -S -G ezbookkeeping -u 1000 ezbookkeeping
|
||||||
RUN apk --no-cache add tzdata
|
RUN apk --no-cache add tzdata
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
Copyright (c) 2020-2025 MaysWind (i@mayswind.net)
|
Copyright (c) 2020-2026 MaysWind (i@mayswind.net)
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
[](https://trendshift.io/repositories/12917)
|
[](https://trendshift.io/repositories/12917)
|
||||||
|
|
||||||
## Introduction
|
## Introduction
|
||||||
ezBookkeeping is a lightweight, self-hosted personal finance app with a user-friendly interface and powerful bookkeeping features. It's easy to deploy, and you can start it with just one single Docker command. Designed to be resource-efficient and highly scalable, it can run smoothly on devices as small as a Raspberry Pi, or scale up to NAS, MicroServers, and even large cluster environments.
|
ezBookkeeping is a lightweight, self-hosted personal finance app with a user-friendly interface and powerful bookkeeping features. It helps you record daily transactions, import data from various sources, and quickly search and filter your bills. You can analyze historical data using built-in charts or perform custom queries with your own chart dimensions to better understand spending patterns and financial trends. ezBookkeeping is easy to deploy, and you can start it with just one single Docker command. Designed to be resource-efficient, it runs smoothly on devices such as Raspberry Pi, NAS, and MicroServers.
|
||||||
|
|
||||||
ezBookkeeping offers tailored interfaces for both mobile and desktop devices. With support for PWA (Progressive Web Apps), you can even [add it to your mobile home screen](https://raw.githubusercontent.com/wiki/mayswind/ezbookkeeping/img/mobile/add_to_home_screen.gif) and use it like a native app.
|
ezBookkeeping offers tailored interfaces for both mobile and desktop devices. With support for PWA (Progressive Web Apps), you can even [add it to your mobile home screen](https://raw.githubusercontent.com/wiki/mayswind/ezbookkeeping/img/mobile/add_to_home_screen.gif) and use it like a native app.
|
||||||
|
|
||||||
@@ -21,9 +21,9 @@ Live Demo: [https://ezbookkeeping-demo.mayswind.net](https://ezbookkeeping-demo.
|
|||||||
- **Open Source & Self-Hosted**
|
- **Open Source & Self-Hosted**
|
||||||
- Built for privacy and control
|
- Built for privacy and control
|
||||||
- **Lightweight & Fast**
|
- **Lightweight & Fast**
|
||||||
- Optimized for performance, runs smoothly even on low-resource environments
|
- Minimal resource usage, runs smoothly even on low-resource devices
|
||||||
- **Easy Installation**
|
- **Easy Installation**
|
||||||
- Docker-ready
|
- Docker support
|
||||||
- Supports SQLite, MySQL, PostgreSQL
|
- Supports SQLite, MySQL, PostgreSQL
|
||||||
- Cross-platform (Windows, macOS, Linux)
|
- Cross-platform (Windows, macOS, Linux)
|
||||||
- Works on x86, amd64, ARM architectures
|
- Works on x86, amd64, ARM architectures
|
||||||
@@ -33,24 +33,28 @@ Live Demo: [https://ezbookkeeping-demo.mayswind.net](https://ezbookkeeping-demo.
|
|||||||
- Dark mode
|
- Dark mode
|
||||||
- **AI-Powered Features**
|
- **AI-Powered Features**
|
||||||
- Receipt image recognition
|
- Receipt image recognition
|
||||||
- Supports MCP (Model Context Protocol) for AI integration
|
- MCP (Model Context Protocol) support for AI integration
|
||||||
|
- API command-line script tools for AI integration
|
||||||
- **Powerful Bookkeeping**
|
- **Powerful Bookkeeping**
|
||||||
- Two-level accounts and categories
|
- Two-level accounts and categories
|
||||||
- Attach images to transactions
|
- Image attachments for transactions
|
||||||
- Location tracking with maps
|
- Location tracking with maps
|
||||||
- Recurring transactions
|
- Scheduled transactions
|
||||||
- Advanced filtering, search, visualization, and analysis
|
- Advanced filtering, search, visualization and analysis
|
||||||
- **Localization & Globalization**
|
- **Localization & Internationalization**
|
||||||
- Multi-language and multi-currency support
|
- Multi-language and multi-currency support
|
||||||
- Automatic exchange rates
|
- Multiple exchange rate sources with automatic updates
|
||||||
- Multi-timezone awareness
|
- Multi-timezone support
|
||||||
- Custom formats for dates, numbers, and currencies
|
- Custom formats for dates, numbers and currencies
|
||||||
- **Security**
|
- **Security**
|
||||||
- Two-factor authentication (2FA)
|
- Two-factor authentication (2FA)
|
||||||
|
- OIDC external authentication
|
||||||
- Login rate limiting
|
- Login rate limiting
|
||||||
- Application lock (PIN code / WebAuthn)
|
- Application lock (PIN code / WebAuthn)
|
||||||
- **Data Import/Export**
|
- **Data Import & Export**
|
||||||
- Supports CSV, OFX, QFX, QIF, IIF, Camt.053, MT940, GnuCash, Firefly III, Beancount, and more
|
- Supports CSV, OFX, QFX, QIF, IIF, Camt.052, Camt.053, MT940, GnuCash, Firefly III, Beancount and more
|
||||||
|
|
||||||
|
For a full list of features, visit the [Full Feature List](https://ezbookkeeping.mayswind.net/comparison/).
|
||||||
|
|
||||||
## Screenshots
|
## Screenshots
|
||||||
### Desktop Version
|
### Desktop Version
|
||||||
@@ -112,16 +116,16 @@ You can also build a Docker image. Make sure you have [Docker](https://www.docke
|
|||||||
## Contributing
|
## Contributing
|
||||||
We welcome contributions of all kinds.
|
We welcome contributions of all kinds.
|
||||||
|
|
||||||
Found a bug? [Submit an issue](https://github.com/mayswind/ezbookkeeping/issues)
|
If you find a bug, please [submit an issue](https://github.com/mayswind/ezbookkeeping/issues) on GitHub.
|
||||||
|
|
||||||
Want to contribute code? Feel free to fork and send a pull request.
|
If you would like to contribute code, you can fork the repository and open a pull request.
|
||||||
|
|
||||||
Contributions of all kinds — bug reports, feature suggestions, documentation improvements, or code — are highly appreciated.
|
Improvements to documentation, feature suggestions, and other forms of feedback are also appreciated.
|
||||||
|
|
||||||
Check out our [Contributor Graph](https://github.com/mayswind/ezbookkeeping/graphs/contributors) to see the amazing people who've already helped.
|
You can view existing contributors on the [Contributor Graph](https://github.com/mayswind/ezbookkeeping/graphs/contributors).
|
||||||
|
|
||||||
## Translating
|
## Translating
|
||||||
Help make ezBookkeeping accessible to users around the world. If you want to contribute a translation, please refer to our [translation guide](https://ezbookkeeping.mayswind.net/translating).
|
Help make ezBookkeeping accessible to users around the world. We welcome help to improve existing translations or add new ones. If you would like to contribute a translation, please refer to the [translation guide](https://ezbookkeeping.mayswind.net/translating).
|
||||||
|
|
||||||
Currently available translations:
|
Currently available translations:
|
||||||
|
|
||||||
@@ -129,22 +133,24 @@ Currently available translations:
|
|||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
| de | Deutsch | [@chrgm](https://github.com/chrgm) |
|
| de | Deutsch | [@chrgm](https://github.com/chrgm) |
|
||||||
| en | English | / |
|
| en | English | / |
|
||||||
| es | Español | [@Miguelonlonlon](https://github.com/Miguelonlonlon) |
|
| es | Español | [@Miguelonlonlon](https://github.com/Miguelonlonlon), [@abrugues](https://github.com/abrugues), [@AndresTeller](https://github.com/AndresTeller), [@diegofercri](https://github.com/diegofercri) |
|
||||||
| fr | Français | [@brieucdlf](https://github.com/brieucdlf) |
|
| fr | Français | [@brieucdlf](https://github.com/brieucdlf) |
|
||||||
| it | Italiano | [@waron97](https://github.com/waron97) |
|
| it | Italiano | [@waron97](https://github.com/waron97) |
|
||||||
| ja | 日本語 | [@tkymmm](https://github.com/tkymmm) |
|
| ja | 日本語 | [@tkymmm](https://github.com/tkymmm) |
|
||||||
|
| kn | ಕನ್ನಡ | [@Darshanbm05](https://github.com/Darshanbm05) |
|
||||||
| ko | 한국어 | [@overworks](https://github.com/overworks) |
|
| ko | 한국어 | [@overworks](https://github.com/overworks) |
|
||||||
| nl | Nederlands | [@automagic](https://github.com/automagics) |
|
| nl | Nederlands | [@automagics](https://github.com/automagics) |
|
||||||
| pt-BR | Português (Brasil) | [@thecodergus](https://github.com/thecodergus) |
|
| pt-BR | Português (Brasil) | [@thecodergus](https://github.com/thecodergus), [@balaios](https://github.com/balaios) |
|
||||||
| ru | Русский | [@artegoser](https://github.com/artegoser) |
|
| ru | Русский | [@artegoser](https://github.com/artegoser), [@dshemin](https://github.com/dshemin) |
|
||||||
|
| sl | Slovenščina | [@thehijacker](https://github.com/thehijacker) |
|
||||||
|
| ta | தமிழ் | [@hhharsha36](https://github.com/hhharsha36) |
|
||||||
| th | ไทย | [@natthavat28](https://github.com/natthavat28) |
|
| th | ไทย | [@natthavat28](https://github.com/natthavat28) |
|
||||||
|
| tr | Türkçe | [@aydnykn](https://github.com/aydnykn) |
|
||||||
| uk | Українська | [@nktlitvinenko](https://github.com/nktlitvinenko) |
|
| uk | Українська | [@nktlitvinenko](https://github.com/nktlitvinenko) |
|
||||||
| vi | Tiếng Việt | [@f97](https://github.com/f97) |
|
| vi | Tiếng Việt | [@f97](https://github.com/f97) |
|
||||||
| zh-Hans | 中文 (简体) | / |
|
| zh-Hans | 中文 (简体) | / |
|
||||||
| zh-Hant | 中文 (繁體) | / |
|
| zh-Hant | 中文 (繁體) | / |
|
||||||
|
|
||||||
Don't see your language? Help us add it.
|
|
||||||
|
|
||||||
## Documentation
|
## Documentation
|
||||||
1. [English](https://ezbookkeeping.mayswind.net)
|
1. [English](https://ezbookkeeping.mayswind.net)
|
||||||
1. [中文 (简体)](https://ezbookkeeping.mayswind.net/zh_Hans)
|
1. [中文 (简体)](https://ezbookkeeping.mayswind.net/zh_Hans)
|
||||||
|
|||||||
@@ -101,6 +101,14 @@ func updateAllDatabaseTablesStructure(c *core.CliContext) error {
|
|||||||
|
|
||||||
log.BootInfof(c, "[database.updateAllDatabaseTablesStructure] transaction category table maintained successfully")
|
log.BootInfof(c, "[database.updateAllDatabaseTablesStructure] transaction category table maintained successfully")
|
||||||
|
|
||||||
|
err = datastore.Container.UserDataStore.SyncStructs(new(models.TransactionTagGroup))
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
log.BootInfof(c, "[database.updateAllDatabaseTablesStructure] transaction tag group table maintained successfully")
|
||||||
|
|
||||||
err = datastore.Container.UserDataStore.SyncStructs(new(models.TransactionTag))
|
err = datastore.Container.UserDataStore.SyncStructs(new(models.TransactionTag))
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -157,5 +165,13 @@ func updateAllDatabaseTablesStructure(c *core.CliContext) error {
|
|||||||
|
|
||||||
log.BootInfof(c, "[database.updateAllDatabaseTablesStructure] user external auth table maintained successfully")
|
log.BootInfof(c, "[database.updateAllDatabaseTablesStructure] user external auth table maintained successfully")
|
||||||
|
|
||||||
|
err = datastore.Container.UserDataStore.SyncStructs(new(models.InsightsExplorer))
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
log.BootInfof(c, "[database.updateAllDatabaseTablesStructure] insights explorer table maintained successfully")
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -195,9 +195,25 @@ func getConfigWithoutSensitiveData(config *settings.Config) *settings.Config {
|
|||||||
clonedConfig.ReceiptImageRecognitionLLMConfig.OpenAICompatibleAPIKey = "****"
|
clonedConfig.ReceiptImageRecognitionLLMConfig.OpenAICompatibleAPIKey = "****"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if clonedConfig.ReceiptImageRecognitionLLMConfig.AnthropicCompatibleAPIKey != "" {
|
||||||
|
clonedConfig.ReceiptImageRecognitionLLMConfig.AnthropicCompatibleAPIKey = "****"
|
||||||
|
}
|
||||||
|
|
||||||
|
if clonedConfig.ReceiptImageRecognitionLLMConfig.AnthropicAPIKey != "" {
|
||||||
|
clonedConfig.ReceiptImageRecognitionLLMConfig.AnthropicAPIKey = "****"
|
||||||
|
}
|
||||||
|
|
||||||
if clonedConfig.ReceiptImageRecognitionLLMConfig.OpenRouterAPIKey != "" {
|
if clonedConfig.ReceiptImageRecognitionLLMConfig.OpenRouterAPIKey != "" {
|
||||||
clonedConfig.ReceiptImageRecognitionLLMConfig.OpenRouterAPIKey = "****"
|
clonedConfig.ReceiptImageRecognitionLLMConfig.OpenRouterAPIKey = "****"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if clonedConfig.ReceiptImageRecognitionLLMConfig.LMStudioToken != "" {
|
||||||
|
clonedConfig.ReceiptImageRecognitionLLMConfig.LMStudioToken = "****"
|
||||||
|
}
|
||||||
|
|
||||||
|
if clonedConfig.ReceiptImageRecognitionLLMConfig.GoogleAIAPIKey != "" {
|
||||||
|
clonedConfig.ReceiptImageRecognitionLLMConfig.GoogleAIAPIKey = "****"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if clonedConfig.OAuth2ClientSecret != "" {
|
if clonedConfig.OAuth2ClientSecret != "" {
|
||||||
|
|||||||
+21
-1
@@ -116,6 +116,7 @@ func startWebServer(c *core.CliContext) error {
|
|||||||
_ = v.RegisterValidation("validCurrency", validators.ValidCurrency)
|
_ = v.RegisterValidation("validCurrency", validators.ValidCurrency)
|
||||||
_ = v.RegisterValidation("validHexRGBColor", validators.ValidHexRGBColor)
|
_ = v.RegisterValidation("validHexRGBColor", validators.ValidHexRGBColor)
|
||||||
_ = v.RegisterValidation("validAmountFilter", validators.ValidAmountFilter)
|
_ = v.RegisterValidation("validAmountFilter", validators.ValidAmountFilter)
|
||||||
|
_ = v.RegisterValidation("validTagFilter", validators.ValidTagFilter)
|
||||||
_ = v.RegisterValidation("validFiscalYearStart", validators.ValidateFiscalYearStart)
|
_ = v.RegisterValidation("validFiscalYearStart", validators.ValidateFiscalYearStart)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -315,6 +316,7 @@ func startWebServer(c *core.CliContext) error {
|
|||||||
|
|
||||||
apiV1Route := apiRoute.Group("/v1")
|
apiV1Route := apiRoute.Group("/v1")
|
||||||
apiV1Route.Use(bindMiddleware(middlewares.JWTAuthorization(config)))
|
apiV1Route.Use(bindMiddleware(middlewares.JWTAuthorization(config)))
|
||||||
|
apiV1Route.Use(bindMiddleware(middlewares.APITokenIpLimit(config)))
|
||||||
{
|
{
|
||||||
// Tokens
|
// Tokens
|
||||||
apiV1Route.GET("/tokens/list.json", bindApi(api.Tokens.TokenListHandler))
|
apiV1Route.GET("/tokens/list.json", bindApi(api.Tokens.TokenListHandler))
|
||||||
@@ -382,6 +384,7 @@ func startWebServer(c *core.CliContext) error {
|
|||||||
apiV1Route.GET("/transactions/count.json", bindApi(api.Transactions.TransactionCountHandler))
|
apiV1Route.GET("/transactions/count.json", bindApi(api.Transactions.TransactionCountHandler))
|
||||||
apiV1Route.GET("/transactions/list.json", bindApi(api.Transactions.TransactionListHandler))
|
apiV1Route.GET("/transactions/list.json", bindApi(api.Transactions.TransactionListHandler))
|
||||||
apiV1Route.GET("/transactions/list/by_month.json", bindApi(api.Transactions.TransactionMonthListHandler))
|
apiV1Route.GET("/transactions/list/by_month.json", bindApi(api.Transactions.TransactionMonthListHandler))
|
||||||
|
apiV1Route.GET("/transactions/list/all.json", bindApi(api.Transactions.TransactionListAllHandler))
|
||||||
apiV1Route.GET("/transactions/reconciliation_statements.json", bindApi(api.Transactions.TransactionReconciliationStatementHandler))
|
apiV1Route.GET("/transactions/reconciliation_statements.json", bindApi(api.Transactions.TransactionReconciliationStatementHandler))
|
||||||
apiV1Route.GET("/transactions/statistics.json", bindApi(api.Transactions.TransactionStatisticsHandler))
|
apiV1Route.GET("/transactions/statistics.json", bindApi(api.Transactions.TransactionStatisticsHandler))
|
||||||
apiV1Route.GET("/transactions/statistics/trends.json", bindApi(api.Transactions.TransactionStatisticsTrendsHandler))
|
apiV1Route.GET("/transactions/statistics/trends.json", bindApi(api.Transactions.TransactionStatisticsTrendsHandler))
|
||||||
@@ -394,7 +397,7 @@ func startWebServer(c *core.CliContext) error {
|
|||||||
apiV1Route.POST("/transactions/delete.json", bindApi(api.Transactions.TransactionDeleteHandler))
|
apiV1Route.POST("/transactions/delete.json", bindApi(api.Transactions.TransactionDeleteHandler))
|
||||||
|
|
||||||
if config.EnableDataImport {
|
if config.EnableDataImport {
|
||||||
apiV1Route.POST("/transactions/parse_dsv_file.json", bindApi(api.Transactions.TransactionParseImportDsvFileDataHandler))
|
apiV1Route.POST("/transactions/parse_custom_file.json", bindApi(api.Transactions.TransactionParseImportCustomFileDataHandler))
|
||||||
apiV1Route.POST("/transactions/parse_import.json", bindApi(api.Transactions.TransactionParseImportFileHandler))
|
apiV1Route.POST("/transactions/parse_import.json", bindApi(api.Transactions.TransactionParseImportFileHandler))
|
||||||
apiV1Route.POST("/transactions/import.json", bindApi(api.Transactions.TransactionImportHandler))
|
apiV1Route.POST("/transactions/import.json", bindApi(api.Transactions.TransactionImportHandler))
|
||||||
apiV1Route.GET("/transactions/import/process.json", bindApi(api.Transactions.TransactionImportProcessHandler))
|
apiV1Route.GET("/transactions/import/process.json", bindApi(api.Transactions.TransactionImportProcessHandler))
|
||||||
@@ -416,6 +419,14 @@ func startWebServer(c *core.CliContext) error {
|
|||||||
apiV1Route.POST("/transaction/categories/move.json", bindApi(api.TransactionCategories.CategoryMoveHandler))
|
apiV1Route.POST("/transaction/categories/move.json", bindApi(api.TransactionCategories.CategoryMoveHandler))
|
||||||
apiV1Route.POST("/transaction/categories/delete.json", bindApi(api.TransactionCategories.CategoryDeleteHandler))
|
apiV1Route.POST("/transaction/categories/delete.json", bindApi(api.TransactionCategories.CategoryDeleteHandler))
|
||||||
|
|
||||||
|
// Transaction Tag Groups
|
||||||
|
apiV1Route.GET("/transaction/tags/groups/list.json", bindApi(api.TransactionTagGroups.TagGroupListHandler))
|
||||||
|
apiV1Route.GET("/transaction/tags/groups/get.json", bindApi(api.TransactionTagGroups.TagGroupGetHandler))
|
||||||
|
apiV1Route.POST("/transaction/tags/groups/add.json", bindApi(api.TransactionTagGroups.TagGroupCreateHandler))
|
||||||
|
apiV1Route.POST("/transaction/tags/groups/modify.json", bindApi(api.TransactionTagGroups.TagGroupModifyHandler))
|
||||||
|
apiV1Route.POST("/transaction/tags/groups/move.json", bindApi(api.TransactionTagGroups.TagGroupMoveHandler))
|
||||||
|
apiV1Route.POST("/transaction/tags/groups/delete.json", bindApi(api.TransactionTagGroups.TagGroupDeleteHandler))
|
||||||
|
|
||||||
// Transaction Tags
|
// Transaction Tags
|
||||||
apiV1Route.GET("/transaction/tags/list.json", bindApi(api.TransactionTags.TagListHandler))
|
apiV1Route.GET("/transaction/tags/list.json", bindApi(api.TransactionTags.TagListHandler))
|
||||||
apiV1Route.GET("/transaction/tags/get.json", bindApi(api.TransactionTags.TagGetHandler))
|
apiV1Route.GET("/transaction/tags/get.json", bindApi(api.TransactionTags.TagGetHandler))
|
||||||
@@ -435,6 +446,15 @@ func startWebServer(c *core.CliContext) error {
|
|||||||
apiV1Route.POST("/transaction/templates/move.json", bindApi(api.TransactionTemplates.TemplateMoveHandler))
|
apiV1Route.POST("/transaction/templates/move.json", bindApi(api.TransactionTemplates.TemplateMoveHandler))
|
||||||
apiV1Route.POST("/transaction/templates/delete.json", bindApi(api.TransactionTemplates.TemplateDeleteHandler))
|
apiV1Route.POST("/transaction/templates/delete.json", bindApi(api.TransactionTemplates.TemplateDeleteHandler))
|
||||||
|
|
||||||
|
// Insights Explorers
|
||||||
|
apiV1Route.GET("/insights/explorers/list.json", bindApi(api.InsightsExplorers.InsightsExplorerListHandler))
|
||||||
|
apiV1Route.GET("/insights/explorers/get.json", bindApi(api.InsightsExplorers.InsightsExplorerGetHandler))
|
||||||
|
apiV1Route.POST("/insights/explorers/add.json", bindApi(api.InsightsExplorers.InsightsExplorerCreateHandler))
|
||||||
|
apiV1Route.POST("/insights/explorers/modify.json", bindApi(api.InsightsExplorers.InsightsExplorerModifyHandler))
|
||||||
|
apiV1Route.POST("/insights/explorers/hide.json", bindApi(api.InsightsExplorers.InsightsExplorerHideHandler))
|
||||||
|
apiV1Route.POST("/insights/explorers/move.json", bindApi(api.InsightsExplorers.InsightsExplorerMoveHandler))
|
||||||
|
apiV1Route.POST("/insights/explorers/delete.json", bindApi(api.InsightsExplorers.InsightsExplorerDeleteHandler))
|
||||||
|
|
||||||
// Large Language Models
|
// Large Language Models
|
||||||
if config.ReceiptImageRecognitionLLMConfig != nil && config.ReceiptImageRecognitionLLMConfig.LLMProvider != "" {
|
if config.ReceiptImageRecognitionLLMConfig != nil && config.ReceiptImageRecognitionLLMConfig.LLMProvider != "" {
|
||||||
if config.TransactionFromAIImageRecognition {
|
if config.TransactionFromAIImageRecognition {
|
||||||
|
|||||||
+38
-7
@@ -1,7 +1,4 @@
|
|||||||
[global]
|
[global]
|
||||||
# Application instance name
|
|
||||||
app_name = ezBookkeeping
|
|
||||||
|
|
||||||
# Either "production", "development"
|
# Either "production", "development"
|
||||||
mode = production
|
mode = production
|
||||||
|
|
||||||
@@ -18,7 +15,7 @@ http_port = 8080
|
|||||||
# The domain name used to access ezBookkeeping
|
# The domain name used to access ezBookkeeping
|
||||||
domain = localhost
|
domain = localhost
|
||||||
|
|
||||||
# The full url used to access ezBookkeeping in browser
|
# The full url used to access ezBookkeeping in browser, supports placeholders: %(protocol)s, %(domain)s, %(http_port)s
|
||||||
root_url = %(protocol)s://%(domain)s:%(http_port)s/
|
root_url = %(protocol)s://%(domain)s:%(http_port)s/
|
||||||
|
|
||||||
# https certification and its key file
|
# https certification and its key file
|
||||||
@@ -172,7 +169,7 @@ transaction_from_ai_image_recognition = false
|
|||||||
max_ai_recognition_picture_size = 10485760
|
max_ai_recognition_picture_size = 10485760
|
||||||
|
|
||||||
[llm_image_recognition]
|
[llm_image_recognition]
|
||||||
# Large Language Model (LLM) provider for receipt image recognition, supports the following types: "openai", "openai_compatible", "openrouter", "ollama", "google_ai"
|
# Large Language Model (LLM) provider for receipt image recognition, supports the following types: "openai", "openai_compatible", "anthropic", "anthropic_compatible", "openrouter", "ollama", "lm_studio", "google_ai"
|
||||||
llm_provider =
|
llm_provider =
|
||||||
|
|
||||||
# For "openai" llm provider only, OpenAI API secret key, please visit https://platform.openai.com/api-keys for more information
|
# For "openai" llm provider only, OpenAI API secret key, please visit https://platform.openai.com/api-keys for more information
|
||||||
@@ -190,6 +187,30 @@ openai_compatible_api_key =
|
|||||||
# For "openai_compatible" llm provider only, receipt image recognition model for creating transactions from images
|
# For "openai_compatible" llm provider only, receipt image recognition model for creating transactions from images
|
||||||
openai_compatible_model_id =
|
openai_compatible_model_id =
|
||||||
|
|
||||||
|
# For "anthropic" llm provider only, Anthropic API key, please visit https://platform.claude.com/settings/keys for more information
|
||||||
|
anthropic_api_key =
|
||||||
|
|
||||||
|
# For "anthropic" llm provider only, receipt image recognition model for creating transactions from images
|
||||||
|
anthropic_model_id =
|
||||||
|
|
||||||
|
# For "anthropic" llm provider only, maximum allowed number of generated tokens for creating transactions from images, default is 1024
|
||||||
|
anthropic_max_tokens = 1024
|
||||||
|
|
||||||
|
# For "anthropic_compatible" llm provider only, Anthropic compatible API base url, e.g. "https://api.anthropic.com/v1/"
|
||||||
|
anthropic_compatible_base_url =
|
||||||
|
|
||||||
|
# For "anthropic_compatible" llm provider only, Anthropic compatible API version, e.g. "2023-06-01". If the LLM service does not require API versioning, leave it blank
|
||||||
|
anthropic_compatible_api_version =
|
||||||
|
|
||||||
|
# For "anthropic_compatible" llm provider only, Anthropic compatible API secret key
|
||||||
|
anthropic_compatible_api_key =
|
||||||
|
|
||||||
|
# For "anthropic_compatible" llm provider only, receipt image recognition model for creating transactions from images
|
||||||
|
anthropic_compatible_model_id =
|
||||||
|
|
||||||
|
# For "anthropic_compatible" llm provider only, maximum allowed number of generated tokens for creating transactions from images, default is 1024
|
||||||
|
anthropic_compatible_max_tokens = 1024
|
||||||
|
|
||||||
# For "openrouter" llm provider only, OpenRouter API key, please visit https://openrouter.ai/settings/keys for more information
|
# For "openrouter" llm provider only, OpenRouter API key, please visit https://openrouter.ai/settings/keys for more information
|
||||||
openrouter_api_key =
|
openrouter_api_key =
|
||||||
|
|
||||||
@@ -202,6 +223,15 @@ ollama_server_url =
|
|||||||
# For "ollama" llm provider only, receipt image recognition model for creating transactions from images
|
# For "ollama" llm provider only, receipt image recognition model for creating transactions from images
|
||||||
ollama_model_id =
|
ollama_model_id =
|
||||||
|
|
||||||
|
# For "lm_studio" llm provider only, LM Studio server url, e.g. "http://127.0.0.1:1234/"
|
||||||
|
lm_studio_server_url =
|
||||||
|
|
||||||
|
# For "lm_studio" llm provider only, LM Studio API token, if "require authentication" is not enabled in LM Studio, leave it blank
|
||||||
|
lm_studio_token =
|
||||||
|
|
||||||
|
# For "lm_studio" llm provider only, receipt image recognition model for creating transactions from images
|
||||||
|
lm_studio_model_id =
|
||||||
|
|
||||||
# For "google_ai" llm provider only, Google AI Studio API key, please visit https://aistudio.google.com/apikey for more information
|
# For "google_ai" llm provider only, Google AI Studio API key, please visit https://aistudio.google.com/apikey for more information
|
||||||
google_ai_api_key =
|
google_ai_api_key =
|
||||||
|
|
||||||
@@ -266,6 +296,9 @@ password_reset_token_expired_time = 3600
|
|||||||
# Set to true to enable API token generation
|
# Set to true to enable API token generation
|
||||||
enable_api_token = false
|
enable_api_token = false
|
||||||
|
|
||||||
|
# Allowed remote IPs for using the API token, a comma-separated list of allowed remote IPs (asterisk * for any addresses, e.g. 192.168.1.* means any IPs in the 192.168.1.x subnet), leave blank to allow all remote IPs
|
||||||
|
api_token_allowed_remote_ips =
|
||||||
|
|
||||||
# Maximum count of password / token check failures (0 - 4294967295) per IP per minute (use the above duplicate checker), default is 5, set to 0 to disable
|
# Maximum count of password / token check failures (0 - 4294967295) per IP per minute (use the above duplicate checker), default is 5, set to 0 to disable
|
||||||
max_failures_per_ip_per_minute = 5
|
max_failures_per_ip_per_minute = 5
|
||||||
|
|
||||||
@@ -496,7 +529,6 @@ custom_map_tile_server_default_zoom_level = 14
|
|||||||
|
|
||||||
[exchange_rates]
|
[exchange_rates]
|
||||||
# Exchange rates data source, supports the following types:
|
# Exchange rates data source, supports the following types:
|
||||||
# "reserve_bank_of_australia": https://www.rba.gov.au/statistics/frequency/exchange-rates.html
|
|
||||||
# "bank_of_canada": https://www.bankofcanada.ca/rates/exchange/daily-exchange-rates/
|
# "bank_of_canada": https://www.bankofcanada.ca/rates/exchange/daily-exchange-rates/
|
||||||
# "czech_national_bank": https://www.cnb.cz/en/financial-markets/foreign-exchange-market/central-bank-exchange-rate-fixing/central-bank-exchange-rate-fixing/
|
# "czech_national_bank": https://www.cnb.cz/en/financial-markets/foreign-exchange-market/central-bank-exchange-rate-fixing/central-bank-exchange-rate-fixing/
|
||||||
# "danmarks_national_bank": https://www.nationalbanken.dk/en/what-we-do/stable-prices-monetary-policy-and-the-danish-economy/exchange-rates
|
# "danmarks_national_bank": https://www.nationalbanken.dk/en/what-we-do/stable-prices-monetary-policy-and-the-danish-economy/exchange-rates
|
||||||
@@ -512,7 +544,6 @@ custom_map_tile_server_default_zoom_level = 14
|
|||||||
# "swiss_national_bank": https://www.snb.ch/en/the-snb/mandates-goals/statistics/statistics-pub/current_interest_exchange_rates
|
# "swiss_national_bank": https://www.snb.ch/en/the-snb/mandates-goals/statistics/statistics-pub/current_interest_exchange_rates
|
||||||
# "national_bank_of_ukraine": https://bank.gov.ua/ua/markets/exchangerates
|
# "national_bank_of_ukraine": https://bank.gov.ua/ua/markets/exchangerates
|
||||||
# "central_bank_of_uzbekistan": https://cbu.uz/en/arkhiv-kursov-valyut/
|
# "central_bank_of_uzbekistan": https://cbu.uz/en/arkhiv-kursov-valyut/
|
||||||
# "international_monetary_fund": https://www.imf.org/external/np/fin/data/param_rms_mth.aspx
|
|
||||||
# "user_custom": users set their own exchange rates data in the UI
|
# "user_custom": users set their own exchange rates data in the UI
|
||||||
data_source = euro_central_bank
|
data_source = euro_central_bank
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,73 @@
|
|||||||
|
{
|
||||||
|
"code": [
|
||||||
|
"jiangshengwu",
|
||||||
|
"vigdail",
|
||||||
|
"f97",
|
||||||
|
"Miguelonlonlon",
|
||||||
|
"seb26",
|
||||||
|
"nktlitvinenko",
|
||||||
|
"lvdou-bing",
|
||||||
|
"dshemin",
|
||||||
|
"lucdsouza",
|
||||||
|
"OuIChien",
|
||||||
|
"RasterCrow"
|
||||||
|
],
|
||||||
|
"translators": {
|
||||||
|
"de": [
|
||||||
|
"chrgm"
|
||||||
|
],
|
||||||
|
"en": [],
|
||||||
|
"es": [
|
||||||
|
"Miguelonlonlon",
|
||||||
|
"abrugues",
|
||||||
|
"AndresTeller",
|
||||||
|
"diegofercri"
|
||||||
|
],
|
||||||
|
"fr": [
|
||||||
|
"brieucdlf"
|
||||||
|
],
|
||||||
|
"it": [
|
||||||
|
"waron97"
|
||||||
|
],
|
||||||
|
"ja": [
|
||||||
|
"tkymmm"
|
||||||
|
],
|
||||||
|
"kn": [
|
||||||
|
"Darshanbm05"
|
||||||
|
],
|
||||||
|
"ko": [
|
||||||
|
"overworks"
|
||||||
|
],
|
||||||
|
"nl": [
|
||||||
|
"automagics"
|
||||||
|
],
|
||||||
|
"pt-BR": [
|
||||||
|
"thecodergus",
|
||||||
|
"balaios"
|
||||||
|
],
|
||||||
|
"ru": [
|
||||||
|
"artegoser",
|
||||||
|
"dshemin"
|
||||||
|
],
|
||||||
|
"sl": [
|
||||||
|
"thehijacker"
|
||||||
|
],
|
||||||
|
"ta": [
|
||||||
|
"hhharsha36"
|
||||||
|
],
|
||||||
|
"th": [
|
||||||
|
"natthavat28"
|
||||||
|
],
|
||||||
|
"tr": [
|
||||||
|
"aydnykn"
|
||||||
|
],
|
||||||
|
"uk": [
|
||||||
|
"nktlitvinenko"
|
||||||
|
],
|
||||||
|
"vi": [
|
||||||
|
"f97"
|
||||||
|
],
|
||||||
|
"zh-Hans": [],
|
||||||
|
"zh-Hant": []
|
||||||
|
}
|
||||||
|
}
|
||||||
+4
-4
@@ -10,7 +10,7 @@ import (
|
|||||||
"github.com/urfave/cli/v3"
|
"github.com/urfave/cli/v3"
|
||||||
|
|
||||||
"github.com/mayswind/ezbookkeeping/cmd"
|
"github.com/mayswind/ezbookkeeping/cmd"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/settings"
|
"github.com/mayswind/ezbookkeeping/pkg/core"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/utils"
|
"github.com/mayswind/ezbookkeeping/pkg/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -26,9 +26,9 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
settings.Version = Version
|
core.Version = Version
|
||||||
settings.CommitHash = CommitHash
|
core.CommitHash = CommitHash
|
||||||
settings.BuildTime = BuildUnixTime
|
core.BuildTime = BuildUnixTime
|
||||||
|
|
||||||
cmd := &cli.Command{
|
cmd := &cli.Command{
|
||||||
Name: "ezBookkeeping",
|
Name: "ezBookkeeping",
|
||||||
|
|||||||
@@ -4,31 +4,31 @@ go 1.25
|
|||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/boombuler/barcode v1.1.0
|
github.com/boombuler/barcode v1.1.0
|
||||||
github.com/coreos/go-oidc/v3 v3.16.0
|
github.com/coreos/go-oidc/v3 v3.17.0
|
||||||
github.com/extrame/xls v0.0.2-0.20200426124601-4a6cf263071b
|
github.com/extrame/xls v0.0.2-0.20200426124601-4a6cf263071b
|
||||||
github.com/gin-contrib/cache v1.4.1
|
github.com/gin-contrib/cache v1.4.1
|
||||||
github.com/gin-contrib/gzip v1.2.5
|
github.com/gin-contrib/gzip v1.2.5
|
||||||
github.com/gin-gonic/gin v1.11.0
|
github.com/gin-gonic/gin v1.11.0
|
||||||
github.com/go-co-op/gocron/v2 v2.17.0
|
github.com/go-co-op/gocron/v2 v2.19.1
|
||||||
github.com/go-playground/validator/v10 v10.28.0
|
github.com/go-playground/validator/v10 v10.30.1
|
||||||
github.com/go-sql-driver/mysql v1.9.3
|
github.com/go-sql-driver/mysql v1.9.3
|
||||||
github.com/golang-jwt/jwt/v5 v5.3.0
|
github.com/golang-jwt/jwt/v5 v5.3.1
|
||||||
github.com/invopop/jsonschema v0.13.0
|
github.com/invopop/jsonschema v0.13.0
|
||||||
github.com/lib/pq v1.10.9
|
github.com/lib/pq v1.11.1
|
||||||
github.com/mattn/go-sqlite3 v1.14.32
|
github.com/mattn/go-sqlite3 v1.14.33
|
||||||
github.com/minio/minio-go/v7 v7.0.95
|
github.com/minio/minio-go/v7 v7.0.98
|
||||||
github.com/patrickmn/go-cache v2.1.0+incompatible
|
github.com/patrickmn/go-cache v2.1.0+incompatible
|
||||||
github.com/pquerna/otp v1.5.0
|
github.com/pquerna/otp v1.5.0
|
||||||
github.com/sirupsen/logrus v1.9.3
|
github.com/sirupsen/logrus v1.9.4
|
||||||
github.com/stretchr/testify v1.11.1
|
github.com/stretchr/testify v1.11.1
|
||||||
github.com/urfave/cli/v3 v3.5.0
|
github.com/urfave/cli/v3 v3.6.2
|
||||||
github.com/wk8/go-ordered-map/v2 v2.1.8
|
github.com/wk8/go-ordered-map/v2 v2.1.8
|
||||||
github.com/xuri/excelize/v2 v2.10.0
|
github.com/xuri/excelize/v2 v2.10.0
|
||||||
golang.org/x/crypto v0.43.0
|
golang.org/x/crypto v0.47.0
|
||||||
golang.org/x/net v0.46.0
|
golang.org/x/net v0.49.0
|
||||||
golang.org/x/oauth2 v0.32.0
|
golang.org/x/oauth2 v0.34.0
|
||||||
golang.org/x/text v0.30.0
|
golang.org/x/text v0.33.0
|
||||||
gopkg.in/ini.v1 v1.67.0
|
gopkg.in/ini.v1 v1.67.1
|
||||||
gopkg.in/mail.v2 v2.3.1
|
gopkg.in/mail.v2 v2.3.1
|
||||||
xorm.io/builder v0.3.13
|
xorm.io/builder v0.3.13
|
||||||
xorm.io/xorm v1.3.11
|
xorm.io/xorm v1.3.11
|
||||||
@@ -52,7 +52,7 @@ require (
|
|||||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||||
github.com/extrame/goyymmdd v0.0.0-20210114090516-7cc815f00d1a // indirect
|
github.com/extrame/goyymmdd v0.0.0-20210114090516-7cc815f00d1a // indirect
|
||||||
github.com/extrame/ole2 v0.0.0-20160812065207-d69429661ad7 // indirect
|
github.com/extrame/ole2 v0.0.0-20160812065207-d69429661ad7 // indirect
|
||||||
github.com/gabriel-vasile/mimetype v1.4.10 // indirect
|
github.com/gabriel-vasile/mimetype v1.4.12 // indirect
|
||||||
github.com/gin-contrib/sse v1.1.0 // indirect
|
github.com/gin-contrib/sse v1.1.0 // indirect
|
||||||
github.com/go-ini/ini v1.67.0 // indirect
|
github.com/go-ini/ini v1.67.0 // indirect
|
||||||
github.com/go-jose/go-jose/v4 v4.1.3 // indirect
|
github.com/go-jose/go-jose/v4 v4.1.3 // indirect
|
||||||
@@ -65,13 +65,14 @@ require (
|
|||||||
github.com/google/uuid v1.6.0 // indirect
|
github.com/google/uuid v1.6.0 // indirect
|
||||||
github.com/jonboulle/clockwork v0.5.0 // indirect
|
github.com/jonboulle/clockwork v0.5.0 // indirect
|
||||||
github.com/json-iterator/go v1.1.12 // indirect
|
github.com/json-iterator/go v1.1.12 // indirect
|
||||||
github.com/klauspost/compress v1.18.0 // indirect
|
github.com/klauspost/compress v1.18.2 // indirect
|
||||||
github.com/klauspost/cpuid/v2 v2.3.0 // indirect
|
github.com/klauspost/cpuid/v2 v2.3.0 // indirect
|
||||||
|
github.com/klauspost/crc32 v1.3.0 // indirect
|
||||||
github.com/leodido/go-urn v1.4.0 // indirect
|
github.com/leodido/go-urn v1.4.0 // indirect
|
||||||
github.com/mailru/easyjson v0.7.7 // indirect
|
github.com/mailru/easyjson v0.7.7 // indirect
|
||||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||||
github.com/memcachier/mc/v3 v3.0.3 // indirect
|
github.com/memcachier/mc/v3 v3.0.3 // indirect
|
||||||
github.com/minio/crc64nvme v1.0.2 // indirect
|
github.com/minio/crc64nvme v1.1.1 // indirect
|
||||||
github.com/minio/md5-simd v1.1.2 // indirect
|
github.com/minio/md5-simd v1.1.2 // indirect
|
||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||||
@@ -90,18 +91,19 @@ require (
|
|||||||
github.com/syndtr/goleveldb v1.0.0 // indirect
|
github.com/syndtr/goleveldb v1.0.0 // indirect
|
||||||
github.com/tealeg/xlsx v1.0.5 // indirect
|
github.com/tealeg/xlsx v1.0.5 // indirect
|
||||||
github.com/tiendc/go-deepcopy v1.7.1 // indirect
|
github.com/tiendc/go-deepcopy v1.7.1 // indirect
|
||||||
github.com/tinylib/msgp v1.3.0 // indirect
|
github.com/tinylib/msgp v1.6.1 // indirect
|
||||||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||||
github.com/ugorji/go/codec v1.3.0 // indirect
|
github.com/ugorji/go/codec v1.3.0 // indirect
|
||||||
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect
|
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect
|
||||||
github.com/xuri/efp v0.0.1 // indirect
|
github.com/xuri/efp v0.0.1 // indirect
|
||||||
github.com/xuri/nfp v0.0.2-0.20250530014748-2ddeb826f9a9 // indirect
|
github.com/xuri/nfp v0.0.2-0.20250530014748-2ddeb826f9a9 // indirect
|
||||||
|
go.yaml.in/yaml/v3 v3.0.4 // indirect
|
||||||
golang.org/x/arch v0.22.0 // indirect
|
golang.org/x/arch v0.22.0 // indirect
|
||||||
golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8 // indirect
|
golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8 // indirect
|
||||||
golang.org/x/mod v0.29.0 // indirect
|
golang.org/x/mod v0.31.0 // indirect
|
||||||
golang.org/x/sync v0.17.0 // indirect
|
golang.org/x/sync v0.19.0 // indirect
|
||||||
golang.org/x/sys v0.37.0 // indirect
|
golang.org/x/sys v0.40.0 // indirect
|
||||||
golang.org/x/tools v0.38.0 // indirect
|
golang.org/x/tools v0.40.0 // indirect
|
||||||
google.golang.org/protobuf v1.36.10 // indirect
|
google.golang.org/protobuf v1.36.10 // indirect
|
||||||
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
|
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
|
|||||||
@@ -28,8 +28,8 @@ github.com/chenzhuoyu/iasm v0.9.1/go.mod h1:Xjy2NpN3h7aUqeqM+woSuuvxmIe6+DDsiNLI
|
|||||||
github.com/cloudwego/base64x v0.1.6 h1:t11wG9AECkCDk5fMSoxmufanudBtJ+/HemLstXDLI2M=
|
github.com/cloudwego/base64x v0.1.6 h1:t11wG9AECkCDk5fMSoxmufanudBtJ+/HemLstXDLI2M=
|
||||||
github.com/cloudwego/base64x v0.1.6/go.mod h1:OFcloc187FXDaYHvrNIjxSe8ncn0OOM8gEHfghB2IPU=
|
github.com/cloudwego/base64x v0.1.6/go.mod h1:OFcloc187FXDaYHvrNIjxSe8ncn0OOM8gEHfghB2IPU=
|
||||||
github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY=
|
github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY=
|
||||||
github.com/coreos/go-oidc/v3 v3.16.0 h1:qRQUCFstKpXwmEjDQTIbyY/5jF00+asXzSkmkoa/mow=
|
github.com/coreos/go-oidc/v3 v3.17.0 h1:hWBGaQfbi0iVviX4ibC7bk8OKT5qNr4klBaCHVNvehc=
|
||||||
github.com/coreos/go-oidc/v3 v3.16.0/go.mod h1:wqPbKFrVnE90vty060SB40FCJ8fTHTxSwyXJqZH+sI8=
|
github.com/coreos/go-oidc/v3 v3.17.0/go.mod h1:wqPbKFrVnE90vty060SB40FCJ8fTHTxSwyXJqZH+sI8=
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.5/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
github.com/cpuguy83/go-md2man/v2 v2.0.5/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
@@ -43,8 +43,8 @@ github.com/extrame/ole2 v0.0.0-20160812065207-d69429661ad7/go.mod h1:GPpMrAfHdb8
|
|||||||
github.com/extrame/xls v0.0.2-0.20200426124601-4a6cf263071b h1:jqW/h4gcXYEB6kVf6iuxjU9ONWA0ugUB94TP9UNmgdg=
|
github.com/extrame/xls v0.0.2-0.20200426124601-4a6cf263071b h1:jqW/h4gcXYEB6kVf6iuxjU9ONWA0ugUB94TP9UNmgdg=
|
||||||
github.com/extrame/xls v0.0.2-0.20200426124601-4a6cf263071b/go.mod h1:iACcgahst7BboCpIMSpnFs4SKyU9ZjsvZBfNbUxZOJI=
|
github.com/extrame/xls v0.0.2-0.20200426124601-4a6cf263071b/go.mod h1:iACcgahst7BboCpIMSpnFs4SKyU9ZjsvZBfNbUxZOJI=
|
||||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||||
github.com/gabriel-vasile/mimetype v1.4.10 h1:zyueNbySn/z8mJZHLt6IPw0KoZsiQNszIpU+bX4+ZK0=
|
github.com/gabriel-vasile/mimetype v1.4.12 h1:e9hWvmLYvtp846tLHam2o++qitpguFiYCKbn0w9jyqw=
|
||||||
github.com/gabriel-vasile/mimetype v1.4.10/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s=
|
github.com/gabriel-vasile/mimetype v1.4.12/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s=
|
||||||
github.com/gin-contrib/cache v1.4.1 h1:HcLwLfw7p+FasNp5VAnFbbBj9SzB4bDtswvon7wYSg4=
|
github.com/gin-contrib/cache v1.4.1 h1:HcLwLfw7p+FasNp5VAnFbbBj9SzB4bDtswvon7wYSg4=
|
||||||
github.com/gin-contrib/cache v1.4.1/go.mod h1:tykDV+FgItJHYEO0eCasuRYsZKPPyb4BYhAjuTlG6RM=
|
github.com/gin-contrib/cache v1.4.1/go.mod h1:tykDV+FgItJHYEO0eCasuRYsZKPPyb4BYhAjuTlG6RM=
|
||||||
github.com/gin-contrib/gzip v1.2.5 h1:fIZs0S+l17pIu1P5XRJOo/YNqfIuPCrZZ3TWB7pjckI=
|
github.com/gin-contrib/gzip v1.2.5 h1:fIZs0S+l17pIu1P5XRJOo/YNqfIuPCrZZ3TWB7pjckI=
|
||||||
@@ -53,8 +53,8 @@ github.com/gin-contrib/sse v1.1.0 h1:n0w2GMuUpWDVp7qSpvze6fAu9iRxJY4Hmj6AmBOU05w
|
|||||||
github.com/gin-contrib/sse v1.1.0/go.mod h1:hxRZ5gVpWMT7Z0B0gSNYqqsSCNIJMjzvm6fqCz9vjwM=
|
github.com/gin-contrib/sse v1.1.0/go.mod h1:hxRZ5gVpWMT7Z0B0gSNYqqsSCNIJMjzvm6fqCz9vjwM=
|
||||||
github.com/gin-gonic/gin v1.11.0 h1:OW/6PLjyusp2PPXtyxKHU0RbX6I/l28FTdDlae5ueWk=
|
github.com/gin-gonic/gin v1.11.0 h1:OW/6PLjyusp2PPXtyxKHU0RbX6I/l28FTdDlae5ueWk=
|
||||||
github.com/gin-gonic/gin v1.11.0/go.mod h1:+iq/FyxlGzII0KHiBGjuNn4UNENUlKbGlNmc+W50Dls=
|
github.com/gin-gonic/gin v1.11.0/go.mod h1:+iq/FyxlGzII0KHiBGjuNn4UNENUlKbGlNmc+W50Dls=
|
||||||
github.com/go-co-op/gocron/v2 v2.17.0 h1:e/oj6fcAM8vOOKZxv2Cgfmjo+s8AXC46po5ZPtaSea4=
|
github.com/go-co-op/gocron/v2 v2.19.1 h1:B4iLeA0NB/2iO3EKQ7NfKn5KsQgZfjb2fkvoZJU3yBI=
|
||||||
github.com/go-co-op/gocron/v2 v2.17.0/go.mod h1:Zii6he+Zfgy5W9B+JKk/KwejFOW0kZTFvHtwIpR4aBI=
|
github.com/go-co-op/gocron/v2 v2.19.1/go.mod h1:5lEiCKk1oVJV39Zg7/YG10OnaVrDAV5GGR6O0663k6U=
|
||||||
github.com/go-ini/ini v1.67.0 h1:z6ZrTEZqSWOTyH2FlglNbNgARyHG8oLW9gMELqKr06A=
|
github.com/go-ini/ini v1.67.0 h1:z6ZrTEZqSWOTyH2FlglNbNgARyHG8oLW9gMELqKr06A=
|
||||||
github.com/go-ini/ini v1.67.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
|
github.com/go-ini/ini v1.67.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
|
||||||
github.com/go-jose/go-jose/v4 v4.1.3 h1:CVLmWDhDVRa6Mi/IgCgaopNosCaHz7zrMeF9MlZRkrs=
|
github.com/go-jose/go-jose/v4 v4.1.3 h1:CVLmWDhDVRa6Mi/IgCgaopNosCaHz7zrMeF9MlZRkrs=
|
||||||
@@ -63,16 +63,16 @@ github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/o
|
|||||||
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
|
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
|
||||||
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
|
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
|
||||||
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
||||||
github.com/go-playground/validator/v10 v10.28.0 h1:Q7ibns33JjyW48gHkuFT91qX48KG0ktULL6FgHdG688=
|
github.com/go-playground/validator/v10 v10.30.1 h1:f3zDSN/zOma+w6+1Wswgd9fLkdwy06ntQJp0BBvFG0w=
|
||||||
github.com/go-playground/validator/v10 v10.28.0/go.mod h1:GoI6I1SjPBh9p7ykNE/yj3fFYbyDOpwMn5KXd+m2hUU=
|
github.com/go-playground/validator/v10 v10.30.1/go.mod h1:oSuBIQzuJxL//3MelwSLD5hc2Tu889bF0Idm9Dg26cM=
|
||||||
github.com/go-sql-driver/mysql v1.9.3 h1:U/N249h2WzJ3Ukj8SowVFjdtZKfu9vlLZxjPXV1aweo=
|
github.com/go-sql-driver/mysql v1.9.3 h1:U/N249h2WzJ3Ukj8SowVFjdtZKfu9vlLZxjPXV1aweo=
|
||||||
github.com/go-sql-driver/mysql v1.9.3/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI64Gl8i5p1WMU=
|
github.com/go-sql-driver/mysql v1.9.3/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI64Gl8i5p1WMU=
|
||||||
github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4=
|
github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4=
|
||||||
github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
|
github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
|
||||||
github.com/goccy/go-yaml v1.18.0 h1:8W7wMFS12Pcas7KU+VVkaiCng+kG8QiFeFwzFb+rwuw=
|
github.com/goccy/go-yaml v1.18.0 h1:8W7wMFS12Pcas7KU+VVkaiCng+kG8QiFeFwzFb+rwuw=
|
||||||
github.com/goccy/go-yaml v1.18.0/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA=
|
github.com/goccy/go-yaml v1.18.0/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA=
|
||||||
github.com/golang-jwt/jwt/v5 v5.3.0 h1:pv4AsKCKKZuqlgs5sUmn4x8UlGa0kEVt/puTpKx9vvo=
|
github.com/golang-jwt/jwt/v5 v5.3.1 h1:kYf81DTWFe7t+1VvL7eS+jKFVWaUnK9cB1qbwn63YCY=
|
||||||
github.com/golang-jwt/jwt/v5 v5.3.0/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE=
|
github.com/golang-jwt/jwt/v5 v5.3.1/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE=
|
||||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||||
github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
|
github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
|
||||||
@@ -90,34 +90,36 @@ github.com/jonboulle/clockwork v0.5.0/go.mod h1:3mZlmanh0g2NDKO5TWZVJAfofYk64M7X
|
|||||||
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
|
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
|
||||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||||
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
|
github.com/klauspost/compress v1.18.2 h1:iiPHWW0YrcFgpBYhsA6D1+fqHssJscY/Tm/y2Uqnapk=
|
||||||
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
|
github.com/klauspost/compress v1.18.2/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4=
|
||||||
github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||||
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||||
github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y=
|
github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y=
|
||||||
github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
|
github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
|
||||||
|
github.com/klauspost/crc32 v1.3.0 h1:sSmTt3gUt81RP655XGZPElI0PelVTZ6YwCRnPSupoFM=
|
||||||
|
github.com/klauspost/crc32 v1.3.0/go.mod h1:D7kQaZhnkX/Y0tstFGf8VUzv2UofNGqCjnC3zdHB0Hw=
|
||||||
github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
|
github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
|
||||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
|
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
|
||||||
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
|
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
|
||||||
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
|
github.com/lib/pq v1.11.1 h1:wuChtj2hfsGmmx3nf1m7xC2XpK6OtelS2shMY+bGMtI=
|
||||||
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
github.com/lib/pq v1.11.1/go.mod h1:/p+8NSbOcwzAEI7wiMXFlgydTwcgTr3OSKMsD2BitpA=
|
||||||
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
|
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
|
||||||
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
|
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
|
||||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||||
github.com/mattn/go-sqlite3 v1.14.32 h1:JD12Ag3oLy1zQA+BNn74xRgaBbdhbNIDYvQUEuuErjs=
|
github.com/mattn/go-sqlite3 v1.14.33 h1:A5blZ5ulQo2AtayQ9/limgHEkFreKj1Dv226a1K73s0=
|
||||||
github.com/mattn/go-sqlite3 v1.14.32/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
github.com/mattn/go-sqlite3 v1.14.33/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
||||||
github.com/memcachier/mc/v3 v3.0.3 h1:qii+lDiPKi36O4Xg+HVKwHu6Oq+Gt17b+uEiA0Drwv4=
|
github.com/memcachier/mc/v3 v3.0.3 h1:qii+lDiPKi36O4Xg+HVKwHu6Oq+Gt17b+uEiA0Drwv4=
|
||||||
github.com/memcachier/mc/v3 v3.0.3/go.mod h1:GzjocBahcXPxt2cmqzknrgqCOmMxiSzhVKPOe90Tpug=
|
github.com/memcachier/mc/v3 v3.0.3/go.mod h1:GzjocBahcXPxt2cmqzknrgqCOmMxiSzhVKPOe90Tpug=
|
||||||
github.com/minio/crc64nvme v1.0.2 h1:6uO1UxGAD+kwqWWp7mBFsi5gAse66C4NXO8cmcVculg=
|
github.com/minio/crc64nvme v1.1.1 h1:8dwx/Pz49suywbO+auHCBpCtlW1OfpcLN7wYgVR6wAI=
|
||||||
github.com/minio/crc64nvme v1.0.2/go.mod h1:eVfm2fAzLlxMdUGc0EEBGSMmPwmXD5XiNRpnu9J3bvg=
|
github.com/minio/crc64nvme v1.1.1/go.mod h1:eVfm2fAzLlxMdUGc0EEBGSMmPwmXD5XiNRpnu9J3bvg=
|
||||||
github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34=
|
github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34=
|
||||||
github.com/minio/md5-simd v1.1.2/go.mod h1:MzdKDxYpY2BT9XQFocsiZf/NKVtR7nkE4RoEpN+20RM=
|
github.com/minio/md5-simd v1.1.2/go.mod h1:MzdKDxYpY2BT9XQFocsiZf/NKVtR7nkE4RoEpN+20RM=
|
||||||
github.com/minio/minio-go/v7 v7.0.95 h1:ywOUPg+PebTMTzn9VDsoFJy32ZuARN9zhB+K3IYEvYU=
|
github.com/minio/minio-go/v7 v7.0.98 h1:MeAVKjLVz+XJ28zFcuYyImNSAh8Mq725uNW4beRisi0=
|
||||||
github.com/minio/minio-go/v7 v7.0.95/go.mod h1:wOOX3uxS334vImCNRVyIDdXX9OsXDm89ToynKgqUKlo=
|
github.com/minio/minio-go/v7 v7.0.98/go.mod h1:cY0Y+W7yozf0mdIclrttzo1Iiu7mEf9y7nk2uXqMOvM=
|
||||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||||
@@ -153,16 +155,18 @@ github.com/robfig/go-cache v0.0.0-20130306151617-9fc39e0dbf62/go.mod h1:65XQgovT
|
|||||||
github.com/rs/xid v1.6.0 h1:fV591PaemRlL6JfRxGDEPl69wICngIQ3shQtzfy2gxU=
|
github.com/rs/xid v1.6.0 h1:fV591PaemRlL6JfRxGDEPl69wICngIQ3shQtzfy2gxU=
|
||||||
github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0=
|
github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0=
|
||||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||||
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
github.com/sirupsen/logrus v1.9.4 h1:TsZE7l11zFCLZnZ+teH4Umoq5BhEIfIzfRDZ1Uzql2w=
|
||||||
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
github.com/sirupsen/logrus v1.9.4/go.mod h1:ftWc9WdOfJ0a92nsE2jF5u5ZwH8Bv2zdeOC42RjbV2g=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||||
|
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
||||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||||
|
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||||
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||||
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||||
github.com/syndtr/goleveldb v1.0.0 h1:fBdIW9lB4Iz0n9khmH8w27SJ3QEJ7+IgjPEwGSZiFdE=
|
github.com/syndtr/goleveldb v1.0.0 h1:fBdIW9lB4Iz0n9khmH8w27SJ3QEJ7+IgjPEwGSZiFdE=
|
||||||
@@ -171,14 +175,14 @@ github.com/tealeg/xlsx v1.0.5 h1:+f8oFmvY8Gw1iUXzPk+kz+4GpbDZPK1FhPiQRd+ypgE=
|
|||||||
github.com/tealeg/xlsx v1.0.5/go.mod h1:btRS8dz54TDnvKNosuAqxrM1QgN1udgk9O34bDCnORM=
|
github.com/tealeg/xlsx v1.0.5/go.mod h1:btRS8dz54TDnvKNosuAqxrM1QgN1udgk9O34bDCnORM=
|
||||||
github.com/tiendc/go-deepcopy v1.7.1 h1:LnubftI6nYaaMOcaz0LphzwraqN8jiWTwm416sitff4=
|
github.com/tiendc/go-deepcopy v1.7.1 h1:LnubftI6nYaaMOcaz0LphzwraqN8jiWTwm416sitff4=
|
||||||
github.com/tiendc/go-deepcopy v1.7.1/go.mod h1:4bKjNC2r7boYOkD2IOuZpYjmlDdzjbpTRyCx+goBCJQ=
|
github.com/tiendc/go-deepcopy v1.7.1/go.mod h1:4bKjNC2r7boYOkD2IOuZpYjmlDdzjbpTRyCx+goBCJQ=
|
||||||
github.com/tinylib/msgp v1.3.0 h1:ULuf7GPooDaIlbyvgAxBV/FI7ynli6LZ1/nVUNu+0ww=
|
github.com/tinylib/msgp v1.6.1 h1:ESRv8eL3u+DNHUoSAAQRE50Hm162zqAnBoGv9PzScPY=
|
||||||
github.com/tinylib/msgp v1.3.0/go.mod h1:ykjzy2wzgrlvpDCRc4LA8UXy6D8bzMSuAF3WD57Gok0=
|
github.com/tinylib/msgp v1.6.1/go.mod h1:RSp0LW9oSxFut3KzESt5Voq4GVWyS+PSulT77roAqEA=
|
||||||
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
|
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
|
||||||
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
|
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
|
||||||
github.com/ugorji/go/codec v1.3.0 h1:Qd2W2sQawAfG8XSvzwhBeoGq71zXOC/Q1E9y/wUcsUA=
|
github.com/ugorji/go/codec v1.3.0 h1:Qd2W2sQawAfG8XSvzwhBeoGq71zXOC/Q1E9y/wUcsUA=
|
||||||
github.com/ugorji/go/codec v1.3.0/go.mod h1:pRBVtBSKl77K30Bv8R2P+cLSGaTtex6fsA2Wjqmfxj4=
|
github.com/ugorji/go/codec v1.3.0/go.mod h1:pRBVtBSKl77K30Bv8R2P+cLSGaTtex6fsA2Wjqmfxj4=
|
||||||
github.com/urfave/cli/v3 v3.5.0 h1:qCuFMmdayTF3zmjG8TSsoBzrDqszNrklYg2x3g4MSgw=
|
github.com/urfave/cli/v3 v3.6.2 h1:lQuqiPrZ1cIz8hz+HcrG0TNZFxU70dPZ3Yl+pSrH9A8=
|
||||||
github.com/urfave/cli/v3 v3.5.0/go.mod h1:ysVLtOEmg2tOy6PknnYVhDoouyC/6N42TMeoMzskhso=
|
github.com/urfave/cli/v3 v3.6.2/go.mod h1:ysVLtOEmg2tOy6PknnYVhDoouyC/6N42TMeoMzskhso=
|
||||||
github.com/wk8/go-ordered-map/v2 v2.1.8 h1:5h/BUHu93oj4gIdvHHHGsScSTMijfx5PeYkE/fJgbpc=
|
github.com/wk8/go-ordered-map/v2 v2.1.8 h1:5h/BUHu93oj4gIdvHHHGsScSTMijfx5PeYkE/fJgbpc=
|
||||||
github.com/wk8/go-ordered-map/v2 v2.1.8/go.mod h1:5nJHM5DyteebpVlHnWMV0rPz6Zp7+xBAnxjb1X5vnTw=
|
github.com/wk8/go-ordered-map/v2 v2.1.8/go.mod h1:5nJHM5DyteebpVlHnWMV0rPz6Zp7+xBAnxjb1X5vnTw=
|
||||||
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM=
|
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM=
|
||||||
@@ -188,32 +192,33 @@ github.com/xuri/excelize/v2 v2.10.0 h1:8aKsP7JD39iKLc6dH5Tw3dgV3sPRh8uRVXu/fMstf
|
|||||||
github.com/xuri/excelize/v2 v2.10.0/go.mod h1:SC5TzhQkaOsTWpANfm+7bJCldzcnU/jrhqkTi/iBHBU=
|
github.com/xuri/excelize/v2 v2.10.0/go.mod h1:SC5TzhQkaOsTWpANfm+7bJCldzcnU/jrhqkTi/iBHBU=
|
||||||
github.com/xuri/nfp v0.0.2-0.20250530014748-2ddeb826f9a9 h1:+C0TIdyyYmzadGaL/HBLbf3WdLgC29pgyhTjAT/0nuE=
|
github.com/xuri/nfp v0.0.2-0.20250530014748-2ddeb826f9a9 h1:+C0TIdyyYmzadGaL/HBLbf3WdLgC29pgyhTjAT/0nuE=
|
||||||
github.com/xuri/nfp v0.0.2-0.20250530014748-2ddeb826f9a9/go.mod h1:WwHg+CVyzlv/TX9xqBFXEZAuxOPxn2k1GNHwG41IIUQ=
|
github.com/xuri/nfp v0.0.2-0.20250530014748-2ddeb826f9a9/go.mod h1:WwHg+CVyzlv/TX9xqBFXEZAuxOPxn2k1GNHwG41IIUQ=
|
||||||
|
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
|
||||||
|
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
|
||||||
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
|
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
|
||||||
golang.org/x/arch v0.22.0 h1:c/Zle32i5ttqRXjdLyyHZESLD/bB90DCU1g9l/0YBDI=
|
golang.org/x/arch v0.22.0 h1:c/Zle32i5ttqRXjdLyyHZESLD/bB90DCU1g9l/0YBDI=
|
||||||
golang.org/x/arch v0.22.0/go.mod h1:dNHoOeKiyja7GTvF9NJS1l3Z2yntpQNzgrjh1cU103A=
|
golang.org/x/arch v0.22.0/go.mod h1:dNHoOeKiyja7GTvF9NJS1l3Z2yntpQNzgrjh1cU103A=
|
||||||
golang.org/x/crypto v0.43.0 h1:dduJYIi3A3KOfdGOHX8AVZ/jGiyPa3IbBozJ5kNuE04=
|
golang.org/x/crypto v0.47.0 h1:V6e3FRj+n4dbpw86FJ8Fv7XVOql7TEwpHapKoMJ/GO8=
|
||||||
golang.org/x/crypto v0.43.0/go.mod h1:BFbav4mRNlXJL4wNeejLpWxB7wMbc79PdRGhWKncxR0=
|
golang.org/x/crypto v0.47.0/go.mod h1:ff3Y9VzzKbwSSEzWqJsJVBnWmRwRSHt/6Op5n9bQc4A=
|
||||||
golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8/go.mod h1:jj3sYF3dwk5D+ghuXyeI3r5MFf+NT2An6/9dOA95KSI=
|
golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8/go.mod h1:jj3sYF3dwk5D+ghuXyeI3r5MFf+NT2An6/9dOA95KSI=
|
||||||
golang.org/x/mod v0.29.0 h1:HV8lRxZC4l2cr3Zq1LvtOsi/ThTgWnUk/y64QSs8GwA=
|
golang.org/x/mod v0.31.0 h1:HaW9xtz0+kOcWKwli0ZXy79Ix+UW/vOfmWI5QVd2tgI=
|
||||||
golang.org/x/mod v0.29.0/go.mod h1:NyhrlYXJ2H4eJiRy/WDBO6HMqZQ6q9nk4JzS3NuCK+w=
|
golang.org/x/mod v0.31.0/go.mod h1:43JraMp9cGx1Rx3AqioxrbrhNsLl2l/iNAvuBkrezpg=
|
||||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.46.0 h1:giFlY12I07fugqwPuWJi68oOnpfqFnJIJzaIIm2JVV4=
|
golang.org/x/net v0.49.0 h1:eeHFmOGUTtaaPSGNmjBKpbng9MulQsJURQUAfUwY++o=
|
||||||
golang.org/x/net v0.46.0/go.mod h1:Q9BGdFy1y4nkUwiLvT5qtyhAnEHgnQ/zd8PfU6nc210=
|
golang.org/x/net v0.49.0/go.mod h1:/ysNB2EvaqvesRkuLAyjI1ycPZlQHM3q01F02UY/MV8=
|
||||||
golang.org/x/oauth2 v0.32.0 h1:jsCblLleRMDrxMN29H3z/k1KliIvpLgCkE6R8FXXNgY=
|
golang.org/x/oauth2 v0.34.0 h1:hqK/t4AKgbqWkdkcAeI8XLmbK+4m4G5YeQRrmiotGlw=
|
||||||
golang.org/x/oauth2 v0.32.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA=
|
golang.org/x/oauth2 v0.34.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA=
|
||||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug=
|
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
|
||||||
golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ=
|
golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ=
|
||||||
golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.30.0 h1:yznKA/E9zq54KzlzBEAWn1NXSQ8DIp/NYMy88xJjl4k=
|
golang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE=
|
||||||
golang.org/x/text v0.30.0/go.mod h1:yDdHFIX9t+tORqspjENWgzaCVXgk0yYnYuSZ8UzzBVM=
|
golang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8=
|
||||||
golang.org/x/tools v0.38.0 h1:Hx2Xv8hISq8Lm16jvBZ2VQf+RLmbd7wVUsALibYI/IQ=
|
golang.org/x/tools v0.40.0 h1:yLkxfA+Qnul4cs9QA3KnlFu0lVmd8JJfoq+E41uSutA=
|
||||||
golang.org/x/tools v0.38.0/go.mod h1:yEsQ/d/YK8cjh0L6rZlY8tgtlKiBNTL14pGDJPJpYQs=
|
golang.org/x/tools v0.40.0/go.mod h1:Ik/tzLRlbscWpqqMRjyWYDisX8bG13FrdXp3o4Sr9lc=
|
||||||
google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE=
|
google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE=
|
||||||
google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
||||||
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc h1:2gGKlE2+asNV9m7xrywl36YYNnBG5ZQ0r/BOOxqPpmk=
|
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc h1:2gGKlE2+asNV9m7xrywl36YYNnBG5ZQ0r/BOOxqPpmk=
|
||||||
@@ -221,8 +226,8 @@ gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc/go.mod
|
|||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||||
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
|
gopkg.in/ini.v1 v1.67.1 h1:tVBILHy0R6e4wkYOn3XmiITt/hEVH4TFMYvAX2Ytz6k=
|
||||||
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
gopkg.in/ini.v1 v1.67.1/go.mod h1:x/cyOwCgZqOkJoDIJ3c1KNHMo10+nLGAhh+kn3Zizss=
|
||||||
gopkg.in/mail.v2 v2.3.1 h1:WYFn/oANrAGP2C0dcV6/pbkPzv8yGzqTjPmTeO7qoXk=
|
gopkg.in/mail.v2 v2.3.1 h1:WYFn/oANrAGP2C0dcV6/pbkPzv8yGzqTjPmTeO7qoXk=
|
||||||
gopkg.in/mail.v2 v2.3.1/go.mod h1:htwXN1Qh09vZJ1NVKxQqHPBaCBbzKhp5GzuJEA4VJWw=
|
gopkg.in/mail.v2 v2.3.1/go.mod h1:htwXN1Qh09vZJ1NVKxQqHPBaCBbzKhp5GzuJEA4VJWw=
|
||||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||||
|
|||||||
Generated
+2458
-1974
File diff suppressed because it is too large
Load Diff
+25
-24
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "ezbookkeeping",
|
"name": "ezbookkeeping",
|
||||||
"version": "1.2.0",
|
"version": "1.4.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
@@ -20,62 +20,63 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@mdi/js": "^7.4.47",
|
"@mdi/js": "^7.4.47",
|
||||||
"@vuepic/vue-datepicker": "^11.0.3",
|
"@vuepic/vue-datepicker": "^12.1.0",
|
||||||
"axios": "^1.12.2",
|
"axios": "^1.13.4",
|
||||||
"cbor-js": "^0.1.0",
|
"cbor-js": "^0.1.0",
|
||||||
|
"chardet": "^2.1.1",
|
||||||
"clipboard": "^2.0.11",
|
"clipboard": "^2.0.11",
|
||||||
"crypto-js": "^4.2.0",
|
"crypto-js": "^4.2.0",
|
||||||
"dom7": "^4.0.6",
|
"dom7": "^4.0.6",
|
||||||
"echarts": "^6.0.0",
|
"echarts": "^6.0.0",
|
||||||
"framework7": "^8.3.4",
|
"framework7": "^9.0.3",
|
||||||
"framework7-icons": "^5.0.5",
|
"framework7-icons": "^5.0.5",
|
||||||
"framework7-vue": "^8.3.4",
|
"framework7-vue": "^9.0.3",
|
||||||
"jalaali-js": "^1.2.8",
|
"jalaali-js": "^1.2.8",
|
||||||
"leaflet": "^1.9.4",
|
"leaflet": "^1.9.4",
|
||||||
"line-awesome": "^1.3.0",
|
"line-awesome": "^1.3.0",
|
||||||
"moment": "^2.30.1",
|
"moment": "^2.30.1",
|
||||||
"moment-timezone": "^0.6.0",
|
"moment-timezone": "^0.6.0",
|
||||||
"pinia": "^3.0.3",
|
"pinia": "^3.0.4",
|
||||||
"register-service-worker": "^1.7.2",
|
"register-service-worker": "^1.7.2",
|
||||||
"skeleton-elements": "^4.0.1",
|
"skeleton-elements": "^4.0.1",
|
||||||
"swiper": "^10.2.0",
|
"swiper": "^12.1.0",
|
||||||
"ua-parser-js": "^1.0.39",
|
"ua-parser-js": "^1.0.39",
|
||||||
"vue": "^3.5.22",
|
"vue": "^3.5.27",
|
||||||
"vue-echarts": "^8.0.1",
|
"vue-echarts": "^8.0.1",
|
||||||
"vue-i18n": "^11.1.12",
|
"vue-i18n": "^11.2.8",
|
||||||
"vue-router": "^4.6.3",
|
"vue-router": "^5.0.2",
|
||||||
"vue3-perfect-scrollbar": "^2.0.0",
|
"vue3-perfect-scrollbar": "^2.0.0",
|
||||||
"vuedraggable": "^4.1.0",
|
"vuedraggable": "^4.1.0",
|
||||||
"vuetify": "^3.10.7"
|
"vuetify": "^3.11.8"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@jest/globals": "^30.2.0",
|
"@jest/globals": "^30.2.0",
|
||||||
"@tsconfig/node24": "^24.0.1",
|
"@tsconfig/node24": "^24.0.4",
|
||||||
"@types/cbor-js": "^0.1.1",
|
"@types/cbor-js": "^0.1.1",
|
||||||
"@types/crypto-js": "^4.2.2",
|
"@types/crypto-js": "^4.2.2",
|
||||||
"@types/git-rev-sync": "^2.0.2",
|
"@types/git-rev-sync": "^2.0.2",
|
||||||
"@types/jalaali-js": "^1.2.0",
|
"@types/jalaali-js": "^1.2.0",
|
||||||
"@types/jest": "^30.0.0",
|
"@types/jest": "^30.0.0",
|
||||||
"@types/node": "^24.9.1",
|
"@types/node": "^24.1.0",
|
||||||
"@types/ua-parser-js": "^0.7.39",
|
"@types/ua-parser-js": "^0.7.39",
|
||||||
"@vitejs/plugin-vue": "^6.0.1",
|
"@vitejs/plugin-vue": "^6.0.4",
|
||||||
"@vue/eslint-config-typescript": "^14.6.0",
|
"@vue/eslint-config-typescript": "^14.6.0",
|
||||||
"@vue/tsconfig": "^0.8.1",
|
"@vue/tsconfig": "^0.8.1",
|
||||||
"cross-env": "^10.1.0",
|
"cross-env": "^10.1.0",
|
||||||
"eslint": "^9.38.0",
|
"eslint": "^9.39.1",
|
||||||
"eslint-plugin-vue": "^10.5.1",
|
"eslint-plugin-vue": "^10.7.0",
|
||||||
"git-rev-sync": "^3.0.2",
|
"git-rev-sync": "^3.0.2",
|
||||||
"jest": "^30.2.0",
|
"jest": "^30.2.0",
|
||||||
"postcss-preset-env": "^10.4.0",
|
"postcss-preset-env": "^11.1.3",
|
||||||
"sass": "^1.93.2",
|
"sass": "^1.97.3",
|
||||||
"ts-jest": "^29.4.5",
|
"ts-jest": "^29.4.6",
|
||||||
"ts-node": "^10.9.2",
|
"ts-node": "^10.9.2",
|
||||||
"typescript": "^5.9.3",
|
"typescript": "^5.9.3",
|
||||||
"vite": "^7.1.12",
|
"vite": "^7.3.1",
|
||||||
"vite-plugin-checker": "^0.11.0",
|
"vite-plugin-checker": "^0.12.0",
|
||||||
"vite-plugin-pwa": "^1.1.0",
|
"vite-plugin-pwa": "^1.2.0",
|
||||||
"vite-plugin-vuetify": "^2.1.2",
|
"vite-plugin-vuetify": "^2.1.3",
|
||||||
"vue-tsc": "^3.1.2"
|
"vue-tsc": "^3.2.4"
|
||||||
},
|
},
|
||||||
"browserslist": [
|
"browserslist": [
|
||||||
"last 5 Chrome versions",
|
"last 5 Chrome versions",
|
||||||
|
|||||||
+27
-16
@@ -150,10 +150,10 @@ func (a *AccountsApi) AccountCreateHandler(c *core.WebContext) (any, *errs.Error
|
|||||||
return nil, errs.NewIncompleteOrIncorrectSubmissionError(err)
|
return nil, errs.NewIncompleteOrIncorrectSubmissionError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
utcOffset, err := c.GetClientTimezoneOffset()
|
clientTimezone, err := c.GetClientTimezone()
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warnf(c, "[accounts.AccountCreateHandler] cannot get client timezone offset, because %s", err.Error())
|
log.Warnf(c, "[accounts.AccountCreateHandler] cannot get client timezone, because %s", err.Error())
|
||||||
return nil, errs.ErrClientTimezoneOffsetInvalid
|
return nil, errs.ErrClientTimezoneOffsetInvalid
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -278,7 +278,7 @@ func (a *AccountsApi) AccountCreateHandler(c *core.WebContext) (any, *errs.Error
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
err = a.accounts.CreateAccounts(c, mainAccount, accountCreateReq.BalanceTime, childrenAccounts, childrenAccountBalanceTimes, utcOffset)
|
err = a.accounts.CreateAccounts(c, mainAccount, accountCreateReq.BalanceTime, childrenAccounts, childrenAccountBalanceTimes, clientTimezone)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf(c, "[accounts.AccountCreateHandler] failed to create account \"id:%d\" for user \"uid:%d\", because %s", mainAccount.AccountId, uid, err.Error())
|
log.Errorf(c, "[accounts.AccountCreateHandler] failed to create account \"id:%d\" for user \"uid:%d\", because %s", mainAccount.AccountId, uid, err.Error())
|
||||||
@@ -315,10 +315,10 @@ func (a *AccountsApi) AccountModifyHandler(c *core.WebContext) (any, *errs.Error
|
|||||||
return nil, errs.ErrAccountIdInvalid
|
return nil, errs.ErrAccountIdInvalid
|
||||||
}
|
}
|
||||||
|
|
||||||
utcOffset, err := c.GetClientTimezoneOffset()
|
clientTimezone, err := c.GetClientTimezone()
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warnf(c, "[accounts.AccountModifyHandler] cannot get client timezone offset, because %s", err.Error())
|
log.Warnf(c, "[accounts.AccountModifyHandler] cannot get client timezone, because %s", err.Error())
|
||||||
return nil, errs.ErrClientTimezoneOffsetInvalid
|
return nil, errs.ErrClientTimezoneOffsetInvalid
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -437,6 +437,17 @@ func (a *AccountsApi) AccountModifyHandler(c *core.WebContext) (any, *errs.Error
|
|||||||
toUpdateAccount := a.getToUpdateAccount(uid, &accountModifyReq, mainAccount, false)
|
toUpdateAccount := a.getToUpdateAccount(uid, &accountModifyReq, mainAccount, false)
|
||||||
|
|
||||||
if toUpdateAccount != nil {
|
if toUpdateAccount != nil {
|
||||||
|
if toUpdateAccount.Category != mainAccount.Category {
|
||||||
|
maxOrderId, err := a.accounts.GetMaxDisplayOrder(c, uid, toUpdateAccount.Category)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf(c, "[accounts.AccountModifyHandler] failed to get max display order for user \"uid:%d\", because %s", uid, err.Error())
|
||||||
|
return nil, errs.Or(err, errs.ErrOperationFailed)
|
||||||
|
}
|
||||||
|
|
||||||
|
toUpdateAccount.DisplayOrder = maxOrderId + 1
|
||||||
|
}
|
||||||
|
|
||||||
anythingUpdate = true
|
anythingUpdate = true
|
||||||
toUpdateAccounts = append(toUpdateAccounts, toUpdateAccount)
|
toUpdateAccounts = append(toUpdateAccounts, toUpdateAccount)
|
||||||
}
|
}
|
||||||
@@ -521,7 +532,7 @@ func (a *AccountsApi) AccountModifyHandler(c *core.WebContext) (any, *errs.Error
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
err = a.accounts.ModifyAccounts(c, mainAccount, toUpdateAccounts, toAddAccounts, toAddAccountBalanceTimes, toDeleteAccountIds, utcOffset)
|
err = a.accounts.ModifyAccounts(c, mainAccount, toUpdateAccounts, toAddAccounts, toAddAccountBalanceTimes, toDeleteAccountIds, clientTimezone)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf(c, "[accounts.AccountModifyHandler] failed to update account \"id:%d\" for user \"uid:%d\", because %s", accountModifyReq.Id, uid, err.Error())
|
log.Errorf(c, "[accounts.AccountModifyHandler] failed to update account \"id:%d\" for user \"uid:%d\", because %s", accountModifyReq.Id, uid, err.Error())
|
||||||
@@ -542,7 +553,6 @@ func (a *AccountsApi) AccountModifyHandler(c *core.WebContext) (any, *errs.Error
|
|||||||
|
|
||||||
account.Type = oldAccount.Type
|
account.Type = oldAccount.Type
|
||||||
account.ParentAccountId = oldAccount.ParentAccountId
|
account.ParentAccountId = oldAccount.ParentAccountId
|
||||||
account.DisplayOrder = oldAccount.DisplayOrder
|
|
||||||
account.Currency = oldAccount.Currency
|
account.Currency = oldAccount.Currency
|
||||||
account.Balance = oldAccount.Balance
|
account.Balance = oldAccount.Balance
|
||||||
|
|
||||||
@@ -762,15 +772,16 @@ func (a *AccountsApi) getToUpdateAccount(uid int64, accountModifyReq *models.Acc
|
|||||||
}
|
}
|
||||||
|
|
||||||
newAccount := &models.Account{
|
newAccount := &models.Account{
|
||||||
AccountId: oldAccount.AccountId,
|
AccountId: oldAccount.AccountId,
|
||||||
Uid: uid,
|
Uid: uid,
|
||||||
Name: accountModifyReq.Name,
|
Name: accountModifyReq.Name,
|
||||||
Category: accountModifyReq.Category,
|
DisplayOrder: oldAccount.DisplayOrder,
|
||||||
Icon: accountModifyReq.Icon,
|
Category: accountModifyReq.Category,
|
||||||
Color: accountModifyReq.Color,
|
Icon: accountModifyReq.Icon,
|
||||||
Comment: accountModifyReq.Comment,
|
Color: accountModifyReq.Color,
|
||||||
Extend: newAccountExtend,
|
Comment: accountModifyReq.Comment,
|
||||||
Hidden: accountModifyReq.Hidden,
|
Extend: newAccountExtend,
|
||||||
|
Hidden: accountModifyReq.Hidden,
|
||||||
}
|
}
|
||||||
|
|
||||||
if newAccount.Name != oldAccount.Name ||
|
if newAccount.Name != oldAccount.Name ||
|
||||||
|
|||||||
+47
-23
@@ -28,9 +28,11 @@ type DataManagementsApi struct {
|
|||||||
transactions *services.TransactionService
|
transactions *services.TransactionService
|
||||||
categories *services.TransactionCategoryService
|
categories *services.TransactionCategoryService
|
||||||
tags *services.TransactionTagService
|
tags *services.TransactionTagService
|
||||||
|
tagGroups *services.TransactionTagGroupService
|
||||||
pictures *services.TransactionPictureService
|
pictures *services.TransactionPictureService
|
||||||
templates *services.TransactionTemplateService
|
templates *services.TransactionTemplateService
|
||||||
userCustomExchangeRates *services.UserCustomExchangeRatesService
|
userCustomExchangeRates *services.UserCustomExchangeRatesService
|
||||||
|
insightsExploreres *services.InsightsExplorerService
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize a data management api singleton instance
|
// Initialize a data management api singleton instance
|
||||||
@@ -45,9 +47,11 @@ var (
|
|||||||
transactions: services.Transactions,
|
transactions: services.Transactions,
|
||||||
categories: services.TransactionCategories,
|
categories: services.TransactionCategories,
|
||||||
tags: services.TransactionTags,
|
tags: services.TransactionTags,
|
||||||
|
tagGroups: services.TransactionTagGroups,
|
||||||
pictures: services.TransactionPictures,
|
pictures: services.TransactionPictures,
|
||||||
templates: services.TransactionTemplates,
|
templates: services.TransactionTemplates,
|
||||||
userCustomExchangeRates: services.UserCustomExchangeRates,
|
userCustomExchangeRates: services.UserCustomExchangeRates,
|
||||||
|
insightsExploreres: services.InsightsExplorers,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -99,6 +103,13 @@ func (a *DataManagementsApi) DataStatisticsHandler(c *core.WebContext) (any, *er
|
|||||||
return nil, errs.ErrOperationFailed
|
return nil, errs.ErrOperationFailed
|
||||||
}
|
}
|
||||||
|
|
||||||
|
totalInsightsExplorerCount, err := a.insightsExploreres.GetTotalInsightsExplorersCountByUid(c, uid)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf(c, "[data_managements.DataStatisticsHandler] failed to get total insights explorer count for user \"uid:%d\", because %s", uid, err.Error())
|
||||||
|
return nil, errs.ErrOperationFailed
|
||||||
|
}
|
||||||
|
|
||||||
totalTransactionTemplateCount, err := a.templates.GetTotalNormalTemplateCountByUid(c, uid)
|
totalTransactionTemplateCount, err := a.templates.GetTotalNormalTemplateCountByUid(c, uid)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -119,6 +130,7 @@ func (a *DataManagementsApi) DataStatisticsHandler(c *core.WebContext) (any, *er
|
|||||||
TotalTransactionTagCount: totalTransactionTagCount,
|
TotalTransactionTagCount: totalTransactionTagCount,
|
||||||
TotalTransactionCount: totalTransactionCount,
|
TotalTransactionCount: totalTransactionCount,
|
||||||
TotalTransactionPictureCount: totalTransactionPictureCount,
|
TotalTransactionPictureCount: totalTransactionPictureCount,
|
||||||
|
TotalInsightsExplorerCount: totalInsightsExplorerCount,
|
||||||
TotalTransactionTemplateCount: totalTransactionTemplateCount,
|
TotalTransactionTemplateCount: totalTransactionTemplateCount,
|
||||||
TotalScheduledTransactionCount: totalScheduledTransactionCount,
|
TotalScheduledTransactionCount: totalScheduledTransactionCount,
|
||||||
}
|
}
|
||||||
@@ -183,6 +195,13 @@ func (a *DataManagementsApi) ClearAllDataHandler(c *core.WebContext) (any, *errs
|
|||||||
return nil, errs.Or(err, errs.ErrOperationFailed)
|
return nil, errs.Or(err, errs.ErrOperationFailed)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
err = a.tagGroups.DeleteAllTagGroups(c, uid)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf(c, "[data_managements.ClearAllDataHandler] failed to delete all transaction tag groups, because %s", err.Error())
|
||||||
|
return nil, errs.Or(err, errs.ErrOperationFailed)
|
||||||
|
}
|
||||||
|
|
||||||
err = a.userCustomExchangeRates.DeleteAllCustomExchangeRates(c, uid)
|
err = a.userCustomExchangeRates.DeleteAllCustomExchangeRates(c, uid)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -190,6 +209,13 @@ func (a *DataManagementsApi) ClearAllDataHandler(c *core.WebContext) (any, *errs
|
|||||||
return nil, errs.Or(err, errs.ErrOperationFailed)
|
return nil, errs.Or(err, errs.ErrOperationFailed)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
err = a.insightsExploreres.DeleteAllInsightsExplorers(c, uid)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf(c, "[data_managements.ClearAllDataHandler] failed to delete all insights explorers, because %s", err.Error())
|
||||||
|
return nil, errs.Or(err, errs.ErrOperationFailed)
|
||||||
|
}
|
||||||
|
|
||||||
log.Infof(c, "[data_managements.ClearAllDataHandler] user \"uid:%d\" has cleared all data", uid)
|
log.Infof(c, "[data_managements.ClearAllDataHandler] user \"uid:%d\" has cleared all data", uid)
|
||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
@@ -298,17 +324,15 @@ func (a *DataManagementsApi) getExportedFileContent(c *core.WebContext, fileType
|
|||||||
err := c.ShouldBindQuery(&exportTransactionDataReq)
|
err := c.ShouldBindQuery(&exportTransactionDataReq)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warnf(c, "[data_managements.ExportDataHandler] parse request failed, because %s", err.Error())
|
log.Warnf(c, "[data_managements.getExportedFileContent] parse request failed, because %s", err.Error())
|
||||||
return nil, "", errs.NewIncompleteOrIncorrectSubmissionError(err)
|
return nil, "", errs.NewIncompleteOrIncorrectSubmissionError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
timezone := time.Local
|
clientTimezone, err := c.GetClientTimezone()
|
||||||
utcOffset, err := c.GetClientTimezoneOffset()
|
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warnf(c, "[data_managements.ExportDataHandler] cannot get client timezone offset, because %s", err.Error())
|
log.Warnf(c, "[data_managements.getExportedFileContent] cannot get client timezone, because %s", err.Error())
|
||||||
} else {
|
clientTimezone = time.Local
|
||||||
timezone = time.FixedZone("Client Timezone", int(utcOffset)*60)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
uid := c.GetCurrentUid()
|
uid := c.GetCurrentUid()
|
||||||
@@ -316,7 +340,7 @@ func (a *DataManagementsApi) getExportedFileContent(c *core.WebContext, fileType
|
|||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if !errs.IsCustomError(err) {
|
if !errs.IsCustomError(err) {
|
||||||
log.Warnf(c, "[data_managements.ExportDataHandler] failed to get user for user \"uid:%d\", because %s", uid, err.Error())
|
log.Warnf(c, "[data_managements.getExportedFileContent] failed to get user for user \"uid:%d\", because %s", uid, err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil, "", errs.ErrUserNotFound
|
return nil, "", errs.ErrUserNotFound
|
||||||
@@ -329,28 +353,28 @@ func (a *DataManagementsApi) getExportedFileContent(c *core.WebContext, fileType
|
|||||||
accounts, err := a.accounts.GetAllAccountsByUid(c, uid)
|
accounts, err := a.accounts.GetAllAccountsByUid(c, uid)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf(c, "[data_managements.ExportDataHandler] failed to get all accounts for user \"uid:%d\", because %s", uid, err.Error())
|
log.Errorf(c, "[data_managements.getExportedFileContent] failed to get all accounts for user \"uid:%d\", because %s", uid, err.Error())
|
||||||
return nil, "", errs.ErrOperationFailed
|
return nil, "", errs.ErrOperationFailed
|
||||||
}
|
}
|
||||||
|
|
||||||
categories, err := a.categories.GetAllCategoriesByUid(c, uid, 0, -1)
|
categories, err := a.categories.GetAllCategoriesByUid(c, uid, 0, -1)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf(c, "[data_managements.ExportDataHandler] failed to get categories for user \"uid:%d\", because %s", uid, err.Error())
|
log.Errorf(c, "[data_managements.getExportedFileContent] failed to get categories for user \"uid:%d\", because %s", uid, err.Error())
|
||||||
return nil, "", errs.ErrOperationFailed
|
return nil, "", errs.ErrOperationFailed
|
||||||
}
|
}
|
||||||
|
|
||||||
tags, err := a.tags.GetAllTagsByUid(c, uid)
|
tags, err := a.tags.GetAllTagsByUid(c, uid)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf(c, "[data_managements.ExportDataHandler] failed to get tags for user \"uid:%d\", because %s", uid, err.Error())
|
log.Errorf(c, "[data_managements.getExportedFileContent] failed to get tags for user \"uid:%d\", because %s", uid, err.Error())
|
||||||
return nil, "", errs.ErrOperationFailed
|
return nil, "", errs.ErrOperationFailed
|
||||||
}
|
}
|
||||||
|
|
||||||
tagIndexes, err := a.tags.GetAllTagIdsMapOfAllTransactions(c, uid)
|
tagIndexes, err := a.tags.GetAllTagIdsMapOfAllTransactions(c, uid)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf(c, "[data_managements.ExportDataHandler] failed to get tag index for user \"uid:%d\", because %s", uid, err.Error())
|
log.Errorf(c, "[data_managements.getExportedFileContent] failed to get tag index for user \"uid:%d\", because %s", uid, err.Error())
|
||||||
return nil, "", errs.ErrOperationFailed
|
return nil, "", errs.ErrOperationFailed
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -361,25 +385,25 @@ func (a *DataManagementsApi) getExportedFileContent(c *core.WebContext, fileType
|
|||||||
allAccountIds, err := a.accounts.GetAccountOrSubAccountIds(c, exportTransactionDataReq.AccountIds, uid)
|
allAccountIds, err := a.accounts.GetAccountOrSubAccountIds(c, exportTransactionDataReq.AccountIds, uid)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warnf(c, "[data_managements.ExportDataHandler] get account error, because %s", err.Error())
|
log.Warnf(c, "[data_managements.getExportedFileContent] get account error, because %s", err.Error())
|
||||||
return nil, "", errs.Or(err, errs.ErrOperationFailed)
|
return nil, "", errs.Or(err, errs.ErrOperationFailed)
|
||||||
}
|
}
|
||||||
|
|
||||||
allCategoryIds, err := a.categories.GetCategoryOrSubCategoryIds(c, exportTransactionDataReq.CategoryIds, uid)
|
allCategoryIds, err := a.categories.GetCategoryOrSubCategoryIds(c, exportTransactionDataReq.CategoryIds, uid)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warnf(c, "[data_managements.ExportDataHandler] get transaction category error, because %s", err.Error())
|
log.Warnf(c, "[data_managements.getExportedFileContent] get transaction category error, because %s", err.Error())
|
||||||
return nil, "", errs.Or(err, errs.ErrOperationFailed)
|
return nil, "", errs.Or(err, errs.ErrOperationFailed)
|
||||||
}
|
}
|
||||||
|
|
||||||
var allTagIds []int64
|
noTags := exportTransactionDataReq.TagFilter == models.TransactionNoTagFilterValue
|
||||||
noTags := exportTransactionDataReq.TagIds == "none"
|
var tagFilters []*models.TransactionTagFilter
|
||||||
|
|
||||||
if !noTags {
|
if !noTags {
|
||||||
allTagIds, err = a.tags.GetTagIds(exportTransactionDataReq.TagIds)
|
tagFilters, err = models.ParseTransactionTagFilter(exportTransactionDataReq.TagFilter)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warnf(c, "[data_managements.ExportDataHandler] get transaction tag ids error, because %s", err.Error())
|
log.Warnf(c, "[data_managements.getExportedFileContent] parse transaction tag filters error, because %s", err.Error())
|
||||||
return nil, "", errs.Or(err, errs.ErrOperationFailed)
|
return nil, "", errs.Or(err, errs.ErrOperationFailed)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -395,10 +419,10 @@ func (a *DataManagementsApi) getExportedFileContent(c *core.WebContext, fileType
|
|||||||
minTransactionTime = utils.GetMinTransactionTimeFromUnixTime(exportTransactionDataReq.MinTime)
|
minTransactionTime = utils.GetMinTransactionTimeFromUnixTime(exportTransactionDataReq.MinTime)
|
||||||
}
|
}
|
||||||
|
|
||||||
allTransactions, err := a.transactions.GetAllSpecifiedTransactions(c, uid, maxTransactionTime, minTransactionTime, exportTransactionDataReq.Type, allCategoryIds, allAccountIds, allTagIds, noTags, exportTransactionDataReq.TagFilterType, exportTransactionDataReq.AmountFilter, exportTransactionDataReq.Keyword, pageCountForDataExport, true)
|
allTransactions, err := a.transactions.GetAllSpecifiedTransactions(c, uid, maxTransactionTime, minTransactionTime, exportTransactionDataReq.Type, allCategoryIds, allAccountIds, tagFilters, noTags, exportTransactionDataReq.AmountFilter, exportTransactionDataReq.Keyword, pageCountForDataExport, true)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf(c, "[data_managements.ExportDataHandler] failed to all transactions user \"uid:%d\", because %s", uid, err.Error())
|
log.Errorf(c, "[data_managements.getExportedFileContent] failed to all transactions user \"uid:%d\", because %s", uid, err.Error())
|
||||||
return nil, "", errs.ErrOperationFailed
|
return nil, "", errs.ErrOperationFailed
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -411,17 +435,17 @@ func (a *DataManagementsApi) getExportedFileContent(c *core.WebContext, fileType
|
|||||||
result, err := dataExporter.ToExportedContent(c, uid, allTransactions, accountMap, categoryMap, tagMap, tagIndexes)
|
result, err := dataExporter.ToExportedContent(c, uid, allTransactions, accountMap, categoryMap, tagMap, tagIndexes)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf(c, "[data_managements.ExportDataHandler] failed to get csv format exported data for \"uid:%d\", because %s", uid, err.Error())
|
log.Errorf(c, "[data_managements.getExportedFileContent] failed to get exported data for \"uid:%d\", because %s", uid, err.Error())
|
||||||
return nil, "", errs.Or(err, errs.ErrOperationFailed)
|
return nil, "", errs.Or(err, errs.ErrOperationFailed)
|
||||||
}
|
}
|
||||||
|
|
||||||
fileName := a.getFileName(user, timezone, fileType)
|
fileName := a.getFileName(user, clientTimezone, fileType)
|
||||||
|
|
||||||
return result, fileName, nil
|
return result, fileName, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *DataManagementsApi) getFileName(user *models.User, timezone *time.Location, fileExtension string) string {
|
func (a *DataManagementsApi) getFileName(user *models.User, clientTimezone *time.Location, fileExtension string) string {
|
||||||
currentTime := utils.FormatUnixTimeToLongDateTimeWithoutSecond(time.Now().Unix(), timezone)
|
currentTime := utils.FormatUnixTimeToLongDateTimeWithoutSecond(time.Now().Unix(), clientTimezone)
|
||||||
currentTime = strings.Replace(currentTime, "-", "_", -1)
|
currentTime = strings.Replace(currentTime, "-", "_", -1)
|
||||||
currentTime = strings.Replace(currentTime, " ", "_", -1)
|
currentTime = strings.Replace(currentTime, " ", "_", -1)
|
||||||
currentTime = strings.Replace(currentTime, ":", "_", -1)
|
currentTime = strings.Replace(currentTime, ":", "_", -1)
|
||||||
|
|||||||
@@ -0,0 +1,274 @@
|
|||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"sort"
|
||||||
|
|
||||||
|
"github.com/mayswind/ezbookkeeping/pkg/core"
|
||||||
|
"github.com/mayswind/ezbookkeeping/pkg/errs"
|
||||||
|
"github.com/mayswind/ezbookkeeping/pkg/log"
|
||||||
|
"github.com/mayswind/ezbookkeeping/pkg/models"
|
||||||
|
"github.com/mayswind/ezbookkeeping/pkg/services"
|
||||||
|
)
|
||||||
|
|
||||||
|
// InsightsExplorersApi represents insights explorers api
|
||||||
|
type InsightsExplorersApi struct {
|
||||||
|
insightsExploreres *services.InsightsExplorerService
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize a insights explorers api singleton instance
|
||||||
|
var (
|
||||||
|
InsightsExplorers = &InsightsExplorersApi{
|
||||||
|
insightsExploreres: services.InsightsExplorers,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// InsightsExplorerListHandler returns insights explorer list of current user
|
||||||
|
func (a *InsightsExplorersApi) InsightsExplorerListHandler(c *core.WebContext) (any, *errs.Error) {
|
||||||
|
uid := c.GetCurrentUid()
|
||||||
|
explorers, err := a.insightsExploreres.GetAllInsightsExplorerNamesByUid(c, uid)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf(c, "[explorers.InsightsExplorerListHandler] failed to get insights explorers for user \"uid:%d\", because %s", uid, err.Error())
|
||||||
|
return nil, errs.Or(err, errs.ErrOperationFailed)
|
||||||
|
}
|
||||||
|
|
||||||
|
explorerResps := make(models.InsightsExplorerInfoResponseSlice, len(explorers))
|
||||||
|
|
||||||
|
for i := 0; i < len(explorers); i++ {
|
||||||
|
explorerResps[i], err = explorers[i].ToInsightsExplorerInfoResponse()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf(c, "[explorers.InsightsExplorerListHandler] failed to get insights explorer response for user \"uid:%d\", because %s", uid, err.Error())
|
||||||
|
return nil, errs.ErrInsightsExplorerDataInvalid
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sort.Sort(explorerResps)
|
||||||
|
|
||||||
|
return explorerResps, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// InsightsExplorerGetHandler returns one specific insights explorer of current user
|
||||||
|
func (a *InsightsExplorersApi) InsightsExplorerGetHandler(c *core.WebContext) (any, *errs.Error) {
|
||||||
|
var explorerGetReq models.InsightsExplorerGetRequest
|
||||||
|
err := c.ShouldBindQuery(&explorerGetReq)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Warnf(c, "[explorers.InsightsExplorerGetHandler] parse request failed, because %s", err.Error())
|
||||||
|
return nil, errs.NewIncompleteOrIncorrectSubmissionError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
uid := c.GetCurrentUid()
|
||||||
|
explorer, err := a.insightsExploreres.GetInsightsExplorerByExplorerId(c, uid, explorerGetReq.Id)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf(c, "[explorers.InsightsExplorerGetHandler] failed to get insights explorer \"id:%d\" for user \"uid:%d\", because %s", explorerGetReq.Id, uid, err.Error())
|
||||||
|
return nil, errs.Or(err, errs.ErrOperationFailed)
|
||||||
|
}
|
||||||
|
|
||||||
|
explorerResp, err := explorer.ToInsightsExplorerInfoResponse()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf(c, "[explorers.InsightsExplorerGetHandler] failed to get insights explorer response for user \"uid:%d\", because %s", uid, err.Error())
|
||||||
|
return nil, errs.ErrInsightsExplorerDataInvalid
|
||||||
|
}
|
||||||
|
|
||||||
|
return explorerResp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// InsightsExplorerCreateHandler saves a new insights explorer by request parameters for current user
|
||||||
|
func (a *InsightsExplorersApi) InsightsExplorerCreateHandler(c *core.WebContext) (any, *errs.Error) {
|
||||||
|
var explorerCreateReq models.InsightsExplorerCreateRequest
|
||||||
|
err := c.ShouldBindJSON(&explorerCreateReq)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Warnf(c, "[explorers.InsightsExplorerCreateHandler] parse request failed, because %s", err.Error())
|
||||||
|
return nil, errs.NewIncompleteOrIncorrectSubmissionError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
uid := c.GetCurrentUid()
|
||||||
|
|
||||||
|
maxOrderId, err := a.insightsExploreres.GetMaxDisplayOrder(c, uid)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf(c, "[explorers.InsightsExplorerCreateHandler] failed to get max display order for user \"uid:%d\", because %s", uid, err.Error())
|
||||||
|
return nil, errs.Or(err, errs.ErrOperationFailed)
|
||||||
|
}
|
||||||
|
|
||||||
|
explorer, err := a.createNewInsightsExplorerModel(uid, &explorerCreateReq, maxOrderId+1)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf(c, "[explorers.InsightsExplorerCreateHandler] failed to parse insights explorer data for user \"uid:%d\", because %s", uid, err.Error())
|
||||||
|
return nil, errs.ErrInsightsExplorerDataInvalid
|
||||||
|
}
|
||||||
|
|
||||||
|
err = a.insightsExploreres.CreateInsightsExplorer(c, explorer)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf(c, "[explorers.InsightsExplorerCreateHandler] failed to create insights explorer \"id:%d\" for user \"uid:%d\", because %s", explorer.ExplorerId, uid, err.Error())
|
||||||
|
return nil, errs.Or(err, errs.ErrOperationFailed)
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Infof(c, "[explorers.InsightsExplorerCreateHandler] user \"uid:%d\" has created a new insights explorer \"id:%d\" successfully", uid, explorer.ExplorerId)
|
||||||
|
|
||||||
|
explorerResp, err := explorer.ToInsightsExplorerInfoResponse()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf(c, "[explorers.InsightsExplorerCreateHandler] failed to get insights explorer response for user \"uid:%d\", because %s", uid, err.Error())
|
||||||
|
return nil, errs.ErrInsightsExplorerDataInvalid
|
||||||
|
}
|
||||||
|
|
||||||
|
return explorerResp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// InsightsExplorerModifyHandler saves an existed insights explorer by request parameters for current user
|
||||||
|
func (a *InsightsExplorersApi) InsightsExplorerModifyHandler(c *core.WebContext) (any, *errs.Error) {
|
||||||
|
var explorerModifyReq models.InsightsExplorerModifyRequest
|
||||||
|
err := c.ShouldBindJSON(&explorerModifyReq)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Warnf(c, "[explorers.InsightsExplorerModifyHandler] parse request failed, because %s", err.Error())
|
||||||
|
return nil, errs.NewIncompleteOrIncorrectSubmissionError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
uid := c.GetCurrentUid()
|
||||||
|
explorer, err := a.insightsExploreres.GetInsightsExplorerByExplorerId(c, uid, explorerModifyReq.Id)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf(c, "[explorers.InsightsExplorerModifyHandler] failed to get insights explorer \"id:%d\" for user \"uid:%d\", because %s", explorerModifyReq.Id, uid, err.Error())
|
||||||
|
return nil, errs.Or(err, errs.ErrOperationFailed)
|
||||||
|
}
|
||||||
|
|
||||||
|
newData, err := json.Marshal(explorerModifyReq.Data)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf(c, "[explorers.InsightsExplorerModifyHandler] failed to parse insights explorer data for user \"uid:%d\", because %s", uid, err.Error())
|
||||||
|
return nil, errs.ErrInsightsExplorerDataInvalid
|
||||||
|
}
|
||||||
|
|
||||||
|
newExplorer := &models.InsightsExplorer{
|
||||||
|
ExplorerId: explorer.ExplorerId,
|
||||||
|
Uid: uid,
|
||||||
|
Name: explorerModifyReq.Name,
|
||||||
|
Data: string(newData),
|
||||||
|
}
|
||||||
|
|
||||||
|
if newExplorer.Name == explorer.Name && newExplorer.Data == explorer.Data {
|
||||||
|
return nil, errs.ErrNothingWillBeUpdated
|
||||||
|
}
|
||||||
|
|
||||||
|
err = a.insightsExploreres.ModifyInsightsExplorer(c, newExplorer)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf(c, "[explorers.InsightsExplorerModifyHandler] failed to update insights explorer \"id:%d\" for user \"uid:%d\", because %s", explorerModifyReq.Id, uid, err.Error())
|
||||||
|
return nil, errs.Or(err, errs.ErrOperationFailed)
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Infof(c, "[explorers.InsightsExplorerModifyHandler] user \"uid:%d\" has updated insights explorer \"id:%d\" successfully", uid, explorerModifyReq.Id)
|
||||||
|
|
||||||
|
explorer.Name = newExplorer.Name
|
||||||
|
explorer.Data = newExplorer.Data
|
||||||
|
explorerResp, err := explorer.ToInsightsExplorerInfoResponse()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf(c, "[explorers.InsightsExplorerModifyHandler] failed to get insights explorer response for user \"uid:%d\", because %s", uid, err.Error())
|
||||||
|
return nil, errs.ErrInsightsExplorerDataInvalid
|
||||||
|
}
|
||||||
|
|
||||||
|
return explorerResp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// InsightsExplorerHideHandler hides a insights explorer by request parameters for current user
|
||||||
|
func (a *InsightsExplorersApi) InsightsExplorerHideHandler(c *core.WebContext) (any, *errs.Error) {
|
||||||
|
var explorerHideReq models.InsightsExplorerHideRequest
|
||||||
|
err := c.ShouldBindJSON(&explorerHideReq)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Warnf(c, "[explorers.InsightsExplorerHideHandler] parse request failed, because %s", err.Error())
|
||||||
|
return nil, errs.NewIncompleteOrIncorrectSubmissionError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
uid := c.GetCurrentUid()
|
||||||
|
err = a.insightsExploreres.HideInsightsExplorer(c, uid, []int64{explorerHideReq.Id}, explorerHideReq.Hidden)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf(c, "[explorers.InsightsExplorerHideHandler] failed to hide insights explorer \"id:%d\" for user \"uid:%d\", because %s", explorerHideReq.Id, uid, err.Error())
|
||||||
|
return nil, errs.Or(err, errs.ErrOperationFailed)
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Infof(c, "[explorers.InsightsExplorerHideHandler] user \"uid:%d\" has hidden insights explorer \"id:%d\"", uid, explorerHideReq.Id)
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// InsightsExplorerMoveHandler moves display order of existed insights explorers by request parameters for current user
|
||||||
|
func (a *InsightsExplorersApi) InsightsExplorerMoveHandler(c *core.WebContext) (any, *errs.Error) {
|
||||||
|
var explorerMoveReq models.InsightsExplorerMoveRequest
|
||||||
|
err := c.ShouldBindJSON(&explorerMoveReq)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Warnf(c, "[explorers.InsightsExplorerMoveHandler] parse request failed, because %s", err.Error())
|
||||||
|
return nil, errs.NewIncompleteOrIncorrectSubmissionError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
uid := c.GetCurrentUid()
|
||||||
|
explorers := make([]*models.InsightsExplorer, len(explorerMoveReq.NewDisplayOrders))
|
||||||
|
|
||||||
|
for i := 0; i < len(explorerMoveReq.NewDisplayOrders); i++ {
|
||||||
|
newDisplayOrder := explorerMoveReq.NewDisplayOrders[i]
|
||||||
|
explorer := &models.InsightsExplorer{
|
||||||
|
Uid: uid,
|
||||||
|
ExplorerId: newDisplayOrder.Id,
|
||||||
|
DisplayOrder: newDisplayOrder.DisplayOrder,
|
||||||
|
}
|
||||||
|
|
||||||
|
explorers[i] = explorer
|
||||||
|
}
|
||||||
|
|
||||||
|
err = a.insightsExploreres.ModifyInsightsExplorerDisplayOrders(c, uid, explorers)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf(c, "[explorers.InsightsExplorerMoveHandler] failed to move insights explorers for user \"uid:%d\", because %s", uid, err.Error())
|
||||||
|
return nil, errs.Or(err, errs.ErrOperationFailed)
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Infof(c, "[explorers.InsightsExplorerMoveHandler] user \"uid:%d\" has moved insights explorers", uid)
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// InsightsExplorerDeleteHandler deletes an existed insights explorer by request parameters for current user
|
||||||
|
func (a *InsightsExplorersApi) InsightsExplorerDeleteHandler(c *core.WebContext) (any, *errs.Error) {
|
||||||
|
var explorerDeleteReq models.InsightsExplorerDeleteRequest
|
||||||
|
err := c.ShouldBindJSON(&explorerDeleteReq)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Warnf(c, "[explorers.InsightsExplorerDeleteHandler] parse request failed, because %s", err.Error())
|
||||||
|
return nil, errs.NewIncompleteOrIncorrectSubmissionError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
uid := c.GetCurrentUid()
|
||||||
|
err = a.insightsExploreres.DeleteInsightsExplorer(c, uid, explorerDeleteReq.Id)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf(c, "[explorers.InsightsExplorerDeleteHandler] failed to delete insights explorer \"id:%d\" for user \"uid:%d\", because %s", explorerDeleteReq.Id, uid, err.Error())
|
||||||
|
return nil, errs.Or(err, errs.ErrOperationFailed)
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Infof(c, "[explorers.InsightsExplorerDeleteHandler] user \"uid:%d\" has deleted insights explorer \"id:%d\"", uid, explorerDeleteReq.Id)
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *InsightsExplorersApi) createNewInsightsExplorerModel(uid int64, explorerCreateReq *models.InsightsExplorerCreateRequest, order int32) (*models.InsightsExplorer, error) {
|
||||||
|
data, err := json.Marshal(explorerCreateReq.Data)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &models.InsightsExplorer{
|
||||||
|
Uid: uid,
|
||||||
|
Name: explorerCreateReq.Name,
|
||||||
|
Data: string(data),
|
||||||
|
DisplayOrder: order,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
+2
-3
@@ -3,7 +3,6 @@ package api
|
|||||||
import (
|
import (
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/core"
|
"github.com/mayswind/ezbookkeeping/pkg/core"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/errs"
|
"github.com/mayswind/ezbookkeeping/pkg/errs"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/settings"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// HealthsApi represents health api
|
// HealthsApi represents health api
|
||||||
@@ -18,8 +17,8 @@ var (
|
|||||||
func (a *HealthsApi) HealthStatusHandler(c *core.WebContext) (any, *errs.Error) {
|
func (a *HealthsApi) HealthStatusHandler(c *core.WebContext) (any, *errs.Error) {
|
||||||
result := make(map[string]string)
|
result := make(map[string]string)
|
||||||
|
|
||||||
result["version"] = settings.Version
|
result["version"] = core.Version
|
||||||
result["commit"] = settings.CommitHash
|
result["commit"] = core.CommitHash
|
||||||
result["status"] = "ok"
|
result["status"] = "ok"
|
||||||
|
|
||||||
return result, nil
|
return result, nil
|
||||||
|
|||||||
@@ -47,14 +47,13 @@ func (a *LargeLanguageModelsApi) RecognizeReceiptImageHandler(c *core.WebContext
|
|||||||
return nil, errs.ErrLargeLanguageModelProviderNotEnabled
|
return nil, errs.ErrLargeLanguageModelProviderNotEnabled
|
||||||
}
|
}
|
||||||
|
|
||||||
utcOffset, err := c.GetClientTimezoneOffset()
|
clientTimezone, err := c.GetClientTimezone()
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warnf(c, "[large_language_models.RecognizeReceiptImageHandler] cannot get client timezone offset, because %s", err.Error())
|
log.Warnf(c, "[large_language_models.RecognizeReceiptImageHandler] cannot get client timezone, because %s", err.Error())
|
||||||
return nil, errs.ErrClientTimezoneOffsetInvalid
|
return nil, errs.ErrClientTimezoneOffsetInvalid
|
||||||
}
|
}
|
||||||
|
|
||||||
timezone := time.FixedZone("Client Timezone", int(utcOffset)*60)
|
|
||||||
uid := c.GetCurrentUid()
|
uid := c.GetCurrentUid()
|
||||||
user, err := a.users.GetUserById(c, uid)
|
user, err := a.users.GetUserById(c, uid)
|
||||||
|
|
||||||
@@ -192,11 +191,12 @@ func (a *LargeLanguageModelsApi) RecognizeReceiptImageHandler(c *core.WebContext
|
|||||||
systemPrompt, err := templates.GetTemplate(templates.SYSTEM_PROMPT_RECEIPT_IMAGE_RECOGNITION)
|
systemPrompt, err := templates.GetTemplate(templates.SYSTEM_PROMPT_RECEIPT_IMAGE_RECOGNITION)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
log.Errorf(c, "[large_language_models.RecognizeReceiptImageHandler] failed to get system prompt template for user \"uid:%d\", because %s", uid, err.Error())
|
||||||
return nil, errs.Or(err, errs.ErrOperationFailed)
|
return nil, errs.Or(err, errs.ErrOperationFailed)
|
||||||
}
|
}
|
||||||
|
|
||||||
systemPromptParams := map[string]any{
|
systemPromptParams := map[string]any{
|
||||||
"CurrentDateTime": utils.FormatUnixTimeToLongDateTime(time.Now().Unix(), timezone),
|
"CurrentDateTime": utils.FormatUnixTimeToLongDateTime(time.Now().Unix(), clientTimezone),
|
||||||
"AllExpenseCategoryNames": strings.Join(expenseCategoryNames, "\n"),
|
"AllExpenseCategoryNames": strings.Join(expenseCategoryNames, "\n"),
|
||||||
"AllIncomeCategoryNames": strings.Join(incomeCategoryNames, "\n"),
|
"AllIncomeCategoryNames": strings.Join(incomeCategoryNames, "\n"),
|
||||||
"AllTransferCategoryNames": strings.Join(transferCategoryNames, "\n"),
|
"AllTransferCategoryNames": strings.Join(transferCategoryNames, "\n"),
|
||||||
@@ -208,6 +208,7 @@ func (a *LargeLanguageModelsApi) RecognizeReceiptImageHandler(c *core.WebContext
|
|||||||
err = systemPrompt.Execute(&bodyBuffer, systemPromptParams)
|
err = systemPrompt.Execute(&bodyBuffer, systemPromptParams)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
log.Errorf(c, "[large_language_models.RecognizeReceiptImageHandler] failed to get final system prompt from template for user \"uid:%d\", because %s", uid, err.Error())
|
||||||
return nil, errs.Or(err, errs.ErrOperationFailed)
|
return nil, errs.Or(err, errs.ErrOperationFailed)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -222,6 +223,7 @@ func (a *LargeLanguageModelsApi) RecognizeReceiptImageHandler(c *core.WebContext
|
|||||||
llmResponse, err := llm.Container.GetJsonResponseByReceiptImageRecognitionModel(c, c.GetCurrentUid(), a.CurrentConfig(), llmRequest)
|
llmResponse, err := llm.Container.GetJsonResponseByReceiptImageRecognitionModel(c, c.GetCurrentUid(), a.CurrentConfig(), llmRequest)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
log.Errorf(c, "[large_language_models.RecognizeReceiptImageHandler] failed to get llm response user \"uid:%d\", because %s", uid, err.Error())
|
||||||
return nil, errs.Or(err, errs.ErrOperationFailed)
|
return nil, errs.Or(err, errs.ErrOperationFailed)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -236,10 +238,10 @@ func (a *LargeLanguageModelsApi) RecognizeReceiptImageHandler(c *core.WebContext
|
|||||||
return nil, errs.Or(err, errs.ErrOperationFailed)
|
return nil, errs.Or(err, errs.ErrOperationFailed)
|
||||||
}
|
}
|
||||||
|
|
||||||
return a.parseRecognizedReceiptImageResponse(c, uid, utcOffset, result, accountMap, expenseCategoryMap, incomeCategoryMap, transferCategoryMap, tagMap)
|
return a.parseRecognizedReceiptImageResponse(c, uid, clientTimezone, result, accountMap, expenseCategoryMap, incomeCategoryMap, transferCategoryMap, tagMap)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *LargeLanguageModelsApi) parseRecognizedReceiptImageResponse(c *core.WebContext, uid int64, utcOffset int16, recognizedResult *models.RecognizedReceiptImageResult, accountMap map[string]*models.Account, expenseCategoryMap map[string]*models.TransactionCategory, incomeCategoryMap map[string]*models.TransactionCategory, transferCategoryMap map[string]*models.TransactionCategory, tagMap map[string]*models.TransactionTag) (*models.RecognizedReceiptImageResponse, *errs.Error) {
|
func (a *LargeLanguageModelsApi) parseRecognizedReceiptImageResponse(c *core.WebContext, uid int64, clientTimezone *time.Location, recognizedResult *models.RecognizedReceiptImageResult, accountMap map[string]*models.Account, expenseCategoryMap map[string]*models.TransactionCategory, incomeCategoryMap map[string]*models.TransactionCategory, transferCategoryMap map[string]*models.TransactionCategory, tagMap map[string]*models.TransactionTag) (*models.RecognizedReceiptImageResponse, *errs.Error) {
|
||||||
recognizedReceiptImageResponse := &models.RecognizedReceiptImageResponse{
|
recognizedReceiptImageResponse := &models.RecognizedReceiptImageResponse{
|
||||||
Type: models.TRANSACTION_TYPE_EXPENSE,
|
Type: models.TRANSACTION_TYPE_EXPENSE,
|
||||||
}
|
}
|
||||||
@@ -288,7 +290,7 @@ func (a *LargeLanguageModelsApi) parseRecognizedReceiptImageResponse(c *core.Web
|
|||||||
|
|
||||||
if len(recognizedResult.Time) > 0 {
|
if len(recognizedResult.Time) > 0 {
|
||||||
longDateTime := a.getLongDateTime(recognizedResult.Time)
|
longDateTime := a.getLongDateTime(recognizedResult.Time)
|
||||||
timestamp, err := utils.ParseFromLongDateTime(longDateTime, utcOffset)
|
timestamp, err := utils.ParseFromLongDateTimeInTimeZone(longDateTime, clientTimezone)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warnf(c, "[large_language_models.parseRecognizedReceiptImageResponse] recoginzed time \"%s\" is invalid", recognizedResult.Time)
|
log.Warnf(c, "[large_language_models.parseRecognizedReceiptImageResponse] recoginzed time \"%s\" is invalid", recognizedResult.Time)
|
||||||
|
|||||||
@@ -5,11 +5,12 @@ import (
|
|||||||
"net/http/httputil"
|
"net/http/httputil"
|
||||||
"net/url"
|
"net/url"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
|
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/core"
|
"github.com/mayswind/ezbookkeeping/pkg/core"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/errs"
|
"github.com/mayswind/ezbookkeeping/pkg/errs"
|
||||||
|
"github.com/mayswind/ezbookkeeping/pkg/httpclient"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/settings"
|
"github.com/mayswind/ezbookkeeping/pkg/settings"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/utils"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const openStreetMapTileImageUrlFormat = "https://tile.openstreetmap.org/{z}/{x}/{y}.png" // https://tile.openstreetmap.org/{z}/{x}/{y}.png
|
const openStreetMapTileImageUrlFormat = "https://tile.openstreetmap.org/{z}/{x}/{y}.png" // https://tile.openstreetmap.org/{z}/{x}/{y}.png
|
||||||
@@ -25,6 +26,8 @@ const tianDiTuMapAnnotationUrlFormat = "https://t0.tianditu.gov.cn/cva_w/wmts?SE
|
|||||||
// MapImageProxy represents map image proxy
|
// MapImageProxy represents map image proxy
|
||||||
type MapImageProxy struct {
|
type MapImageProxy struct {
|
||||||
ApiUsingConfig
|
ApiUsingConfig
|
||||||
|
mutex sync.Mutex
|
||||||
|
transport *http.Transport
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize a map image proxy singleton instance
|
// Initialize a map image proxy singleton instance
|
||||||
@@ -36,6 +39,18 @@ var (
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func (p *MapImageProxy) initializeHttpTransport() {
|
||||||
|
p.mutex.Lock()
|
||||||
|
defer p.mutex.Unlock()
|
||||||
|
|
||||||
|
if p.transport != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
p.transport = http.DefaultTransport.(*http.Transport).Clone()
|
||||||
|
httpclient.SetProxyUrl(p.transport, p.CurrentConfig().MapProxy)
|
||||||
|
}
|
||||||
|
|
||||||
// MapTileImageProxyHandler returns map tile image
|
// MapTileImageProxyHandler returns map tile image
|
||||||
func (p *MapImageProxy) MapTileImageProxyHandler(c *core.WebContext) (*httputil.ReverseProxy, *errs.Error) {
|
func (p *MapImageProxy) MapTileImageProxyHandler(c *core.WebContext) (*httputil.ReverseProxy, *errs.Error) {
|
||||||
return p.mapImageProxyHandler(c, func(c *core.WebContext, mapProvider string) (string, *errs.Error) {
|
return p.mapImageProxyHandler(c, func(c *core.WebContext, mapProvider string) (string, *errs.Error) {
|
||||||
@@ -109,8 +124,9 @@ func (p *MapImageProxy) mapImageProxyHandler(c *core.WebContext, fn func(c *core
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
transport := http.DefaultTransport.(*http.Transport).Clone()
|
if p.transport == nil {
|
||||||
utils.SetProxyUrl(transport, p.CurrentConfig().MapProxy)
|
p.initializeHttpTransport()
|
||||||
|
}
|
||||||
|
|
||||||
director := func(req *http.Request) {
|
director := func(req *http.Request) {
|
||||||
imageRawUrl := targetUrl
|
imageRawUrl := targetUrl
|
||||||
@@ -126,7 +142,7 @@ func (p *MapImageProxy) mapImageProxyHandler(c *core.WebContext, fn func(c *core
|
|||||||
}
|
}
|
||||||
|
|
||||||
return &httputil.ReverseProxy{
|
return &httputil.ReverseProxy{
|
||||||
Transport: transport,
|
Transport: p.transport,
|
||||||
Director: director,
|
Director: director,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import (
|
|||||||
"github.com/mayswind/ezbookkeeping/pkg/utils"
|
"github.com/mayswind/ezbookkeeping/pkg/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
const mcpServerName = "ezBookkeeping-mcp"
|
const mcpServerName = core.ApplicationName + "-mcp"
|
||||||
|
|
||||||
// ModelContextProtocolAPI represents model context protocol api
|
// ModelContextProtocolAPI represents model context protocol api
|
||||||
type ModelContextProtocolAPI struct {
|
type ModelContextProtocolAPI struct {
|
||||||
@@ -102,8 +102,8 @@ func (a *ModelContextProtocolAPI) InitializeHandler(c *core.WebContext, jsonRPCR
|
|||||||
},
|
},
|
||||||
ServerInfo: &mcp.MCPImplementation{
|
ServerInfo: &mcp.MCPImplementation{
|
||||||
Name: mcpServerName,
|
Name: mcpServerName,
|
||||||
Title: a.CurrentConfig().AppName,
|
Title: core.ApplicationName,
|
||||||
Version: settings.Version,
|
Version: core.Version,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -20,10 +20,10 @@ import (
|
|||||||
"github.com/mayswind/ezbookkeeping/pkg/validators"
|
"github.com/mayswind/ezbookkeeping/pkg/validators"
|
||||||
)
|
)
|
||||||
|
|
||||||
const oauth2CallbackPageUrlSuccessFormat = "%sdesktop/#/oauth2_callback?platform=%s&provider=%s&token=%s"
|
const oauth2CallbackPageUrlSuccessFormat = "%sdesktop#/oauth2_callback?platform=%s&provider=%s&token=%s"
|
||||||
const oauth2CallbackPageUrlNeedVerifyFormat = "%sdesktop/#/oauth2_callback?platform=%s&provider=%s&userName=%s&token=%s"
|
const oauth2CallbackPageUrlNeedVerifyFormat = "%sdesktop#/oauth2_callback?platform=%s&provider=%s&userName=%s&token=%s"
|
||||||
const oauth2CallbackPageUrlFailedFormat = "%sdesktop/#/oauth2_callback?errorCode=%d&errorMessage=%s"
|
const oauth2CallbackPageUrlFailedFormat = "%sdesktop#/oauth2_callback?errorCode=%d&errorMessage=%s"
|
||||||
const oauth2CallbackPageUrlErrorMessageFormat = "%sdesktop/#/oauth2_callback?errorMessage=%s"
|
const oauth2CallbackPageUrlErrorMessageFormat = "%sdesktop#/oauth2_callback?errorMessage=%s"
|
||||||
|
|
||||||
// OAuth2AuthenticationApi represents OAuth 2.0 authorization api
|
// OAuth2AuthenticationApi represents OAuth 2.0 authorization api
|
||||||
type OAuth2AuthenticationApi struct {
|
type OAuth2AuthenticationApi struct {
|
||||||
@@ -208,9 +208,20 @@ func (a *OAuth2AuthenticationApi) CallbackHandler(c *core.WebContext) (string, *
|
|||||||
return a.redirectToFailedCallbackPage(c, errs.ErrCannotRetrieveUserInfo)
|
return a.redirectToFailedCallbackPage(c, errs.ErrCannotRetrieveUserInfo)
|
||||||
}
|
}
|
||||||
|
|
||||||
if oauth2UserInfo.UserName == "" || oauth2UserInfo.Email == "" {
|
log.Infof(c, "[oauth2_authentications.CallbackHandler] oauth 2.0 user info, userName: %s, email: %s", oauth2UserInfo.UserName, oauth2UserInfo.Email)
|
||||||
log.Errorf(c, "[oauth2_authentications.CallbackHandler] invalid oauth 2.0 user info, userName: %s, email: %s", oauth2UserInfo.UserName, oauth2UserInfo.Email)
|
|
||||||
return a.redirectToFailedCallbackPage(c, errs.ErrCannotRetrieveUserInfo)
|
if oauth2UserInfo.UserName == "" && oauth2UserInfo.Email == "" {
|
||||||
|
return a.redirectToFailedCallbackPage(c, errs.ErrOAuth2UserNameAndEmailEmpty)
|
||||||
|
}
|
||||||
|
|
||||||
|
if a.CurrentConfig().OAuth2UserIdentifier == settings.OAuth2UserIdentifierEmail && oauth2UserInfo.Email == "" {
|
||||||
|
log.Errorf(c, "[oauth2_authentications.CallbackHandler] invalid oauth 2.0 user info, email is empty")
|
||||||
|
return a.redirectToFailedCallbackPage(c, errs.ErrOAuth2EmailEmpty)
|
||||||
|
}
|
||||||
|
|
||||||
|
if a.CurrentConfig().OAuth2UserIdentifier == settings.OAuth2UserIdentifierUsername && oauth2UserInfo.UserName == "" {
|
||||||
|
log.Errorf(c, "[oauth2_authentications.CallbackHandler] invalid oauth 2.0 user info, userName is empty")
|
||||||
|
return a.redirectToFailedCallbackPage(c, errs.ErrOAuth2UserNameEmpty)
|
||||||
}
|
}
|
||||||
|
|
||||||
userExternalAuthType := oauth2.GetExternalUserAuthType()
|
userExternalAuthType := oauth2.GetExternalUserAuthType()
|
||||||
@@ -221,7 +232,7 @@ func (a *OAuth2AuthenticationApi) CallbackHandler(c *core.WebContext) (string, *
|
|||||||
} else if a.CurrentConfig().OAuth2UserIdentifier == settings.OAuth2UserIdentifierUsername {
|
} else if a.CurrentConfig().OAuth2UserIdentifier == settings.OAuth2UserIdentifierUsername {
|
||||||
userExternalAuth, err = a.userExternalAuths.GetUserExternalAuthByExternalUserName(c, oauth2UserInfo.UserName, userExternalAuthType)
|
userExternalAuth, err = a.userExternalAuths.GetUserExternalAuthByExternalUserName(c, oauth2UserInfo.UserName, userExternalAuthType)
|
||||||
} else {
|
} else {
|
||||||
userExternalAuth, err = a.userExternalAuths.GetUserExternalAuthByExternalEmail(c, oauth2UserInfo.Email, userExternalAuthType)
|
return a.redirectToFailedCallbackPage(c, errs.ErrNotSupported)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil && !errors.Is(err, errs.ErrUserExternalAuthNotFound) {
|
if err != nil && !errors.Is(err, errs.ErrUserExternalAuthNotFound) {
|
||||||
@@ -257,7 +268,7 @@ func (a *OAuth2AuthenticationApi) CallbackHandler(c *core.WebContext) (string, *
|
|||||||
} else if a.CurrentConfig().OAuth2UserIdentifier == settings.OAuth2UserIdentifierUsername {
|
} else if a.CurrentConfig().OAuth2UserIdentifier == settings.OAuth2UserIdentifierUsername {
|
||||||
user, err = a.users.GetUserByUsername(c, oauth2UserInfo.UserName)
|
user, err = a.users.GetUserByUsername(c, oauth2UserInfo.UserName)
|
||||||
} else {
|
} else {
|
||||||
user, err = a.users.GetUserByEmail(c, oauth2UserInfo.Email)
|
err = errs.ErrNotSupported
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil && !errors.Is(err, errs.ErrUserNotFound) {
|
if err != nil && !errors.Is(err, errs.ErrUserNotFound) {
|
||||||
@@ -267,6 +278,14 @@ func (a *OAuth2AuthenticationApi) CallbackHandler(c *core.WebContext) (string, *
|
|||||||
}
|
}
|
||||||
|
|
||||||
if user == nil && a.CurrentConfig().EnableUserRegister && a.CurrentConfig().OAuth2AutoRegister {
|
if user == nil && a.CurrentConfig().EnableUserRegister && a.CurrentConfig().OAuth2AutoRegister {
|
||||||
|
if oauth2UserInfo.UserName == "" {
|
||||||
|
return a.redirectToFailedCallbackPage(c, errs.ErrOAuth2UserNameEmptyCannotRegister)
|
||||||
|
}
|
||||||
|
|
||||||
|
if oauth2UserInfo.Email == "" {
|
||||||
|
return a.redirectToFailedCallbackPage(c, errs.ErrOAuth2EmailEmptyCannotRegister)
|
||||||
|
}
|
||||||
|
|
||||||
userName := strings.TrimSpace(oauth2UserInfo.UserName)
|
userName := strings.TrimSpace(oauth2UserInfo.UserName)
|
||||||
email := strings.TrimSpace(oauth2UserInfo.Email)
|
email := strings.TrimSpace(oauth2UserInfo.Email)
|
||||||
nickName := strings.TrimSpace(oauth2UserInfo.NickName)
|
nickName := strings.TrimSpace(oauth2UserInfo.NickName)
|
||||||
|
|||||||
+4
-5
@@ -3,7 +3,6 @@ package api
|
|||||||
import (
|
import (
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/core"
|
"github.com/mayswind/ezbookkeeping/pkg/core"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/errs"
|
"github.com/mayswind/ezbookkeeping/pkg/errs"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/settings"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// SystemsApi represents system api
|
// SystemsApi represents system api
|
||||||
@@ -18,11 +17,11 @@ var (
|
|||||||
func (a *SystemsApi) VersionHandler(c *core.WebContext) (any, *errs.Error) {
|
func (a *SystemsApi) VersionHandler(c *core.WebContext) (any, *errs.Error) {
|
||||||
result := make(map[string]string)
|
result := make(map[string]string)
|
||||||
|
|
||||||
result["version"] = settings.Version
|
result["version"] = core.Version
|
||||||
result["commitHash"] = settings.CommitHash
|
result["commitHash"] = core.CommitHash
|
||||||
|
|
||||||
if settings.BuildTime != "" {
|
if core.BuildTime != "" {
|
||||||
result["buildTime"] = settings.BuildTime
|
result["buildTime"] = core.BuildTime
|
||||||
}
|
}
|
||||||
|
|
||||||
return result, nil
|
return result, nil
|
||||||
|
|||||||
+4
-4
@@ -69,10 +69,10 @@ func (a *TokensApi) TokenListHandler(c *core.WebContext) (any, *errs.Error) {
|
|||||||
tokenResp.IsCurrent = true
|
tokenResp.IsCurrent = true
|
||||||
}
|
}
|
||||||
|
|
||||||
if token.TokenType == core.USER_TOKEN_TYPE_API && token.UserAgent != services.TokenUserAgentCreatedViaCli {
|
if token.TokenType == core.USER_TOKEN_TYPE_API && token.UserAgent != core.TokenUserAgentCreatedViaCli {
|
||||||
tokenResp.UserAgent = services.TokenUserAgentForAPI
|
tokenResp.UserAgent = core.TokenUserAgentForAPI
|
||||||
} else if token.TokenType == core.USER_TOKEN_TYPE_MCP && token.UserAgent != services.TokenUserAgentCreatedViaCli {
|
} else if token.TokenType == core.USER_TOKEN_TYPE_MCP && token.UserAgent != core.TokenUserAgentCreatedViaCli {
|
||||||
tokenResp.UserAgent = services.TokenUserAgentForMCP
|
tokenResp.UserAgent = core.TokenUserAgentForMCP
|
||||||
}
|
}
|
||||||
|
|
||||||
tokenResps[i] = tokenResp
|
tokenResps[i] = tokenResp
|
||||||
|
|||||||
@@ -214,6 +214,7 @@ func (a *TransactionCategoriesApi) CategoryModifyHandler(c *core.WebContext) (an
|
|||||||
Uid: uid,
|
Uid: uid,
|
||||||
ParentCategoryId: categoryModifyReq.ParentId,
|
ParentCategoryId: categoryModifyReq.ParentId,
|
||||||
Name: categoryModifyReq.Name,
|
Name: categoryModifyReq.Name,
|
||||||
|
DisplayOrder: category.DisplayOrder,
|
||||||
Icon: categoryModifyReq.Icon,
|
Icon: categoryModifyReq.Icon,
|
||||||
Color: categoryModifyReq.Color,
|
Color: categoryModifyReq.Color,
|
||||||
Comment: categoryModifyReq.Comment,
|
Comment: categoryModifyReq.Comment,
|
||||||
@@ -259,6 +260,15 @@ func (a *TransactionCategoriesApi) CategoryModifyHandler(c *core.WebContext) (an
|
|||||||
if toPrimaryCategory.ParentCategoryId != models.LevelOneTransactionCategoryParentId {
|
if toPrimaryCategory.ParentCategoryId != models.LevelOneTransactionCategoryParentId {
|
||||||
return nil, errs.Or(err, errs.ErrNotAllowUseSecondaryTransactionAsPrimaryCategory)
|
return nil, errs.Or(err, errs.ErrNotAllowUseSecondaryTransactionAsPrimaryCategory)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
maxOrderId, err := a.categories.GetMaxSubCategoryDisplayOrder(c, uid, category.Type, newCategory.ParentCategoryId)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf(c, "[transaction_categories.CategoryModifyHandler] failed to get max display order for user \"uid:%d\", because %s", uid, err.Error())
|
||||||
|
return nil, errs.Or(err, errs.ErrOperationFailed)
|
||||||
|
}
|
||||||
|
|
||||||
|
newCategory.DisplayOrder = maxOrderId + 1
|
||||||
}
|
}
|
||||||
|
|
||||||
err = a.categories.ModifyCategory(c, newCategory)
|
err = a.categories.ModifyCategory(c, newCategory)
|
||||||
@@ -271,7 +281,6 @@ func (a *TransactionCategoriesApi) CategoryModifyHandler(c *core.WebContext) (an
|
|||||||
log.Infof(c, "[transaction_categories.CategoryModifyHandler] user \"uid:%d\" has updated category \"id:%d\" successfully", uid, categoryModifyReq.Id)
|
log.Infof(c, "[transaction_categories.CategoryModifyHandler] user \"uid:%d\" has updated category \"id:%d\" successfully", uid, categoryModifyReq.Id)
|
||||||
|
|
||||||
newCategory.Type = category.Type
|
newCategory.Type = category.Type
|
||||||
newCategory.DisplayOrder = category.DisplayOrder
|
|
||||||
categoryResp := newCategory.ToTransactionCategoryInfoResponse()
|
categoryResp := newCategory.ToTransactionCategoryInfoResponse()
|
||||||
|
|
||||||
return categoryResp, nil
|
return categoryResp, nil
|
||||||
|
|||||||
@@ -0,0 +1,210 @@
|
|||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sort"
|
||||||
|
|
||||||
|
"github.com/mayswind/ezbookkeeping/pkg/core"
|
||||||
|
"github.com/mayswind/ezbookkeeping/pkg/errs"
|
||||||
|
"github.com/mayswind/ezbookkeeping/pkg/log"
|
||||||
|
"github.com/mayswind/ezbookkeeping/pkg/models"
|
||||||
|
"github.com/mayswind/ezbookkeeping/pkg/services"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TransactionTagGroupsApi represents transaction tag group api
|
||||||
|
type TransactionTagGroupsApi struct {
|
||||||
|
tagGroups *services.TransactionTagGroupService
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize a transaction tag group api singleton instance
|
||||||
|
var (
|
||||||
|
TransactionTagGroups = &TransactionTagGroupsApi{
|
||||||
|
tagGroups: services.TransactionTagGroups,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// TagGroupListHandler returns transaction tag group list of current user
|
||||||
|
func (a *TransactionTagGroupsApi) TagGroupListHandler(c *core.WebContext) (any, *errs.Error) {
|
||||||
|
uid := c.GetCurrentUid()
|
||||||
|
tagGroups, err := a.tagGroups.GetAllTagGroupsByUid(c, uid)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf(c, "[transaction_tag_groups.TagGroupListHandler] failed to get tag groups for user \"uid:%d\", because %s", uid, err.Error())
|
||||||
|
return nil, errs.Or(err, errs.ErrOperationFailed)
|
||||||
|
}
|
||||||
|
|
||||||
|
tagGroupResps := make(models.TransactionTagGroupInfoResponseSlice, len(tagGroups))
|
||||||
|
|
||||||
|
for i := 0; i < len(tagGroups); i++ {
|
||||||
|
tagGroupResps[i] = tagGroups[i].ToTransactionTagGroupInfoResponse()
|
||||||
|
}
|
||||||
|
|
||||||
|
sort.Sort(tagGroupResps)
|
||||||
|
|
||||||
|
return tagGroupResps, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// TagGroupGetHandler returns one specific transaction tag group of current user
|
||||||
|
func (a *TransactionTagGroupsApi) TagGroupGetHandler(c *core.WebContext) (any, *errs.Error) {
|
||||||
|
var tagGroupGetReq models.TransactionTagGroupGetRequest
|
||||||
|
err := c.ShouldBindQuery(&tagGroupGetReq)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Warnf(c, "[transaction_tag_groups.TagGroupGetHandler] parse request failed, because %s", err.Error())
|
||||||
|
return nil, errs.NewIncompleteOrIncorrectSubmissionError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
uid := c.GetCurrentUid()
|
||||||
|
tagGroup, err := a.tagGroups.GetTagGroupByTagGroupId(c, uid, tagGroupGetReq.Id)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf(c, "[transaction_tag_groups.TagGroupGetHandler] failed to get tag group \"id:%d\" for user \"uid:%d\", because %s", tagGroupGetReq.Id, uid, err.Error())
|
||||||
|
return nil, errs.Or(err, errs.ErrOperationFailed)
|
||||||
|
}
|
||||||
|
|
||||||
|
tagGroupResp := tagGroup.ToTransactionTagGroupInfoResponse()
|
||||||
|
|
||||||
|
return tagGroupResp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// TagGroupCreateHandler saves a new transaction tag group by request parameters for current user
|
||||||
|
func (a *TransactionTagGroupsApi) TagGroupCreateHandler(c *core.WebContext) (any, *errs.Error) {
|
||||||
|
var tagGroupCreateReq models.TransactionTagGroupCreateRequest
|
||||||
|
err := c.ShouldBindJSON(&tagGroupCreateReq)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Warnf(c, "[transaction_tag_groups.TagGroupCreateHandler] parse request failed, because %s", err.Error())
|
||||||
|
return nil, errs.NewIncompleteOrIncorrectSubmissionError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
uid := c.GetCurrentUid()
|
||||||
|
|
||||||
|
maxOrderId, err := a.tagGroups.GetMaxDisplayOrder(c, uid)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf(c, "[transaction_tag_groups.TagGroupCreateHandler] failed to get max display order for user \"uid:%d\", because %s", uid, err.Error())
|
||||||
|
return nil, errs.Or(err, errs.ErrOperationFailed)
|
||||||
|
}
|
||||||
|
|
||||||
|
tagGroup := a.createNewTagGroupModel(uid, &tagGroupCreateReq, maxOrderId+1)
|
||||||
|
|
||||||
|
err = a.tagGroups.CreateTagGroup(c, tagGroup)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf(c, "[transaction_tag_groups.TagGroupCreateHandler] failed to create tag group \"id:%d\" for user \"uid:%d\", because %s", tagGroup.TagGroupId, uid, err.Error())
|
||||||
|
return nil, errs.Or(err, errs.ErrOperationFailed)
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Infof(c, "[transaction_tag_groups.TagGroupCreateHandler] user \"uid:%d\" has created a new tag group \"id:%d\" successfully", uid, tagGroup.TagGroupId)
|
||||||
|
|
||||||
|
tagGroupResp := tagGroup.ToTransactionTagGroupInfoResponse()
|
||||||
|
|
||||||
|
return tagGroupResp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// TagGroupModifyHandler saves an existed transaction tag group by request parameters for current user
|
||||||
|
func (a *TransactionTagGroupsApi) TagGroupModifyHandler(c *core.WebContext) (any, *errs.Error) {
|
||||||
|
var tagGroupModifyReq models.TransactionTagGroupModifyRequest
|
||||||
|
err := c.ShouldBindJSON(&tagGroupModifyReq)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Warnf(c, "[transaction_tag_groups.TagGroupModifyHandler] parse request failed, because %s", err.Error())
|
||||||
|
return nil, errs.NewIncompleteOrIncorrectSubmissionError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
uid := c.GetCurrentUid()
|
||||||
|
tagGroup, err := a.tagGroups.GetTagGroupByTagGroupId(c, uid, tagGroupModifyReq.Id)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf(c, "[transaction_tag_groups.TagGroupModifyHandler] failed to get tag group \"id:%d\" for user \"uid:%d\", because %s", tagGroupModifyReq.Id, uid, err.Error())
|
||||||
|
return nil, errs.Or(err, errs.ErrOperationFailed)
|
||||||
|
}
|
||||||
|
|
||||||
|
newTagGroup := &models.TransactionTagGroup{
|
||||||
|
TagGroupId: tagGroup.TagGroupId,
|
||||||
|
Uid: uid,
|
||||||
|
Name: tagGroupModifyReq.Name,
|
||||||
|
}
|
||||||
|
|
||||||
|
if newTagGroup.Name == tagGroup.Name {
|
||||||
|
return nil, errs.ErrNothingWillBeUpdated
|
||||||
|
}
|
||||||
|
|
||||||
|
err = a.tagGroups.ModifyTagGroup(c, newTagGroup)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf(c, "[transaction_tag_groups.TagGroupModifyHandler] failed to update tag group \"id:%d\" for user \"uid:%d\", because %s", tagGroupModifyReq.Id, uid, err.Error())
|
||||||
|
return nil, errs.Or(err, errs.ErrOperationFailed)
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Infof(c, "[transaction_tag_groups.TagGroupModifyHandler] user \"uid:%d\" has updated tag group \"id:%d\" successfully", uid, tagGroupModifyReq.Id)
|
||||||
|
|
||||||
|
tagGroup.Name = newTagGroup.Name
|
||||||
|
tagGroupResp := tagGroup.ToTransactionTagGroupInfoResponse()
|
||||||
|
|
||||||
|
return tagGroupResp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// TagGroupMoveHandler moves display order of existed transaction tag groups by request parameters for current user
|
||||||
|
func (a *TransactionTagGroupsApi) TagGroupMoveHandler(c *core.WebContext) (any, *errs.Error) {
|
||||||
|
var tagGroupMoveReq models.TransactionTagGroupMoveRequest
|
||||||
|
err := c.ShouldBindJSON(&tagGroupMoveReq)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Warnf(c, "[transaction_tag_groups.TagGroupMoveHandler] parse request failed, because %s", err.Error())
|
||||||
|
return nil, errs.NewIncompleteOrIncorrectSubmissionError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
uid := c.GetCurrentUid()
|
||||||
|
tagGroups := make([]*models.TransactionTagGroup, len(tagGroupMoveReq.NewDisplayOrders))
|
||||||
|
|
||||||
|
for i := 0; i < len(tagGroupMoveReq.NewDisplayOrders); i++ {
|
||||||
|
newDisplayOrder := tagGroupMoveReq.NewDisplayOrders[i]
|
||||||
|
tagGroup := &models.TransactionTagGroup{
|
||||||
|
Uid: uid,
|
||||||
|
TagGroupId: newDisplayOrder.Id,
|
||||||
|
DisplayOrder: newDisplayOrder.DisplayOrder,
|
||||||
|
}
|
||||||
|
|
||||||
|
tagGroups[i] = tagGroup
|
||||||
|
}
|
||||||
|
|
||||||
|
err = a.tagGroups.ModifyTagGroupDisplayOrders(c, uid, tagGroups)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf(c, "[transaction_tag_groups.TagGroupMoveHandler] failed to move tag groups for user \"uid:%d\", because %s", uid, err.Error())
|
||||||
|
return nil, errs.Or(err, errs.ErrOperationFailed)
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Infof(c, "[transaction_tag_groups.TagGroupMoveHandler] user \"uid:%d\" has moved tag groups", uid)
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// TagGroupDeleteHandler deletes an existed transaction tag group by request parameters for current user
|
||||||
|
func (a *TransactionTagGroupsApi) TagGroupDeleteHandler(c *core.WebContext) (any, *errs.Error) {
|
||||||
|
var tagGroupDeleteReq models.TransactionTagGroupDeleteRequest
|
||||||
|
err := c.ShouldBindJSON(&tagGroupDeleteReq)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Warnf(c, "[transaction_tag_groups.TagGroupDeleteHandler] parse request failed, because %s", err.Error())
|
||||||
|
return nil, errs.NewIncompleteOrIncorrectSubmissionError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
uid := c.GetCurrentUid()
|
||||||
|
err = a.tagGroups.DeleteTagGroup(c, uid, tagGroupDeleteReq.Id)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf(c, "[transaction_tag_groups.TagGroupDeleteHandler] failed to delete tag group \"id:%d\" for user \"uid:%d\", because %s", tagGroupDeleteReq.Id, uid, err.Error())
|
||||||
|
return nil, errs.Or(err, errs.ErrOperationFailed)
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Infof(c, "[transaction_tag_groups.TagGroupDeleteHandler] user \"uid:%d\" has deleted tag group \"id:%d\"", uid, tagGroupDeleteReq.Id)
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *TransactionTagGroupsApi) createNewTagGroupModel(uid int64, tagGroupCreateReq *models.TransactionTagGroupCreateRequest, order int32) *models.TransactionTagGroup {
|
||||||
|
return &models.TransactionTagGroup{
|
||||||
|
Uid: uid,
|
||||||
|
Name: tagGroupCreateReq.Name,
|
||||||
|
DisplayOrder: order,
|
||||||
|
}
|
||||||
|
}
|
||||||
+80
-10
@@ -12,13 +12,15 @@ import (
|
|||||||
|
|
||||||
// TransactionTagsApi represents transaction tag api
|
// TransactionTagsApi represents transaction tag api
|
||||||
type TransactionTagsApi struct {
|
type TransactionTagsApi struct {
|
||||||
tags *services.TransactionTagService
|
tags *services.TransactionTagService
|
||||||
|
tagGroups *services.TransactionTagGroupService
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize a transaction tag api singleton instance
|
// Initialize a transaction tag api singleton instance
|
||||||
var (
|
var (
|
||||||
TransactionTags = &TransactionTagsApi{
|
TransactionTags = &TransactionTagsApi{
|
||||||
tags: services.TransactionTags,
|
tags: services.TransactionTags,
|
||||||
|
tagGroups: services.TransactionTagGroups,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -78,7 +80,21 @@ func (a *TransactionTagsApi) TagCreateHandler(c *core.WebContext) (any, *errs.Er
|
|||||||
|
|
||||||
uid := c.GetCurrentUid()
|
uid := c.GetCurrentUid()
|
||||||
|
|
||||||
maxOrderId, err := a.tags.GetMaxDisplayOrder(c, uid)
|
if tagCreateReq.GroupId > 0 {
|
||||||
|
tagGroup, err := a.tagGroups.GetTagGroupByTagGroupId(c, uid, tagCreateReq.GroupId)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf(c, "[transaction_tags.TagCreateHandler] failed to get tag group \"id:%d\" for user \"uid:%d\", because %s", tagCreateReq.GroupId, uid, err.Error())
|
||||||
|
return nil, errs.Or(err, errs.ErrOperationFailed)
|
||||||
|
}
|
||||||
|
|
||||||
|
if tagGroup == nil {
|
||||||
|
log.Warnf(c, "[transaction_tags.TagCreateHandler] the tag group \"id:%d\" does not exist for user \"uid:%d\"", tagCreateReq.GroupId, uid)
|
||||||
|
return nil, errs.ErrTransactionTagGroupNotFound
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
maxOrderId, err := a.tags.GetMaxDisplayOrder(c, uid, tagCreateReq.GroupId)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf(c, "[transaction_tags.TagCreateHandler] failed to get max display order for user \"uid:%d\", because %s", uid, err.Error())
|
log.Errorf(c, "[transaction_tags.TagCreateHandler] failed to get max display order for user \"uid:%d\", because %s", uid, err.Error())
|
||||||
@@ -111,9 +127,30 @@ func (a *TransactionTagsApi) TagCreateBatchHandler(c *core.WebContext) (any, *er
|
|||||||
return nil, errs.NewIncompleteOrIncorrectSubmissionError(err)
|
return nil, errs.NewIncompleteOrIncorrectSubmissionError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for i := 0; i < len(tagCreateBatchReq.Tags); i++ {
|
||||||
|
if tagCreateBatchReq.Tags[i].GroupId != tagCreateBatchReq.GroupId {
|
||||||
|
log.Warnf(c, "[transaction_tags.TagCreateBatchHandler] the group id \"%d\" of tag#%d is inconsistent with the batch group id \"%d\"", tagCreateBatchReq.Tags[i].GroupId, i, tagCreateBatchReq.GroupId)
|
||||||
|
return nil, errs.ErrTransactionTagGroupIdInvalid
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
uid := c.GetCurrentUid()
|
uid := c.GetCurrentUid()
|
||||||
|
|
||||||
maxOrderId, err := a.tags.GetMaxDisplayOrder(c, uid)
|
if tagCreateBatchReq.GroupId > 0 {
|
||||||
|
tagGroup, err := a.tagGroups.GetTagGroupByTagGroupId(c, uid, tagCreateBatchReq.GroupId)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf(c, "[transaction_tags.TagCreateBatchHandler] failed to get tag group \"id:%d\" for user \"uid:%d\", because %s", tagCreateBatchReq.GroupId, uid, err.Error())
|
||||||
|
return nil, errs.Or(err, errs.ErrOperationFailed)
|
||||||
|
}
|
||||||
|
|
||||||
|
if tagGroup == nil {
|
||||||
|
log.Warnf(c, "[transaction_tags.TagCreateBatchHandler] the tag group \"id:%d\" does not exist for user \"uid:%d\"", tagCreateBatchReq.GroupId, uid)
|
||||||
|
return nil, errs.ErrTransactionTagGroupNotFound
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
maxOrderId, err := a.tags.GetMaxDisplayOrder(c, uid, tagCreateBatchReq.GroupId)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf(c, "[transaction_tags.TagCreateBatchHandler] failed to get max display order for user \"uid:%d\", because %s", uid, err.Error())
|
log.Errorf(c, "[transaction_tags.TagCreateBatchHandler] failed to get max display order for user \"uid:%d\", because %s", uid, err.Error())
|
||||||
@@ -160,17 +197,46 @@ func (a *TransactionTagsApi) TagModifyHandler(c *core.WebContext) (any, *errs.Er
|
|||||||
return nil, errs.Or(err, errs.ErrOperationFailed)
|
return nil, errs.Or(err, errs.ErrOperationFailed)
|
||||||
}
|
}
|
||||||
|
|
||||||
newTag := &models.TransactionTag{
|
if tagModifyReq.GroupId != tag.TagGroupId && tagModifyReq.GroupId > 0 {
|
||||||
TagId: tag.TagId,
|
tagGroup, err := a.tagGroups.GetTagGroupByTagGroupId(c, uid, tagModifyReq.GroupId)
|
||||||
Uid: uid,
|
|
||||||
Name: tagModifyReq.Name,
|
if err != nil {
|
||||||
|
log.Errorf(c, "[transaction_tags.TagModifyHandler] failed to get tag group \"id:%d\" for user \"uid:%d\", because %s", tagModifyReq.GroupId, uid, err.Error())
|
||||||
|
return nil, errs.Or(err, errs.ErrOperationFailed)
|
||||||
|
}
|
||||||
|
|
||||||
|
if tagGroup == nil {
|
||||||
|
log.Warnf(c, "[transaction_tags.TagModifyHandler] the tag group \"id:%d\" does not exist for user \"uid:%d\"", tagModifyReq.GroupId, uid)
|
||||||
|
return nil, errs.ErrTransactionTagGroupNotFound
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if newTag.Name == tag.Name {
|
newTag := &models.TransactionTag{
|
||||||
|
TagId: tag.TagId,
|
||||||
|
Uid: uid,
|
||||||
|
Name: tagModifyReq.Name,
|
||||||
|
TagGroupId: tagModifyReq.GroupId,
|
||||||
|
DisplayOrder: tag.DisplayOrder,
|
||||||
|
}
|
||||||
|
|
||||||
|
tagNameChanged := newTag.Name != tag.Name
|
||||||
|
|
||||||
|
if !tagNameChanged && newTag.TagGroupId == tag.TagGroupId {
|
||||||
return nil, errs.ErrNothingWillBeUpdated
|
return nil, errs.ErrNothingWillBeUpdated
|
||||||
}
|
}
|
||||||
|
|
||||||
err = a.tags.ModifyTag(c, newTag)
|
if newTag.TagGroupId != tag.TagGroupId {
|
||||||
|
maxOrderId, err := a.tags.GetMaxDisplayOrder(c, uid, newTag.TagGroupId)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf(c, "[transaction_tags.TagModifyHandler] failed to get max display order for user \"uid:%d\", because %s", uid, err.Error())
|
||||||
|
return nil, errs.Or(err, errs.ErrOperationFailed)
|
||||||
|
}
|
||||||
|
|
||||||
|
newTag.DisplayOrder = maxOrderId + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
err = a.tags.ModifyTag(c, newTag, tagNameChanged)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf(c, "[transaction_tags.TagModifyHandler] failed to update tag \"id:%d\" for user \"uid:%d\", because %s", tagModifyReq.Id, uid, err.Error())
|
log.Errorf(c, "[transaction_tags.TagModifyHandler] failed to update tag \"id:%d\" for user \"uid:%d\", because %s", tagModifyReq.Id, uid, err.Error())
|
||||||
@@ -180,6 +246,8 @@ func (a *TransactionTagsApi) TagModifyHandler(c *core.WebContext) (any, *errs.Er
|
|||||||
log.Infof(c, "[transaction_tags.TagModifyHandler] user \"uid:%d\" has updated tag \"id:%d\" successfully", uid, tagModifyReq.Id)
|
log.Infof(c, "[transaction_tags.TagModifyHandler] user \"uid:%d\" has updated tag \"id:%d\" successfully", uid, tagModifyReq.Id)
|
||||||
|
|
||||||
tag.Name = newTag.Name
|
tag.Name = newTag.Name
|
||||||
|
tag.TagGroupId = newTag.TagGroupId
|
||||||
|
tag.DisplayOrder = newTag.DisplayOrder
|
||||||
tagResp := tag.ToTransactionTagInfoResponse()
|
tagResp := tag.ToTransactionTagInfoResponse()
|
||||||
|
|
||||||
return tagResp, nil
|
return tagResp, nil
|
||||||
@@ -268,6 +336,7 @@ func (a *TransactionTagsApi) createNewTagModel(uid int64, tagCreateReq *models.T
|
|||||||
return &models.TransactionTag{
|
return &models.TransactionTag{
|
||||||
Uid: uid,
|
Uid: uid,
|
||||||
Name: tagCreateReq.Name,
|
Name: tagCreateReq.Name,
|
||||||
|
TagGroupId: tagCreateReq.GroupId,
|
||||||
DisplayOrder: order,
|
DisplayOrder: order,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -278,6 +347,7 @@ func (a *TransactionTagsApi) createNewTagModels(uid int64, tagCreateBatchReq *mo
|
|||||||
for i := 0; i < len(tagCreateBatchReq.Tags); i++ {
|
for i := 0; i < len(tagCreateBatchReq.Tags); i++ {
|
||||||
tagCreateReq := tagCreateBatchReq.Tags[i]
|
tagCreateReq := tagCreateBatchReq.Tags[i]
|
||||||
tag := a.createNewTagModel(uid, tagCreateReq, order+int32(i))
|
tag := a.createNewTagModel(uid, tagCreateReq, order+int32(i))
|
||||||
|
tag.TagGroupId = tagCreateBatchReq.GroupId
|
||||||
tags[i] = tag
|
tags[i] = tag
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+318
-102
@@ -4,8 +4,10 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"math"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
orderedmap "github.com/wk8/go-ordered-map/v2"
|
orderedmap "github.com/wk8/go-ordered-map/v2"
|
||||||
|
|
||||||
@@ -83,19 +85,19 @@ func (a *TransactionsApi) TransactionCountHandler(c *core.WebContext) (any, *err
|
|||||||
return nil, errs.Or(err, errs.ErrOperationFailed)
|
return nil, errs.Or(err, errs.ErrOperationFailed)
|
||||||
}
|
}
|
||||||
|
|
||||||
var allTagIds []int64
|
noTags := transactionCountReq.TagFilter == models.TransactionNoTagFilterValue
|
||||||
noTags := transactionCountReq.TagIds == "none"
|
var tagFilters []*models.TransactionTagFilter
|
||||||
|
|
||||||
if !noTags {
|
if !noTags {
|
||||||
allTagIds, err = a.transactionTags.GetTagIds(transactionCountReq.TagIds)
|
tagFilters, err = models.ParseTransactionTagFilter(transactionCountReq.TagFilter)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warnf(c, "[transactions.TransactionCountHandler] get transaction tag ids error, because %s", err.Error())
|
log.Warnf(c, "[transactions.TransactionCountHandler] parse transaction filters error, because %s", err.Error())
|
||||||
return nil, errs.Or(err, errs.ErrOperationFailed)
|
return nil, errs.Or(err, errs.ErrOperationFailed)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
totalCount, err := a.transactions.GetTransactionCount(c, uid, transactionCountReq.MaxTime, transactionCountReq.MinTime, transactionCountReq.Type, allCategoryIds, allAccountIds, allTagIds, noTags, transactionCountReq.TagFilterType, transactionCountReq.AmountFilter, transactionCountReq.Keyword)
|
totalCount, err := a.transactions.GetTransactionCount(c, uid, transactionCountReq.MaxTime, transactionCountReq.MinTime, transactionCountReq.Type, allCategoryIds, allAccountIds, tagFilters, noTags, transactionCountReq.AmountFilter, transactionCountReq.Keyword)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf(c, "[transactions.TransactionCountHandler] failed to get transaction count for user \"uid:%d\", because %s", uid, err.Error())
|
log.Errorf(c, "[transactions.TransactionCountHandler] failed to get transaction count for user \"uid:%d\", because %s", uid, err.Error())
|
||||||
@@ -119,10 +121,10 @@ func (a *TransactionsApi) TransactionListHandler(c *core.WebContext) (any, *errs
|
|||||||
return nil, errs.NewIncompleteOrIncorrectSubmissionError(err)
|
return nil, errs.NewIncompleteOrIncorrectSubmissionError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
utcOffset, err := c.GetClientTimezoneOffset()
|
clientTimezone, err := c.GetClientTimezone()
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warnf(c, "[transactions.TransactionListHandler] cannot get client timezone offset, because %s", err.Error())
|
log.Warnf(c, "[transactions.TransactionListHandler] cannot get client timezone, because %s", err.Error())
|
||||||
return nil, errs.ErrClientTimezoneOffsetInvalid
|
return nil, errs.ErrClientTimezoneOffsetInvalid
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -151,14 +153,14 @@ func (a *TransactionsApi) TransactionListHandler(c *core.WebContext) (any, *errs
|
|||||||
return nil, errs.Or(err, errs.ErrOperationFailed)
|
return nil, errs.Or(err, errs.ErrOperationFailed)
|
||||||
}
|
}
|
||||||
|
|
||||||
var allTagIds []int64
|
noTags := transactionListReq.TagFilter == models.TransactionNoTagFilterValue
|
||||||
noTags := transactionListReq.TagIds == "none"
|
var tagFilters []*models.TransactionTagFilter
|
||||||
|
|
||||||
if !noTags {
|
if !noTags {
|
||||||
allTagIds, err = a.transactionTags.GetTagIds(transactionListReq.TagIds)
|
tagFilters, err = models.ParseTransactionTagFilter(transactionListReq.TagFilter)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warnf(c, "[transactions.TransactionListHandler] get transaction tag ids error, because %s", err.Error())
|
log.Warnf(c, "[transactions.TransactionListHandler] parse transaction tag filters error, because %s", err.Error())
|
||||||
return nil, errs.Or(err, errs.ErrOperationFailed)
|
return nil, errs.Or(err, errs.ErrOperationFailed)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -166,7 +168,7 @@ func (a *TransactionsApi) TransactionListHandler(c *core.WebContext) (any, *errs
|
|||||||
var totalCount int64
|
var totalCount int64
|
||||||
|
|
||||||
if transactionListReq.WithCount {
|
if transactionListReq.WithCount {
|
||||||
totalCount, err = a.transactions.GetTransactionCount(c, uid, transactionListReq.MaxTime, transactionListReq.MinTime, transactionListReq.Type, allCategoryIds, allAccountIds, allTagIds, noTags, transactionListReq.TagFilterType, transactionListReq.AmountFilter, transactionListReq.Keyword)
|
totalCount, err = a.transactions.GetTransactionCount(c, uid, transactionListReq.MaxTime, transactionListReq.MinTime, transactionListReq.Type, allCategoryIds, allAccountIds, tagFilters, noTags, transactionListReq.AmountFilter, transactionListReq.Keyword)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf(c, "[transactions.TransactionListHandler] failed to get transaction count for user \"uid:%d\", because %s", uid, err.Error())
|
log.Errorf(c, "[transactions.TransactionListHandler] failed to get transaction count for user \"uid:%d\", because %s", uid, err.Error())
|
||||||
@@ -174,7 +176,7 @@ func (a *TransactionsApi) TransactionListHandler(c *core.WebContext) (any, *errs
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
transactions, err := a.transactions.GetTransactionsByMaxTime(c, uid, transactionListReq.MaxTime, transactionListReq.MinTime, transactionListReq.Type, allCategoryIds, allAccountIds, allTagIds, noTags, transactionListReq.TagFilterType, transactionListReq.AmountFilter, transactionListReq.Keyword, transactionListReq.Page, transactionListReq.Count, true, true)
|
transactions, err := a.transactions.GetTransactionsByMaxTime(c, uid, transactionListReq.MaxTime, transactionListReq.MinTime, transactionListReq.Type, allCategoryIds, allAccountIds, tagFilters, noTags, transactionListReq.AmountFilter, transactionListReq.Keyword, transactionListReq.Page, transactionListReq.Count, true, true)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf(c, "[transactions.TransactionListHandler] failed to get transactions earlier than \"%d\" for user \"uid:%d\", because %s", transactionListReq.MaxTime, uid, err.Error())
|
log.Errorf(c, "[transactions.TransactionListHandler] failed to get transactions earlier than \"%d\" for user \"uid:%d\", because %s", transactionListReq.MaxTime, uid, err.Error())
|
||||||
@@ -190,7 +192,15 @@ func (a *TransactionsApi) TransactionListHandler(c *core.WebContext) (any, *errs
|
|||||||
transactions = transactions[:transactionListReq.Count]
|
transactions = transactions[:transactionListReq.Count]
|
||||||
}
|
}
|
||||||
|
|
||||||
transactionResult, err := a.getTransactionResponseListResult(c, user, transactions, utcOffset, transactionListReq.WithPictures, transactionListReq.TrimAccount, transactionListReq.TrimCategory, transactionListReq.TrimTag)
|
accountMap, categoryMap, tagMap, allTransactionTagIds, pictureInfoMap, err := a.getTransactionEssentialDataByTransactionIds(c, user, transactions, transactionListReq.WithPictures, transactionListReq.TrimCategory, transactionListReq.TrimTag)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf(c, "[transactions.TransactionListHandler] failed to get essential data for assembling transaction result for user \"uid:%d\", because %s", uid, err.Error())
|
||||||
|
return nil, errs.Or(err, errs.ErrOperationFailed)
|
||||||
|
}
|
||||||
|
|
||||||
|
transactions = a.filterTransactions(c, uid, transactions, accountMap)
|
||||||
|
transactionResult, err := a.getTransactionResponseListResult(c, user, transactions, accountMap, categoryMap, tagMap, allTransactionTagIds, pictureInfoMap, clientTimezone, transactionListReq.WithPictures, transactionListReq.TrimAccount, transactionListReq.TrimCategory, transactionListReq.TrimTag)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf(c, "[transactions.TransactionListHandler] failed to assemble transaction result for user \"uid:%d\", because %s", uid, err.Error())
|
log.Errorf(c, "[transactions.TransactionListHandler] failed to assemble transaction result for user \"uid:%d\", because %s", uid, err.Error())
|
||||||
@@ -222,10 +232,10 @@ func (a *TransactionsApi) TransactionMonthListHandler(c *core.WebContext) (any,
|
|||||||
return nil, errs.NewIncompleteOrIncorrectSubmissionError(err)
|
return nil, errs.NewIncompleteOrIncorrectSubmissionError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
utcOffset, err := c.GetClientTimezoneOffset()
|
clientTimezone, err := c.GetClientTimezone()
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warnf(c, "[transactions.TransactionMonthListHandler] cannot get client timezone offset, because %s", err.Error())
|
log.Warnf(c, "[transactions.TransactionMonthListHandler] cannot get client timezone, because %s", err.Error())
|
||||||
return nil, errs.ErrClientTimezoneOffsetInvalid
|
return nil, errs.ErrClientTimezoneOffsetInvalid
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -254,26 +264,34 @@ func (a *TransactionsApi) TransactionMonthListHandler(c *core.WebContext) (any,
|
|||||||
return nil, errs.Or(err, errs.ErrOperationFailed)
|
return nil, errs.Or(err, errs.ErrOperationFailed)
|
||||||
}
|
}
|
||||||
|
|
||||||
var allTagIds []int64
|
noTags := transactionListReq.TagFilter == models.TransactionNoTagFilterValue
|
||||||
noTags := transactionListReq.TagIds == "none"
|
var tagFilters []*models.TransactionTagFilter
|
||||||
|
|
||||||
if !noTags {
|
if !noTags {
|
||||||
allTagIds, err = a.transactionTags.GetTagIds(transactionListReq.TagIds)
|
tagFilters, err = models.ParseTransactionTagFilter(transactionListReq.TagFilter)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warnf(c, "[transactions.TransactionMonthListHandler] get transaction tag ids error, because %s", err.Error())
|
log.Warnf(c, "[transactions.TransactionMonthListHandler] parse transaction tag filters error, because %s", err.Error())
|
||||||
return nil, errs.Or(err, errs.ErrOperationFailed)
|
return nil, errs.Or(err, errs.ErrOperationFailed)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
transactions, err := a.transactions.GetTransactionsInMonthByPage(c, uid, transactionListReq.Year, transactionListReq.Month, transactionListReq.Type, allCategoryIds, allAccountIds, allTagIds, noTags, transactionListReq.TagFilterType, transactionListReq.AmountFilter, transactionListReq.Keyword)
|
transactions, err := a.transactions.GetTransactionsInMonthByPage(c, uid, transactionListReq.Year, transactionListReq.Month, transactionListReq.Type, allCategoryIds, allAccountIds, tagFilters, noTags, transactionListReq.AmountFilter, transactionListReq.Keyword)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf(c, "[transactions.TransactionMonthListHandler] failed to get transactions in month \"%d-%d\" for user \"uid:%d\", because %s", transactionListReq.Year, transactionListReq.Month, uid, err.Error())
|
log.Errorf(c, "[transactions.TransactionMonthListHandler] failed to get transactions in month \"%d-%d\" for user \"uid:%d\", because %s", transactionListReq.Year, transactionListReq.Month, uid, err.Error())
|
||||||
return nil, errs.Or(err, errs.ErrOperationFailed)
|
return nil, errs.Or(err, errs.ErrOperationFailed)
|
||||||
}
|
}
|
||||||
|
|
||||||
transactionResult, err := a.getTransactionResponseListResult(c, user, transactions, utcOffset, transactionListReq.WithPictures, transactionListReq.TrimAccount, transactionListReq.TrimCategory, transactionListReq.TrimTag)
|
accountMap, categoryMap, tagMap, allTransactionTagIds, pictureInfoMap, err := a.getTransactionEssentialDataByTransactionIds(c, user, transactions, transactionListReq.WithPictures, transactionListReq.TrimCategory, transactionListReq.TrimTag)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf(c, "[transactions.TransactionMonthListHandler] failed to get essential data for assembling transaction result for user \"uid:%d\", because %s", uid, err.Error())
|
||||||
|
return nil, errs.Or(err, errs.ErrOperationFailed)
|
||||||
|
}
|
||||||
|
|
||||||
|
transactions = a.filterTransactions(c, uid, transactions, accountMap)
|
||||||
|
transactionResult, err := a.getTransactionResponseListResult(c, user, transactions, accountMap, categoryMap, tagMap, allTransactionTagIds, pictureInfoMap, clientTimezone, transactionListReq.WithPictures, transactionListReq.TrimAccount, transactionListReq.TrimCategory, transactionListReq.TrimTag)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf(c, "[transactions.TransactionMonthListHandler] failed to assemble transaction result for user \"uid:%d\", because %s", uid, err.Error())
|
log.Errorf(c, "[transactions.TransactionMonthListHandler] failed to assemble transaction result for user \"uid:%d\", because %s", uid, err.Error())
|
||||||
@@ -288,6 +306,106 @@ func (a *TransactionsApi) TransactionMonthListHandler(c *core.WebContext) (any,
|
|||||||
return transactionResps, nil
|
return transactionResps, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TransactionListAllHandler returns all transaction list of current user
|
||||||
|
func (a *TransactionsApi) TransactionListAllHandler(c *core.WebContext) (any, *errs.Error) {
|
||||||
|
var transactionAllListReq models.TransactionAllListRequest
|
||||||
|
err := c.ShouldBindQuery(&transactionAllListReq)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Warnf(c, "[transactions.TransactionListAllHandler] parse request failed, because %s", err.Error())
|
||||||
|
return nil, errs.NewIncompleteOrIncorrectSubmissionError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
clientTimezone, err := c.GetClientTimezone()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Warnf(c, "[transactions.TransactionListAllHandler] cannot get client timezone, because %s", err.Error())
|
||||||
|
return nil, errs.ErrClientTimezoneOffsetInvalid
|
||||||
|
}
|
||||||
|
|
||||||
|
uid := c.GetCurrentUid()
|
||||||
|
user, err := a.users.GetUserById(c, uid)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
if !errs.IsCustomError(err) {
|
||||||
|
log.Errorf(c, "[transactions.TransactionListAllHandler] failed to get user, because %s", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, errs.ErrUserNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
allAccountIds, err := a.accounts.GetAccountOrSubAccountIds(c, transactionAllListReq.AccountIds, uid)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Warnf(c, "[transactions.TransactionListAllHandler] get account error, because %s", err.Error())
|
||||||
|
return nil, errs.Or(err, errs.ErrOperationFailed)
|
||||||
|
}
|
||||||
|
|
||||||
|
allCategoryIds, err := a.transactionCategories.GetCategoryOrSubCategoryIds(c, transactionAllListReq.CategoryIds, uid)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Warnf(c, "[transactions.TransactionListAllHandler] get transaction category error, because %s", err.Error())
|
||||||
|
return nil, errs.Or(err, errs.ErrOperationFailed)
|
||||||
|
}
|
||||||
|
|
||||||
|
noTags := transactionAllListReq.TagFilter == models.TransactionNoTagFilterValue
|
||||||
|
var tagFilters []*models.TransactionTagFilter
|
||||||
|
|
||||||
|
if !noTags {
|
||||||
|
tagFilters, err = models.ParseTransactionTagFilter(transactionAllListReq.TagFilter)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Warnf(c, "[transactions.TransactionListAllHandler] parse transaction tag filters error, because %s", err.Error())
|
||||||
|
return nil, errs.Or(err, errs.ErrOperationFailed)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
maxTransactionTime := int64(math.MaxInt64)
|
||||||
|
minTransactionTime := int64(0)
|
||||||
|
|
||||||
|
if transactionAllListReq.EndTime > 0 {
|
||||||
|
maxTransactionTime = utils.GetMaxTransactionTimeFromUnixTime(transactionAllListReq.EndTime)
|
||||||
|
}
|
||||||
|
|
||||||
|
if transactionAllListReq.StartTime > 0 {
|
||||||
|
minTransactionTime = utils.GetMinTransactionTimeFromUnixTime(transactionAllListReq.StartTime)
|
||||||
|
}
|
||||||
|
|
||||||
|
allTransactions, err := a.transactions.GetAllSpecifiedTransactions(c, uid, maxTransactionTime, minTransactionTime, transactionAllListReq.Type, allCategoryIds, allAccountIds, tagFilters, noTags, transactionAllListReq.AmountFilter, transactionAllListReq.Keyword, pageCountForDataExport, true)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf(c, "[transactions.TransactionListAllHandler] failed to get all transactions for user \"uid:%d\", because %s", uid, err.Error())
|
||||||
|
return nil, errs.Or(err, errs.ErrOperationFailed)
|
||||||
|
}
|
||||||
|
|
||||||
|
var accountMap map[int64]*models.Account
|
||||||
|
var categoryMap map[int64]*models.TransactionCategory
|
||||||
|
var tagMap map[int64]*models.TransactionTag
|
||||||
|
var allTransactionTagIds map[int64][]int64
|
||||||
|
var pictureInfoMap map[int64][]*models.TransactionPictureInfo
|
||||||
|
|
||||||
|
if minTransactionTime == 0 && maxTransactionTime == math.MaxInt64 && len(allCategoryIds) < 1 && len(allAccountIds) < 1 && len(tagFilters) < 1 && transactionAllListReq.AmountFilter == "" && transactionAllListReq.Keyword == "" {
|
||||||
|
accountMap, categoryMap, tagMap, allTransactionTagIds, pictureInfoMap, err = a.getTransactionAllEssentialData(c, user, transactionAllListReq.WithPictures, transactionAllListReq.TrimCategory, transactionAllListReq.TrimTag)
|
||||||
|
} else {
|
||||||
|
accountMap, categoryMap, tagMap, allTransactionTagIds, pictureInfoMap, err = a.getTransactionEssentialDataByTransactionIds(c, user, allTransactions, transactionAllListReq.WithPictures, transactionAllListReq.TrimCategory, transactionAllListReq.TrimTag)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf(c, "[transactions.TransactionListAllHandler] failed to get essential data for assembling transaction result for user \"uid:%d\", because %s", uid, err.Error())
|
||||||
|
return nil, errs.Or(err, errs.ErrOperationFailed)
|
||||||
|
}
|
||||||
|
|
||||||
|
allTransactions = a.filterTransactions(c, uid, allTransactions, accountMap)
|
||||||
|
transactionResult, err := a.getTransactionResponseListResult(c, user, allTransactions, accountMap, categoryMap, tagMap, allTransactionTagIds, pictureInfoMap, clientTimezone, transactionAllListReq.WithPictures, transactionAllListReq.TrimAccount, transactionAllListReq.TrimCategory, transactionAllListReq.TrimTag)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf(c, "[transactions.TransactionListAllHandler] failed to assemble transaction result for user \"uid:%d\", because %s", uid, err.Error())
|
||||||
|
return nil, errs.Or(err, errs.ErrOperationFailed)
|
||||||
|
}
|
||||||
|
|
||||||
|
return transactionResult, nil
|
||||||
|
}
|
||||||
|
|
||||||
// TransactionReconciliationStatementHandler returns transaction reconciliation statement list of current user
|
// TransactionReconciliationStatementHandler returns transaction reconciliation statement list of current user
|
||||||
func (a *TransactionsApi) TransactionReconciliationStatementHandler(c *core.WebContext) (any, *errs.Error) {
|
func (a *TransactionsApi) TransactionReconciliationStatementHandler(c *core.WebContext) (any, *errs.Error) {
|
||||||
var reconciliationStatementRequest models.TransactionReconciliationStatementRequest
|
var reconciliationStatementRequest models.TransactionReconciliationStatementRequest
|
||||||
@@ -298,10 +416,10 @@ func (a *TransactionsApi) TransactionReconciliationStatementHandler(c *core.WebC
|
|||||||
return nil, errs.NewIncompleteOrIncorrectSubmissionError(err)
|
return nil, errs.NewIncompleteOrIncorrectSubmissionError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
utcOffset, err := c.GetClientTimezoneOffset()
|
clientTimezone, err := c.GetClientTimezone()
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warnf(c, "[transactions.TransactionReconciliationStatementHandler] cannot get client timezone offset, because %s", err.Error())
|
log.Warnf(c, "[transactions.TransactionReconciliationStatementHandler] cannot get client timezone, because %s", err.Error())
|
||||||
return nil, errs.ErrClientTimezoneOffsetInvalid
|
return nil, errs.ErrClientTimezoneOffsetInvalid
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -357,7 +475,24 @@ func (a *TransactionsApi) TransactionReconciliationStatementHandler(c *core.WebC
|
|||||||
transactionAccountBalanceMap[transactionWithBalance.RelatedId] = transactionWithBalance
|
transactionAccountBalanceMap[transactionWithBalance.RelatedId] = transactionWithBalance
|
||||||
}
|
}
|
||||||
|
|
||||||
transactionResult, err := a.getTransactionResponseListResult(c, user, transactions, utcOffset, false, true, true, true)
|
allAccountIds := make([]int64, 0, len(transactions)*2)
|
||||||
|
|
||||||
|
for i := 0; i < len(transactions); i++ {
|
||||||
|
allAccountIds = append(allAccountIds, transactions[i].AccountId)
|
||||||
|
|
||||||
|
if transactions[i].Type == models.TRANSACTION_DB_TYPE_TRANSFER_IN || transactions[i].Type == models.TRANSACTION_DB_TYPE_TRANSFER_OUT {
|
||||||
|
allAccountIds = append(allAccountIds, transactions[i].RelatedAccountId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
allAccounts, err := a.accounts.GetAccountsByAccountIds(c, uid, utils.ToUniqueInt64Slice(allAccountIds))
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf(c, "[transactions.TransactionReconciliationStatementHandler] failed to get essential data for assembling transaction result for user \"uid:%d\", because %s", uid, err.Error())
|
||||||
|
return nil, errs.Or(err, errs.ErrOperationFailed)
|
||||||
|
}
|
||||||
|
|
||||||
|
transactionResult, err := a.getTransactionResponseListResult(c, user, transactions, allAccounts, nil, nil, nil, nil, clientTimezone, false, true, true, true)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf(c, "[transactions.TransactionReconciliationStatementHandler] failed to assemble transaction result for user \"uid:%d\", because %s", uid, err.Error())
|
log.Errorf(c, "[transactions.TransactionReconciliationStatementHandler] failed to assemble transaction result for user \"uid:%d\", because %s", uid, err.Error())
|
||||||
@@ -406,27 +541,27 @@ func (a *TransactionsApi) TransactionStatisticsHandler(c *core.WebContext) (any,
|
|||||||
return nil, errs.NewIncompleteOrIncorrectSubmissionError(err)
|
return nil, errs.NewIncompleteOrIncorrectSubmissionError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
utcOffset, err := c.GetClientTimezoneOffset()
|
clientTimezone, err := c.GetClientTimezone()
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warnf(c, "[transactions.TransactionStatisticsHandler] cannot get client timezone offset, because %s", err.Error())
|
log.Warnf(c, "[transactions.TransactionStatisticsHandler] cannot get client timezone, because %s", err.Error())
|
||||||
return nil, errs.ErrClientTimezoneOffsetInvalid
|
return nil, errs.ErrClientTimezoneOffsetInvalid
|
||||||
}
|
}
|
||||||
|
|
||||||
var allTagIds []int64
|
noTags := statisticReq.TagFilter == models.TransactionNoTagFilterValue
|
||||||
noTags := statisticReq.TagIds == "none"
|
var tagFilters []*models.TransactionTagFilter
|
||||||
|
|
||||||
if !noTags {
|
if !noTags {
|
||||||
allTagIds, err = a.transactionTags.GetTagIds(statisticReq.TagIds)
|
tagFilters, err = models.ParseTransactionTagFilter(statisticReq.TagFilter)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warnf(c, "[transactions.TransactionStatisticsHandler] get transaction tag ids error, because %s", err.Error())
|
log.Warnf(c, "[transactions.TransactionStatisticsHandler] parse transaction tag filters error, because %s", err.Error())
|
||||||
return nil, errs.Or(err, errs.ErrOperationFailed)
|
return nil, errs.Or(err, errs.ErrOperationFailed)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uid := c.GetCurrentUid()
|
uid := c.GetCurrentUid()
|
||||||
totalAmounts, err := a.transactions.GetAccountsAndCategoriesTotalInflowAndOutflow(c, uid, statisticReq.StartTime, statisticReq.EndTime, allTagIds, noTags, statisticReq.TagFilterType, statisticReq.Keyword, utcOffset, statisticReq.UseTransactionTimezone)
|
totalAmounts, err := a.transactions.GetAccountsAndCategoriesTotalInflowAndOutflow(c, uid, statisticReq.StartTime, statisticReq.EndTime, tagFilters, noTags, statisticReq.Keyword, clientTimezone, statisticReq.UseTransactionTimezone)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf(c, "[transactions.TransactionStatisticsHandler] failed to get accounts and categories total income and expense for user \"uid:%d\", because %s", uid, err.Error())
|
log.Errorf(c, "[transactions.TransactionStatisticsHandler] failed to get accounts and categories total income and expense for user \"uid:%d\", because %s", uid, err.Error())
|
||||||
@@ -467,10 +602,10 @@ func (a *TransactionsApi) TransactionStatisticsTrendsHandler(c *core.WebContext)
|
|||||||
return nil, errs.NewIncompleteOrIncorrectSubmissionError(err)
|
return nil, errs.NewIncompleteOrIncorrectSubmissionError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
utcOffset, err := c.GetClientTimezoneOffset()
|
clientTimezone, err := c.GetClientTimezone()
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warnf(c, "[transactions.TransactionStatisticsTrendsHandler] cannot get client timezone offset, because %s", err.Error())
|
log.Warnf(c, "[transactions.TransactionStatisticsTrendsHandler] cannot get client timezone, because %s", err.Error())
|
||||||
return nil, errs.ErrClientTimezoneOffsetInvalid
|
return nil, errs.ErrClientTimezoneOffsetInvalid
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -481,20 +616,20 @@ func (a *TransactionsApi) TransactionStatisticsTrendsHandler(c *core.WebContext)
|
|||||||
return nil, errs.Or(err, errs.ErrOperationFailed)
|
return nil, errs.Or(err, errs.ErrOperationFailed)
|
||||||
}
|
}
|
||||||
|
|
||||||
var allTagIds []int64
|
noTags := statisticTrendsReq.TagFilter == models.TransactionNoTagFilterValue
|
||||||
noTags := statisticTrendsReq.TagIds == "none"
|
var tagFilters []*models.TransactionTagFilter
|
||||||
|
|
||||||
if !noTags {
|
if !noTags {
|
||||||
allTagIds, err = a.transactionTags.GetTagIds(statisticTrendsReq.TagIds)
|
tagFilters, err = models.ParseTransactionTagFilter(statisticTrendsReq.TagFilter)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warnf(c, "[transactions.TransactionStatisticsTrendsHandler] get transaction tag ids error, because %s", err.Error())
|
log.Warnf(c, "[transactions.TransactionStatisticsTrendsHandler] parse transaction tag filters error, because %s", err.Error())
|
||||||
return nil, errs.Or(err, errs.ErrOperationFailed)
|
return nil, errs.Or(err, errs.ErrOperationFailed)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uid := c.GetCurrentUid()
|
uid := c.GetCurrentUid()
|
||||||
allMonthlyTotalAmounts, err := a.transactions.GetAccountsAndCategoriesMonthlyInflowAndOutflow(c, uid, startYear, startMonth, endYear, endMonth, allTagIds, noTags, statisticTrendsReq.TagFilterType, statisticTrendsReq.Keyword, utcOffset, statisticTrendsReq.UseTransactionTimezone)
|
allMonthlyTotalAmounts, err := a.transactions.GetAccountsAndCategoriesMonthlyInflowAndOutflow(c, uid, startYear, startMonth, endYear, endMonth, tagFilters, noTags, statisticTrendsReq.Keyword, clientTimezone, statisticTrendsReq.UseTransactionTimezone)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf(c, "[transactions.TransactionStatisticsTrendsHandler] failed to get accounts and categories total income and expense for user \"uid:%d\", because %s", uid, err.Error())
|
log.Errorf(c, "[transactions.TransactionStatisticsTrendsHandler] failed to get accounts and categories total income and expense for user \"uid:%d\", because %s", uid, err.Error())
|
||||||
@@ -542,10 +677,10 @@ func (a *TransactionsApi) TransactionStatisticsAssetTrendsHandler(c *core.WebCon
|
|||||||
return nil, errs.NewIncompleteOrIncorrectSubmissionError(err)
|
return nil, errs.NewIncompleteOrIncorrectSubmissionError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
utcOffset, err := c.GetClientTimezoneOffset()
|
clientTimezone, err := c.GetClientTimezone()
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warnf(c, "[transactions.TransactionStatisticsAssetTrendsHandler] cannot get client timezone offset, because %s", err.Error())
|
log.Warnf(c, "[transactions.TransactionStatisticsAssetTrendsHandler] cannot get client timezone, because %s", err.Error())
|
||||||
return nil, errs.ErrClientTimezoneOffsetInvalid
|
return nil, errs.ErrClientTimezoneOffsetInvalid
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -563,7 +698,7 @@ func (a *TransactionsApi) TransactionStatisticsAssetTrendsHandler(c *core.WebCon
|
|||||||
minTransactionTime = utils.GetMinTransactionTimeFromUnixTime(statisticAssetTrendsReq.StartTime)
|
minTransactionTime = utils.GetMinTransactionTimeFromUnixTime(statisticAssetTrendsReq.StartTime)
|
||||||
}
|
}
|
||||||
|
|
||||||
accountDailyBalances, err := a.transactions.GetAllAccountsDailyOpeningAndClosingBalance(c, uid, maxTransactionTime, minTransactionTime, utcOffset)
|
accountDailyBalances, err := a.transactions.GetAllAccountsDailyOpeningAndClosingBalance(c, uid, maxTransactionTime, minTransactionTime, clientTimezone)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf(c, "[transactions.TransactionStatisticsAssetTrendsHandler] failed to get transactions from \"%d\" to \"%d\" for user \"uid:%d\", because %s", statisticAssetTrendsReq.StartTime, statisticAssetTrendsReq.EndTime, uid, err.Error())
|
log.Errorf(c, "[transactions.TransactionStatisticsAssetTrendsHandler] failed to get transactions from \"%d\" to \"%d\" for user \"uid:%d\", because %s", statisticAssetTrendsReq.StartTime, statisticAssetTrendsReq.EndTime, uid, err.Error())
|
||||||
@@ -643,10 +778,10 @@ func (a *TransactionsApi) TransactionAmountsHandler(c *core.WebContext) (any, *e
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
utcOffset, err := c.GetClientTimezoneOffset()
|
clientTimezone, err := c.GetClientTimezone()
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warnf(c, "[transactions.TransactionAmountsHandler] cannot get client timezone offset, because %s", err.Error())
|
log.Warnf(c, "[transactions.TransactionAmountsHandler] cannot get client timezone, because %s", err.Error())
|
||||||
return nil, errs.ErrClientTimezoneOffsetInvalid
|
return nil, errs.ErrClientTimezoneOffsetInvalid
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -665,7 +800,7 @@ func (a *TransactionsApi) TransactionAmountsHandler(c *core.WebContext) (any, *e
|
|||||||
for i := 0; i < len(requestItems); i++ {
|
for i := 0; i < len(requestItems); i++ {
|
||||||
requestItem := requestItems[i]
|
requestItem := requestItems[i]
|
||||||
|
|
||||||
incomeAmounts, expenseAmounts, err := a.transactions.GetAccountsTotalIncomeAndExpense(c, uid, requestItem.StartTime, requestItem.EndTime, excludeAccountIds, excludeCategoryIds, utcOffset, transactionAmountsReq.UseTransactionTimezone)
|
incomeAmounts, expenseAmounts, err := a.transactions.GetAccountsTotalIncomeAndExpense(c, uid, requestItem.StartTime, requestItem.EndTime, excludeAccountIds, excludeCategoryIds, clientTimezone, transactionAmountsReq.UseTransactionTimezone)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf(c, "[transactions.TransactionAmountsHandler] failed to get transaction amounts item for user \"uid:%d\", because %s", uid, err.Error())
|
log.Errorf(c, "[transactions.TransactionAmountsHandler] failed to get transaction amounts item for user \"uid:%d\", because %s", uid, err.Error())
|
||||||
@@ -746,10 +881,10 @@ func (a *TransactionsApi) TransactionGetHandler(c *core.WebContext) (any, *errs.
|
|||||||
return nil, errs.NewIncompleteOrIncorrectSubmissionError(err)
|
return nil, errs.NewIncompleteOrIncorrectSubmissionError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
utcOffset, err := c.GetClientTimezoneOffset()
|
clientTimezone, err := c.GetClientTimezone()
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warnf(c, "[transactions.TransactionGetHandler] cannot get client timezone offset, because %s", err.Error())
|
log.Warnf(c, "[transactions.TransactionGetHandler] cannot get client timezone, because %s", err.Error())
|
||||||
return nil, errs.ErrClientTimezoneOffsetInvalid
|
return nil, errs.ErrClientTimezoneOffsetInvalid
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -835,7 +970,7 @@ func (a *TransactionsApi) TransactionGetHandler(c *core.WebContext) (any, *errs.
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
transactionEditable := transaction.IsEditable(user, utcOffset, accountMap[transaction.AccountId], accountMap[transaction.RelatedAccountId])
|
transactionEditable := transaction.IsEditable(user, clientTimezone, accountMap[transaction.AccountId], accountMap[transaction.RelatedAccountId])
|
||||||
transactionTagIds := allTransactionTagIds[transaction.TransactionId]
|
transactionTagIds := allTransactionTagIds[transaction.TransactionId]
|
||||||
transactionResp := transaction.ToTransactionInfoResponse(transactionTagIds, transactionEditable)
|
transactionResp := transaction.ToTransactionInfoResponse(transactionTagIds, transactionEditable)
|
||||||
|
|
||||||
@@ -876,6 +1011,13 @@ func (a *TransactionsApi) TransactionCreateHandler(c *core.WebContext) (any, *er
|
|||||||
return nil, errs.NewIncompleteOrIncorrectSubmissionError(err)
|
return nil, errs.NewIncompleteOrIncorrectSubmissionError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
clientTimezone, err := c.GetClientTimezone()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Warnf(c, "[transactions.TransactionCreateHandler] cannot get client timezone, because %s", err.Error())
|
||||||
|
return nil, errs.ErrClientTimezoneOffsetInvalid
|
||||||
|
}
|
||||||
|
|
||||||
tagIds, err := utils.StringArrayToInt64Array(transactionCreateReq.TagIds)
|
tagIds, err := utils.StringArrayToInt64Array(transactionCreateReq.TagIds)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -933,7 +1075,7 @@ func (a *TransactionsApi) TransactionCreateHandler(c *core.WebContext) (any, *er
|
|||||||
}
|
}
|
||||||
|
|
||||||
transaction := a.createNewTransactionModel(uid, &transactionCreateReq, c.ClientIP())
|
transaction := a.createNewTransactionModel(uid, &transactionCreateReq, c.ClientIP())
|
||||||
transactionEditable := user.CanEditTransactionByTransactionTime(transaction.TransactionTime, transactionCreateReq.UtcOffset)
|
transactionEditable := user.CanEditTransactionByTransactionTime(transaction.TransactionTime, clientTimezone)
|
||||||
|
|
||||||
if !transactionEditable {
|
if !transactionEditable {
|
||||||
return nil, errs.ErrCannotCreateTransactionWithThisTransactionTime
|
return nil, errs.ErrCannotCreateTransactionWithThisTransactionTime
|
||||||
@@ -1006,6 +1148,13 @@ func (a *TransactionsApi) TransactionModifyHandler(c *core.WebContext) (any, *er
|
|||||||
return nil, errs.NewIncompleteOrIncorrectSubmissionError(err)
|
return nil, errs.NewIncompleteOrIncorrectSubmissionError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
clientTimezone, err := c.GetClientTimezone()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Warnf(c, "[transactions.TransactionModifyHandler] cannot get client timezone, because %s", err.Error())
|
||||||
|
return nil, errs.ErrClientTimezoneOffsetInvalid
|
||||||
|
}
|
||||||
|
|
||||||
tagIds, err := utils.StringArrayToInt64Array(transactionModifyReq.TagIds)
|
tagIds, err := utils.StringArrayToInt64Array(transactionModifyReq.TagIds)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -1119,8 +1268,8 @@ func (a *TransactionsApi) TransactionModifyHandler(c *core.WebContext) (any, *er
|
|||||||
return nil, errs.ErrNothingWillBeUpdated
|
return nil, errs.ErrNothingWillBeUpdated
|
||||||
}
|
}
|
||||||
|
|
||||||
transactionEditable := user.CanEditTransactionByTransactionTime(transaction.TransactionTime, transaction.TimezoneUtcOffset)
|
transactionEditable := user.CanEditTransactionByTransactionTime(transaction.TransactionTime, clientTimezone)
|
||||||
newTransactionEditable := user.CanEditTransactionByTransactionTime(newTransaction.TransactionTime, transactionModifyReq.UtcOffset)
|
newTransactionEditable := user.CanEditTransactionByTransactionTime(newTransaction.TransactionTime, clientTimezone)
|
||||||
|
|
||||||
if !transactionEditable || !newTransactionEditable {
|
if !transactionEditable || !newTransactionEditable {
|
||||||
return nil, errs.ErrCannotModifyTransactionWithThisTransactionTime
|
return nil, errs.ErrCannotModifyTransactionWithThisTransactionTime
|
||||||
@@ -1256,10 +1405,10 @@ func (a *TransactionsApi) TransactionDeleteHandler(c *core.WebContext) (any, *er
|
|||||||
return nil, errs.NewIncompleteOrIncorrectSubmissionError(err)
|
return nil, errs.NewIncompleteOrIncorrectSubmissionError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
utcOffset, err := c.GetClientTimezoneOffset()
|
clientTimezone, err := c.GetClientTimezone()
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warnf(c, "[transactions.TransactionDeleteHandler] cannot get client timezone offset, because %s", err.Error())
|
log.Warnf(c, "[transactions.TransactionDeleteHandler] cannot get client timezone, because %s", err.Error())
|
||||||
return nil, errs.ErrClientTimezoneOffsetInvalid
|
return nil, errs.ErrClientTimezoneOffsetInvalid
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1286,7 +1435,7 @@ func (a *TransactionsApi) TransactionDeleteHandler(c *core.WebContext) (any, *er
|
|||||||
return nil, errs.ErrTransactionTypeInvalid
|
return nil, errs.ErrTransactionTypeInvalid
|
||||||
}
|
}
|
||||||
|
|
||||||
transactionEditable := user.CanEditTransactionByTransactionTime(transaction.TransactionTime, utcOffset)
|
transactionEditable := user.CanEditTransactionByTransactionTime(transaction.TransactionTime, clientTimezone)
|
||||||
|
|
||||||
if !transactionEditable {
|
if !transactionEditable {
|
||||||
return nil, errs.ErrCannotDeleteTransactionWithThisTransactionTime
|
return nil, errs.ErrCannotDeleteTransactionWithThisTransactionTime
|
||||||
@@ -1303,13 +1452,13 @@ func (a *TransactionsApi) TransactionDeleteHandler(c *core.WebContext) (any, *er
|
|||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// TransactionParseImportDsvFileDataHandler returns the parsed file data by request parameters for current user
|
// TransactionParseImportCustomFileDataHandler returns the parsed file data by request parameters for current user
|
||||||
func (a *TransactionsApi) TransactionParseImportDsvFileDataHandler(c *core.WebContext) (any, *errs.Error) {
|
func (a *TransactionsApi) TransactionParseImportCustomFileDataHandler(c *core.WebContext) (any, *errs.Error) {
|
||||||
uid := c.GetCurrentUid()
|
uid := c.GetCurrentUid()
|
||||||
form, err := c.MultipartForm()
|
form, err := c.MultipartForm()
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf(c, "[transactions.TransactionParseImportDsvFileDataHandler] failed to get multi-part form data for user \"uid:%d\", because %s", uid, err.Error())
|
log.Errorf(c, "[transactions.TransactionParseImportCustomFileDataHandler] failed to get multi-part form data for user \"uid:%d\", because %s", uid, err.Error())
|
||||||
return nil, errs.ErrParameterInvalid
|
return nil, errs.ErrParameterInvalid
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1321,18 +1470,18 @@ func (a *TransactionsApi) TransactionParseImportDsvFileDataHandler(c *core.WebCo
|
|||||||
|
|
||||||
fileType := fileTypes[0]
|
fileType := fileTypes[0]
|
||||||
|
|
||||||
if !converters.IsCustomDelimiterSeparatedValuesFileType(fileType) {
|
if !converters.IsCustomFileFormatFileType(fileType) {
|
||||||
return nil, errs.Or(err, errs.ErrImportFileTypeNotSupported)
|
return nil, errs.Or(err, errs.ErrImportFileTypeNotSupported)
|
||||||
}
|
}
|
||||||
|
|
||||||
fileEncodings := form.Value["fileEncoding"]
|
fileEncodings := form.Value["fileEncoding"]
|
||||||
|
fileEncoding := ""
|
||||||
|
|
||||||
if len(fileEncodings) < 1 || fileEncodings[0] == "" {
|
if len(fileEncodings) > 0 {
|
||||||
return nil, errs.ErrImportFileEncodingIsEmpty
|
fileEncoding = fileEncodings[0]
|
||||||
}
|
}
|
||||||
|
|
||||||
fileEncoding := fileEncodings[0]
|
dataParser, err := converters.CreateNewCustomFileFormatTransactionDataParser(fileType, fileEncoding)
|
||||||
dataParser, err := converters.CreateNewDelimiterSeparatedValuesDataParser(fileType, fileEncoding)
|
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errs.Or(err, errs.ErrImportFileTypeNotSupported)
|
return nil, errs.Or(err, errs.ErrImportFileTypeNotSupported)
|
||||||
@@ -1341,24 +1490,24 @@ func (a *TransactionsApi) TransactionParseImportDsvFileDataHandler(c *core.WebCo
|
|||||||
importFiles := form.File["file"]
|
importFiles := form.File["file"]
|
||||||
|
|
||||||
if len(importFiles) < 1 {
|
if len(importFiles) < 1 {
|
||||||
log.Warnf(c, "[transactions.TransactionParseImportDsvFileDataHandler] there is no import file in request for user \"uid:%d\"", uid)
|
log.Warnf(c, "[transactions.TransactionParseImportCustomFileDataHandler] there is no import file in request for user \"uid:%d\"", uid)
|
||||||
return nil, errs.ErrNoFilesUpload
|
return nil, errs.ErrNoFilesUpload
|
||||||
}
|
}
|
||||||
|
|
||||||
if importFiles[0].Size < 1 {
|
if importFiles[0].Size < 1 {
|
||||||
log.Warnf(c, "[transactions.TransactionParseImportDsvFileDataHandler] the size of import file in request is zero for user \"uid:%d\"", uid)
|
log.Warnf(c, "[transactions.TransactionParseImportCustomFileDataHandler] the size of import file in request is zero for user \"uid:%d\"", uid)
|
||||||
return nil, errs.ErrUploadedFileEmpty
|
return nil, errs.ErrUploadedFileEmpty
|
||||||
}
|
}
|
||||||
|
|
||||||
if importFiles[0].Size > int64(a.CurrentConfig().MaxImportFileSize) {
|
if importFiles[0].Size > int64(a.CurrentConfig().MaxImportFileSize) {
|
||||||
log.Warnf(c, "[transactions.TransactionParseImportDsvFileDataHandler] the upload file size \"%d\" exceeds the maximum size \"%d\" of import file for user \"uid:%d\"", importFiles[0].Size, a.CurrentConfig().MaxImportFileSize, uid)
|
log.Warnf(c, "[transactions.TransactionParseImportCustomFileDataHandler] the upload file size \"%d\" exceeds the maximum size \"%d\" of import file for user \"uid:%d\"", importFiles[0].Size, a.CurrentConfig().MaxImportFileSize, uid)
|
||||||
return nil, errs.ErrExceedMaxUploadFileSize
|
return nil, errs.ErrExceedMaxUploadFileSize
|
||||||
}
|
}
|
||||||
|
|
||||||
importFile, err := importFiles[0].Open()
|
importFile, err := importFiles[0].Open()
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf(c, "[transactions.TransactionParseImportDsvFileDataHandler] failed to get import file from request for user \"uid:%d\", because %s", uid, err.Error())
|
log.Errorf(c, "[transactions.TransactionParseImportCustomFileDataHandler] failed to get import file from request for user \"uid:%d\", because %s", uid, err.Error())
|
||||||
return nil, errs.ErrOperationFailed
|
return nil, errs.ErrOperationFailed
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1366,14 +1515,14 @@ func (a *TransactionsApi) TransactionParseImportDsvFileDataHandler(c *core.WebCo
|
|||||||
fileData, err := io.ReadAll(importFile)
|
fileData, err := io.ReadAll(importFile)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf(c, "[transactions.TransactionParseImportDsvFileDataHandler] failed to read import file data for user \"uid:%d\", because %s", uid, err.Error())
|
log.Errorf(c, "[transactions.TransactionParseImportCustomFileDataHandler] failed to read import file data for user \"uid:%d\", because %s", uid, err.Error())
|
||||||
return nil, errs.Or(err, errs.ErrOperationFailed)
|
return nil, errs.Or(err, errs.ErrOperationFailed)
|
||||||
}
|
}
|
||||||
|
|
||||||
allLines, err := dataParser.ParseDsvFileLines(c, fileData)
|
allLines, err := dataParser.ParseDataLines(c, fileData)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf(c, "[transactions.TransactionParseImportDsvFileDataHandler] failed to parse import file data for user \"uid:%d\", because %s", uid, err.Error())
|
log.Errorf(c, "[transactions.TransactionParseImportCustomFileDataHandler] failed to parse import file data for user \"uid:%d\", because %s", uid, err.Error())
|
||||||
return nil, errs.Or(err, errs.ErrOperationFailed)
|
return nil, errs.Or(err, errs.ErrOperationFailed)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1390,10 +1539,10 @@ func (a *TransactionsApi) TransactionParseImportFileHandler(c *core.WebContext)
|
|||||||
return nil, errs.ErrParameterInvalid
|
return nil, errs.ErrParameterInvalid
|
||||||
}
|
}
|
||||||
|
|
||||||
utcOffset, err := c.GetClientTimezoneOffset()
|
clientTimezone, err := c.GetClientTimezone()
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warnf(c, "[transactions.TransactionParseImportFileHandler] cannot get client timezone offset, because %s", err.Error())
|
log.Warnf(c, "[transactions.TransactionParseImportFileHandler] cannot get client timezone, because %s", err.Error())
|
||||||
return nil, errs.ErrClientTimezoneOffsetInvalid
|
return nil, errs.ErrClientTimezoneOffsetInvalid
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1405,17 +1554,25 @@ func (a *TransactionsApi) TransactionParseImportFileHandler(c *core.WebContext)
|
|||||||
|
|
||||||
fileType := fileTypes[0]
|
fileType := fileTypes[0]
|
||||||
|
|
||||||
|
textualOptions := form.Value["options"]
|
||||||
|
textualOption := ""
|
||||||
|
|
||||||
|
if len(textualOptions) > 0 {
|
||||||
|
textualOption = textualOptions[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
additionalOptions := converter.ParseImporterOptions(textualOption)
|
||||||
|
|
||||||
var dataImporter converter.TransactionDataImporter
|
var dataImporter converter.TransactionDataImporter
|
||||||
|
|
||||||
if converters.IsCustomDelimiterSeparatedValuesFileType(fileType) {
|
if converters.IsCustomFileFormatFileType(fileType) {
|
||||||
fileEncodings := form.Value["fileEncoding"]
|
fileEncodings := form.Value["fileEncoding"]
|
||||||
|
fileEncoding := ""
|
||||||
|
|
||||||
if len(fileEncodings) < 1 || fileEncodings[0] == "" {
|
if len(fileEncodings) > 0 {
|
||||||
return nil, errs.ErrImportFileEncodingIsEmpty
|
fileEncoding = fileEncodings[0]
|
||||||
}
|
}
|
||||||
|
|
||||||
fileEncoding := fileEncodings[0]
|
|
||||||
|
|
||||||
columnMappings := form.Value["columnMapping"]
|
columnMappings := form.Value["columnMapping"]
|
||||||
|
|
||||||
if len(columnMappings) < 1 || columnMappings[0] == "" {
|
if len(columnMappings) < 1 || columnMappings[0] == "" {
|
||||||
@@ -1499,7 +1656,7 @@ func (a *TransactionsApi) TransactionParseImportFileHandler(c *core.WebContext)
|
|||||||
transactionTagSeparator = transactionTagSeparators[0]
|
transactionTagSeparator = transactionTagSeparators[0]
|
||||||
}
|
}
|
||||||
|
|
||||||
dataImporter, err = converters.CreateNewDelimiterSeparatedValuesDataImporter(fileType, fileEncoding, columnIndexMapping, transactionTypeNameMapping, hasHeaderLine, timeFormats[0], timezoneFormat, amountDecimalSeparator, amountDigitGroupingSymbol, geoLocationSeparator, geoLocationOrder, transactionTagSeparator)
|
dataImporter, err = converters.CreateNewCustomTransactionDataImporter(fileType, fileEncoding, columnIndexMapping, transactionTypeNameMapping, hasHeaderLine, timeFormats[0], timezoneFormat, amountDecimalSeparator, amountDigitGroupingSymbol, geoLocationSeparator, geoLocationOrder, transactionTagSeparator)
|
||||||
} else {
|
} else {
|
||||||
dataImporter, err = converters.GetTransactionDataImporter(fileType)
|
dataImporter, err = converters.GetTransactionDataImporter(fileType)
|
||||||
}
|
}
|
||||||
@@ -1581,7 +1738,7 @@ func (a *TransactionsApi) TransactionParseImportFileHandler(c *core.WebContext)
|
|||||||
|
|
||||||
tagMap := a.transactionTags.GetVisibleTagNameMapByList(tags)
|
tagMap := a.transactionTags.GetVisibleTagNameMapByList(tags)
|
||||||
|
|
||||||
parsedTransactions, _, _, _, _, _, err := dataImporter.ParseImportedData(c, user, fileData, utcOffset, accountMap, expenseCategoryMap, incomeCategoryMap, transferCategoryMap, tagMap)
|
parsedTransactions, _, _, _, _, _, err := dataImporter.ParseImportedData(c, user, fileData, clientTimezone, additionalOptions, accountMap, expenseCategoryMap, incomeCategoryMap, transferCategoryMap, tagMap)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf(c, "[transactions.TransactionParseImportFileHandler] failed to parse imported data for user \"uid:%d\", because %s", user.Uid, err.Error())
|
log.Errorf(c, "[transactions.TransactionParseImportFileHandler] failed to parse imported data for user \"uid:%d\", because %s", user.Uid, err.Error())
|
||||||
@@ -1612,6 +1769,13 @@ func (a *TransactionsApi) TransactionImportHandler(c *core.WebContext) (any, *er
|
|||||||
return nil, errs.NewIncompleteOrIncorrectSubmissionError(err)
|
return nil, errs.NewIncompleteOrIncorrectSubmissionError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
clientTimezone, err := c.GetClientTimezone()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Warnf(c, "[transactions.TransactionImportHandler] cannot get client timezone, because %s", err.Error())
|
||||||
|
return nil, errs.ErrClientTimezoneOffsetInvalid
|
||||||
|
}
|
||||||
|
|
||||||
uid := c.GetCurrentUid()
|
uid := c.GetCurrentUid()
|
||||||
|
|
||||||
if a.CurrentConfig().EnableDuplicateSubmissionsCheck && transactionImportReq.ClientSessionId != "" {
|
if a.CurrentConfig().EnableDuplicateSubmissionsCheck && transactionImportReq.ClientSessionId != "" {
|
||||||
@@ -1697,7 +1861,7 @@ func (a *TransactionsApi) TransactionImportHandler(c *core.WebContext) (any, *er
|
|||||||
for i := 0; i < len(transactionImportReq.Transactions); i++ {
|
for i := 0; i < len(transactionImportReq.Transactions); i++ {
|
||||||
transactionCreateReq := transactionImportReq.Transactions[i]
|
transactionCreateReq := transactionImportReq.Transactions[i]
|
||||||
transaction := a.createNewTransactionModel(uid, transactionCreateReq, c.ClientIP())
|
transaction := a.createNewTransactionModel(uid, transactionCreateReq, c.ClientIP())
|
||||||
transactionEditable := user.CanEditTransactionByTransactionTime(transaction.TransactionTime, transactionCreateReq.UtcOffset)
|
transactionEditable := user.CanEditTransactionByTransactionTime(transaction.TransactionTime, clientTimezone)
|
||||||
|
|
||||||
if !transactionEditable {
|
if !transactionEditable {
|
||||||
return nil, errs.ErrCannotCreateTransactionWithThisTransactionTime
|
return nil, errs.ErrCannotCreateTransactionWithThisTransactionTime
|
||||||
@@ -1814,7 +1978,61 @@ func (a *TransactionsApi) getTransactionTagInfoResponses(tagIds []int64, allTran
|
|||||||
return allTags
|
return allTags
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *TransactionsApi) getTransactionResponseListResult(c *core.WebContext, user *models.User, transactions []*models.Transaction, utcOffset int16, withPictures bool, trimAccount bool, trimCategory bool, trimTag bool) (models.TransactionInfoResponseSlice, error) {
|
func (a *TransactionsApi) getTransactionAllEssentialData(c *core.WebContext, user *models.User, withPictures bool, trimCategory bool, trimTag bool) (accountMap map[int64]*models.Account, categoryMap map[int64]*models.TransactionCategory, tagMap map[int64]*models.TransactionTag, allTransactionTagIds map[int64][]int64, pictureInfoMap map[int64][]*models.TransactionPictureInfo, err error) {
|
||||||
|
uid := user.Uid
|
||||||
|
allAccounts, err := a.accounts.GetAllAccountsByUid(c, uid)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf(c, "[transactions.getTransactionAllEssentialData] failed to get all accounts for user \"uid:%d\", because %s", uid, err.Error())
|
||||||
|
return nil, nil, nil, nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
accountMap = a.accounts.GetAccountMapByList(allAccounts)
|
||||||
|
|
||||||
|
allTagIndexes, err := a.transactionTags.GetAllTagIdsOfAllTransactions(c, uid)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf(c, "[transactions.getTransactionAllEssentialData] failed to get all transactions tag ids for user \"uid:%d\", because %s", uid, err.Error())
|
||||||
|
return nil, nil, nil, nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
allTransactionTagIds = a.transactionTags.GetGroupedTransactionTagIds(allTagIndexes)
|
||||||
|
|
||||||
|
if !trimCategory {
|
||||||
|
allCategories, err := a.transactionCategories.GetAllCategoriesByUid(c, uid, 0, -1)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf(c, "[transactions.getTransactionAllEssentialData] failed to get all transactions categories for user \"uid:%d\", because %s", uid, err.Error())
|
||||||
|
return nil, nil, nil, nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
categoryMap = a.transactionCategories.GetCategoryMapByList(allCategories)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !trimTag {
|
||||||
|
allTags, err := a.transactionTags.GetAllTagsByUid(c, uid)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf(c, "[transactions.getTransactionAllEssentialData] failed to get all transactions tags for user \"uid:%d\", because %s", uid, err.Error())
|
||||||
|
return nil, nil, nil, nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
tagMap = a.transactionTags.GetTagMapByList(allTags)
|
||||||
|
}
|
||||||
|
|
||||||
|
if withPictures && a.CurrentConfig().EnableTransactionPictures {
|
||||||
|
pictureInfoMap, err = a.transactionPictures.GetAllPictureInfosOfAllTransactions(c, uid)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf(c, "[transactions.getTransactionAllEssentialData] failed to get all transactions pictures for user \"uid:%d\", because %s", uid, err.Error())
|
||||||
|
return nil, nil, nil, nil, nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return accountMap, categoryMap, tagMap, allTransactionTagIds, pictureInfoMap, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *TransactionsApi) getTransactionEssentialDataByTransactionIds(c *core.WebContext, user *models.User, transactions []*models.Transaction, withPictures bool, trimCategory bool, trimTag bool) (accountMap map[int64]*models.Account, categoryMap map[int64]*models.TransactionCategory, tagMap map[int64]*models.TransactionTag, allTransactionTagIds map[int64][]int64, pictureInfoMap map[int64][]*models.TransactionPictureInfo, err error) {
|
||||||
uid := user.Uid
|
uid := user.Uid
|
||||||
transactionIds := make([]int64, len(transactions))
|
transactionIds := make([]int64, len(transactions))
|
||||||
accountIds := make([]int64, 0, len(transactions)*2)
|
accountIds := make([]int64, 0, len(transactions)*2)
|
||||||
@@ -1837,32 +2055,26 @@ func (a *TransactionsApi) getTransactionResponseListResult(c *core.WebContext, u
|
|||||||
categoryIds = append(categoryIds, transactions[i].CategoryId)
|
categoryIds = append(categoryIds, transactions[i].CategoryId)
|
||||||
}
|
}
|
||||||
|
|
||||||
allAccounts, err := a.accounts.GetAccountsByAccountIds(c, uid, utils.ToUniqueInt64Slice(accountIds))
|
accountMap, err = a.accounts.GetAccountsByAccountIds(c, uid, utils.ToUniqueInt64Slice(accountIds))
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf(c, "[transactions.getTransactionResponseListResult] failed to get accounts for user \"uid:%d\", because %s", uid, err.Error())
|
log.Errorf(c, "[transactions.getTransactionEssentialDataByTransactionIds] failed to get accounts for user \"uid:%d\", because %s", uid, err.Error())
|
||||||
return nil, err
|
return nil, nil, nil, nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
transactions = a.filterTransactions(c, uid, transactions, allAccounts)
|
allTransactionTagIds, err = a.transactionTags.GetAllTagIdsOfTransactions(c, uid, transactionIds)
|
||||||
|
|
||||||
allTransactionTagIds, err := a.transactionTags.GetAllTagIdsOfTransactions(c, uid, transactionIds)
|
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf(c, "[transactions.getTransactionResponseListResult] failed to get transactions tag ids for user \"uid:%d\", because %s", uid, err.Error())
|
log.Errorf(c, "[transactions.getTransactionEssentialDataByTransactionIds] failed to get transactions tag ids for user \"uid:%d\", because %s", uid, err.Error())
|
||||||
return nil, err
|
return nil, nil, nil, nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var categoryMap map[int64]*models.TransactionCategory
|
|
||||||
var tagMap map[int64]*models.TransactionTag
|
|
||||||
var pictureInfoMap map[int64][]*models.TransactionPictureInfo
|
|
||||||
|
|
||||||
if !trimCategory {
|
if !trimCategory {
|
||||||
categoryMap, err = a.transactionCategories.GetCategoriesByCategoryIds(c, uid, utils.ToUniqueInt64Slice(categoryIds))
|
categoryMap, err = a.transactionCategories.GetCategoriesByCategoryIds(c, uid, utils.ToUniqueInt64Slice(categoryIds))
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf(c, "[transactions.getTransactionResponseListResult] failed to get transactions categories for user \"uid:%d\", because %s", uid, err.Error())
|
log.Errorf(c, "[transactions.getTransactionEssentialDataByTransactionIds] failed to get transactions categories for user \"uid:%d\", because %s", uid, err.Error())
|
||||||
return nil, err
|
return nil, nil, nil, nil, nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1870,8 +2082,8 @@ func (a *TransactionsApi) getTransactionResponseListResult(c *core.WebContext, u
|
|||||||
tagMap, err = a.transactionTags.GetTagsByTagIds(c, uid, utils.ToUniqueInt64Slice(a.transactionTags.GetTransactionTagIds(allTransactionTagIds)))
|
tagMap, err = a.transactionTags.GetTagsByTagIds(c, uid, utils.ToUniqueInt64Slice(a.transactionTags.GetTransactionTagIds(allTransactionTagIds)))
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf(c, "[transactions.getTransactionResponseListResult] failed to get transactions tags for user \"uid:%d\", because %s", uid, err.Error())
|
log.Errorf(c, "[transactions.getTransactionEssentialDataByTransactionIds] failed to get transactions tags for user \"uid:%d\", because %s", uid, err.Error())
|
||||||
return nil, err
|
return nil, nil, nil, nil, nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1879,11 +2091,15 @@ func (a *TransactionsApi) getTransactionResponseListResult(c *core.WebContext, u
|
|||||||
pictureInfoMap, err = a.transactionPictures.GetPictureInfosByTransactionIds(c, uid, utils.ToUniqueInt64Slice(a.transactions.GetTransactionIds(transactions)))
|
pictureInfoMap, err = a.transactionPictures.GetPictureInfosByTransactionIds(c, uid, utils.ToUniqueInt64Slice(a.transactions.GetTransactionIds(transactions)))
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf(c, "[transactions.getTransactionResponseListResult] failed to get transactions pictures for user \"uid:%d\", because %s", uid, err.Error())
|
log.Errorf(c, "[transactions.getTransactionEssentialDataByTransactionIds] failed to get transactions pictures for user \"uid:%d\", because %s", uid, err.Error())
|
||||||
return nil, err
|
return nil, nil, nil, nil, nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return accountMap, categoryMap, tagMap, allTransactionTagIds, pictureInfoMap, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *TransactionsApi) getTransactionResponseListResult(c *core.WebContext, user *models.User, transactions []*models.Transaction, allAccounts map[int64]*models.Account, categoryMap map[int64]*models.TransactionCategory, tagMap map[int64]*models.TransactionTag, allTransactionTagIds map[int64][]int64, pictureInfoMap map[int64][]*models.TransactionPictureInfo, clientTimezone *time.Location, withPictures bool, trimAccount bool, trimCategory bool, trimTag bool) (models.TransactionInfoResponseSlice, error) {
|
||||||
result := make(models.TransactionInfoResponseSlice, len(transactions))
|
result := make(models.TransactionInfoResponseSlice, len(transactions))
|
||||||
|
|
||||||
for i := 0; i < len(transactions); i++ {
|
for i := 0; i < len(transactions); i++ {
|
||||||
@@ -1893,7 +2109,7 @@ func (a *TransactionsApi) getTransactionResponseListResult(c *core.WebContext, u
|
|||||||
transaction = a.transactions.GetRelatedTransferTransaction(transaction)
|
transaction = a.transactions.GetRelatedTransferTransaction(transaction)
|
||||||
}
|
}
|
||||||
|
|
||||||
transactionEditable := transaction.IsEditable(user, utcOffset, allAccounts[transaction.AccountId], allAccounts[transaction.RelatedAccountId])
|
transactionEditable := transaction.IsEditable(user, clientTimezone, allAccounts[transaction.AccountId], allAccounts[transaction.RelatedAccountId])
|
||||||
transactionTagIds := allTransactionTagIds[transaction.TransactionId]
|
transactionTagIds := allTransactionTagIds[transaction.TransactionId]
|
||||||
result[i] = transaction.ToTransactionInfoResponse(transactionTagIds, transactionEditable)
|
result[i] = transaction.ToTransactionInfoResponse(transactionTagIds, transactionEditable)
|
||||||
|
|
||||||
@@ -1907,17 +2123,17 @@ func (a *TransactionsApi) getTransactionResponseListResult(c *core.WebContext, u
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !trimCategory {
|
if !trimCategory && categoryMap != nil {
|
||||||
if category := categoryMap[transaction.CategoryId]; category != nil {
|
if category := categoryMap[transaction.CategoryId]; category != nil {
|
||||||
result[i].Category = category.ToTransactionCategoryInfoResponse()
|
result[i].Category = category.ToTransactionCategoryInfoResponse()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !trimTag {
|
if !trimTag && tagMap != nil && transactionTagIds != nil {
|
||||||
result[i].Tags = a.getTransactionTagInfoResponses(transactionTagIds, tagMap)
|
result[i].Tags = a.getTransactionTagInfoResponses(transactionTagIds, tagMap)
|
||||||
}
|
}
|
||||||
|
|
||||||
if withPictures && a.CurrentConfig().EnableTransactionPictures {
|
if withPictures && a.CurrentConfig().EnableTransactionPictures && pictureInfoMap != nil {
|
||||||
pictureInfos, exists := pictureInfoMap[transaction.TransactionId]
|
pictureInfos, exists := pictureInfoMap[transaction.TransactionId]
|
||||||
|
|
||||||
if exists {
|
if exists {
|
||||||
|
|||||||
@@ -85,7 +85,7 @@ func (a *TwoFactorAuthorizationsApi) TwoFactorEnableRequestHandler(c *core.WebCo
|
|||||||
return nil, errs.ErrNotPermittedToPerformThisAction
|
return nil, errs.ErrNotPermittedToPerformThisAction
|
||||||
}
|
}
|
||||||
|
|
||||||
key, err := a.twoFactorAuthorizations.GenerateTwoFactorSecret(c, user)
|
key, err := a.twoFactorAuthorizations.GenerateTwoFactorSecret(c, user, c.GetClientLocale())
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf(c, "[twofactor_authorizations.TwoFactorEnableRequestHandler] failed to generate two-factor secret, because %s", err.Error())
|
log.Errorf(c, "[twofactor_authorizations.TwoFactorEnableRequestHandler] failed to generate two-factor secret, because %s", err.Error())
|
||||||
|
|||||||
@@ -13,8 +13,8 @@ import (
|
|||||||
"github.com/mayswind/ezbookkeeping/pkg/auth/oauth2/provider/oidc"
|
"github.com/mayswind/ezbookkeeping/pkg/auth/oauth2/provider/oidc"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/core"
|
"github.com/mayswind/ezbookkeeping/pkg/core"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/errs"
|
"github.com/mayswind/ezbookkeeping/pkg/errs"
|
||||||
|
"github.com/mayswind/ezbookkeeping/pkg/httpclient"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/settings"
|
"github.com/mayswind/ezbookkeeping/pkg/settings"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/utils"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// OAuth2Container contains the current OAuth 2.0 authentication provider
|
// OAuth2Container contains the current OAuth 2.0 authentication provider
|
||||||
@@ -67,7 +67,7 @@ func InitializeOAuth2Provider(config *settings.Config) error {
|
|||||||
|
|
||||||
Container.current = oauth2Provider
|
Container.current = oauth2Provider
|
||||||
Container.usePKCE = config.OAuth2UsePKCE
|
Container.usePKCE = config.OAuth2UsePKCE
|
||||||
Container.oauth2HttpClient = utils.NewHttpClient(config.OAuth2RequestTimeout, config.OAuth2Proxy, config.OAuth2SkipTLSVerify, settings.GetUserAgent())
|
Container.oauth2HttpClient = httpclient.NewHttpClient(config.OAuth2RequestTimeout, config.OAuth2Proxy, config.OAuth2SkipTLSVerify, core.GetOutgoingUserAgent(), config.EnableDebugLog)
|
||||||
Container.externalUserAuthType = externalUserAuthType
|
Container.externalUserAuthType = externalUserAuthType
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import (
|
|||||||
"github.com/mayswind/ezbookkeeping/pkg/auth/oauth2/provider"
|
"github.com/mayswind/ezbookkeeping/pkg/auth/oauth2/provider"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/core"
|
"github.com/mayswind/ezbookkeeping/pkg/core"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/errs"
|
"github.com/mayswind/ezbookkeeping/pkg/errs"
|
||||||
|
"github.com/mayswind/ezbookkeeping/pkg/httpclient"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/log"
|
"github.com/mayswind/ezbookkeeping/pkg/log"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/settings"
|
"github.com/mayswind/ezbookkeeping/pkg/settings"
|
||||||
)
|
)
|
||||||
@@ -59,6 +60,11 @@ func (p *CommonOAuth2Provider) GetUserInfo(c core.Context, oauth2Token *oauth2.T
|
|||||||
}
|
}
|
||||||
|
|
||||||
oauth2Client := oauth2.NewClient(c, oauth2.StaticTokenSource(oauth2Token))
|
oauth2Client := oauth2.NewClient(c, oauth2.StaticTokenSource(oauth2Token))
|
||||||
|
|
||||||
|
req = req.WithContext(httpclient.CustomHttpResponseLog(c, func(data []byte) {
|
||||||
|
log.Debugf(c, "[common_oauth2_provider.GetUserInfo] response is %s", data)
|
||||||
|
}))
|
||||||
|
|
||||||
resp, err := oauth2Client.Do(req)
|
resp, err := oauth2Client.Do(req)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -69,8 +75,6 @@ func (p *CommonOAuth2Provider) GetUserInfo(c core.Context, oauth2Token *oauth2.T
|
|||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
body, err := io.ReadAll(resp.Body)
|
body, err := io.ReadAll(resp.Body)
|
||||||
|
|
||||||
log.Debugf(c, "[common_oauth2_provider.GetUserInfo] response is %s", body)
|
|
||||||
|
|
||||||
if resp.StatusCode != 200 {
|
if resp.StatusCode != 200 {
|
||||||
log.Errorf(c, "[common_oauth2_provider.GetUserInfo] failed to get user info response, because response code is %d", resp.StatusCode)
|
log.Errorf(c, "[common_oauth2_provider.GetUserInfo] failed to get user info response, because response code is %d", resp.StatusCode)
|
||||||
return nil, errs.ErrFailedToRequestRemoteApi
|
return nil, errs.ErrFailedToRequestRemoteApi
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import (
|
|||||||
"github.com/mayswind/ezbookkeeping/pkg/auth/oauth2/provider"
|
"github.com/mayswind/ezbookkeeping/pkg/auth/oauth2/provider"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/core"
|
"github.com/mayswind/ezbookkeeping/pkg/core"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/errs"
|
"github.com/mayswind/ezbookkeeping/pkg/errs"
|
||||||
|
"github.com/mayswind/ezbookkeeping/pkg/httpclient"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/log"
|
"github.com/mayswind/ezbookkeeping/pkg/log"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/settings"
|
"github.com/mayswind/ezbookkeeping/pkg/settings"
|
||||||
)
|
)
|
||||||
@@ -61,6 +62,11 @@ func (p *GithubOAuth2Provider) GetUserInfo(c core.Context, oauth2Token *oauth2.T
|
|||||||
}
|
}
|
||||||
|
|
||||||
oauth2Client := oauth2.NewClient(c, oauth2.StaticTokenSource(oauth2Token))
|
oauth2Client := oauth2.NewClient(c, oauth2.StaticTokenSource(oauth2Token))
|
||||||
|
|
||||||
|
req = req.WithContext(httpclient.CustomHttpResponseLog(c, func(data []byte) {
|
||||||
|
log.Debugf(c, "[github_oauth2_provider.GetUserInfo] user profile response is %s", data)
|
||||||
|
}))
|
||||||
|
|
||||||
resp, err := oauth2Client.Do(req)
|
resp, err := oauth2Client.Do(req)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -71,8 +77,6 @@ func (p *GithubOAuth2Provider) GetUserInfo(c core.Context, oauth2Token *oauth2.T
|
|||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
body, err := io.ReadAll(resp.Body)
|
body, err := io.ReadAll(resp.Body)
|
||||||
|
|
||||||
log.Debugf(c, "[github_oauth2_provider.GetUserInfo] user profile response is %s", body)
|
|
||||||
|
|
||||||
if resp.StatusCode != 200 {
|
if resp.StatusCode != 200 {
|
||||||
log.Errorf(c, "[github_oauth2_provider.GetUserInfo] failed to get user info response, because response code is %d", resp.StatusCode)
|
log.Errorf(c, "[github_oauth2_provider.GetUserInfo] failed to get user info response, because response code is %d", resp.StatusCode)
|
||||||
return nil, errs.ErrFailedToRequestRemoteApi
|
return nil, errs.ErrFailedToRequestRemoteApi
|
||||||
@@ -92,6 +96,10 @@ func (p *GithubOAuth2Provider) GetUserInfo(c core.Context, oauth2Token *oauth2.T
|
|||||||
return nil, errs.ErrFailedToRequestRemoteApi
|
return nil, errs.ErrFailedToRequestRemoteApi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
req = req.WithContext(httpclient.CustomHttpResponseLog(c, func(data []byte) {
|
||||||
|
log.Debugf(c, "[github_oauth2_provider.GetUserInfo] user emails response is %s", data)
|
||||||
|
}))
|
||||||
|
|
||||||
resp, err = oauth2Client.Do(req)
|
resp, err = oauth2Client.Do(req)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -102,8 +110,6 @@ func (p *GithubOAuth2Provider) GetUserInfo(c core.Context, oauth2Token *oauth2.T
|
|||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
body, err = io.ReadAll(resp.Body)
|
body, err = io.ReadAll(resp.Body)
|
||||||
|
|
||||||
log.Debugf(c, "[github_oauth2_provider.GetUserInfo] user emails response is %s", body)
|
|
||||||
|
|
||||||
if resp.StatusCode != 200 {
|
if resp.StatusCode != 200 {
|
||||||
log.Errorf(c, "[github_oauth2_provider.GetUserInfo] failed to get user emails response, because response code is %d", resp.StatusCode)
|
log.Errorf(c, "[github_oauth2_provider.GetUserInfo] failed to get user emails response, because response code is %d", resp.StatusCode)
|
||||||
return nil, errs.ErrFailedToRequestRemoteApi
|
return nil, errs.ErrFailedToRequestRemoteApi
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import (
|
|||||||
"github.com/mayswind/ezbookkeeping/pkg/auth/oauth2/provider"
|
"github.com/mayswind/ezbookkeeping/pkg/auth/oauth2/provider"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/core"
|
"github.com/mayswind/ezbookkeeping/pkg/core"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/errs"
|
"github.com/mayswind/ezbookkeeping/pkg/errs"
|
||||||
|
"github.com/mayswind/ezbookkeeping/pkg/httpclient"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/log"
|
"github.com/mayswind/ezbookkeeping/pkg/log"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/settings"
|
"github.com/mayswind/ezbookkeeping/pkg/settings"
|
||||||
)
|
)
|
||||||
@@ -18,6 +19,7 @@ import (
|
|||||||
// OIDCClaims represents OIDC claims
|
// OIDCClaims represents OIDC claims
|
||||||
type OIDCClaims struct {
|
type OIDCClaims struct {
|
||||||
PreferredUserName string `json:"preferred_username"`
|
PreferredUserName string `json:"preferred_username"`
|
||||||
|
UserName string `json:"username"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Email string `json:"email"`
|
Email string `json:"email"`
|
||||||
}
|
}
|
||||||
@@ -92,7 +94,9 @@ func (p *OIDCProvider) GetUserInfo(c core.Context, oauth2Token *oauth2.Token) (*
|
|||||||
nickName := claims.Name
|
nickName := claims.Name
|
||||||
|
|
||||||
if userName == "" || email == "" || nickName == "" {
|
if userName == "" || email == "" || nickName == "" {
|
||||||
userInfo, err := p.oidcProvider.UserInfo(c, oauth2.StaticTokenSource(oauth2Token))
|
userInfo, err := p.oidcProvider.UserInfo(httpclient.CustomHttpResponseLog(c, func(data []byte) {
|
||||||
|
log.Debugf(c, "[oidc_provider.GetUserInfo] response is %s", data)
|
||||||
|
}), oauth2.StaticTokenSource(oauth2Token))
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf(c, "[oidc_provider.GetUserInfo] failed to get user info, because %s", err.Error())
|
log.Errorf(c, "[oidc_provider.GetUserInfo] failed to get user info, because %s", err.Error())
|
||||||
@@ -110,6 +114,10 @@ func (p *OIDCProvider) GetUserInfo(c core.Context, oauth2Token *oauth2.Token) (*
|
|||||||
userName = claims.PreferredUserName
|
userName = claims.PreferredUserName
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if userName == "" {
|
||||||
|
userName = claims.UserName
|
||||||
|
}
|
||||||
|
|
||||||
if email == "" {
|
if email == "" {
|
||||||
email = claims.Email
|
email = claims.Email
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/converters"
|
"github.com/mayswind/ezbookkeeping/pkg/converters"
|
||||||
|
"github.com/mayswind/ezbookkeeping/pkg/converters/converter"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/core"
|
"github.com/mayswind/ezbookkeeping/pkg/core"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/errs"
|
"github.com/mayswind/ezbookkeeping/pkg/errs"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/log"
|
"github.com/mayswind/ezbookkeeping/pkg/log"
|
||||||
@@ -149,7 +150,7 @@ func (l *UserDataCli) ModifyUserPassword(c *core.CliContext, username string, pa
|
|||||||
Password: password,
|
Password: password,
|
||||||
}
|
}
|
||||||
|
|
||||||
_, _, err = l.users.UpdateUser(c, userNew, false)
|
err = l.users.UpdateUserPassword(c, userNew)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.CliErrorf(c, "[user_data.ModifyUserPassword] failed to update user \"%s\" password, because %s", user.Username, err.Error())
|
log.CliErrorf(c, "[user_data.ModifyUserPassword] failed to update user \"%s\" password, because %s", user.Username, err.Error())
|
||||||
@@ -818,7 +819,7 @@ func (l *UserDataCli) ImportTransaction(c *core.CliContext, username string, fil
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
parsedTransactions, newAccounts, newSubExpenseCategories, newSubIncomeCategories, newSubTransferCategories, newTags, err := dataImporter.ParseImportedData(c, user, data, utils.GetTimezoneOffsetMinutes(time.Local), accountMap, expenseCategoryMap, incomeCategoryMap, transferCategoryMap, tagMap)
|
parsedTransactions, newAccounts, newSubExpenseCategories, newSubIncomeCategories, newSubTransferCategories, newTags, err := dataImporter.ParseImportedData(c, user, data, time.Local, converter.DefaultImporterOptions, accountMap, expenseCategoryMap, incomeCategoryMap, transferCategoryMap, tagMap)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.CliErrorf(c, "[user_data.ImportTransaction] failed to parse imported data for \"%s\", because %s", username, err.Error())
|
log.CliErrorf(c, "[user_data.ImportTransaction] failed to parse imported data for \"%s\", because %s", username, err.Error())
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package alipay
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"time"
|
||||||
|
|
||||||
"golang.org/x/text/encoding/simplifiedchinese"
|
"golang.org/x/text/encoding/simplifiedchinese"
|
||||||
"golang.org/x/text/transform"
|
"golang.org/x/text/transform"
|
||||||
@@ -53,7 +54,7 @@ type alipayTransactionDataCsvFileImporter struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ParseImportedData returns the imported data by parsing the alipay transaction csv data
|
// ParseImportedData returns the imported data by parsing the alipay transaction csv data
|
||||||
func (c *alipayTransactionDataCsvFileImporter) ParseImportedData(ctx core.Context, user *models.User, data []byte, defaultTimezoneOffset int16, accountMap map[string]*models.Account, expenseCategoryMap map[string]map[string]*models.TransactionCategory, incomeCategoryMap map[string]map[string]*models.TransactionCategory, transferCategoryMap map[string]map[string]*models.TransactionCategory, tagMap map[string]*models.TransactionTag) (models.ImportedTransactionSlice, []*models.Account, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionTag, error) {
|
func (c *alipayTransactionDataCsvFileImporter) ParseImportedData(ctx core.Context, user *models.User, data []byte, defaultTimezone *time.Location, additionalOptions converter.TransactionDataImporterOptions, accountMap map[string]*models.Account, expenseCategoryMap map[string]map[string]*models.TransactionCategory, incomeCategoryMap map[string]map[string]*models.TransactionCategory, transferCategoryMap map[string]map[string]*models.TransactionCategory, tagMap map[string]*models.TransactionTag) (models.ImportedTransactionSlice, []*models.Account, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionTag, error) {
|
||||||
enc := simplifiedchinese.GB18030
|
enc := simplifiedchinese.GB18030
|
||||||
reader := transform.NewReader(bytes.NewReader(data), enc.NewDecoder())
|
reader := transform.NewReader(bytes.NewReader(data), enc.NewDecoder())
|
||||||
|
|
||||||
@@ -83,5 +84,5 @@ func (c *alipayTransactionDataCsvFileImporter) ParseImportedData(ctx core.Contex
|
|||||||
transactionDataTable := datatable.CreateNewTransactionDataTableFromCommonDataTable(commonDataTable, alipayTransactionSupportedColumns, transactionRowParser)
|
transactionDataTable := datatable.CreateNewTransactionDataTableFromCommonDataTable(commonDataTable, alipayTransactionSupportedColumns, transactionRowParser)
|
||||||
dataTableImporter := converter.CreateNewSimpleImporterWithTypeNameMapping(alipayTransactionTypeNameMapping)
|
dataTableImporter := converter.CreateNewSimpleImporterWithTypeNameMapping(alipayTransactionTypeNameMapping)
|
||||||
|
|
||||||
return dataTableImporter.ParseImportedData(ctx, user, transactionDataTable, defaultTimezoneOffset, accountMap, expenseCategoryMap, incomeCategoryMap, transferCategoryMap, tagMap)
|
return dataTableImporter.ParseImportedData(ctx, user, transactionDataTable, defaultTimezone, additionalOptions, accountMap, expenseCategoryMap, incomeCategoryMap, transferCategoryMap, tagMap)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import (
|
|||||||
|
|
||||||
"golang.org/x/text/encoding/simplifiedchinese"
|
"golang.org/x/text/encoding/simplifiedchinese"
|
||||||
|
|
||||||
|
"github.com/mayswind/ezbookkeeping/pkg/converters/converter"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/core"
|
"github.com/mayswind/ezbookkeeping/pkg/core"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/errs"
|
"github.com/mayswind/ezbookkeeping/pkg/errs"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/models"
|
"github.com/mayswind/ezbookkeeping/pkg/models"
|
||||||
@@ -15,7 +16,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func TestAlipayCsvFileImporterParseImportedData_MinimumValidData(t *testing.T) {
|
func TestAlipayCsvFileImporterParseImportedData_MinimumValidData(t *testing.T) {
|
||||||
converter := AlipayWebTransactionDataCsvFileImporter
|
importer := AlipayWebTransactionDataCsvFileImporter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
@@ -35,7 +36,7 @@ func TestAlipayCsvFileImporterParseImportedData_MinimumValidData(t *testing.T) {
|
|||||||
"------------------------------------------------------------------------------------\n")
|
"------------------------------------------------------------------------------------\n")
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
allNewTransactions, allNewAccounts, allNewSubExpenseCategories, allNewSubIncomeCategories, allNewSubTransferCategories, allNewTags, err := converter.ParseImportedData(context, user, []byte(data), 0, nil, nil, nil, nil, nil)
|
allNewTransactions, allNewAccounts, allNewSubExpenseCategories, allNewSubIncomeCategories, allNewSubTransferCategories, allNewTags, err := importer.ParseImportedData(context, user, []byte(data), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
assert.Equal(t, 4, len(allNewTransactions))
|
assert.Equal(t, 4, len(allNewTransactions))
|
||||||
@@ -94,7 +95,7 @@ func TestAlipayCsvFileImporterParseImportedData_MinimumValidData(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestAlipayCsvFileImporterParseImportedData_ParseRefundTransaction(t *testing.T) {
|
func TestAlipayCsvFileImporterParseImportedData_ParseRefundTransaction(t *testing.T) {
|
||||||
converter := AlipayWebTransactionDataCsvFileImporter
|
importer := AlipayWebTransactionDataCsvFileImporter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
@@ -112,7 +113,7 @@ func TestAlipayCsvFileImporterParseImportedData_ParseRefundTransaction(t *testin
|
|||||||
"------------------------------------------------------------------------------------\n")
|
"------------------------------------------------------------------------------------\n")
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
allNewTransactions, _, _, _, _, _, err := converter.ParseImportedData(context, user, []byte(data1), 0, nil, nil, nil, nil, nil)
|
allNewTransactions, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(data1), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
assert.Equal(t, int64(1234567890), allNewTransactions[0].Uid)
|
assert.Equal(t, int64(1234567890), allNewTransactions[0].Uid)
|
||||||
@@ -132,7 +133,7 @@ func TestAlipayCsvFileImporterParseImportedData_ParseRefundTransaction(t *testin
|
|||||||
"------------------------------------------------------------------------------------\n")
|
"------------------------------------------------------------------------------------\n")
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
allNewTransactions, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte(data2), 0, nil, nil, nil, nil, nil)
|
allNewTransactions, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(data2), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
assert.Equal(t, int64(1234567890), allNewTransactions[0].Uid)
|
assert.Equal(t, int64(1234567890), allNewTransactions[0].Uid)
|
||||||
@@ -144,7 +145,7 @@ func TestAlipayCsvFileImporterParseImportedData_ParseRefundTransaction(t *testin
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestAlipayCsvFileImporterParseImportedData_ParseInvestmentRefundTransaction(t *testing.T) {
|
func TestAlipayCsvFileImporterParseImportedData_ParseInvestmentRefundTransaction(t *testing.T) {
|
||||||
converter := AlipayAppTransactionDataCsvFileImporter
|
importer := AlipayAppTransactionDataCsvFileImporter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
@@ -163,7 +164,7 @@ func TestAlipayCsvFileImporterParseImportedData_ParseInvestmentRefundTransaction
|
|||||||
"2024-09-01 01:00:00,Test Account2,xxx-买入,不计收支,0.01,Test Account,退款成功,\n" +
|
"2024-09-01 01:00:00,Test Account2,xxx-买入,不计收支,0.01,Test Account,退款成功,\n" +
|
||||||
"2024-09-01 02:00:00,Test Account2,xxx-买入退款,不计收支,0.01,Test Account,退款成功,\n")
|
"2024-09-01 02:00:00,Test Account2,xxx-买入退款,不计收支,0.01,Test Account,退款成功,\n")
|
||||||
|
|
||||||
allNewTransactions, _, _, _, _, _, err := converter.ParseImportedData(context, user, []byte(data1), 0, nil, nil, nil, nil, nil)
|
allNewTransactions, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(data1), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
assert.Equal(t, 2, len(allNewTransactions))
|
assert.Equal(t, 2, len(allNewTransactions))
|
||||||
@@ -184,7 +185,7 @@ func TestAlipayCsvFileImporterParseImportedData_ParseInvestmentRefundTransaction
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestAlipayCsvFileImporterParseImportedData_ParseInvalidTime(t *testing.T) {
|
func TestAlipayCsvFileImporterParseImportedData_ParseInvalidTime(t *testing.T) {
|
||||||
converter := AlipayWebTransactionDataCsvFileImporter
|
importer := AlipayWebTransactionDataCsvFileImporter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
@@ -201,7 +202,7 @@ func TestAlipayCsvFileImporterParseImportedData_ParseInvalidTime(t *testing.T) {
|
|||||||
"------------------------------------------------------------------------------------\n")
|
"------------------------------------------------------------------------------------\n")
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
_, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte(data1), 0, nil, nil, nil, nil, nil)
|
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(data1), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrTransactionTimeInvalid.Message)
|
assert.EqualError(t, err, errs.ErrTransactionTimeInvalid.Message)
|
||||||
|
|
||||||
data2, err := simplifiedchinese.GB18030.NewEncoder().String("支付宝交易记录明细查询\n" +
|
data2, err := simplifiedchinese.GB18030.NewEncoder().String("支付宝交易记录明细查询\n" +
|
||||||
@@ -213,12 +214,12 @@ func TestAlipayCsvFileImporterParseImportedData_ParseInvalidTime(t *testing.T) {
|
|||||||
"------------------------------------------------------------------------------------\n")
|
"------------------------------------------------------------------------------------\n")
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
_, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte(data2), 0, nil, nil, nil, nil, nil)
|
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(data2), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrTransactionTimeInvalid.Message)
|
assert.EqualError(t, err, errs.ErrTransactionTimeInvalid.Message)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAlipayCsvFileImporterParseImportedData_ParseInvalidType(t *testing.T) {
|
func TestAlipayCsvFileImporterParseImportedData_ParseInvalidType(t *testing.T) {
|
||||||
converter := AlipayWebTransactionDataCsvFileImporter
|
importer := AlipayWebTransactionDataCsvFileImporter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
@@ -235,12 +236,12 @@ func TestAlipayCsvFileImporterParseImportedData_ParseInvalidType(t *testing.T) {
|
|||||||
"------------------------------------------------------------------------------------\n")
|
"------------------------------------------------------------------------------------\n")
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
_, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte(data), 0, nil, nil, nil, nil, nil)
|
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(data), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrNotFoundTransactionDataInFile.Message)
|
assert.EqualError(t, err, errs.ErrNotFoundTransactionDataInFile.Message)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAlipayCsvFileImporterParseImportedData_ParseAccountName(t *testing.T) {
|
func TestAlipayCsvFileImporterParseImportedData_ParseAccountName(t *testing.T) {
|
||||||
converter := AlipayWebTransactionDataCsvFileImporter
|
importer := AlipayWebTransactionDataCsvFileImporter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
@@ -258,7 +259,7 @@ func TestAlipayCsvFileImporterParseImportedData_ParseAccountName(t *testing.T) {
|
|||||||
"------------------------------------------------------------------------------------\n")
|
"------------------------------------------------------------------------------------\n")
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
allNewTransactions, _, _, _, _, _, err := converter.ParseImportedData(context, user, []byte(data1), 0, nil, nil, nil, nil, nil)
|
allNewTransactions, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(data1), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
assert.Equal(t, 1, len(allNewTransactions))
|
assert.Equal(t, 1, len(allNewTransactions))
|
||||||
@@ -274,7 +275,7 @@ func TestAlipayCsvFileImporterParseImportedData_ParseAccountName(t *testing.T) {
|
|||||||
"------------------------------------------------------------------------------------\n")
|
"------------------------------------------------------------------------------------\n")
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
allNewTransactions, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte(data2), 0, nil, nil, nil, nil, nil)
|
allNewTransactions, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(data2), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
assert.Equal(t, 1, len(allNewTransactions))
|
assert.Equal(t, 1, len(allNewTransactions))
|
||||||
@@ -290,7 +291,7 @@ func TestAlipayCsvFileImporterParseImportedData_ParseAccountName(t *testing.T) {
|
|||||||
"------------------------------------------------------------------------------------\n")
|
"------------------------------------------------------------------------------------\n")
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
allNewTransactions, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte(data3), 0, nil, nil, nil, nil, nil)
|
allNewTransactions, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(data3), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
assert.Equal(t, 1, len(allNewTransactions))
|
assert.Equal(t, 1, len(allNewTransactions))
|
||||||
@@ -307,7 +308,7 @@ func TestAlipayCsvFileImporterParseImportedData_ParseAccountName(t *testing.T) {
|
|||||||
"------------------------------------------------------------------------------------\n")
|
"------------------------------------------------------------------------------------\n")
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
allNewTransactions, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte(data4), 0, nil, nil, nil, nil, nil)
|
allNewTransactions, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(data4), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
assert.Equal(t, 1, len(allNewTransactions))
|
assert.Equal(t, 1, len(allNewTransactions))
|
||||||
@@ -324,7 +325,7 @@ func TestAlipayCsvFileImporterParseImportedData_ParseAccountName(t *testing.T) {
|
|||||||
"------------------------------------------------------------------------------------\n")
|
"------------------------------------------------------------------------------------\n")
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
allNewTransactions, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte(data5), 0, nil, nil, nil, nil, nil)
|
allNewTransactions, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(data5), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
assert.Equal(t, 1, len(allNewTransactions))
|
assert.Equal(t, 1, len(allNewTransactions))
|
||||||
@@ -341,7 +342,7 @@ func TestAlipayCsvFileImporterParseImportedData_ParseAccountName(t *testing.T) {
|
|||||||
"------------------------------------------------------------------------------------\n")
|
"------------------------------------------------------------------------------------\n")
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
allNewTransactions, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte(data6), 0, nil, nil, nil, nil, nil)
|
allNewTransactions, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(data6), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
assert.Equal(t, 1, len(allNewTransactions))
|
assert.Equal(t, 1, len(allNewTransactions))
|
||||||
@@ -358,7 +359,7 @@ func TestAlipayCsvFileImporterParseImportedData_ParseAccountName(t *testing.T) {
|
|||||||
"------------------------------------------------------------------------------------\n")
|
"------------------------------------------------------------------------------------\n")
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
allNewTransactions, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte(data7), 0, nil, nil, nil, nil, nil)
|
allNewTransactions, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(data7), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
assert.Equal(t, 1, len(allNewTransactions))
|
assert.Equal(t, 1, len(allNewTransactions))
|
||||||
@@ -367,7 +368,7 @@ func TestAlipayCsvFileImporterParseImportedData_ParseAccountName(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestAlipayCsvFileImporterParseImportedData_ParseCategory(t *testing.T) {
|
func TestAlipayCsvFileImporterParseImportedData_ParseCategory(t *testing.T) {
|
||||||
converter := AlipayAppTransactionDataCsvFileImporter
|
importer := AlipayAppTransactionDataCsvFileImporter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
@@ -388,7 +389,7 @@ func TestAlipayCsvFileImporterParseImportedData_ParseCategory(t *testing.T) {
|
|||||||
"2024-09-01 23:59:59,Test Category3,充值-普通充值,不计收支,0.05,交易成功,\n")
|
"2024-09-01 23:59:59,Test Category3,充值-普通充值,不计收支,0.05,交易成功,\n")
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
allNewTransactions, _, allNewSubExpenseCategories, allNewSubIncomeCategories, allNewSubTransferCategories, _, err := converter.ParseImportedData(context, user, []byte(data1), 0, nil, nil, nil, nil, nil)
|
allNewTransactions, _, allNewSubExpenseCategories, allNewSubIncomeCategories, allNewSubTransferCategories, _, err := importer.ParseImportedData(context, user, []byte(data1), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
assert.Equal(t, 3, len(allNewTransactions))
|
assert.Equal(t, 3, len(allNewTransactions))
|
||||||
@@ -407,7 +408,7 @@ func TestAlipayCsvFileImporterParseImportedData_ParseCategory(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestAlipayCsvFileImporterParseImportedData_ParseRelatedAccount(t *testing.T) {
|
func TestAlipayCsvFileImporterParseImportedData_ParseRelatedAccount(t *testing.T) {
|
||||||
converter := AlipayAppTransactionDataCsvFileImporter
|
importer := AlipayAppTransactionDataCsvFileImporter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
@@ -434,7 +435,7 @@ func TestAlipayCsvFileImporterParseImportedData_ParseRelatedAccount(t *testing.T
|
|||||||
"2024-09-01 08:00:00,Test Account4,信用卡还款,不计收支,0.01,Test Account,还款成功,repayment,\n")
|
"2024-09-01 08:00:00,Test Account4,信用卡还款,不计收支,0.01,Test Account,还款成功,repayment,\n")
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
allNewTransactions, allNewAccounts, _, _, _, _, err := converter.ParseImportedData(context, user, []byte(data1), 0, nil, nil, nil, nil, nil)
|
allNewTransactions, allNewAccounts, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(data1), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
assert.Equal(t, 9, len(allNewTransactions))
|
assert.Equal(t, 9, len(allNewTransactions))
|
||||||
@@ -529,7 +530,7 @@ func TestAlipayCsvFileImporterParseImportedData_ParseRelatedAccount(t *testing.T
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestAlipayCsvFileImporterParseImportedData_ParseDescription(t *testing.T) {
|
func TestAlipayCsvFileImporterParseImportedData_ParseDescription(t *testing.T) {
|
||||||
converter := AlipayWebTransactionDataCsvFileImporter
|
importer := AlipayWebTransactionDataCsvFileImporter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
@@ -546,7 +547,7 @@ func TestAlipayCsvFileImporterParseImportedData_ParseDescription(t *testing.T) {
|
|||||||
"------------------------------------------------------------------------------------\n")
|
"------------------------------------------------------------------------------------\n")
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
allNewTransactions, _, _, _, _, _, err := converter.ParseImportedData(context, user, []byte(data1), 0, nil, nil, nil, nil, nil)
|
allNewTransactions, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(data1), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
assert.Equal(t, 1, len(allNewTransactions))
|
assert.Equal(t, 1, len(allNewTransactions))
|
||||||
@@ -561,7 +562,7 @@ func TestAlipayCsvFileImporterParseImportedData_ParseDescription(t *testing.T) {
|
|||||||
"------------------------------------------------------------------------------------\n")
|
"------------------------------------------------------------------------------------\n")
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
allNewTransactions, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte(data2), 0, nil, nil, nil, nil, nil)
|
allNewTransactions, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(data2), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
assert.Equal(t, 1, len(allNewTransactions))
|
assert.Equal(t, 1, len(allNewTransactions))
|
||||||
@@ -569,7 +570,7 @@ func TestAlipayCsvFileImporterParseImportedData_ParseDescription(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestAlipayCsvFileImporterParseImportedData_SkipClosedIncomeOrTransferTransaction(t *testing.T) {
|
func TestAlipayCsvFileImporterParseImportedData_SkipClosedIncomeOrTransferTransaction(t *testing.T) {
|
||||||
converter := AlipayWebTransactionDataCsvFileImporter
|
importer := AlipayWebTransactionDataCsvFileImporter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
@@ -586,12 +587,12 @@ func TestAlipayCsvFileImporterParseImportedData_SkipClosedIncomeOrTransferTransa
|
|||||||
"2024-09-01 23:59:59 ,充值-普通充值 ,0.05 ,不计收支 ,交易关闭 ,\n" +
|
"2024-09-01 23:59:59 ,充值-普通充值 ,0.05 ,不计收支 ,交易关闭 ,\n" +
|
||||||
"------------------------------------------------------------------------------------\n")
|
"------------------------------------------------------------------------------------\n")
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
_, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte(data), 0, nil, nil, nil, nil, nil)
|
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(data), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrNotFoundTransactionDataInFile.Message)
|
assert.EqualError(t, err, errs.ErrNotFoundTransactionDataInFile.Message)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAlipayCsvFileImporterParseImportedData_SkipUnknownProductTransferTransaction(t *testing.T) {
|
func TestAlipayCsvFileImporterParseImportedData_SkipUnknownProductTransferTransaction(t *testing.T) {
|
||||||
converter := AlipayWebTransactionDataCsvFileImporter
|
importer := AlipayWebTransactionDataCsvFileImporter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
@@ -607,12 +608,12 @@ func TestAlipayCsvFileImporterParseImportedData_SkipUnknownProductTransferTransa
|
|||||||
"2024-09-01 23:59:59 ,xxxx ,0.05 ,不计收支 ,交易成功 ,\n" +
|
"2024-09-01 23:59:59 ,xxxx ,0.05 ,不计收支 ,交易成功 ,\n" +
|
||||||
"------------------------------------------------------------------------------------\n")
|
"------------------------------------------------------------------------------------\n")
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
_, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte(data), 0, nil, nil, nil, nil, nil)
|
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(data), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrNotFoundTransactionDataInFile.Message)
|
assert.EqualError(t, err, errs.ErrNotFoundTransactionDataInFile.Message)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAlipayCsvFileImporterParseImportedData_SkipUnknownStatusTransaction(t *testing.T) {
|
func TestAlipayCsvFileImporterParseImportedData_SkipUnknownStatusTransaction(t *testing.T) {
|
||||||
converter := AlipayWebTransactionDataCsvFileImporter
|
importer := AlipayWebTransactionDataCsvFileImporter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
@@ -628,12 +629,12 @@ func TestAlipayCsvFileImporterParseImportedData_SkipUnknownStatusTransaction(t *
|
|||||||
"2024-09-01 01:23:45 ,xxxx ,0.12 ,收入 ,xxxx ,\n" +
|
"2024-09-01 01:23:45 ,xxxx ,0.12 ,收入 ,xxxx ,\n" +
|
||||||
"------------------------------------------------------------------------------------\n")
|
"------------------------------------------------------------------------------------\n")
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
_, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte(data), 0, nil, nil, nil, nil, nil)
|
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(data), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrNotFoundTransactionDataInFile.Message)
|
assert.EqualError(t, err, errs.ErrNotFoundTransactionDataInFile.Message)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAlipayCsvFileImporterParseImportedData_MissingFileHeader(t *testing.T) {
|
func TestAlipayCsvFileImporterParseImportedData_MissingFileHeader(t *testing.T) {
|
||||||
converter := AlipayWebTransactionDataCsvFileImporter
|
importer := AlipayWebTransactionDataCsvFileImporter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
@@ -647,15 +648,15 @@ func TestAlipayCsvFileImporterParseImportedData_MissingFileHeader(t *testing.T)
|
|||||||
"------------------------------------------------------------------------------------\n")
|
"------------------------------------------------------------------------------------\n")
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
_, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte(data), 0, nil, nil, nil, nil, nil)
|
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(data), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrInvalidFileHeader.Message)
|
assert.EqualError(t, err, errs.ErrInvalidFileHeader.Message)
|
||||||
|
|
||||||
_, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte(""), 0, nil, nil, nil, nil, nil)
|
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(""), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrInvalidFileHeader.Message)
|
assert.EqualError(t, err, errs.ErrInvalidFileHeader.Message)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAlipayCsvFileImporterParseImportedData_MissingRequiredColumn(t *testing.T) {
|
func TestAlipayCsvFileImporterParseImportedData_MissingRequiredColumn(t *testing.T) {
|
||||||
converter := AlipayWebTransactionDataCsvFileImporter
|
importer := AlipayWebTransactionDataCsvFileImporter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
@@ -671,7 +672,7 @@ func TestAlipayCsvFileImporterParseImportedData_MissingRequiredColumn(t *testing
|
|||||||
"金额(元),收/支 ,交易状态 ,\n" +
|
"金额(元),收/支 ,交易状态 ,\n" +
|
||||||
"0.12 ,收入 ,交易成功 ,\n" +
|
"0.12 ,收入 ,交易成功 ,\n" +
|
||||||
"------------------------------------------------------------------------------------\n")
|
"------------------------------------------------------------------------------------\n")
|
||||||
_, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte(data1), 0, nil, nil, nil, nil, nil)
|
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(data1), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrMissingRequiredFieldInHeaderRow.Message)
|
assert.EqualError(t, err, errs.ErrMissingRequiredFieldInHeaderRow.Message)
|
||||||
|
|
||||||
// Missing Amount Column
|
// Missing Amount Column
|
||||||
@@ -682,7 +683,7 @@ func TestAlipayCsvFileImporterParseImportedData_MissingRequiredColumn(t *testing
|
|||||||
"交易创建时间 ,收/支 ,交易状态 ,\n" +
|
"交易创建时间 ,收/支 ,交易状态 ,\n" +
|
||||||
"2024-09-01 12:34:56 ,收入 ,交易成功 ,\n" +
|
"2024-09-01 12:34:56 ,收入 ,交易成功 ,\n" +
|
||||||
"------------------------------------------------------------------------------------\n")
|
"------------------------------------------------------------------------------------\n")
|
||||||
_, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte(data2), 0, nil, nil, nil, nil, nil)
|
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(data2), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrMissingRequiredFieldInHeaderRow.Message)
|
assert.EqualError(t, err, errs.ErrMissingRequiredFieldInHeaderRow.Message)
|
||||||
|
|
||||||
// Missing Status Column
|
// Missing Status Column
|
||||||
@@ -693,7 +694,7 @@ func TestAlipayCsvFileImporterParseImportedData_MissingRequiredColumn(t *testing
|
|||||||
"交易创建时间 ,金额(元),收/支 ,\n" +
|
"交易创建时间 ,金额(元),收/支 ,\n" +
|
||||||
"2024-09-01 12:34:56 ,0.12 ,收入 ,\n" +
|
"2024-09-01 12:34:56 ,0.12 ,收入 ,\n" +
|
||||||
"------------------------------------------------------------------------------------\n")
|
"------------------------------------------------------------------------------------\n")
|
||||||
_, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte(data3), 0, nil, nil, nil, nil, nil)
|
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(data3), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrMissingRequiredFieldInHeaderRow.Message)
|
assert.EqualError(t, err, errs.ErrMissingRequiredFieldInHeaderRow.Message)
|
||||||
|
|
||||||
// Missing Type Column
|
// Missing Type Column
|
||||||
@@ -704,12 +705,12 @@ func TestAlipayCsvFileImporterParseImportedData_MissingRequiredColumn(t *testing
|
|||||||
"交易创建时间 ,金额(元),交易状态 ,\n" +
|
"交易创建时间 ,金额(元),交易状态 ,\n" +
|
||||||
"2024-09-01 12:34:56 ,0.12 ,交易成功 ,\n" +
|
"2024-09-01 12:34:56 ,0.12 ,交易成功 ,\n" +
|
||||||
"------------------------------------------------------------------------------------\n")
|
"------------------------------------------------------------------------------------\n")
|
||||||
_, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte(data4), 0, nil, nil, nil, nil, nil)
|
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(data4), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrMissingRequiredFieldInHeaderRow.Message)
|
assert.EqualError(t, err, errs.ErrMissingRequiredFieldInHeaderRow.Message)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAlipayCsvFileImporterParseImportedData_NoTransactionData(t *testing.T) {
|
func TestAlipayCsvFileImporterParseImportedData_NoTransactionData(t *testing.T) {
|
||||||
converter := AlipayWebTransactionDataCsvFileImporter
|
importer := AlipayWebTransactionDataCsvFileImporter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
@@ -723,6 +724,6 @@ func TestAlipayCsvFileImporterParseImportedData_NoTransactionData(t *testing.T)
|
|||||||
"---------------------------------交易记录明细列表------------------------------------\n" +
|
"---------------------------------交易记录明细列表------------------------------------\n" +
|
||||||
"交易创建时间 ,金额(元),收/支 ,交易状态 ,\n" +
|
"交易创建时间 ,金额(元),收/支 ,交易状态 ,\n" +
|
||||||
"------------------------------------------------------------------------------------\n")
|
"------------------------------------------------------------------------------------\n")
|
||||||
_, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte(data1), 0, nil, nil, nil, nil, nil)
|
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(data1), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrNotFoundTransactionDataInFile.Message)
|
assert.EqualError(t, err, errs.ErrNotFoundTransactionDataInFile.Message)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import (
|
|||||||
|
|
||||||
const alipayTransactionDataStatusSuccessName = "交易成功"
|
const alipayTransactionDataStatusSuccessName = "交易成功"
|
||||||
const alipayTransactionDataStatusPaymentSuccessName = "支付成功"
|
const alipayTransactionDataStatusPaymentSuccessName = "支付成功"
|
||||||
|
const alipayTransactionDataStatusPendingGoodsReceiptConfirmationName = "等待确认收货"
|
||||||
const alipayTransactionDataStatusRepaymentSuccessName = "还款成功"
|
const alipayTransactionDataStatusRepaymentSuccessName = "还款成功"
|
||||||
const alipayTransactionDataStatusClosedName = "交易关闭"
|
const alipayTransactionDataStatusClosedName = "交易关闭"
|
||||||
const alipayTransactionDataStatusRefundSuccessName = "退款成功"
|
const alipayTransactionDataStatusRefundSuccessName = "退款成功"
|
||||||
@@ -46,6 +47,7 @@ func (p *alipayTransactionDataRowParser) Parse(ctx core.Context, user *models.Us
|
|||||||
|
|
||||||
if dataRow.GetData(p.columns.statusColumnName) != alipayTransactionDataStatusSuccessName &&
|
if dataRow.GetData(p.columns.statusColumnName) != alipayTransactionDataStatusSuccessName &&
|
||||||
dataRow.GetData(p.columns.statusColumnName) != alipayTransactionDataStatusPaymentSuccessName &&
|
dataRow.GetData(p.columns.statusColumnName) != alipayTransactionDataStatusPaymentSuccessName &&
|
||||||
|
dataRow.GetData(p.columns.statusColumnName) != alipayTransactionDataStatusPendingGoodsReceiptConfirmationName &&
|
||||||
dataRow.GetData(p.columns.statusColumnName) != alipayTransactionDataStatusRepaymentSuccessName &&
|
dataRow.GetData(p.columns.statusColumnName) != alipayTransactionDataStatusRepaymentSuccessName &&
|
||||||
dataRow.GetData(p.columns.statusColumnName) != alipayTransactionDataStatusClosedName &&
|
dataRow.GetData(p.columns.statusColumnName) != alipayTransactionDataStatusClosedName &&
|
||||||
dataRow.GetData(p.columns.statusColumnName) != alipayTransactionDataStatusRefundSuccessName &&
|
dataRow.GetData(p.columns.statusColumnName) != alipayTransactionDataStatusRefundSuccessName &&
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
package beancount
|
package beancount
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/converters/converter"
|
"github.com/mayswind/ezbookkeeping/pkg/converters/converter"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/core"
|
"github.com/mayswind/ezbookkeeping/pkg/core"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/models"
|
"github.com/mayswind/ezbookkeeping/pkg/models"
|
||||||
@@ -24,7 +26,7 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// ParseImportedData returns the imported data by parsing the Beancount transaction data
|
// ParseImportedData returns the imported data by parsing the Beancount transaction data
|
||||||
func (c *beancountTransactionDataImporter) ParseImportedData(ctx core.Context, user *models.User, data []byte, defaultTimezoneOffset int16, accountMap map[string]*models.Account, expenseCategoryMap map[string]map[string]*models.TransactionCategory, incomeCategoryMap map[string]map[string]*models.TransactionCategory, transferCategoryMap map[string]map[string]*models.TransactionCategory, tagMap map[string]*models.TransactionTag) (models.ImportedTransactionSlice, []*models.Account, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionTag, error) {
|
func (c *beancountTransactionDataImporter) ParseImportedData(ctx core.Context, user *models.User, data []byte, defaultTimezone *time.Location, additionalOptions converter.TransactionDataImporterOptions, accountMap map[string]*models.Account, expenseCategoryMap map[string]map[string]*models.TransactionCategory, incomeCategoryMap map[string]map[string]*models.TransactionCategory, transferCategoryMap map[string]map[string]*models.TransactionCategory, tagMap map[string]*models.TransactionTag) (models.ImportedTransactionSlice, []*models.Account, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionTag, error) {
|
||||||
beancountDataReader, err := createNewBeancountDataReader(ctx, data)
|
beancountDataReader, err := createNewBeancountDataReader(ctx, data)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -45,5 +47,5 @@ func (c *beancountTransactionDataImporter) ParseImportedData(ctx core.Context, u
|
|||||||
|
|
||||||
dataTableImporter := converter.CreateNewImporterWithTypeNameMapping(beancountTransactionTypeNameMapping, "", "", BEANCOUNT_TRANSACTION_TAG_SEPARATOR)
|
dataTableImporter := converter.CreateNewImporterWithTypeNameMapping(beancountTransactionTypeNameMapping, "", "", BEANCOUNT_TRANSACTION_TAG_SEPARATOR)
|
||||||
|
|
||||||
return dataTableImporter.ParseImportedData(ctx, user, transactionDataTable, defaultTimezoneOffset, accountMap, expenseCategoryMap, incomeCategoryMap, transferCategoryMap, tagMap)
|
return dataTableImporter.ParseImportedData(ctx, user, transactionDataTable, defaultTimezone, additionalOptions, accountMap, expenseCategoryMap, incomeCategoryMap, transferCategoryMap, tagMap)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,9 +2,11 @@ package beancount
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
|
"github.com/mayswind/ezbookkeeping/pkg/converters/converter"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/core"
|
"github.com/mayswind/ezbookkeeping/pkg/core"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/errs"
|
"github.com/mayswind/ezbookkeeping/pkg/errs"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/models"
|
"github.com/mayswind/ezbookkeeping/pkg/models"
|
||||||
@@ -12,7 +14,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func TestBeancountTransactionDataFileParseImportedData_MinimumValidData(t *testing.T) {
|
func TestBeancountTransactionDataFileParseImportedData_MinimumValidData(t *testing.T) {
|
||||||
converter := BeancountTransactionDataImporter
|
importer := BeancountTransactionDataImporter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
@@ -20,7 +22,7 @@ func TestBeancountTransactionDataFileParseImportedData_MinimumValidData(t *testi
|
|||||||
DefaultCurrency: "CNY",
|
DefaultCurrency: "CNY",
|
||||||
}
|
}
|
||||||
|
|
||||||
allNewTransactions, allNewAccounts, allNewSubExpenseCategories, allNewSubIncomeCategories, allNewSubTransferCategories, allNewTags, err := converter.ParseImportedData(context, user, []byte(
|
allNewTransactions, allNewAccounts, allNewSubExpenseCategories, allNewSubIncomeCategories, allNewSubTransferCategories, allNewTags, err := importer.ParseImportedData(context, user, []byte(
|
||||||
"2024-09-01 *\n"+
|
"2024-09-01 *\n"+
|
||||||
" Equity:Opening-Balances -123.45 CNY\n"+
|
" Equity:Opening-Balances -123.45 CNY\n"+
|
||||||
" Assets:TestAccount 123.45 CNY\n"+
|
" Assets:TestAccount 123.45 CNY\n"+
|
||||||
@@ -32,7 +34,7 @@ func TestBeancountTransactionDataFileParseImportedData_MinimumValidData(t *testi
|
|||||||
" Expenses:TestCategory2 1.00 CNY\n"+
|
" Expenses:TestCategory2 1.00 CNY\n"+
|
||||||
"2024-09-04 *\n"+
|
"2024-09-04 *\n"+
|
||||||
" Assets:TestAccount -0.05 CNY\n"+
|
" Assets:TestAccount -0.05 CNY\n"+
|
||||||
" Assets:TestAccount2 0.05 CNY\n"), 0, nil, nil, nil, nil, nil)
|
" Assets:TestAccount2 0.05 CNY\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
|
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
@@ -91,7 +93,7 @@ func TestBeancountTransactionDataFileParseImportedData_MinimumValidData(t *testi
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestBeancountTransactionDataFileParseImportedData_MinimumValidData2(t *testing.T) {
|
func TestBeancountTransactionDataFileParseImportedData_MinimumValidData2(t *testing.T) {
|
||||||
converter := BeancountTransactionDataImporter
|
importer := BeancountTransactionDataImporter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
@@ -99,7 +101,7 @@ func TestBeancountTransactionDataFileParseImportedData_MinimumValidData2(t *test
|
|||||||
DefaultCurrency: "CNY",
|
DefaultCurrency: "CNY",
|
||||||
}
|
}
|
||||||
|
|
||||||
allNewTransactions, allNewAccounts, allNewSubExpenseCategories, allNewSubIncomeCategories, allNewSubTransferCategories, allNewTags, err := converter.ParseImportedData(context, user, []byte(
|
allNewTransactions, allNewAccounts, allNewSubExpenseCategories, allNewSubIncomeCategories, allNewSubTransferCategories, allNewTags, err := importer.ParseImportedData(context, user, []byte(
|
||||||
"2024-09-01 *\n"+
|
"2024-09-01 *\n"+
|
||||||
" Assets:TestAccount 123.45 CNY\n"+
|
" Assets:TestAccount 123.45 CNY\n"+
|
||||||
" Equity:Opening-Balances -123.45 CNY\n"+
|
" Equity:Opening-Balances -123.45 CNY\n"+
|
||||||
@@ -111,7 +113,7 @@ func TestBeancountTransactionDataFileParseImportedData_MinimumValidData2(t *test
|
|||||||
" Assets:TestAccount -1.00 CNY\n"+
|
" Assets:TestAccount -1.00 CNY\n"+
|
||||||
"2024-09-04 *\n"+
|
"2024-09-04 *\n"+
|
||||||
" Assets:TestAccount2 0.05 CNY\n"+
|
" Assets:TestAccount2 0.05 CNY\n"+
|
||||||
" Assets:TestAccount -0.05 CNY\n"), 0, nil, nil, nil, nil, nil)
|
" Assets:TestAccount -0.05 CNY\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
|
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
@@ -170,7 +172,7 @@ func TestBeancountTransactionDataFileParseImportedData_MinimumValidData2(t *test
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestBeancountTransactionDataFileParseImportedData_ParseInvalidTime(t *testing.T) {
|
func TestBeancountTransactionDataFileParseImportedData_ParseInvalidTime(t *testing.T) {
|
||||||
converter := BeancountTransactionDataImporter
|
importer := BeancountTransactionDataImporter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
@@ -178,15 +180,15 @@ func TestBeancountTransactionDataFileParseImportedData_ParseInvalidTime(t *testi
|
|||||||
DefaultCurrency: "CNY",
|
DefaultCurrency: "CNY",
|
||||||
}
|
}
|
||||||
|
|
||||||
_, _, _, _, _, _, err := converter.ParseImportedData(context, user, []byte(
|
_, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(
|
||||||
"2024/09/01 *\n"+
|
"2024/09/01 *\n"+
|
||||||
" Equity:Opening-Balances -123.45 CNY\n"+
|
" Equity:Opening-Balances -123.45 CNY\n"+
|
||||||
" Assets:TestAccount 123.45 CNY\n"), 0, nil, nil, nil, nil, nil)
|
" Assets:TestAccount 123.45 CNY\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrNotFoundTransactionDataInFile.Message)
|
assert.EqualError(t, err, errs.ErrNotFoundTransactionDataInFile.Message)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestBeancountTransactionDataFileParseImportedData_ParseValidCurrency(t *testing.T) {
|
func TestBeancountTransactionDataFileParseImportedData_ParseValidCurrency(t *testing.T) {
|
||||||
converter := BeancountTransactionDataImporter
|
importer := BeancountTransactionDataImporter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
@@ -194,10 +196,10 @@ func TestBeancountTransactionDataFileParseImportedData_ParseValidCurrency(t *tes
|
|||||||
DefaultCurrency: "CNY",
|
DefaultCurrency: "CNY",
|
||||||
}
|
}
|
||||||
|
|
||||||
allNewTransactions, allNewAccounts, _, _, _, _, err := converter.ParseImportedData(context, user, []byte(
|
allNewTransactions, allNewAccounts, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(
|
||||||
"2024-09-01 * \"Payee Name\" \"Hello\nWorld\"\n"+
|
"2024-09-01 * \"Payee Name\" \"Hello\nWorld\"\n"+
|
||||||
" Assets:TestAccount -0.12 USD\n"+
|
" Assets:TestAccount -0.12 USD\n"+
|
||||||
" Assets:TestAccount2 0.84 CNY\n"), 0, nil, nil, nil, nil, nil)
|
" Assets:TestAccount2 0.84 CNY\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
|
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
@@ -222,7 +224,7 @@ func TestBeancountTransactionDataFileParseImportedData_ParseValidCurrency(t *tes
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestBeancountTransactionDataFileParseImportedData_ParseInvalidAmount(t *testing.T) {
|
func TestBeancountTransactionDataFileParseImportedData_ParseInvalidAmount(t *testing.T) {
|
||||||
converter := BeancountTransactionDataImporter
|
importer := BeancountTransactionDataImporter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
@@ -230,21 +232,21 @@ func TestBeancountTransactionDataFileParseImportedData_ParseInvalidAmount(t *tes
|
|||||||
DefaultCurrency: "CNY",
|
DefaultCurrency: "CNY",
|
||||||
}
|
}
|
||||||
|
|
||||||
_, _, _, _, _, _, err := converter.ParseImportedData(context, user, []byte(
|
_, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(
|
||||||
"2024-09-01 *\n"+
|
"2024-09-01 *\n"+
|
||||||
" Equity:Opening-Balances -abc CNY\n"+
|
" Equity:Opening-Balances -abc CNY\n"+
|
||||||
" Assets:TestAccount abc CNY\n"), 0, nil, nil, nil, nil, nil)
|
" Assets:TestAccount abc CNY\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrAmountInvalid.Message)
|
assert.EqualError(t, err, errs.ErrAmountInvalid.Message)
|
||||||
|
|
||||||
_, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte(
|
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(
|
||||||
"2024-09-01 *\n"+
|
"2024-09-01 *\n"+
|
||||||
" Equity:Opening-Balances -1/0 CNY\n"+
|
" Equity:Opening-Balances -1/0 CNY\n"+
|
||||||
" Assets:TestAccount 1/0 CNY\n"), 0, nil, nil, nil, nil, nil)
|
" Assets:TestAccount 1/0 CNY\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrAmountInvalid.Message)
|
assert.EqualError(t, err, errs.ErrAmountInvalid.Message)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestBeancountTransactionDataFileParseImportedData_ParseDescription(t *testing.T) {
|
func TestBeancountTransactionDataFileParseImportedData_ParseDescription(t *testing.T) {
|
||||||
converter := BeancountTransactionDataImporter
|
importer := BeancountTransactionDataImporter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
@@ -252,13 +254,13 @@ func TestBeancountTransactionDataFileParseImportedData_ParseDescription(t *testi
|
|||||||
DefaultCurrency: "CNY",
|
DefaultCurrency: "CNY",
|
||||||
}
|
}
|
||||||
|
|
||||||
allNewTransactions, _, _, _, _, _, err := converter.ParseImportedData(context, user, []byte(
|
allNewTransactions, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(
|
||||||
"2024-09-01 * \"foo bar\t#test\n\"\n"+
|
"2024-09-01 * \"foo bar\t#test\n\"\n"+
|
||||||
" Equity:Opening-Balances -123.45 CNY\n"+
|
" Equity:Opening-Balances -123.45 CNY\n"+
|
||||||
" Assets:TestAccount 123.45 CNY\n"+
|
" Assets:TestAccount 123.45 CNY\n"+
|
||||||
"2024-09-02 * \"Payee Name\" \"Hello\nWorld\"\n"+
|
"2024-09-02 * \"Payee Name\" \"Hello\nWorld\"\n"+
|
||||||
" Income:TestCategory -0.12 CNY\n"+
|
" Income:TestCategory -0.12 CNY\n"+
|
||||||
" Assets:TestAccount 0.12 CNY\n"), 0, nil, nil, nil, nil, nil)
|
" Assets:TestAccount 0.12 CNY\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
|
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
@@ -269,7 +271,7 @@ func TestBeancountTransactionDataFileParseImportedData_ParseDescription(t *testi
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestBeancountTransactionDataFileParseImportedData_InvalidTransaction(t *testing.T) {
|
func TestBeancountTransactionDataFileParseImportedData_InvalidTransaction(t *testing.T) {
|
||||||
converter := BeancountTransactionDataImporter
|
importer := BeancountTransactionDataImporter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
@@ -277,33 +279,33 @@ func TestBeancountTransactionDataFileParseImportedData_InvalidTransaction(t *tes
|
|||||||
DefaultCurrency: "CNY",
|
DefaultCurrency: "CNY",
|
||||||
}
|
}
|
||||||
|
|
||||||
_, _, _, _, _, _, err := converter.ParseImportedData(context, user, []byte(
|
_, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(
|
||||||
"2024-09-02 * \"Payee Name\" \"Hello\nWorld\"\n"+
|
"2024-09-02 * \"Payee Name\" \"Hello\nWorld\"\n"+
|
||||||
" Assets:TestAccount 0.11 CNY\n"+
|
" Assets:TestAccount 0.11 CNY\n"+
|
||||||
" Assets:TestAccount2 0.11 CNY\n"), 0, nil, nil, nil, nil, nil)
|
" Assets:TestAccount2 0.11 CNY\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrInvalidBeancountFile.Message)
|
assert.EqualError(t, err, errs.ErrInvalidBeancountFile.Message)
|
||||||
|
|
||||||
_, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte(
|
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(
|
||||||
"2024-09-02 * \"Payee Name\" \"Hello\nWorld\"\n"+
|
"2024-09-02 * \"Payee Name\" \"Hello\nWorld\"\n"+
|
||||||
" Expenses:TestCategory -0.11 CNY\n"+
|
" Expenses:TestCategory -0.11 CNY\n"+
|
||||||
" Expenses:TestCategory2 0.11 CNY\n"), 0, nil, nil, nil, nil, nil)
|
" Expenses:TestCategory2 0.11 CNY\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrThereAreNotSupportedTransactionType.Message)
|
assert.EqualError(t, err, errs.ErrThereAreNotSupportedTransactionType.Message)
|
||||||
|
|
||||||
_, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte(
|
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(
|
||||||
"2024-09-02 * \"Payee Name\" \"Hello\nWorld\"\n"+
|
"2024-09-02 * \"Payee Name\" \"Hello\nWorld\"\n"+
|
||||||
" Income:TestCategory -0.11 CNY\n"+
|
" Income:TestCategory -0.11 CNY\n"+
|
||||||
" Income:TestCategory2 0.11 CNY\n"), 0, nil, nil, nil, nil, nil)
|
" Income:TestCategory2 0.11 CNY\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrThereAreNotSupportedTransactionType.Message)
|
assert.EqualError(t, err, errs.ErrThereAreNotSupportedTransactionType.Message)
|
||||||
|
|
||||||
_, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte(
|
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(
|
||||||
"2024-09-02 * \"Payee Name\" \"Hello\nWorld\"\n"+
|
"2024-09-02 * \"Payee Name\" \"Hello\nWorld\"\n"+
|
||||||
" Equity:TestCategory -0.11 CNY\n"+
|
" Equity:TestCategory -0.11 CNY\n"+
|
||||||
" Equity:TestCategory2 0.11 CNY\n"), 0, nil, nil, nil, nil, nil)
|
" Equity:TestCategory2 0.11 CNY\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrThereAreNotSupportedTransactionType.Message)
|
assert.EqualError(t, err, errs.ErrThereAreNotSupportedTransactionType.Message)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestBeancountTransactionDataFileParseImportedData_NotSupportedToParseSplitTransaction(t *testing.T) {
|
func TestBeancountTransactionDataFileParseImportedData_NotSupportedToParseSplitTransaction(t *testing.T) {
|
||||||
converter := BeancountTransactionDataImporter
|
importer := BeancountTransactionDataImporter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
@@ -311,16 +313,16 @@ func TestBeancountTransactionDataFileParseImportedData_NotSupportedToParseSplitT
|
|||||||
DefaultCurrency: "CNY",
|
DefaultCurrency: "CNY",
|
||||||
}
|
}
|
||||||
|
|
||||||
_, _, _, _, _, _, err := converter.ParseImportedData(context, user, []byte(
|
_, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(
|
||||||
"2024-09-02 * \"Payee Name\" \"Hello\nWorld\"\n"+
|
"2024-09-02 * \"Payee Name\" \"Hello\nWorld\"\n"+
|
||||||
" Assets:TestAccount -0.23 CNY\n"+
|
" Assets:TestAccount -0.23 CNY\n"+
|
||||||
" Assets:TestAccount2 0.11 CNY\n"+
|
" Assets:TestAccount2 0.11 CNY\n"+
|
||||||
" Assets:TestAccount3 0.12 CNY\n"), 0, nil, nil, nil, nil, nil)
|
" Assets:TestAccount3 0.12 CNY\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrNotSupportedSplitTransactions.Message)
|
assert.EqualError(t, err, errs.ErrNotSupportedSplitTransactions.Message)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestBeancountTransactionDataFileParseImportedData_MissingTransactionRequiredData(t *testing.T) {
|
func TestBeancountTransactionDataFileParseImportedData_MissingTransactionRequiredData(t *testing.T) {
|
||||||
converter := BeancountTransactionDataImporter
|
importer := BeancountTransactionDataImporter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
@@ -329,30 +331,30 @@ func TestBeancountTransactionDataFileParseImportedData_MissingTransactionRequire
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Missing Transaction Time
|
// Missing Transaction Time
|
||||||
_, _, _, _, _, _, err := converter.ParseImportedData(context, user, []byte(
|
_, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(
|
||||||
"* \"narration\"\n"+
|
"* \"narration\"\n"+
|
||||||
" Equity:Opening-Balances -123.45 CNY\n"+
|
" Equity:Opening-Balances -123.45 CNY\n"+
|
||||||
" Assets:TestAccount 123.45 CNY\n"), 0, nil, nil, nil, nil, nil)
|
" Assets:TestAccount 123.45 CNY\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrNotFoundTransactionDataInFile.Message)
|
assert.EqualError(t, err, errs.ErrNotFoundTransactionDataInFile.Message)
|
||||||
|
|
||||||
// Missing Account Name
|
// Missing Account Name
|
||||||
_, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte(
|
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(
|
||||||
"2024-09-01 * \"narration\"\n"+
|
"2024-09-01 * \"narration\"\n"+
|
||||||
" Equity:Opening-Balances -123.45 CNY\n"+
|
" Equity:Opening-Balances -123.45 CNY\n"+
|
||||||
" 123.45 CNY\n"), 0, nil, nil, nil, nil, nil)
|
" 123.45 CNY\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrInvalidBeancountFile.Message)
|
assert.EqualError(t, err, errs.ErrInvalidBeancountFile.Message)
|
||||||
|
|
||||||
// Missing Amount
|
// Missing Amount
|
||||||
_, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte(
|
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(
|
||||||
"2024-09-01 * \"narration\"\n"+
|
"2024-09-01 * \"narration\"\n"+
|
||||||
" Equity:Opening-Balances\n"+
|
" Equity:Opening-Balances\n"+
|
||||||
" Assets:TestAccount\n"), 0, nil, nil, nil, nil, nil)
|
" Assets:TestAccount\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrInvalidBeancountFile.Message)
|
assert.EqualError(t, err, errs.ErrInvalidBeancountFile.Message)
|
||||||
|
|
||||||
// Missing Commodity
|
// Missing Commodity
|
||||||
_, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte(
|
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(
|
||||||
"2024-09-01 * \"narration\"\n"+
|
"2024-09-01 * \"narration\"\n"+
|
||||||
" Equity:Opening-Balances -123.45\n"+
|
" Equity:Opening-Balances -123.45\n"+
|
||||||
" Assets:TestAccount 123.45\n"), 0, nil, nil, nil, nil, nil)
|
" Assets:TestAccount 123.45\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrInvalidBeancountFile.Message)
|
assert.EqualError(t, err, errs.ErrInvalidBeancountFile.Message)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,11 +9,20 @@ const (
|
|||||||
CAMT_INDICATOR_DEBIT camtCreditDebitIndicator = "DBIT"
|
CAMT_INDICATOR_DEBIT camtCreditDebitIndicator = "DBIT"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type camt052File struct {
|
||||||
|
XMLName xml.Name `xml:"Document"`
|
||||||
|
BankToCustomerAccountReport *camtBankToCustomerAccountReport `xml:"BkToCstmrAcctRpt"`
|
||||||
|
}
|
||||||
|
|
||||||
type camt053File struct {
|
type camt053File struct {
|
||||||
XMLName xml.Name `xml:"Document"`
|
XMLName xml.Name `xml:"Document"`
|
||||||
BankToCustomerStatement *camtBankToCustomerStatement `xml:"BkToCstmrStmt"`
|
BankToCustomerStatement *camtBankToCustomerStatement `xml:"BkToCstmrStmt"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type camtBankToCustomerAccountReport struct {
|
||||||
|
Statements []*camtStatement `xml:"Rpt"`
|
||||||
|
}
|
||||||
|
|
||||||
type camtBankToCustomerStatement struct {
|
type camtBankToCustomerStatement struct {
|
||||||
Statements []*camtStatement `xml:"Stmt"`
|
Statements []*camtStatement `xml:"Stmt"`
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,11 +10,30 @@ import (
|
|||||||
"github.com/mayswind/ezbookkeeping/pkg/errs"
|
"github.com/mayswind/ezbookkeeping/pkg/errs"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// camt052FileReader defines the structure of camt.052 file reader
|
||||||
|
type camt052FileReader struct {
|
||||||
|
xmlDecoder *xml.Decoder
|
||||||
|
}
|
||||||
|
|
||||||
// camt053FileReader defines the structure of camt.053 file reader
|
// camt053FileReader defines the structure of camt.053 file reader
|
||||||
type camt053FileReader struct {
|
type camt053FileReader struct {
|
||||||
xmlDecoder *xml.Decoder
|
xmlDecoder *xml.Decoder
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// read returns the imported camt.052 data
|
||||||
|
// Reference: https://www.iso20022.org/message-set/1196/download
|
||||||
|
func (r *camt052FileReader) read(ctx core.Context) (*camt052File, error) {
|
||||||
|
file := &camt052File{}
|
||||||
|
|
||||||
|
err := r.xmlDecoder.Decode(&file)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return file, nil
|
||||||
|
}
|
||||||
|
|
||||||
// read returns the imported camt.053 data
|
// read returns the imported camt.053 data
|
||||||
// Reference: https://www.iso20022.org/message-set/1196/download
|
// Reference: https://www.iso20022.org/message-set/1196/download
|
||||||
func (r *camt053FileReader) read(ctx core.Context) (*camt053File, error) {
|
func (r *camt053FileReader) read(ctx core.Context) (*camt053File, error) {
|
||||||
@@ -29,6 +48,19 @@ func (r *camt053FileReader) read(ctx core.Context) (*camt053File, error) {
|
|||||||
return file, nil
|
return file, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func createNewCamt052FileReader(data []byte) (*camt052FileReader, error) {
|
||||||
|
if len(data) > 5 && data[0] == 0x3C && data[1] == 0x3F && data[2] == 0x78 && data[3] == 0x6D && data[4] == 0x6C { // <?xml
|
||||||
|
xmlDecoder := xml.NewDecoder(bytes.NewReader(data))
|
||||||
|
xmlDecoder.CharsetReader = charset.NewReaderLabel
|
||||||
|
|
||||||
|
return &camt052FileReader{
|
||||||
|
xmlDecoder: xmlDecoder,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, errs.ErrInvalidXmlFile
|
||||||
|
}
|
||||||
|
|
||||||
func createNewCamt053FileReader(data []byte) (*camt053FileReader, error) {
|
func createNewCamt053FileReader(data []byte) (*camt053FileReader, error) {
|
||||||
if len(data) > 5 && data[0] == 0x3C && data[1] == 0x3F && data[2] == 0x78 && data[3] == 0x6D && data[4] == 0x6C { // <?xml
|
if len(data) > 5 && data[0] == 0x3C && data[1] == 0x3F && data[2] == 0x78 && data[3] == 0x6D && data[4] == 0x6C { // <?xml
|
||||||
xmlDecoder := xml.NewDecoder(bytes.NewReader(data))
|
xmlDecoder := xml.NewDecoder(bytes.NewReader(data))
|
||||||
|
|||||||
@@ -231,7 +231,7 @@ func (t *camtStatementTransactionDataRowIterator) parseTransaction(ctx core.Cont
|
|||||||
}
|
}
|
||||||
|
|
||||||
data[datatable.TRANSACTION_DATA_TABLE_TRANSACTION_TIME] = utils.FormatUnixTimeToLongDateTime(dateTime.Unix(), dateTime.Location())
|
data[datatable.TRANSACTION_DATA_TABLE_TRANSACTION_TIME] = utils.FormatUnixTimeToLongDateTime(dateTime.Unix(), dateTime.Location())
|
||||||
data[datatable.TRANSACTION_DATA_TABLE_TRANSACTION_TIMEZONE] = utils.FormatTimezoneOffset(dateTime.Location())
|
data[datatable.TRANSACTION_DATA_TABLE_TRANSACTION_TIMEZONE] = utils.FormatTimezoneOffset(dateTime.Unix(), dateTime.Location())
|
||||||
} else if entry.BookingDate != nil && entry.BookingDate.Date != "" {
|
} else if entry.BookingDate != nil && entry.BookingDate.Date != "" {
|
||||||
data[datatable.TRANSACTION_DATA_TABLE_TRANSACTION_TIME] = fmt.Sprintf("%s 00:00:00", entry.BookingDate.Date)
|
data[datatable.TRANSACTION_DATA_TABLE_TRANSACTION_TIME] = fmt.Sprintf("%s 00:00:00", entry.BookingDate.Date)
|
||||||
data[datatable.TRANSACTION_DATA_TABLE_TRANSACTION_TIMEZONE] = datatable.TRANSACTION_DATA_TABLE_TIMEZONE_NOT_AVAILABLE
|
data[datatable.TRANSACTION_DATA_TABLE_TRANSACTION_TIMEZONE] = datatable.TRANSACTION_DATA_TABLE_TIMEZONE_NOT_AVAILABLE
|
||||||
@@ -303,12 +303,12 @@ func (t *camtStatementTransactionDataRowIterator) parseTransaction(ctx core.Cont
|
|||||||
return data, nil
|
return data, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func createNewCamtStatementTransactionDataTable(file *camt053File) (*camtStatementTransactionDataTable, error) {
|
func createNewCamtStatementTransactionDataTable(camtStatements []*camtStatement) (*camtStatementTransactionDataTable, error) {
|
||||||
if file == nil || file.BankToCustomerStatement == nil || len(file.BankToCustomerStatement.Statements) == 0 {
|
if len(camtStatements) == 0 {
|
||||||
return nil, errs.ErrNotFoundTransactionDataInFile
|
return nil, errs.ErrNotFoundTransactionDataInFile
|
||||||
}
|
}
|
||||||
|
|
||||||
return &camtStatementTransactionDataTable{
|
return &camtStatementTransactionDataTable{
|
||||||
allStatements: file.BankToCustomerStatement.Statements,
|
allStatements: camtStatements,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,11 @@
|
|||||||
package camt
|
package camt
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/converters/converter"
|
"github.com/mayswind/ezbookkeeping/pkg/converters/converter"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/core"
|
"github.com/mayswind/ezbookkeeping/pkg/core"
|
||||||
|
"github.com/mayswind/ezbookkeeping/pkg/errs"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/models"
|
"github.com/mayswind/ezbookkeeping/pkg/models"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/utils"
|
"github.com/mayswind/ezbookkeeping/pkg/utils"
|
||||||
)
|
)
|
||||||
@@ -13,17 +16,51 @@ var camtTransactionTypeNameMapping = map[models.TransactionType]string{
|
|||||||
models.TRANSACTION_TYPE_TRANSFER: utils.IntToString(int(models.TRANSACTION_TYPE_TRANSFER)),
|
models.TRANSACTION_TYPE_TRANSFER: utils.IntToString(int(models.TRANSACTION_TYPE_TRANSFER)),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// camt052TransactionDataImporter defines the structure of camt.052 file importer for transaction data
|
||||||
|
type camt052TransactionDataImporter struct {
|
||||||
|
}
|
||||||
|
|
||||||
// camt053TransactionDataImporter defines the structure of camt.053 file importer for transaction data
|
// camt053TransactionDataImporter defines the structure of camt.053 file importer for transaction data
|
||||||
type camt053TransactionDataImporter struct {
|
type camt053TransactionDataImporter struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize a camt.053 transaction data importer singleton instance
|
// Initialize camt.052 and camt.053 transaction data importer singleton instances
|
||||||
var (
|
var (
|
||||||
|
Camt052TransactionDataImporter = &camt052TransactionDataImporter{}
|
||||||
Camt053TransactionDataImporter = &camt053TransactionDataImporter{}
|
Camt053TransactionDataImporter = &camt053TransactionDataImporter{}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// ParseImportedData returns the imported data by parsing the camt.052 file transaction data
|
||||||
|
func (c *camt052TransactionDataImporter) ParseImportedData(ctx core.Context, user *models.User, data []byte, defaultTimezone *time.Location, additionalOptions converter.TransactionDataImporterOptions, accountMap map[string]*models.Account, expenseCategoryMap map[string]map[string]*models.TransactionCategory, incomeCategoryMap map[string]map[string]*models.TransactionCategory, transferCategoryMap map[string]map[string]*models.TransactionCategory, tagMap map[string]*models.TransactionTag) (models.ImportedTransactionSlice, []*models.Account, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionTag, error) {
|
||||||
|
camt052DataReader, err := createNewCamt052FileReader(data)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, nil, nil, nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
camt052Data, err := camt052DataReader.read(ctx)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, nil, nil, nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if camt052Data.BankToCustomerAccountReport == nil || camt052Data.BankToCustomerAccountReport.Statements == nil {
|
||||||
|
return nil, nil, nil, nil, nil, nil, errs.ErrNotFoundTransactionDataInFile
|
||||||
|
}
|
||||||
|
|
||||||
|
transactionDataTable, err := createNewCamtStatementTransactionDataTable(camt052Data.BankToCustomerAccountReport.Statements)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, nil, nil, nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
dataTableImporter := converter.CreateNewSimpleImporterWithTypeNameMapping(camtTransactionTypeNameMapping)
|
||||||
|
|
||||||
|
return dataTableImporter.ParseImportedData(ctx, user, transactionDataTable, defaultTimezone, additionalOptions, accountMap, expenseCategoryMap, incomeCategoryMap, transferCategoryMap, tagMap)
|
||||||
|
}
|
||||||
|
|
||||||
// ParseImportedData returns the imported data by parsing the camt.053 file transaction data
|
// ParseImportedData returns the imported data by parsing the camt.053 file transaction data
|
||||||
func (c *camt053TransactionDataImporter) ParseImportedData(ctx core.Context, user *models.User, data []byte, defaultTimezoneOffset int16, accountMap map[string]*models.Account, expenseCategoryMap map[string]map[string]*models.TransactionCategory, incomeCategoryMap map[string]map[string]*models.TransactionCategory, transferCategoryMap map[string]map[string]*models.TransactionCategory, tagMap map[string]*models.TransactionTag) (models.ImportedTransactionSlice, []*models.Account, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionTag, error) {
|
func (c *camt053TransactionDataImporter) ParseImportedData(ctx core.Context, user *models.User, data []byte, defaultTimezone *time.Location, additionalOptions converter.TransactionDataImporterOptions, accountMap map[string]*models.Account, expenseCategoryMap map[string]map[string]*models.TransactionCategory, incomeCategoryMap map[string]map[string]*models.TransactionCategory, transferCategoryMap map[string]map[string]*models.TransactionCategory, tagMap map[string]*models.TransactionTag) (models.ImportedTransactionSlice, []*models.Account, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionTag, error) {
|
||||||
camt053DataReader, err := createNewCamt053FileReader(data)
|
camt053DataReader, err := createNewCamt053FileReader(data)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -36,7 +73,11 @@ func (c *camt053TransactionDataImporter) ParseImportedData(ctx core.Context, use
|
|||||||
return nil, nil, nil, nil, nil, nil, err
|
return nil, nil, nil, nil, nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
transactionDataTable, err := createNewCamtStatementTransactionDataTable(camt053Data)
|
if camt053Data.BankToCustomerStatement == nil || camt053Data.BankToCustomerStatement.Statements == nil {
|
||||||
|
return nil, nil, nil, nil, nil, nil, errs.ErrNotFoundTransactionDataInFile
|
||||||
|
}
|
||||||
|
|
||||||
|
transactionDataTable, err := createNewCamtStatementTransactionDataTable(camt053Data.BankToCustomerStatement.Statements)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, nil, nil, nil, nil, err
|
return nil, nil, nil, nil, nil, nil, err
|
||||||
@@ -44,5 +85,5 @@ func (c *camt053TransactionDataImporter) ParseImportedData(ctx core.Context, use
|
|||||||
|
|
||||||
dataTableImporter := converter.CreateNewSimpleImporterWithTypeNameMapping(camtTransactionTypeNameMapping)
|
dataTableImporter := converter.CreateNewSimpleImporterWithTypeNameMapping(camtTransactionTypeNameMapping)
|
||||||
|
|
||||||
return dataTableImporter.ParseImportedData(ctx, user, transactionDataTable, defaultTimezoneOffset, accountMap, expenseCategoryMap, incomeCategoryMap, transferCategoryMap, tagMap)
|
return dataTableImporter.ParseImportedData(ctx, user, transactionDataTable, defaultTimezone, additionalOptions, accountMap, expenseCategoryMap, incomeCategoryMap, transferCategoryMap, tagMap)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,17 +2,19 @@ package camt
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
|
"github.com/mayswind/ezbookkeeping/pkg/converters/converter"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/core"
|
"github.com/mayswind/ezbookkeeping/pkg/core"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/errs"
|
"github.com/mayswind/ezbookkeeping/pkg/errs"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/models"
|
"github.com/mayswind/ezbookkeeping/pkg/models"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/utils"
|
"github.com/mayswind/ezbookkeeping/pkg/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestCamt053TransactionDataFileParseImportedData_MinimumValidData(t *testing.T) {
|
func TestCamt052TransactionDataFileParseImportedData_MinimumValidData(t *testing.T) {
|
||||||
converter := Camt053TransactionDataImporter
|
importer := Camt052TransactionDataImporter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
@@ -20,7 +22,110 @@ func TestCamt053TransactionDataFileParseImportedData_MinimumValidData(t *testing
|
|||||||
DefaultCurrency: "CNY",
|
DefaultCurrency: "CNY",
|
||||||
}
|
}
|
||||||
|
|
||||||
allNewTransactions, allNewAccounts, allNewSubExpenseCategories, allNewSubIncomeCategories, allNewSubTransferCategories, allNewTags, err := converter.ParseImportedData(context, user, []byte(
|
allNewTransactions, allNewAccounts, allNewSubExpenseCategories, allNewSubIncomeCategories, allNewSubTransferCategories, allNewTags, err := importer.ParseImportedData(context, user, []byte(
|
||||||
|
`<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<Document xmlns="urn:iso:std:iso:20022:tech:xsd:camt.052.001.02">
|
||||||
|
<BkToCstmrAcctRpt>
|
||||||
|
<Rpt>
|
||||||
|
<Acct>
|
||||||
|
<Id>
|
||||||
|
<IBAN>123</IBAN>
|
||||||
|
</Id>
|
||||||
|
<Ccy>CNY</Ccy>
|
||||||
|
</Acct>
|
||||||
|
<Ntry>
|
||||||
|
<BookgDt>
|
||||||
|
<DtTm>2024-09-01T01:23:45+08:00</DtTm>
|
||||||
|
</BookgDt>
|
||||||
|
<CdtDbtInd>CRDT</CdtDbtInd>
|
||||||
|
<Amt Ccy="CNY">123.45</Amt>
|
||||||
|
</Ntry>
|
||||||
|
<Ntry>
|
||||||
|
<BookgDt>
|
||||||
|
<DtTm>2024-09-01T12:34:56+08:00</DtTm>
|
||||||
|
</BookgDt>
|
||||||
|
<CdtDbtInd>DBIT</CdtDbtInd>
|
||||||
|
<Amt Ccy="CNY">0.12</Amt>
|
||||||
|
</Ntry>
|
||||||
|
</Rpt>
|
||||||
|
<Rpt>
|
||||||
|
<Acct>
|
||||||
|
<Id>
|
||||||
|
<Othr>
|
||||||
|
<Id>456</Id>
|
||||||
|
</Othr>
|
||||||
|
</Id>
|
||||||
|
<Ccy>USD</Ccy>
|
||||||
|
</Acct>
|
||||||
|
<Ntry>
|
||||||
|
<BookgDt>
|
||||||
|
<DtTm>2024-09-01T23:59:59+08:00</DtTm>
|
||||||
|
</BookgDt>
|
||||||
|
<CdtDbtInd>CRDT</CdtDbtInd>
|
||||||
|
<Amt Ccy="USD">1.23</Amt>
|
||||||
|
</Ntry>
|
||||||
|
</Rpt>
|
||||||
|
</BkToCstmrAcctRpt>
|
||||||
|
</Document>`), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
|
|
||||||
|
assert.Nil(t, err)
|
||||||
|
|
||||||
|
assert.Equal(t, 3, len(allNewTransactions))
|
||||||
|
assert.Equal(t, 2, len(allNewAccounts))
|
||||||
|
assert.Equal(t, 1, len(allNewSubExpenseCategories))
|
||||||
|
assert.Equal(t, 1, len(allNewSubIncomeCategories))
|
||||||
|
assert.Equal(t, 0, len(allNewSubTransferCategories))
|
||||||
|
assert.Equal(t, 0, len(allNewTags))
|
||||||
|
|
||||||
|
assert.Equal(t, int64(1234567890), allNewTransactions[0].Uid)
|
||||||
|
assert.Equal(t, models.TRANSACTION_DB_TYPE_INCOME, allNewTransactions[0].Type)
|
||||||
|
assert.Equal(t, int64(1725125025), utils.GetUnixTimeFromTransactionTime(allNewTransactions[0].TransactionTime))
|
||||||
|
assert.Equal(t, int64(12345), allNewTransactions[0].Amount)
|
||||||
|
assert.Equal(t, "123", allNewTransactions[0].OriginalSourceAccountName)
|
||||||
|
assert.Equal(t, "CNY", allNewTransactions[0].OriginalSourceAccountCurrency)
|
||||||
|
assert.Equal(t, "", allNewTransactions[0].OriginalCategoryName)
|
||||||
|
|
||||||
|
assert.Equal(t, int64(1234567890), allNewTransactions[1].Uid)
|
||||||
|
assert.Equal(t, models.TRANSACTION_DB_TYPE_EXPENSE, allNewTransactions[1].Type)
|
||||||
|
assert.Equal(t, int64(1725165296), utils.GetUnixTimeFromTransactionTime(allNewTransactions[1].TransactionTime))
|
||||||
|
assert.Equal(t, int64(12), allNewTransactions[1].Amount)
|
||||||
|
assert.Equal(t, "123", allNewTransactions[1].OriginalSourceAccountName)
|
||||||
|
assert.Equal(t, "CNY", allNewTransactions[1].OriginalSourceAccountCurrency)
|
||||||
|
assert.Equal(t, "", allNewTransactions[1].OriginalCategoryName)
|
||||||
|
|
||||||
|
assert.Equal(t, int64(1234567890), allNewTransactions[2].Uid)
|
||||||
|
assert.Equal(t, models.TRANSACTION_DB_TYPE_INCOME, allNewTransactions[2].Type)
|
||||||
|
assert.Equal(t, int64(1725206399), utils.GetUnixTimeFromTransactionTime(allNewTransactions[2].TransactionTime))
|
||||||
|
assert.Equal(t, int64(123), allNewTransactions[2].Amount)
|
||||||
|
assert.Equal(t, "456", allNewTransactions[2].OriginalSourceAccountName)
|
||||||
|
assert.Equal(t, "USD", allNewTransactions[2].OriginalSourceAccountCurrency)
|
||||||
|
assert.Equal(t, "", allNewTransactions[2].OriginalCategoryName)
|
||||||
|
|
||||||
|
assert.Equal(t, int64(1234567890), allNewAccounts[0].Uid)
|
||||||
|
assert.Equal(t, "123", allNewAccounts[0].Name)
|
||||||
|
assert.Equal(t, "CNY", allNewAccounts[0].Currency)
|
||||||
|
|
||||||
|
assert.Equal(t, int64(1234567890), allNewAccounts[1].Uid)
|
||||||
|
assert.Equal(t, "456", allNewAccounts[1].Name)
|
||||||
|
assert.Equal(t, "USD", allNewAccounts[1].Currency)
|
||||||
|
|
||||||
|
assert.Equal(t, int64(1234567890), allNewSubExpenseCategories[0].Uid)
|
||||||
|
assert.Equal(t, "", allNewSubExpenseCategories[0].Name)
|
||||||
|
|
||||||
|
assert.Equal(t, int64(1234567890), allNewSubIncomeCategories[0].Uid)
|
||||||
|
assert.Equal(t, "", allNewSubIncomeCategories[0].Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCamt053TransactionDataFileParseImportedData_MinimumValidData(t *testing.T) {
|
||||||
|
importer := Camt053TransactionDataImporter
|
||||||
|
context := core.NewNullContext()
|
||||||
|
|
||||||
|
user := &models.User{
|
||||||
|
Uid: 1234567890,
|
||||||
|
DefaultCurrency: "CNY",
|
||||||
|
}
|
||||||
|
|
||||||
|
allNewTransactions, allNewAccounts, allNewSubExpenseCategories, allNewSubIncomeCategories, allNewSubTransferCategories, allNewTags, err := importer.ParseImportedData(context, user, []byte(
|
||||||
`<?xml version="1.0" encoding="UTF-8"?>
|
`<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<Document xmlns="urn:iso:std:iso:20022:tech:xsd:camt.053.001.02">
|
<Document xmlns="urn:iso:std:iso:20022:tech:xsd:camt.053.001.02">
|
||||||
<BkToCstmrStmt>
|
<BkToCstmrStmt>
|
||||||
@@ -64,7 +169,7 @@ func TestCamt053TransactionDataFileParseImportedData_MinimumValidData(t *testing
|
|||||||
</Ntry>
|
</Ntry>
|
||||||
</Stmt>
|
</Stmt>
|
||||||
</BkToCstmrStmt>
|
</BkToCstmrStmt>
|
||||||
</Document>`), 0, nil, nil, nil, nil, nil)
|
</Document>`), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
|
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
@@ -115,7 +220,7 @@ func TestCamt053TransactionDataFileParseImportedData_MinimumValidData(t *testing
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestCamt053TransactionDataFileParseImportedData_ParseValidTransactionTime(t *testing.T) {
|
func TestCamt053TransactionDataFileParseImportedData_ParseValidTransactionTime(t *testing.T) {
|
||||||
converter := Camt053TransactionDataImporter
|
importer := Camt053TransactionDataImporter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
@@ -123,7 +228,7 @@ func TestCamt053TransactionDataFileParseImportedData_ParseValidTransactionTime(t
|
|||||||
DefaultCurrency: "CNY",
|
DefaultCurrency: "CNY",
|
||||||
}
|
}
|
||||||
|
|
||||||
allNewTransactions, _, _, _, _, _, err := converter.ParseImportedData(context, user, []byte(
|
allNewTransactions, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(
|
||||||
`<?xml version="1.0" encoding="UTF-8"?>
|
`<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<Document xmlns="urn:iso:std:iso:20022:tech:xsd:camt.053.001.02">
|
<Document xmlns="urn:iso:std:iso:20022:tech:xsd:camt.053.001.02">
|
||||||
<BkToCstmrStmt>
|
<BkToCstmrStmt>
|
||||||
@@ -157,7 +262,7 @@ func TestCamt053TransactionDataFileParseImportedData_ParseValidTransactionTime(t
|
|||||||
</Ntry>
|
</Ntry>
|
||||||
</Stmt>
|
</Stmt>
|
||||||
</BkToCstmrStmt>
|
</BkToCstmrStmt>
|
||||||
</Document>`), 0, nil, nil, nil, nil, nil)
|
</Document>`), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
|
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
assert.Equal(t, 3, len(allNewTransactions))
|
assert.Equal(t, 3, len(allNewTransactions))
|
||||||
@@ -168,7 +273,7 @@ func TestCamt053TransactionDataFileParseImportedData_ParseValidTransactionTime(t
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestCamt053TransactionDataFileParseImportedData_ParseInvalidTransactionTime(t *testing.T) {
|
func TestCamt053TransactionDataFileParseImportedData_ParseInvalidTransactionTime(t *testing.T) {
|
||||||
converter := Camt053TransactionDataImporter
|
importer := Camt053TransactionDataImporter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
@@ -176,7 +281,7 @@ func TestCamt053TransactionDataFileParseImportedData_ParseInvalidTransactionTime
|
|||||||
DefaultCurrency: "CNY",
|
DefaultCurrency: "CNY",
|
||||||
}
|
}
|
||||||
|
|
||||||
_, _, _, _, _, _, err := converter.ParseImportedData(context, user, []byte(
|
_, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(
|
||||||
`<?xml version="1.0" encoding="UTF-8"?>
|
`<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<Document xmlns="urn:iso:std:iso:20022:tech:xsd:camt.053.001.02">
|
<Document xmlns="urn:iso:std:iso:20022:tech:xsd:camt.053.001.02">
|
||||||
<BkToCstmrStmt>
|
<BkToCstmrStmt>
|
||||||
@@ -196,10 +301,10 @@ func TestCamt053TransactionDataFileParseImportedData_ParseInvalidTransactionTime
|
|||||||
</Ntry>
|
</Ntry>
|
||||||
</Stmt>
|
</Stmt>
|
||||||
</BkToCstmrStmt>
|
</BkToCstmrStmt>
|
||||||
</Document>`), 0, nil, nil, nil, nil, nil)
|
</Document>`), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrTransactionTimeInvalid.Message)
|
assert.EqualError(t, err, errs.ErrTransactionTimeInvalid.Message)
|
||||||
|
|
||||||
_, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte(
|
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(
|
||||||
`<?xml version="1.0" encoding="UTF-8"?>
|
`<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<Document xmlns="urn:iso:std:iso:20022:tech:xsd:camt.053.001.02">
|
<Document xmlns="urn:iso:std:iso:20022:tech:xsd:camt.053.001.02">
|
||||||
<BkToCstmrStmt>
|
<BkToCstmrStmt>
|
||||||
@@ -219,10 +324,10 @@ func TestCamt053TransactionDataFileParseImportedData_ParseInvalidTransactionTime
|
|||||||
</Ntry>
|
</Ntry>
|
||||||
</Stmt>
|
</Stmt>
|
||||||
</BkToCstmrStmt>
|
</BkToCstmrStmt>
|
||||||
</Document>`), 0, nil, nil, nil, nil, nil)
|
</Document>`), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrTransactionTimeInvalid.Message)
|
assert.EqualError(t, err, errs.ErrTransactionTimeInvalid.Message)
|
||||||
|
|
||||||
_, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte(
|
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(
|
||||||
`<?xml version="1.0" encoding="UTF-8"?>
|
`<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<Document xmlns="urn:iso:std:iso:20022:tech:xsd:camt.053.001.02">
|
<Document xmlns="urn:iso:std:iso:20022:tech:xsd:camt.053.001.02">
|
||||||
<BkToCstmrStmt>
|
<BkToCstmrStmt>
|
||||||
@@ -242,10 +347,10 @@ func TestCamt053TransactionDataFileParseImportedData_ParseInvalidTransactionTime
|
|||||||
</Ntry>
|
</Ntry>
|
||||||
</Stmt>
|
</Stmt>
|
||||||
</BkToCstmrStmt>
|
</BkToCstmrStmt>
|
||||||
</Document>`), 0, nil, nil, nil, nil, nil)
|
</Document>`), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrTransactionTimeInvalid.Message)
|
assert.EqualError(t, err, errs.ErrTransactionTimeInvalid.Message)
|
||||||
|
|
||||||
_, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte(
|
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(
|
||||||
`<?xml version="1.0" encoding="UTF-8"?>
|
`<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<Document xmlns="urn:iso:std:iso:20022:tech:xsd:camt.053.001.02">
|
<Document xmlns="urn:iso:std:iso:20022:tech:xsd:camt.053.001.02">
|
||||||
<BkToCstmrStmt>
|
<BkToCstmrStmt>
|
||||||
@@ -265,12 +370,12 @@ func TestCamt053TransactionDataFileParseImportedData_ParseInvalidTransactionTime
|
|||||||
</Ntry>
|
</Ntry>
|
||||||
</Stmt>
|
</Stmt>
|
||||||
</BkToCstmrStmt>
|
</BkToCstmrStmt>
|
||||||
</Document>`), 0, nil, nil, nil, nil, nil)
|
</Document>`), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrTransactionTimeInvalid.Message)
|
assert.EqualError(t, err, errs.ErrTransactionTimeInvalid.Message)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCamt053TransactionDataFileParseImportedData_ParseTransactionValidAmountAndCurrency(t *testing.T) {
|
func TestCamt053TransactionDataFileParseImportedData_ParseTransactionValidAmountAndCurrency(t *testing.T) {
|
||||||
converter := Camt053TransactionDataImporter
|
importer := Camt053TransactionDataImporter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
@@ -278,7 +383,7 @@ func TestCamt053TransactionDataFileParseImportedData_ParseTransactionValidAmount
|
|||||||
DefaultCurrency: "CNY",
|
DefaultCurrency: "CNY",
|
||||||
}
|
}
|
||||||
|
|
||||||
allNewTransactions, _, _, _, _, _, err := converter.ParseImportedData(context, user, []byte(
|
allNewTransactions, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(
|
||||||
`<?xml version="1.0" encoding="UTF-8"?>
|
`<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<Document xmlns="urn:iso:std:iso:20022:tech:xsd:camt.053.001.02">
|
<Document xmlns="urn:iso:std:iso:20022:tech:xsd:camt.053.001.02">
|
||||||
<BkToCstmrStmt>
|
<BkToCstmrStmt>
|
||||||
@@ -314,7 +419,7 @@ func TestCamt053TransactionDataFileParseImportedData_ParseTransactionValidAmount
|
|||||||
</Ntry>
|
</Ntry>
|
||||||
</Stmt>
|
</Stmt>
|
||||||
</BkToCstmrStmt>
|
</BkToCstmrStmt>
|
||||||
</Document>`), 0, nil, nil, nil, nil, nil)
|
</Document>`), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
|
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
assert.Equal(t, 2, len(allNewTransactions))
|
assert.Equal(t, 2, len(allNewTransactions))
|
||||||
@@ -323,7 +428,7 @@ func TestCamt053TransactionDataFileParseImportedData_ParseTransactionValidAmount
|
|||||||
assert.Equal(t, "USD", allNewTransactions[1].OriginalSourceAccountCurrency)
|
assert.Equal(t, "USD", allNewTransactions[1].OriginalSourceAccountCurrency)
|
||||||
assert.Equal(t, int64(10023), allNewTransactions[1].Amount)
|
assert.Equal(t, int64(10023), allNewTransactions[1].Amount)
|
||||||
|
|
||||||
allNewTransactions, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte(
|
allNewTransactions, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(
|
||||||
`<?xml version="1.0" encoding="UTF-8"?>
|
`<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<Document xmlns="urn:iso:std:iso:20022:tech:xsd:camt.053.001.02">
|
<Document xmlns="urn:iso:std:iso:20022:tech:xsd:camt.053.001.02">
|
||||||
<BkToCstmrStmt>
|
<BkToCstmrStmt>
|
||||||
@@ -365,7 +470,7 @@ func TestCamt053TransactionDataFileParseImportedData_ParseTransactionValidAmount
|
|||||||
</Ntry>
|
</Ntry>
|
||||||
</Stmt>
|
</Stmt>
|
||||||
</BkToCstmrStmt>
|
</BkToCstmrStmt>
|
||||||
</Document>`), 0, nil, nil, nil, nil, nil)
|
</Document>`), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
|
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
assert.Equal(t, 2, len(allNewTransactions))
|
assert.Equal(t, 2, len(allNewTransactions))
|
||||||
@@ -374,7 +479,7 @@ func TestCamt053TransactionDataFileParseImportedData_ParseTransactionValidAmount
|
|||||||
assert.Equal(t, "USD", allNewTransactions[1].OriginalSourceAccountCurrency)
|
assert.Equal(t, "USD", allNewTransactions[1].OriginalSourceAccountCurrency)
|
||||||
assert.Equal(t, int64(9999), allNewTransactions[1].Amount)
|
assert.Equal(t, int64(9999), allNewTransactions[1].Amount)
|
||||||
|
|
||||||
allNewTransactions, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte(
|
allNewTransactions, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(
|
||||||
`<?xml version="1.0" encoding="UTF-8"?>
|
`<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<Document xmlns="urn:iso:std:iso:20022:tech:xsd:camt.053.001.02">
|
<Document xmlns="urn:iso:std:iso:20022:tech:xsd:camt.053.001.02">
|
||||||
<BkToCstmrStmt>
|
<BkToCstmrStmt>
|
||||||
@@ -403,14 +508,14 @@ func TestCamt053TransactionDataFileParseImportedData_ParseTransactionValidAmount
|
|||||||
</Ntry>
|
</Ntry>
|
||||||
</Stmt>
|
</Stmt>
|
||||||
</BkToCstmrStmt>
|
</BkToCstmrStmt>
|
||||||
</Document>`), 0, nil, nil, nil, nil, nil)
|
</Document>`), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
|
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
assert.Equal(t, 1, len(allNewTransactions))
|
assert.Equal(t, 1, len(allNewTransactions))
|
||||||
assert.Equal(t, "USD", allNewTransactions[0].OriginalSourceAccountCurrency)
|
assert.Equal(t, "USD", allNewTransactions[0].OriginalSourceAccountCurrency)
|
||||||
assert.Equal(t, int64(12345), allNewTransactions[0].Amount)
|
assert.Equal(t, int64(12345), allNewTransactions[0].Amount)
|
||||||
|
|
||||||
allNewTransactions, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte(
|
allNewTransactions, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(
|
||||||
`<?xml version="1.0" encoding="UTF-8"?>
|
`<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<Document xmlns="urn:iso:std:iso:20022:tech:xsd:camt.053.001.02">
|
<Document xmlns="urn:iso:std:iso:20022:tech:xsd:camt.053.001.02">
|
||||||
<BkToCstmrStmt>
|
<BkToCstmrStmt>
|
||||||
@@ -430,7 +535,7 @@ func TestCamt053TransactionDataFileParseImportedData_ParseTransactionValidAmount
|
|||||||
</Ntry>
|
</Ntry>
|
||||||
</Stmt>
|
</Stmt>
|
||||||
</BkToCstmrStmt>
|
</BkToCstmrStmt>
|
||||||
</Document>`), 0, nil, nil, nil, nil, nil)
|
</Document>`), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
|
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
assert.Equal(t, 1, len(allNewTransactions))
|
assert.Equal(t, 1, len(allNewTransactions))
|
||||||
@@ -439,7 +544,7 @@ func TestCamt053TransactionDataFileParseImportedData_ParseTransactionValidAmount
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestCamt053TransactionDataFileParseImportedData_ParseTransactionInvalidAmountAndCurrency(t *testing.T) {
|
func TestCamt053TransactionDataFileParseImportedData_ParseTransactionInvalidAmountAndCurrency(t *testing.T) {
|
||||||
converter := Camt053TransactionDataImporter
|
importer := Camt053TransactionDataImporter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
@@ -447,7 +552,7 @@ func TestCamt053TransactionDataFileParseImportedData_ParseTransactionInvalidAmou
|
|||||||
DefaultCurrency: "CNY",
|
DefaultCurrency: "CNY",
|
||||||
}
|
}
|
||||||
|
|
||||||
_, _, _, _, _, _, err := converter.ParseImportedData(context, user, []byte(
|
_, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(
|
||||||
`<?xml version="1.0" encoding="UTF-8"?>
|
`<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<Document xmlns="urn:iso:std:iso:20022:tech:xsd:camt.053.001.02">
|
<Document xmlns="urn:iso:std:iso:20022:tech:xsd:camt.053.001.02">
|
||||||
<BkToCstmrStmt>
|
<BkToCstmrStmt>
|
||||||
@@ -467,10 +572,10 @@ func TestCamt053TransactionDataFileParseImportedData_ParseTransactionInvalidAmou
|
|||||||
</Ntry>
|
</Ntry>
|
||||||
</Stmt>
|
</Stmt>
|
||||||
</BkToCstmrStmt>
|
</BkToCstmrStmt>
|
||||||
</Document>`), 0, nil, nil, nil, nil, nil)
|
</Document>`), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrAmountInvalid.Message)
|
assert.EqualError(t, err, errs.ErrAmountInvalid.Message)
|
||||||
|
|
||||||
_, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte(
|
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(
|
||||||
`<?xml version="1.0" encoding="UTF-8"?>
|
`<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<Document xmlns="urn:iso:std:iso:20022:tech:xsd:camt.053.001.02">
|
<Document xmlns="urn:iso:std:iso:20022:tech:xsd:camt.053.001.02">
|
||||||
<BkToCstmrStmt>
|
<BkToCstmrStmt>
|
||||||
@@ -498,10 +603,10 @@ func TestCamt053TransactionDataFileParseImportedData_ParseTransactionInvalidAmou
|
|||||||
</Ntry>
|
</Ntry>
|
||||||
</Stmt>
|
</Stmt>
|
||||||
</BkToCstmrStmt>
|
</BkToCstmrStmt>
|
||||||
</Document>`), 0, nil, nil, nil, nil, nil)
|
</Document>`), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrAmountInvalid.Message)
|
assert.EqualError(t, err, errs.ErrAmountInvalid.Message)
|
||||||
|
|
||||||
_, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte(
|
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(
|
||||||
`<?xml version="1.0" encoding="UTF-8"?>
|
`<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<Document xmlns="urn:iso:std:iso:20022:tech:xsd:camt.053.001.02">
|
<Document xmlns="urn:iso:std:iso:20022:tech:xsd:camt.053.001.02">
|
||||||
<BkToCstmrStmt>
|
<BkToCstmrStmt>
|
||||||
@@ -529,12 +634,12 @@ func TestCamt053TransactionDataFileParseImportedData_ParseTransactionInvalidAmou
|
|||||||
</Ntry>
|
</Ntry>
|
||||||
</Stmt>
|
</Stmt>
|
||||||
</BkToCstmrStmt>
|
</BkToCstmrStmt>
|
||||||
</Document>`), 0, nil, nil, nil, nil, nil)
|
</Document>`), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrAmountInvalid.Message)
|
assert.EqualError(t, err, errs.ErrAmountInvalid.Message)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCamt053TransactionDataFileParseImportedData_ParseDescription(t *testing.T) {
|
func TestCamt053TransactionDataFileParseImportedData_ParseDescription(t *testing.T) {
|
||||||
converter := Camt053TransactionDataImporter
|
importer := Camt053TransactionDataImporter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
@@ -542,7 +647,7 @@ func TestCamt053TransactionDataFileParseImportedData_ParseDescription(t *testing
|
|||||||
DefaultCurrency: "CNY",
|
DefaultCurrency: "CNY",
|
||||||
}
|
}
|
||||||
|
|
||||||
allNewTransactions, _, _, _, _, _, err := converter.ParseImportedData(context, user, []byte(
|
allNewTransactions, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(
|
||||||
`<?xml version="1.0" encoding="UTF-8"?>
|
`<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<Document xmlns="urn:iso:std:iso:20022:tech:xsd:camt.053.001.02">
|
<Document xmlns="urn:iso:std:iso:20022:tech:xsd:camt.053.001.02">
|
||||||
<BkToCstmrStmt>
|
<BkToCstmrStmt>
|
||||||
@@ -572,13 +677,13 @@ func TestCamt053TransactionDataFileParseImportedData_ParseDescription(t *testing
|
|||||||
</Ntry>
|
</Ntry>
|
||||||
</Stmt>
|
</Stmt>
|
||||||
</BkToCstmrStmt>
|
</BkToCstmrStmt>
|
||||||
</Document>`), 0, nil, nil, nil, nil, nil)
|
</Document>`), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
|
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
assert.Equal(t, 1, len(allNewTransactions))
|
assert.Equal(t, 1, len(allNewTransactions))
|
||||||
assert.Equal(t, "Test Transaction", allNewTransactions[0].Comment)
|
assert.Equal(t, "Test Transaction", allNewTransactions[0].Comment)
|
||||||
|
|
||||||
allNewTransactions, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte(
|
allNewTransactions, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(
|
||||||
`<?xml version="1.0" encoding="UTF-8"?>
|
`<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<Document xmlns="urn:iso:std:iso:20022:tech:xsd:camt.053.001.02">
|
<Document xmlns="urn:iso:std:iso:20022:tech:xsd:camt.053.001.02">
|
||||||
<BkToCstmrStmt>
|
<BkToCstmrStmt>
|
||||||
@@ -607,13 +712,13 @@ func TestCamt053TransactionDataFileParseImportedData_ParseDescription(t *testing
|
|||||||
</Ntry>
|
</Ntry>
|
||||||
</Stmt>
|
</Stmt>
|
||||||
</BkToCstmrStmt>
|
</BkToCstmrStmt>
|
||||||
</Document>`), 0, nil, nil, nil, nil, nil)
|
</Document>`), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
|
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
assert.Equal(t, 1, len(allNewTransactions))
|
assert.Equal(t, 1, len(allNewTransactions))
|
||||||
assert.Equal(t, "Test Line 1\nTest Line 2", allNewTransactions[0].Comment)
|
assert.Equal(t, "Test Line 1\nTest Line 2", allNewTransactions[0].Comment)
|
||||||
|
|
||||||
allNewTransactions, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte(
|
allNewTransactions, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(
|
||||||
`<?xml version="1.0" encoding="UTF-8"?>
|
`<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<Document xmlns="urn:iso:std:iso:20022:tech:xsd:camt.053.001.02">
|
<Document xmlns="urn:iso:std:iso:20022:tech:xsd:camt.053.001.02">
|
||||||
<BkToCstmrStmt>
|
<BkToCstmrStmt>
|
||||||
@@ -634,7 +739,7 @@ func TestCamt053TransactionDataFileParseImportedData_ParseDescription(t *testing
|
|||||||
</Ntry>
|
</Ntry>
|
||||||
</Stmt>
|
</Stmt>
|
||||||
</BkToCstmrStmt>
|
</BkToCstmrStmt>
|
||||||
</Document>`), 0, nil, nil, nil, nil, nil)
|
</Document>`), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
|
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
assert.Equal(t, 1, len(allNewTransactions))
|
assert.Equal(t, 1, len(allNewTransactions))
|
||||||
@@ -642,7 +747,7 @@ func TestCamt053TransactionDataFileParseImportedData_ParseDescription(t *testing
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestCamt053TransactionDataFileParseImportedData_MissingAccountNode(t *testing.T) {
|
func TestCamt053TransactionDataFileParseImportedData_MissingAccountNode(t *testing.T) {
|
||||||
converter := Camt053TransactionDataImporter
|
importer := Camt053TransactionDataImporter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
@@ -650,7 +755,7 @@ func TestCamt053TransactionDataFileParseImportedData_MissingAccountNode(t *testi
|
|||||||
DefaultCurrency: "CNY",
|
DefaultCurrency: "CNY",
|
||||||
}
|
}
|
||||||
|
|
||||||
_, _, _, _, _, _, err := converter.ParseImportedData(context, user, []byte(
|
_, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(
|
||||||
`<?xml version="1.0" encoding="UTF-8"?>
|
`<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<Document xmlns="urn:iso:std:iso:20022:tech:xsd:camt.053.001.02">
|
<Document xmlns="urn:iso:std:iso:20022:tech:xsd:camt.053.001.02">
|
||||||
<BkToCstmrStmt>
|
<BkToCstmrStmt>
|
||||||
@@ -664,12 +769,12 @@ func TestCamt053TransactionDataFileParseImportedData_MissingAccountNode(t *testi
|
|||||||
</Ntry>
|
</Ntry>
|
||||||
</Stmt>
|
</Stmt>
|
||||||
</BkToCstmrStmt>
|
</BkToCstmrStmt>
|
||||||
</Document>`), 0, nil, nil, nil, nil, nil)
|
</Document>`), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrMissingAccountData.Message)
|
assert.EqualError(t, err, errs.ErrMissingAccountData.Message)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCamt053TransactionDataFileParseImportedData_MissingTransactionRequiredNode(t *testing.T) {
|
func TestCamt053TransactionDataFileParseImportedData_MissingTransactionRequiredNode(t *testing.T) {
|
||||||
converter := Camt053TransactionDataImporter
|
importer := Camt053TransactionDataImporter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
@@ -677,7 +782,7 @@ func TestCamt053TransactionDataFileParseImportedData_MissingTransactionRequiredN
|
|||||||
DefaultCurrency: "CNY",
|
DefaultCurrency: "CNY",
|
||||||
}
|
}
|
||||||
|
|
||||||
_, _, _, _, _, _, err := converter.ParseImportedData(context, user, []byte(
|
_, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(
|
||||||
`<?xml version="1.0" encoding="UTF-8"?>
|
`<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<Document xmlns="urn:iso:std:iso:20022:tech:xsd:camt.053.001.02">
|
<Document xmlns="urn:iso:std:iso:20022:tech:xsd:camt.053.001.02">
|
||||||
<BkToCstmrStmt>
|
<BkToCstmrStmt>
|
||||||
@@ -694,10 +799,10 @@ func TestCamt053TransactionDataFileParseImportedData_MissingTransactionRequiredN
|
|||||||
</Ntry>
|
</Ntry>
|
||||||
</Stmt>
|
</Stmt>
|
||||||
</BkToCstmrStmt>
|
</BkToCstmrStmt>
|
||||||
</Document>`), 0, nil, nil, nil, nil, nil)
|
</Document>`), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrMissingTransactionTime.Message)
|
assert.EqualError(t, err, errs.ErrMissingTransactionTime.Message)
|
||||||
|
|
||||||
_, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte(
|
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(
|
||||||
`<?xml version="1.0" encoding="UTF-8"?>
|
`<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<Document xmlns="urn:iso:std:iso:20022:tech:xsd:camt.053.001.02">
|
<Document xmlns="urn:iso:std:iso:20022:tech:xsd:camt.053.001.02">
|
||||||
<BkToCstmrStmt>
|
<BkToCstmrStmt>
|
||||||
@@ -716,10 +821,10 @@ func TestCamt053TransactionDataFileParseImportedData_MissingTransactionRequiredN
|
|||||||
</Ntry>
|
</Ntry>
|
||||||
</Stmt>
|
</Stmt>
|
||||||
</BkToCstmrStmt>
|
</BkToCstmrStmt>
|
||||||
</Document>`), 0, nil, nil, nil, nil, nil)
|
</Document>`), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrTransactionTypeInvalid.Message)
|
assert.EqualError(t, err, errs.ErrTransactionTypeInvalid.Message)
|
||||||
|
|
||||||
_, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte(
|
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(
|
||||||
`<?xml version="1.0" encoding="UTF-8"?>
|
`<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<Document xmlns="urn:iso:std:iso:20022:tech:xsd:camt.053.001.02">
|
<Document xmlns="urn:iso:std:iso:20022:tech:xsd:camt.053.001.02">
|
||||||
<BkToCstmrStmt>
|
<BkToCstmrStmt>
|
||||||
@@ -738,10 +843,10 @@ func TestCamt053TransactionDataFileParseImportedData_MissingTransactionRequiredN
|
|||||||
</Ntry>
|
</Ntry>
|
||||||
</Stmt>
|
</Stmt>
|
||||||
</BkToCstmrStmt>
|
</BkToCstmrStmt>
|
||||||
</Document>`), 0, nil, nil, nil, nil, nil)
|
</Document>`), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrAmountInvalid.Message)
|
assert.EqualError(t, err, errs.ErrAmountInvalid.Message)
|
||||||
|
|
||||||
_, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte(
|
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(
|
||||||
`<?xml version="1.0" encoding="UTF-8"?>
|
`<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<Document xmlns="urn:iso:std:iso:20022:tech:xsd:camt.053.001.02">
|
<Document xmlns="urn:iso:std:iso:20022:tech:xsd:camt.053.001.02">
|
||||||
<BkToCstmrStmt>
|
<BkToCstmrStmt>
|
||||||
@@ -760,6 +865,6 @@ func TestCamt053TransactionDataFileParseImportedData_MissingTransactionRequiredN
|
|||||||
</Ntry>
|
</Ntry>
|
||||||
</Stmt>
|
</Stmt>
|
||||||
</BkToCstmrStmt>
|
</BkToCstmrStmt>
|
||||||
</Document>`), 0, nil, nil, nil, nil, nil)
|
</Document>`), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrAccountCurrencyInvalid.Message)
|
assert.EqualError(t, err, errs.ErrAccountCurrencyInvalid.Message)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,10 +28,11 @@ func (c *DataTableTransactionDataExporter) BuildExportedContent(ctx core.Context
|
|||||||
}
|
}
|
||||||
|
|
||||||
dataRowMap := make(map[datatable.TransactionDataTableColumn]string, 15)
|
dataRowMap := make(map[datatable.TransactionDataTableColumn]string, 15)
|
||||||
|
transactionUnixTime := utils.GetUnixTimeFromTransactionTime(transaction.TransactionTime)
|
||||||
transactionTimeZone := time.FixedZone("Transaction Timezone", int(transaction.TimezoneUtcOffset)*60)
|
transactionTimeZone := time.FixedZone("Transaction Timezone", int(transaction.TimezoneUtcOffset)*60)
|
||||||
|
|
||||||
dataRowMap[datatable.TRANSACTION_DATA_TABLE_TRANSACTION_TIME] = utils.FormatUnixTimeToLongDateTime(utils.GetUnixTimeFromTransactionTime(transaction.TransactionTime), transactionTimeZone)
|
dataRowMap[datatable.TRANSACTION_DATA_TABLE_TRANSACTION_TIME] = utils.FormatUnixTimeToLongDateTime(transactionUnixTime, transactionTimeZone)
|
||||||
dataRowMap[datatable.TRANSACTION_DATA_TABLE_TRANSACTION_TIMEZONE] = utils.FormatTimezoneOffset(transactionTimeZone)
|
dataRowMap[datatable.TRANSACTION_DATA_TABLE_TRANSACTION_TIMEZONE] = utils.FormatTimezoneOffset(transactionUnixTime, transactionTimeZone)
|
||||||
dataRowMap[datatable.TRANSACTION_DATA_TABLE_TRANSACTION_TYPE] = dataTableBuilder.ReplaceDelimiters(c.getDisplayTransactionTypeName(transaction.Type))
|
dataRowMap[datatable.TRANSACTION_DATA_TABLE_TRANSACTION_TYPE] = dataTableBuilder.ReplaceDelimiters(c.getDisplayTransactionTypeName(transaction.Type))
|
||||||
dataRowMap[datatable.TRANSACTION_DATA_TABLE_CATEGORY] = c.getExportedTransactionCategoryName(dataTableBuilder, transaction.CategoryId, categoryMap)
|
dataRowMap[datatable.TRANSACTION_DATA_TABLE_CATEGORY] = c.getExportedTransactionCategoryName(dataTableBuilder, transaction.CategoryId, categoryMap)
|
||||||
dataRowMap[datatable.TRANSACTION_DATA_TABLE_SUB_CATEGORY] = c.getExportedTransactionSubCategoryName(dataTableBuilder, transaction.CategoryId, categoryMap)
|
dataRowMap[datatable.TRANSACTION_DATA_TABLE_SUB_CATEGORY] = c.getExportedTransactionSubCategoryName(dataTableBuilder, transaction.CategoryId, categoryMap)
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package converter
|
|||||||
import (
|
import (
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/converters/datatable"
|
"github.com/mayswind/ezbookkeeping/pkg/converters/datatable"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/core"
|
"github.com/mayswind/ezbookkeeping/pkg/core"
|
||||||
@@ -29,7 +30,7 @@ type DataTableTransactionDataImporter struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ParseImportedData returns the imported transaction data
|
// ParseImportedData returns the imported transaction data
|
||||||
func (c *DataTableTransactionDataImporter) ParseImportedData(ctx core.Context, user *models.User, dataTable datatable.TransactionDataTable, defaultTimezoneOffset int16, accountMap map[string]*models.Account, expenseCategoryMap map[string]map[string]*models.TransactionCategory, incomeCategoryMap map[string]map[string]*models.TransactionCategory, transferCategoryMap map[string]map[string]*models.TransactionCategory, tagMap map[string]*models.TransactionTag) (models.ImportedTransactionSlice, []*models.Account, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionTag, error) {
|
func (c *DataTableTransactionDataImporter) ParseImportedData(ctx core.Context, user *models.User, dataTable datatable.TransactionDataTable, defaultTimezone *time.Location, additionalOptions TransactionDataImporterOptions, accountMap map[string]*models.Account, expenseCategoryMap map[string]map[string]*models.TransactionCategory, incomeCategoryMap map[string]map[string]*models.TransactionCategory, transferCategoryMap map[string]map[string]*models.TransactionCategory, tagMap map[string]*models.TransactionTag) (models.ImportedTransactionSlice, []*models.Account, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionTag, error) {
|
||||||
if dataTable.TransactionRowCount() < 1 {
|
if dataTable.TransactionRowCount() < 1 {
|
||||||
log.Errorf(ctx, "[data_table_transaction_data_importer.ParseImportedData] cannot parse import data for user \"uid:%d\", because data table row count is less 1", user.Uid)
|
log.Errorf(ctx, "[data_table_transaction_data_importer.ParseImportedData] cannot parse import data for user \"uid:%d\", because data table row count is less 1", user.Uid)
|
||||||
return nil, nil, nil, nil, nil, nil, errs.ErrNotFoundTransactionDataInFile
|
return nil, nil, nil, nil, nil, nil, errs.ErrNotFoundTransactionDataInFile
|
||||||
@@ -94,7 +95,7 @@ func (c *DataTableTransactionDataImporter) ParseImportedData(ctx core.Context, u
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
timezoneOffset := defaultTimezoneOffset
|
timezone := defaultTimezone
|
||||||
|
|
||||||
if dataTable.HasColumn(datatable.TRANSACTION_DATA_TABLE_TRANSACTION_TIMEZONE) &&
|
if dataTable.HasColumn(datatable.TRANSACTION_DATA_TABLE_TRANSACTION_TIMEZONE) &&
|
||||||
dataRow.GetData(datatable.TRANSACTION_DATA_TABLE_TRANSACTION_TIMEZONE) != datatable.TRANSACTION_DATA_TABLE_TIMEZONE_NOT_AVAILABLE {
|
dataRow.GetData(datatable.TRANSACTION_DATA_TABLE_TRANSACTION_TIMEZONE) != datatable.TRANSACTION_DATA_TABLE_TIMEZONE_NOT_AVAILABLE {
|
||||||
@@ -105,10 +106,10 @@ func (c *DataTableTransactionDataImporter) ParseImportedData(ctx core.Context, u
|
|||||||
return nil, nil, nil, nil, nil, nil, errs.ErrTransactionTimeZoneInvalid
|
return nil, nil, nil, nil, nil, nil, errs.ErrTransactionTimeZoneInvalid
|
||||||
}
|
}
|
||||||
|
|
||||||
timezoneOffset = utils.GetTimezoneOffsetMinutes(transactionTimezone)
|
timezone = transactionTimezone
|
||||||
}
|
}
|
||||||
|
|
||||||
transactionTime, err := utils.ParseFromLongDateTime(dataRow.GetData(datatable.TRANSACTION_DATA_TABLE_TRANSACTION_TIME), timezoneOffset)
|
transactionTime, err := utils.ParseFromLongDateTimeInTimeZone(dataRow.GetData(datatable.TRANSACTION_DATA_TABLE_TRANSACTION_TIME), timezone)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf(ctx, "[data_table_transaction_data_importer.ParseImportedData] cannot parse time \"%s\" in data row \"index:%d\" for user \"uid:%d\", because %s", dataRow.GetData(datatable.TRANSACTION_DATA_TABLE_TRANSACTION_TIME), dataRowIndex, user.Uid, err.Error())
|
log.Errorf(ctx, "[data_table_transaction_data_importer.ParseImportedData] cannot parse time \"%s\" in data row \"index:%d\" for user \"uid:%d\", because %s", dataRow.GetData(datatable.TRANSACTION_DATA_TABLE_TRANSACTION_TIME), dataRowIndex, user.Uid, err.Error())
|
||||||
@@ -303,6 +304,7 @@ func (c *DataTableTransactionDataImporter) ParseImportedData(ctx core.Context, u
|
|||||||
|
|
||||||
var tagIds []string
|
var tagIds []string
|
||||||
var tagNames []string
|
var tagNames []string
|
||||||
|
tagNamesMap := make(map[string]bool)
|
||||||
|
|
||||||
if dataTable.HasColumn(datatable.TRANSACTION_DATA_TABLE_TAGS) {
|
if dataTable.HasColumn(datatable.TRANSACTION_DATA_TABLE_TAGS) {
|
||||||
var tagNameItems []string
|
var tagNameItems []string
|
||||||
@@ -320,19 +322,39 @@ func (c *DataTableTransactionDataImporter) ParseImportedData(ctx core.Context, u
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
tag, exists := tagMap[tagName]
|
allNewTags, tagIds, tagNames = c.addTag(user, tagName, tagNamesMap, tagMap, allNewTags, tagIds, tagNames)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if !exists {
|
if dataTable.HasColumn(datatable.TRANSACTION_DATA_TABLE_PAYEE) && additionalOptions.IsPayeeAsTag() {
|
||||||
tag = c.createNewTransactionTagModel(user.Uid, tagName)
|
payee := dataRow.GetData(datatable.TRANSACTION_DATA_TABLE_PAYEE)
|
||||||
allNewTags = append(allNewTags, tag)
|
|
||||||
tagMap[tagName] = tag
|
|
||||||
}
|
|
||||||
|
|
||||||
if tag != nil {
|
if payee != "" {
|
||||||
tagIds = append(tagIds, utils.Int64ToString(tag.TagId))
|
allNewTags, tagIds, tagNames = c.addTag(user, payee, tagNamesMap, tagMap, allNewTags, tagIds, tagNames)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
tagNames = append(tagNames, tagName)
|
if dataTable.HasColumn(datatable.TRANSACTION_DATA_TABLE_MEMBER) && additionalOptions.IsMemberAsTag() {
|
||||||
|
member := dataRow.GetData(datatable.TRANSACTION_DATA_TABLE_MEMBER)
|
||||||
|
|
||||||
|
if member != "" {
|
||||||
|
allNewTags, tagIds, tagNames = c.addTag(user, member, tagNamesMap, tagMap, allNewTags, tagIds, tagNames)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if dataTable.HasColumn(datatable.TRANSACTION_DATA_TABLE_PROJECT) && additionalOptions.IsProjectAsTag() {
|
||||||
|
project := dataRow.GetData(datatable.TRANSACTION_DATA_TABLE_PROJECT)
|
||||||
|
|
||||||
|
if project != "" {
|
||||||
|
allNewTags, tagIds, tagNames = c.addTag(user, project, tagNamesMap, tagMap, allNewTags, tagIds, tagNames)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if dataTable.HasColumn(datatable.TRANSACTION_DATA_TABLE_MERCHANT) && additionalOptions.IsMerchantAsTag() {
|
||||||
|
merchant := dataRow.GetData(datatable.TRANSACTION_DATA_TABLE_MERCHANT)
|
||||||
|
|
||||||
|
if merchant != "" {
|
||||||
|
allNewTags, tagIds, tagNames = c.addTag(user, merchant, tagNamesMap, tagMap, allNewTags, tagIds, tagNames)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -342,13 +364,17 @@ func (c *DataTableTransactionDataImporter) ParseImportedData(ctx core.Context, u
|
|||||||
description = dataRow.GetData(datatable.TRANSACTION_DATA_TABLE_DESCRIPTION)
|
description = dataRow.GetData(datatable.TRANSACTION_DATA_TABLE_DESCRIPTION)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if description == "" && additionalOptions.IsPayeeAsDescription() && dataTable.HasColumn(datatable.TRANSACTION_DATA_TABLE_PAYEE) {
|
||||||
|
description = dataRow.GetData(datatable.TRANSACTION_DATA_TABLE_PAYEE)
|
||||||
|
}
|
||||||
|
|
||||||
transaction := &models.ImportTransaction{
|
transaction := &models.ImportTransaction{
|
||||||
Transaction: &models.Transaction{
|
Transaction: &models.Transaction{
|
||||||
Uid: user.Uid,
|
Uid: user.Uid,
|
||||||
Type: transactionDbType,
|
Type: transactionDbType,
|
||||||
CategoryId: categoryId,
|
CategoryId: categoryId,
|
||||||
TransactionTime: utils.GetMinTransactionTimeFromUnixTime(transactionTime.Unix()),
|
TransactionTime: utils.GetMinTransactionTimeFromUnixTime(transactionTime.Unix()),
|
||||||
TimezoneUtcOffset: timezoneOffset,
|
TimezoneUtcOffset: utils.GetTimezoneOffsetMinutes(transactionTime.Unix(), timezone),
|
||||||
AccountId: account.AccountId,
|
AccountId: account.AccountId,
|
||||||
Amount: amount,
|
Amount: amount,
|
||||||
HideAmount: false,
|
HideAmount: false,
|
||||||
@@ -459,6 +485,27 @@ func (c *DataTableTransactionDataImporter) getTransactionCategory(categories map
|
|||||||
return subCategory, exists
|
return subCategory, exists
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *DataTableTransactionDataImporter) addTag(user *models.User, tagName string, tagNamesMap map[string]bool, tagMap map[string]*models.TransactionTag, allNewTags []*models.TransactionTag, tagIds []string, tagNames []string) ([]*models.TransactionTag, []string, []string) {
|
||||||
|
if tagName != "" && !tagNamesMap[tagName] {
|
||||||
|
tag, exists := tagMap[tagName]
|
||||||
|
|
||||||
|
if !exists {
|
||||||
|
tag = c.createNewTransactionTagModel(user.Uid, tagName)
|
||||||
|
allNewTags = append(allNewTags, tag)
|
||||||
|
tagMap[tagName] = tag
|
||||||
|
}
|
||||||
|
|
||||||
|
if tag != nil {
|
||||||
|
tagIds = append(tagIds, utils.Int64ToString(tag.TagId))
|
||||||
|
}
|
||||||
|
|
||||||
|
tagNames = append(tagNames, tagName)
|
||||||
|
tagNamesMap[tagName] = true
|
||||||
|
}
|
||||||
|
|
||||||
|
return allNewTags, tagIds, tagNames
|
||||||
|
}
|
||||||
|
|
||||||
func (c *DataTableTransactionDataImporter) createNewAccountModel(uid int64, accountName string, currency string) *models.Account {
|
func (c *DataTableTransactionDataImporter) createNewAccountModel(uid int64, accountName string, currency string) *models.Account {
|
||||||
return &models.Account{
|
return &models.Account{
|
||||||
Uid: uid,
|
Uid: uid,
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
package converter
|
package converter
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/core"
|
"github.com/mayswind/ezbookkeeping/pkg/core"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/models"
|
"github.com/mayswind/ezbookkeeping/pkg/models"
|
||||||
)
|
)
|
||||||
@@ -14,7 +16,7 @@ type TransactionDataExporter interface {
|
|||||||
// TransactionDataImporter defines the structure of transaction data importer
|
// TransactionDataImporter defines the structure of transaction data importer
|
||||||
type TransactionDataImporter interface {
|
type TransactionDataImporter interface {
|
||||||
// ParseImportedData returns the imported data
|
// ParseImportedData returns the imported data
|
||||||
ParseImportedData(ctx core.Context, user *models.User, data []byte, defaultTimezoneOffset int16, accountMap map[string]*models.Account, expenseCategoryMap map[string]map[string]*models.TransactionCategory, incomeCategoryMap map[string]map[string]*models.TransactionCategory, transferCategoryMap map[string]map[string]*models.TransactionCategory, tagMap map[string]*models.TransactionTag) (models.ImportedTransactionSlice, []*models.Account, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionTag, error)
|
ParseImportedData(ctx core.Context, user *models.User, data []byte, defaultTimezone *time.Location, additionalOptions TransactionDataImporterOptions, accountMap map[string]*models.Account, expenseCategoryMap map[string]map[string]*models.TransactionCategory, incomeCategoryMap map[string]map[string]*models.TransactionCategory, transferCategoryMap map[string]map[string]*models.TransactionCategory, tagMap map[string]*models.TransactionTag) (models.ImportedTransactionSlice, []*models.Account, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionTag, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TransactionDataConverter defines the structure of transaction data converter
|
// TransactionDataConverter defines the structure of transaction data converter
|
||||||
|
|||||||
@@ -0,0 +1,118 @@
|
|||||||
|
package converter
|
||||||
|
|
||||||
|
import "strings"
|
||||||
|
|
||||||
|
// TransactionDataImporterOptions defines the options for transaction data importer
|
||||||
|
type TransactionDataImporterOptions struct {
|
||||||
|
payeeAsTag bool
|
||||||
|
payeeAsDescription bool
|
||||||
|
memberAsTag bool
|
||||||
|
projectAsTag bool
|
||||||
|
merchantAsTag bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// DefaultImporterOptions provides the default options for transaction data importer
|
||||||
|
var DefaultImporterOptions = TransactionDataImporterOptions{
|
||||||
|
payeeAsTag: false,
|
||||||
|
payeeAsDescription: false,
|
||||||
|
memberAsTag: false,
|
||||||
|
projectAsTag: false,
|
||||||
|
merchantAsTag: false,
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsPayeeAsTag returns whether to import payee as tag
|
||||||
|
func (o TransactionDataImporterOptions) IsPayeeAsTag() bool {
|
||||||
|
return o.payeeAsTag
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsPayeeAsDescription returns whether to import payee as description
|
||||||
|
func (o TransactionDataImporterOptions) IsPayeeAsDescription() bool {
|
||||||
|
return o.payeeAsDescription
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsMemberAsTag returns whether to import member as tag
|
||||||
|
func (o TransactionDataImporterOptions) IsMemberAsTag() bool {
|
||||||
|
return o.memberAsTag
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsProjectAsTag returns whether to import project as tag
|
||||||
|
func (o TransactionDataImporterOptions) IsProjectAsTag() bool {
|
||||||
|
return o.projectAsTag
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsMerchantAsTag returns whether to import merchant as tag
|
||||||
|
func (o TransactionDataImporterOptions) IsMerchantAsTag() bool {
|
||||||
|
return o.merchantAsTag
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithPayeeAsTag sets the option to import payee as tag
|
||||||
|
func (o TransactionDataImporterOptions) WithPayeeAsTag() TransactionDataImporterOptions {
|
||||||
|
cloned := o.Clone()
|
||||||
|
cloned.payeeAsTag = true
|
||||||
|
return cloned
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithPayeeAsDescription sets the option to import payee as description
|
||||||
|
func (o TransactionDataImporterOptions) WithPayeeAsDescription() TransactionDataImporterOptions {
|
||||||
|
cloned := o.Clone()
|
||||||
|
cloned.payeeAsDescription = true
|
||||||
|
return cloned
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithMemberAsTag sets the option to import member as tag
|
||||||
|
func (o TransactionDataImporterOptions) WithMemberAsTag() TransactionDataImporterOptions {
|
||||||
|
cloned := o.Clone()
|
||||||
|
cloned.memberAsTag = true
|
||||||
|
return cloned
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithProjectAsTag sets the option to import project as tag
|
||||||
|
func (o TransactionDataImporterOptions) WithProjectAsTag() TransactionDataImporterOptions {
|
||||||
|
cloned := o.Clone()
|
||||||
|
cloned.projectAsTag = true
|
||||||
|
return cloned
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithMerchantAsTag sets the option to import merchant as tag
|
||||||
|
func (o TransactionDataImporterOptions) WithMerchantAsTag() TransactionDataImporterOptions {
|
||||||
|
cloned := o.Clone()
|
||||||
|
cloned.merchantAsTag = true
|
||||||
|
return cloned
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clone creates a copy of the options instance
|
||||||
|
func (o TransactionDataImporterOptions) Clone() TransactionDataImporterOptions {
|
||||||
|
return TransactionDataImporterOptions{
|
||||||
|
payeeAsTag: o.payeeAsTag,
|
||||||
|
payeeAsDescription: o.payeeAsDescription,
|
||||||
|
memberAsTag: o.memberAsTag,
|
||||||
|
projectAsTag: o.projectAsTag,
|
||||||
|
merchantAsTag: o.merchantAsTag,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseImporterOptions parses the textual options to the instance
|
||||||
|
func ParseImporterOptions(s string) TransactionDataImporterOptions {
|
||||||
|
options := TransactionDataImporterOptions{}
|
||||||
|
|
||||||
|
if s == "" {
|
||||||
|
return options
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, option := range strings.Split(s, ",") {
|
||||||
|
switch option {
|
||||||
|
case "payeeAsTag":
|
||||||
|
options.payeeAsTag = true
|
||||||
|
case "payeeAsDescription":
|
||||||
|
options.payeeAsDescription = true
|
||||||
|
case "memberAsTag":
|
||||||
|
options.memberAsTag = true
|
||||||
|
case "projectAsTag":
|
||||||
|
options.projectAsTag = true
|
||||||
|
case "merchantAsTag":
|
||||||
|
options.merchantAsTag = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return options
|
||||||
|
}
|
||||||
@@ -0,0 +1,110 @@
|
|||||||
|
package converter
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestParseImporterOptions(t *testing.T) {
|
||||||
|
actualValue := ParseImporterOptions("payeeAsTag,memberAsTag")
|
||||||
|
expectedValue := TransactionDataImporterOptions{
|
||||||
|
payeeAsTag: true,
|
||||||
|
memberAsTag: true,
|
||||||
|
projectAsTag: false,
|
||||||
|
merchantAsTag: false,
|
||||||
|
}
|
||||||
|
assert.Equal(t, expectedValue, actualValue)
|
||||||
|
assert.Equal(t, true, actualValue.IsPayeeAsTag())
|
||||||
|
assert.Equal(t, true, actualValue.IsMemberAsTag())
|
||||||
|
assert.Equal(t, false, actualValue.IsProjectAsTag())
|
||||||
|
assert.Equal(t, false, actualValue.IsMerchantAsTag())
|
||||||
|
|
||||||
|
actualValue = ParseImporterOptions("")
|
||||||
|
expectedValue = TransactionDataImporterOptions{
|
||||||
|
payeeAsTag: false,
|
||||||
|
memberAsTag: false,
|
||||||
|
projectAsTag: false,
|
||||||
|
merchantAsTag: false,
|
||||||
|
}
|
||||||
|
assert.Equal(t, expectedValue, actualValue)
|
||||||
|
assert.Equal(t, false, actualValue.IsPayeeAsTag())
|
||||||
|
assert.Equal(t, false, actualValue.IsMemberAsTag())
|
||||||
|
assert.Equal(t, false, actualValue.IsProjectAsTag())
|
||||||
|
assert.Equal(t, false, actualValue.IsMerchantAsTag())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestParseImporterOptions_WithAllOptions(t *testing.T) {
|
||||||
|
actualValue := ParseImporterOptions("payeeAsTag,payeeAsDescription,memberAsTag,projectAsTag,merchantAsTag")
|
||||||
|
expectedValue := TransactionDataImporterOptions{
|
||||||
|
payeeAsTag: true,
|
||||||
|
payeeAsDescription: true,
|
||||||
|
memberAsTag: true,
|
||||||
|
projectAsTag: true,
|
||||||
|
merchantAsTag: true,
|
||||||
|
}
|
||||||
|
assert.Equal(t, expectedValue, actualValue)
|
||||||
|
assert.Equal(t, true, actualValue.IsPayeeAsTag())
|
||||||
|
assert.Equal(t, true, actualValue.IsPayeeAsDescription())
|
||||||
|
assert.Equal(t, true, actualValue.IsMemberAsTag())
|
||||||
|
assert.Equal(t, true, actualValue.IsProjectAsTag())
|
||||||
|
assert.Equal(t, true, actualValue.IsMerchantAsTag())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestParseImporterOptions_WithInvalidOptions(t *testing.T) {
|
||||||
|
actualValue := ParseImporterOptions("invalidOption,payeeAsTag,memberAsTag")
|
||||||
|
expectedValue := TransactionDataImporterOptions{
|
||||||
|
payeeAsTag: true,
|
||||||
|
memberAsTag: true,
|
||||||
|
projectAsTag: false,
|
||||||
|
merchantAsTag: false,
|
||||||
|
}
|
||||||
|
assert.Equal(t, expectedValue, actualValue)
|
||||||
|
assert.Equal(t, true, actualValue.IsPayeeAsTag())
|
||||||
|
assert.Equal(t, true, actualValue.IsMemberAsTag())
|
||||||
|
assert.Equal(t, false, actualValue.IsProjectAsTag())
|
||||||
|
assert.Equal(t, false, actualValue.IsMerchantAsTag())
|
||||||
|
|
||||||
|
actualValue = ParseImporterOptions("invalidOption")
|
||||||
|
expectedValue = TransactionDataImporterOptions{
|
||||||
|
payeeAsTag: false,
|
||||||
|
memberAsTag: false,
|
||||||
|
projectAsTag: false,
|
||||||
|
merchantAsTag: false,
|
||||||
|
}
|
||||||
|
assert.Equal(t, expectedValue, actualValue)
|
||||||
|
assert.Equal(t, false, actualValue.IsPayeeAsTag())
|
||||||
|
assert.Equal(t, false, actualValue.IsMemberAsTag())
|
||||||
|
assert.Equal(t, false, actualValue.IsProjectAsTag())
|
||||||
|
assert.Equal(t, false, actualValue.IsMerchantAsTag())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestParseImporterOptions_Clone(t *testing.T) {
|
||||||
|
original := TransactionDataImporterOptions{
|
||||||
|
payeeAsTag: true,
|
||||||
|
payeeAsDescription: false,
|
||||||
|
memberAsTag: false,
|
||||||
|
projectAsTag: true,
|
||||||
|
merchantAsTag: false,
|
||||||
|
}
|
||||||
|
|
||||||
|
cloned := original.Clone()
|
||||||
|
assert.Equal(t, original, cloned)
|
||||||
|
|
||||||
|
// Modify cloned options and verify original options are not affected
|
||||||
|
cloned.payeeAsTag = false
|
||||||
|
cloned.payeeAsDescription = true
|
||||||
|
cloned.memberAsTag = true
|
||||||
|
|
||||||
|
assert.Equal(t, true, original.payeeAsTag)
|
||||||
|
assert.Equal(t, false, original.payeeAsDescription)
|
||||||
|
assert.Equal(t, false, original.memberAsTag)
|
||||||
|
assert.Equal(t, true, original.projectAsTag)
|
||||||
|
assert.Equal(t, false, original.merchantAsTag)
|
||||||
|
|
||||||
|
assert.Equal(t, false, cloned.payeeAsTag)
|
||||||
|
assert.Equal(t, true, cloned.payeeAsDescription)
|
||||||
|
assert.Equal(t, true, cloned.memberAsTag)
|
||||||
|
assert.Equal(t, true, cloned.projectAsTag)
|
||||||
|
assert.Equal(t, false, cloned.merchantAsTag)
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
package custom
|
||||||
|
|
||||||
|
import "github.com/mayswind/ezbookkeeping/pkg/core"
|
||||||
|
|
||||||
|
// CustomTransactionDataParser represents the parser for custom transaction data files
|
||||||
|
type CustomTransactionDataParser interface {
|
||||||
|
ParseDataLines(ctx core.Context, data []byte) ([][]string, error)
|
||||||
|
}
|
||||||
+71
-63
@@ -1,10 +1,11 @@
|
|||||||
package dsv
|
package custom
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/csv"
|
"encoding/csv"
|
||||||
"io"
|
"io"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"golang.org/x/text/encoding"
|
"golang.org/x/text/encoding"
|
||||||
"golang.org/x/text/encoding/charmap"
|
"golang.org/x/text/encoding/charmap"
|
||||||
@@ -13,6 +14,7 @@ import (
|
|||||||
"golang.org/x/text/encoding/simplifiedchinese"
|
"golang.org/x/text/encoding/simplifiedchinese"
|
||||||
"golang.org/x/text/encoding/traditionalchinese"
|
"golang.org/x/text/encoding/traditionalchinese"
|
||||||
"golang.org/x/text/encoding/unicode"
|
"golang.org/x/text/encoding/unicode"
|
||||||
|
"golang.org/x/text/encoding/unicode/utf32"
|
||||||
"golang.org/x/text/transform"
|
"golang.org/x/text/transform"
|
||||||
|
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/converters/converter"
|
"github.com/mayswind/ezbookkeeping/pkg/converters/converter"
|
||||||
@@ -28,59 +30,61 @@ import (
|
|||||||
var supportedFileTypeSeparators = map[string]rune{
|
var supportedFileTypeSeparators = map[string]rune{
|
||||||
"custom_csv": ',',
|
"custom_csv": ',',
|
||||||
"custom_tsv": '\t',
|
"custom_tsv": '\t',
|
||||||
|
"custom_ssv": ';',
|
||||||
}
|
}
|
||||||
|
|
||||||
var supportedFileEncodings = map[string]encoding.Encoding{
|
var supportedFileEncodings = map[string]encoding.Encoding{
|
||||||
"utf-8": unicode.UTF8, // UTF-8
|
"utf-8": unicode.UTF8BOM, // UTF-8
|
||||||
"utf-8-bom": unicode.UTF8BOM, // UTF-8 with BOM
|
"utf-16le": unicode.UTF16(unicode.LittleEndian, unicode.UseBOM), // UTF-16 Little Endian
|
||||||
"utf-16le": unicode.UTF16(unicode.LittleEndian, unicode.IgnoreBOM), // UTF-16 Little Endian
|
"utf-16be": unicode.UTF16(unicode.BigEndian, unicode.UseBOM), // UTF-16 Big Endian
|
||||||
"utf-16be": unicode.UTF16(unicode.BigEndian, unicode.IgnoreBOM), // UTF-16 Big Endian
|
"utf-32le": utf32.UTF32(utf32.LittleEndian, utf32.UseBOM), // UTF-32 Little Endian
|
||||||
"cp437": charmap.CodePage437, // OEM United States (CP-437)
|
"utf-32be": utf32.UTF32(utf32.BigEndian, utf32.UseBOM), // UTF-32 Big Endian
|
||||||
"cp863": charmap.CodePage863, // OEM Canadian French (CP-863)
|
"cp437": charmap.CodePage437, // OEM United States (CP-437)
|
||||||
"cp037": charmap.CodePage037, // IBM EBCDIC US/Canada (CP-037)
|
"cp863": charmap.CodePage863, // OEM Canadian French (CP-863)
|
||||||
"cp1047": charmap.CodePage1047, // IBM EBCDIC Open Systems (CP-1047)
|
"cp037": charmap.CodePage037, // IBM EBCDIC US/Canada (CP-037)
|
||||||
"cp1140": charmap.CodePage1140, // IBM EBCDIC US/Canada with Euro (CP-1140)
|
"cp1047": charmap.CodePage1047, // IBM EBCDIC Open Systems (CP-1047)
|
||||||
"iso-8859-1": charmap.ISO8859_1, // Western European (ISO-8859-1)
|
"cp1140": charmap.CodePage1140, // IBM EBCDIC US/Canada with Euro (CP-1140)
|
||||||
"cp850": charmap.CodePage850, // Western European (CP-850)
|
"iso-8859-1": charmap.ISO8859_1, // Western European (ISO-8859-1)
|
||||||
"cp858": charmap.CodePage858, // Western European with Euro (CP-858)
|
"cp850": charmap.CodePage850, // Western European (CP-850)
|
||||||
"windows-1252": charmap.Windows1252, // Western European (Windows-1252)
|
"cp858": charmap.CodePage858, // Western European with Euro (CP-858)
|
||||||
"iso-8859-15": charmap.ISO8859_15, // Western European (ISO-8859-15)
|
"windows-1252": charmap.Windows1252, // Western European (Windows-1252)
|
||||||
"iso-8859-4": charmap.ISO8859_4, // North European (ISO-8859-4)
|
"iso-8859-15": charmap.ISO8859_15, // Western European (ISO-8859-15)
|
||||||
"iso-8859-10": charmap.ISO8859_10, // North European (ISO-8859-10)
|
"iso-8859-4": charmap.ISO8859_4, // North European (ISO-8859-4)
|
||||||
"cp865": charmap.CodePage865, // North European (CP-865)
|
"iso-8859-10": charmap.ISO8859_10, // North European (ISO-8859-10)
|
||||||
"iso-8859-2": charmap.ISO8859_2, // Central European (ISO-8859-2)
|
"cp865": charmap.CodePage865, // North European (CP-865)
|
||||||
"cp852": charmap.CodePage852, // Central European (CP-852)
|
"iso-8859-2": charmap.ISO8859_2, // Central European (ISO-8859-2)
|
||||||
"windows-1250": charmap.Windows1250, // Central European (Windows-1250)
|
"cp852": charmap.CodePage852, // Central European (CP-852)
|
||||||
"iso-8859-14": charmap.ISO8859_14, // Celtic (ISO-8859-14)
|
"windows-1250": charmap.Windows1250, // Central European (Windows-1250)
|
||||||
"iso-8859-3": charmap.ISO8859_3, // South European (ISO-8859-3)
|
"iso-8859-14": charmap.ISO8859_14, // Celtic (ISO-8859-14)
|
||||||
"cp860": charmap.CodePage860, // Portuguese (CP-860)
|
"iso-8859-3": charmap.ISO8859_3, // South European (ISO-8859-3)
|
||||||
"iso-8859-7": charmap.ISO8859_7, // Greek (ISO-8859-7)
|
"cp860": charmap.CodePage860, // Portuguese (CP-860)
|
||||||
"windows-1253": charmap.Windows1253, // Greek (Windows-1253)
|
"iso-8859-7": charmap.ISO8859_7, // Greek (ISO-8859-7)
|
||||||
"iso-8859-9": charmap.ISO8859_9, // Turkish (ISO-8859-9)
|
"windows-1253": charmap.Windows1253, // Greek (Windows-1253)
|
||||||
"windows-1254": charmap.Windows1254, // Turkish (Windows-1254)
|
"iso-8859-9": charmap.ISO8859_9, // Turkish (ISO-8859-9)
|
||||||
"iso-8859-13": charmap.ISO8859_13, // Baltic (ISO-8859-13)
|
"windows-1254": charmap.Windows1254, // Turkish (Windows-1254)
|
||||||
"windows-1257": charmap.Windows1257, // Baltic (Windows-1257)
|
"iso-8859-13": charmap.ISO8859_13, // Baltic (ISO-8859-13)
|
||||||
"iso-8859-16": charmap.ISO8859_16, // South-Eastern European (ISO-8859-16)
|
"windows-1257": charmap.Windows1257, // Baltic (Windows-1257)
|
||||||
"iso-8859-5": charmap.ISO8859_5, // Cyrillic (ISO-8859-5)
|
"iso-8859-16": charmap.ISO8859_16, // South-Eastern European (ISO-8859-16)
|
||||||
"cp855": charmap.CodePage855, // Cyrillic (CP-855)
|
"iso-8859-5": charmap.ISO8859_5, // Cyrillic (ISO-8859-5)
|
||||||
"cp866": charmap.CodePage866, // Cyrillic (CP-866)
|
"cp855": charmap.CodePage855, // Cyrillic (CP-855)
|
||||||
"windows-1251": charmap.Windows1251, // Cyrillic (Windows-1251)
|
"cp866": charmap.CodePage866, // Cyrillic (CP-866)
|
||||||
"koi8r": charmap.KOI8R, // Cyrillic (KOI8-R)
|
"windows-1251": charmap.Windows1251, // Cyrillic (Windows-1251)
|
||||||
"koi8u": charmap.KOI8U, // Cyrillic (KOI8-U)
|
"koi8r": charmap.KOI8R, // Cyrillic (KOI8-R)
|
||||||
"iso-8859-6": charmap.ISO8859_6, // Arabic (ISO-8859-6)
|
"koi8u": charmap.KOI8U, // Cyrillic (KOI8-U)
|
||||||
"windows-1256": charmap.Windows1256, // Arabic (Windows-1256)
|
"iso-8859-6": charmap.ISO8859_6, // Arabic (ISO-8859-6)
|
||||||
"iso-8859-8": charmap.ISO8859_8, // Hebrew (ISO-8859-8)
|
"windows-1256": charmap.Windows1256, // Arabic (Windows-1256)
|
||||||
"cp862": charmap.CodePage862, // Hebrew (CP-862)
|
"iso-8859-8": charmap.ISO8859_8, // Hebrew (ISO-8859-8)
|
||||||
"windows-1255": charmap.Windows1255, // Hebrew (Windows-1255)
|
"cp862": charmap.CodePage862, // Hebrew (CP-862)
|
||||||
"windows-874": charmap.Windows874, // Thai (Windows-874)
|
"windows-1255": charmap.Windows1255, // Hebrew (Windows-1255)
|
||||||
"windows-1258": charmap.Windows1258, // Vietnamese (Windows-1258)
|
"windows-874": charmap.Windows874, // Thai (Windows-874)
|
||||||
"gb18030": simplifiedchinese.GB18030, // Chinese (Simplified, GB18030)
|
"windows-1258": charmap.Windows1258, // Vietnamese (Windows-1258)
|
||||||
"gbk": simplifiedchinese.GBK, // Chinese (Simplified, GBK)
|
"gb18030": simplifiedchinese.GB18030, // Chinese (Simplified, GB18030)
|
||||||
"big5": traditionalchinese.Big5, // Chinese (Traditional, Big5)
|
"gbk": simplifiedchinese.GBK, // Chinese (Simplified, GBK)
|
||||||
"euc-kr": korean.EUCKR, // Korean (EUC-KR)
|
"big5": traditionalchinese.Big5, // Chinese (Traditional, Big5)
|
||||||
"euc-jp": japanese.EUCJP, // Japanese (EUC-JP)
|
"euc-kr": korean.EUCKR, // Korean (EUC-KR)
|
||||||
"iso-2022-jp": japanese.ISO2022JP, // Japanese (ISO-2022-JP)
|
"euc-jp": japanese.EUCJP, // Japanese (EUC-JP)
|
||||||
"shift_jis": japanese.ShiftJIS, // Japanese (Shift JIS)
|
"iso-2022-jp": japanese.ISO2022JP, // Japanese (ISO-2022-JP)
|
||||||
|
"shift_jis": japanese.ShiftJIS, // Japanese (Shift JIS)
|
||||||
}
|
}
|
||||||
|
|
||||||
var customTransactionTypeNameMapping = map[models.TransactionType]string{
|
var customTransactionTypeNameMapping = map[models.TransactionType]string{
|
||||||
@@ -90,10 +94,6 @@ var customTransactionTypeNameMapping = map[models.TransactionType]string{
|
|||||||
models.TRANSACTION_TYPE_TRANSFER: utils.IntToString(int(models.TRANSACTION_TYPE_TRANSFER)),
|
models.TRANSACTION_TYPE_TRANSFER: utils.IntToString(int(models.TRANSACTION_TYPE_TRANSFER)),
|
||||||
}
|
}
|
||||||
|
|
||||||
type CustomTransactionDataDsvFileParser interface {
|
|
||||||
ParseDsvFileLines(ctx core.Context, data []byte) ([][]string, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
// customTransactionDataDsvFileImporter defines the structure of custom dsv importer for transaction data
|
// customTransactionDataDsvFileImporter defines the structure of custom dsv importer for transaction data
|
||||||
type customTransactionDataDsvFileImporter struct {
|
type customTransactionDataDsvFileImporter struct {
|
||||||
fileEncoding encoding.Encoding
|
fileEncoding encoding.Encoding
|
||||||
@@ -110,8 +110,8 @@ type customTransactionDataDsvFileImporter struct {
|
|||||||
transactionTagSeparator string
|
transactionTagSeparator string
|
||||||
}
|
}
|
||||||
|
|
||||||
// ParseDsvFileLines returns the parsed file lines for specified the dsv file data
|
// ParseDataLines returns the parsed file lines for specified the dsv file data
|
||||||
func (c *customTransactionDataDsvFileImporter) ParseDsvFileLines(ctx core.Context, data []byte) ([][]string, error) {
|
func (c *customTransactionDataDsvFileImporter) ParseDataLines(ctx core.Context, data []byte) ([][]string, error) {
|
||||||
reader := transform.NewReader(bytes.NewReader(data), c.fileEncoding.NewDecoder())
|
reader := transform.NewReader(bytes.NewReader(data), c.fileEncoding.NewDecoder())
|
||||||
csvReader := csv.NewReader(reader)
|
csvReader := csv.NewReader(reader)
|
||||||
csvReader.Comma = c.separator
|
csvReader.Comma = c.separator
|
||||||
@@ -127,7 +127,7 @@ func (c *customTransactionDataDsvFileImporter) ParseDsvFileLines(ctx core.Contex
|
|||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf(ctx, "[custom_transaction_data_dsv_file_importer.ParseDsvFileLines] cannot parse dsv data, because %s", err.Error())
|
log.Errorf(ctx, "[custom_transaction_data_dsv_file_importer.ParseDataLines] cannot parse dsv data, because %s", err.Error())
|
||||||
return nil, errs.ErrInvalidCSVFile
|
return nil, errs.ErrInvalidCSVFile
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -146,8 +146,8 @@ func (c *customTransactionDataDsvFileImporter) ParseDsvFileLines(ctx core.Contex
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ParseImportedData returns the imported data by parsing the custom transaction dsv data
|
// ParseImportedData returns the imported data by parsing the custom transaction dsv data
|
||||||
func (c *customTransactionDataDsvFileImporter) ParseImportedData(ctx core.Context, user *models.User, data []byte, defaultTimezoneOffset int16, accountMap map[string]*models.Account, expenseCategoryMap map[string]map[string]*models.TransactionCategory, incomeCategoryMap map[string]map[string]*models.TransactionCategory, transferCategoryMap map[string]map[string]*models.TransactionCategory, tagMap map[string]*models.TransactionTag) (models.ImportedTransactionSlice, []*models.Account, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionTag, error) {
|
func (c *customTransactionDataDsvFileImporter) ParseImportedData(ctx core.Context, user *models.User, data []byte, defaultTimezone *time.Location, additionalOptions converter.TransactionDataImporterOptions, accountMap map[string]*models.Account, expenseCategoryMap map[string]map[string]*models.TransactionCategory, incomeCategoryMap map[string]map[string]*models.TransactionCategory, transferCategoryMap map[string]map[string]*models.TransactionCategory, tagMap map[string]*models.TransactionTag) (models.ImportedTransactionSlice, []*models.Account, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionTag, error) {
|
||||||
allLines, err := c.ParseDsvFileLines(ctx, data)
|
allLines, err := c.ParseDataLines(ctx, data)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, nil, nil, nil, nil, err
|
return nil, nil, nil, nil, nil, nil, err
|
||||||
@@ -157,7 +157,7 @@ func (c *customTransactionDataDsvFileImporter) ParseImportedData(ctx core.Contex
|
|||||||
transactionDataTable := CreateNewCustomPlainTextDataTable(dataTable, c.columnIndexMapping, c.transactionTypeNameMapping, c.timeFormat, c.timezoneFormat, c.amountDecimalSeparator, c.amountDigitGroupingSymbol)
|
transactionDataTable := CreateNewCustomPlainTextDataTable(dataTable, c.columnIndexMapping, c.transactionTypeNameMapping, c.timeFormat, c.timezoneFormat, c.amountDecimalSeparator, c.amountDigitGroupingSymbol)
|
||||||
dataTableImporter := converter.CreateNewImporterWithTypeNameMapping(customTransactionTypeNameMapping, c.geoLocationSeparator, c.geoLocationOrder, c.transactionTagSeparator)
|
dataTableImporter := converter.CreateNewImporterWithTypeNameMapping(customTransactionTypeNameMapping, c.geoLocationSeparator, c.geoLocationOrder, c.transactionTagSeparator)
|
||||||
|
|
||||||
return dataTableImporter.ParseImportedData(ctx, user, transactionDataTable, defaultTimezoneOffset, accountMap, expenseCategoryMap, incomeCategoryMap, transferCategoryMap, tagMap)
|
return dataTableImporter.ParseImportedData(ctx, user, transactionDataTable, defaultTimezone, additionalOptions, accountMap, expenseCategoryMap, incomeCategoryMap, transferCategoryMap, tagMap)
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsDelimiterSeparatedValuesFileType returns whether the file type is the delimiter-separated values file type
|
// IsDelimiterSeparatedValuesFileType returns whether the file type is the delimiter-separated values file type
|
||||||
@@ -166,14 +166,18 @@ func IsDelimiterSeparatedValuesFileType(fileType string) bool {
|
|||||||
return exists
|
return exists
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateNewCustomTransactionDataDsvFileParser returns a new custom dsv parser for transaction data
|
// CreateNewCustomTransactionDataDsvFileParser returns a new custom transaction data parser
|
||||||
func CreateNewCustomTransactionDataDsvFileParser(fileType string, fileEncoding string) (CustomTransactionDataDsvFileParser, error) {
|
func CreateNewCustomTransactionDataDsvFileParser(fileType string, fileEncoding string) (CustomTransactionDataParser, error) {
|
||||||
separator, exists := supportedFileTypeSeparators[fileType]
|
separator, exists := supportedFileTypeSeparators[fileType]
|
||||||
|
|
||||||
if !exists {
|
if !exists {
|
||||||
return nil, errs.ErrImportFileTypeNotSupported
|
return nil, errs.ErrImportFileTypeNotSupported
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if fileEncoding == "" {
|
||||||
|
return nil, errs.ErrImportFileEncodingIsEmpty
|
||||||
|
}
|
||||||
|
|
||||||
enc, exists := supportedFileEncodings[fileEncoding]
|
enc, exists := supportedFileEncodings[fileEncoding]
|
||||||
|
|
||||||
if !exists {
|
if !exists {
|
||||||
@@ -194,6 +198,10 @@ func CreateNewCustomTransactionDataDsvFileImporter(fileType string, fileEncoding
|
|||||||
return nil, errs.ErrImportFileTypeNotSupported
|
return nil, errs.ErrImportFileTypeNotSupported
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if fileEncoding == "" {
|
||||||
|
return nil, errs.ErrImportFileEncodingIsEmpty
|
||||||
|
}
|
||||||
|
|
||||||
enc, exists := supportedFileEncodings[fileEncoding]
|
enc, exists := supportedFileEncodings[fileEncoding]
|
||||||
|
|
||||||
if !exists {
|
if !exists {
|
||||||
+199
-119
@@ -1,10 +1,12 @@
|
|||||||
package dsv
|
package custom
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
|
"github.com/mayswind/ezbookkeeping/pkg/converters/converter"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/converters/datatable"
|
"github.com/mayswind/ezbookkeeping/pkg/converters/datatable"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/core"
|
"github.com/mayswind/ezbookkeeping/pkg/core"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/errs"
|
"github.com/mayswind/ezbookkeeping/pkg/errs"
|
||||||
@@ -15,19 +17,21 @@ import (
|
|||||||
func TestIsDelimiterSeparatedValuesFileType(t *testing.T) {
|
func TestIsDelimiterSeparatedValuesFileType(t *testing.T) {
|
||||||
assert.True(t, IsDelimiterSeparatedValuesFileType("custom_csv"))
|
assert.True(t, IsDelimiterSeparatedValuesFileType("custom_csv"))
|
||||||
assert.True(t, IsDelimiterSeparatedValuesFileType("custom_tsv"))
|
assert.True(t, IsDelimiterSeparatedValuesFileType("custom_tsv"))
|
||||||
|
assert.True(t, IsDelimiterSeparatedValuesFileType("custom_ssv"))
|
||||||
|
|
||||||
assert.False(t, IsDelimiterSeparatedValuesFileType("dsv"))
|
assert.False(t, IsDelimiterSeparatedValuesFileType("dsv"))
|
||||||
assert.False(t, IsDelimiterSeparatedValuesFileType("csv"))
|
assert.False(t, IsDelimiterSeparatedValuesFileType("csv"))
|
||||||
assert.False(t, IsDelimiterSeparatedValuesFileType("tsv"))
|
assert.False(t, IsDelimiterSeparatedValuesFileType("tsv"))
|
||||||
|
assert.False(t, IsDelimiterSeparatedValuesFileType("ssv"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCustomTransactionDataDsvFileParser_ParseDsvFileLines(t *testing.T) {
|
func TestCustomTransactionDataDsvFileParser_ParseDataLines(t *testing.T) {
|
||||||
converter, err := CreateNewCustomTransactionDataDsvFileParser("custom_csv", "utf-8")
|
importer, err := CreateNewCustomTransactionDataDsvFileParser("custom_csv", "utf-8")
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
allLines, err := converter.ParseDsvFileLines(context, []byte(
|
allLines, err := importer.ParseDataLines(context, []byte(
|
||||||
"2024-09-01 00:00:00,B,123.45\n"+
|
"2024-09-01 00:00:00,B,123.45\n"+
|
||||||
"2024-09-01 01:23:45,I,0.12\n"))
|
"2024-09-01 01:23:45,I,0.12\n"))
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
@@ -44,10 +48,10 @@ func TestCustomTransactionDataDsvFileParser_ParseDsvFileLines(t *testing.T) {
|
|||||||
assert.Equal(t, "I", allLines[1][1])
|
assert.Equal(t, "I", allLines[1][1])
|
||||||
assert.Equal(t, "0.12", allLines[1][2])
|
assert.Equal(t, "0.12", allLines[1][2])
|
||||||
|
|
||||||
converter, err = CreateNewCustomTransactionDataDsvFileParser("custom_tsv", "utf-8")
|
importer, err = CreateNewCustomTransactionDataDsvFileParser("custom_tsv", "utf-8")
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
allLines, err = converter.ParseDsvFileLines(context, []byte(
|
allLines, err = importer.ParseDataLines(context, []byte(
|
||||||
"2024-09-01 12:34:56\tE\t1.00\n"+
|
"2024-09-01 12:34:56\tE\t1.00\n"+
|
||||||
"2024-09-01 23:59:59\tT\t0.05"))
|
"2024-09-01 23:59:59\tT\t0.05"))
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
@@ -63,6 +67,26 @@ func TestCustomTransactionDataDsvFileParser_ParseDsvFileLines(t *testing.T) {
|
|||||||
assert.Equal(t, "2024-09-01 23:59:59", allLines[1][0])
|
assert.Equal(t, "2024-09-01 23:59:59", allLines[1][0])
|
||||||
assert.Equal(t, "T", allLines[1][1])
|
assert.Equal(t, "T", allLines[1][1])
|
||||||
assert.Equal(t, "0.05", allLines[1][2])
|
assert.Equal(t, "0.05", allLines[1][2])
|
||||||
|
|
||||||
|
importer, err = CreateNewCustomTransactionDataDsvFileParser("custom_ssv", "utf-8")
|
||||||
|
assert.Nil(t, err)
|
||||||
|
|
||||||
|
allLines, err = importer.ParseDataLines(context, []byte(
|
||||||
|
"2024-09-01 12:34:56;E;1.00\n"+
|
||||||
|
"2024-09-01 23:59:59;T;0.05"))
|
||||||
|
assert.Nil(t, err)
|
||||||
|
|
||||||
|
assert.Equal(t, 2, len(allLines))
|
||||||
|
|
||||||
|
assert.Equal(t, 3, len(allLines[0]))
|
||||||
|
assert.Equal(t, "2024-09-01 12:34:56", allLines[0][0])
|
||||||
|
assert.Equal(t, "E", allLines[0][1])
|
||||||
|
assert.Equal(t, "1.00", allLines[0][2])
|
||||||
|
|
||||||
|
assert.Equal(t, 3, len(allLines[1]))
|
||||||
|
assert.Equal(t, "2024-09-01 23:59:59", allLines[1][0])
|
||||||
|
assert.Equal(t, "T", allLines[1][1])
|
||||||
|
assert.Equal(t, "0.05", allLines[1][2])
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCustomTransactionDataDsvFileImporter_MinimumValidData(t *testing.T) {
|
func TestCustomTransactionDataDsvFileImporter_MinimumValidData(t *testing.T) {
|
||||||
@@ -77,7 +101,7 @@ func TestCustomTransactionDataDsvFileImporter_MinimumValidData(t *testing.T) {
|
|||||||
"E": models.TRANSACTION_TYPE_EXPENSE,
|
"E": models.TRANSACTION_TYPE_EXPENSE,
|
||||||
"T": models.TRANSACTION_TYPE_TRANSFER,
|
"T": models.TRANSACTION_TYPE_TRANSFER,
|
||||||
}
|
}
|
||||||
converter, err := CreateNewCustomTransactionDataDsvFileImporter("custom_csv", "utf-8", columnIndexMapping, transactionTypeMapping, false, "YYYY-MM-DD HH:mm:ss", ".", "", "", "", "", "")
|
importer, err := CreateNewCustomTransactionDataDsvFileImporter("custom_csv", "utf-8", columnIndexMapping, transactionTypeMapping, false, "YYYY-MM-DD HH:mm:ss", ".", "", "", "", "", "")
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
@@ -87,11 +111,11 @@ func TestCustomTransactionDataDsvFileImporter_MinimumValidData(t *testing.T) {
|
|||||||
DefaultCurrency: "CNY",
|
DefaultCurrency: "CNY",
|
||||||
}
|
}
|
||||||
|
|
||||||
allNewTransactions, allNewAccounts, allNewSubExpenseCategories, allNewSubIncomeCategories, allNewSubTransferCategories, allNewTags, err := converter.ParseImportedData(context, user, []byte(
|
allNewTransactions, allNewAccounts, allNewSubExpenseCategories, allNewSubIncomeCategories, allNewSubTransferCategories, allNewTags, err := importer.ParseImportedData(context, user, []byte(
|
||||||
"2024-09-01 00:00:00,B,123.45\n"+
|
"2024-09-01 00:00:00,B,123.45\n"+
|
||||||
"2024-09-01 01:23:45,I,0.12\n"+
|
"2024-09-01 01:23:45,I,0.12\n"+
|
||||||
"2024-09-01 12:34:56,E,1.00\n"+
|
"2024-09-01 12:34:56,E,1.00\n"+
|
||||||
"2024-09-01 23:59:59,T,0.05"), 0, nil, nil, nil, nil, nil)
|
"2024-09-01 23:59:59,T,0.05"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
|
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
@@ -168,7 +192,7 @@ func TestCustomTransactionDataDsvFileImporter_WithAllSupportedColumns(t *testing
|
|||||||
"Expense": models.TRANSACTION_TYPE_EXPENSE,
|
"Expense": models.TRANSACTION_TYPE_EXPENSE,
|
||||||
"Transfer": models.TRANSACTION_TYPE_TRANSFER,
|
"Transfer": models.TRANSACTION_TYPE_TRANSFER,
|
||||||
}
|
}
|
||||||
converter, err := CreateNewCustomTransactionDataDsvFileImporter("custom_csv", "utf-8", columnIndexMapping, transactionTypeMapping, true, "YYYY-MM-DD HH:mm:ss", "", ".", "", " ", "lonlat", ";")
|
importer, err := CreateNewCustomTransactionDataDsvFileImporter("custom_csv", "utf-8", columnIndexMapping, transactionTypeMapping, true, "YYYY-MM-DD HH:mm:ss", "", ".", "", " ", "lonlat", ";")
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
@@ -178,12 +202,12 @@ func TestCustomTransactionDataDsvFileImporter_WithAllSupportedColumns(t *testing
|
|||||||
DefaultCurrency: "CNY",
|
DefaultCurrency: "CNY",
|
||||||
}
|
}
|
||||||
|
|
||||||
allNewTransactions, allNewAccounts, allNewSubExpenseCategories, allNewSubIncomeCategories, allNewSubTransferCategories, allNewTags, err := converter.ParseImportedData(context, user, []byte(
|
allNewTransactions, allNewAccounts, allNewSubExpenseCategories, allNewSubIncomeCategories, allNewSubTransferCategories, allNewTags, err := importer.ParseImportedData(context, user, []byte(
|
||||||
"\"Time\",\"Timezone\",\"Type\",\"Category\",\"Sub Category\",\"Account\",\"Account Currency\",\"Amount\",\"Account2\",\"Account2 Currency\",\"Account2 Amount\",\"Geographic Location\",\"Tags\",\"Description\"\n"+
|
"\"Time\",\"Timezone\",\"Type\",\"Category\",\"Sub Category\",\"Account\",\"Account Currency\",\"Amount\",\"Account2\",\"Account2 Currency\",\"Account2 Amount\",\"Geographic Location\",\"Tags\",\"Description\"\n"+
|
||||||
"\"2024-09-01 00:00:00\",\"+08:00\",\"Balance Modification\",\"\",\"\",\"Test Account\",\"CNY\",\"123.45\",\"\",\"\",\"\",\"\",\"\",\"\"\n"+
|
"\"2024-09-01 00:00:00\",\"+08:00\",\"Balance Modification\",\"\",\"\",\"Test Account\",\"CNY\",\"123.45\",\"\",\"\",\"\",\"\",\"\",\"\"\n"+
|
||||||
"\"2024-09-01 01:23:45\",\"+08:00\",\"Income\",\"Test Category\",\"Test Sub Category\",\"Test Account\",\"CNY\",\"0.12\",\"\",\"\",\"\",\"123.450000 45.670000\",\"Test Tag;Test Tag2\",\"Hello World\"\n"+
|
"\"2024-09-01 01:23:45\",\"+08:00\",\"Income\",\"Test Category\",\"Test Sub Category\",\"Test Account\",\"CNY\",\"0.12\",\"\",\"\",\"\",\"123.450000 45.670000\",\"Test Tag;Test Tag2\",\"Hello World\"\n"+
|
||||||
"\"2024-09-01 12:34:56\",\"+00:00\",\"Expense\",\"Test Category2\",\"Test Sub Category2\",\"Test Account\",\"CNY\",\"1.00\",\"\",\"\",\"\",\"\",\"Test Tag\",\"Foo#Bar\"\n"+
|
"\"2024-09-01 12:34:56\",\"+00:00\",\"Expense\",\"Test Category2\",\"Test Sub Category2\",\"Test Account\",\"CNY\",\"1.00\",\"\",\"\",\"\",\"\",\"Test Tag\",\"Foo#Bar\"\n"+
|
||||||
"\"2024-09-01 23:59:59\",\"-05:00\",\"Transfer\",\"Test Category3\",\"Test Sub Category3\",\"Test Account\",\"CNY\",\"0.05\",\"Test Account2\",\"USD\",\"0.35\",\"\",\"Test Tag2\",\"foo\tbar\""), 0, nil, nil, nil, nil, nil)
|
"\"2024-09-01 23:59:59\",\"-05:00\",\"Transfer\",\"Test Category3\",\"Test Sub Category3\",\"Test Account\",\"CNY\",\"0.05\",\"Test Account2\",\"USD\",\"0.35\",\"\",\"Test Tag2\",\"foo\tbar\""), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
|
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
@@ -261,7 +285,7 @@ func TestCustomTransactionDataDsvFileImporter_ParseInvalidTime(t *testing.T) {
|
|||||||
transactionTypeMapping := map[string]models.TransactionType{
|
transactionTypeMapping := map[string]models.TransactionType{
|
||||||
"E": models.TRANSACTION_TYPE_EXPENSE,
|
"E": models.TRANSACTION_TYPE_EXPENSE,
|
||||||
}
|
}
|
||||||
converter, err := CreateNewCustomTransactionDataDsvFileImporter("custom_csv", "utf-8", columnIndexMapping, transactionTypeMapping, false, "YYYY-MM-DD HH:mm:ss", "", ".", "", "", "", "")
|
importer, err := CreateNewCustomTransactionDataDsvFileImporter("custom_csv", "utf-8", columnIndexMapping, transactionTypeMapping, false, "YYYY-MM-DD HH:mm:ss", "", ".", "", "", "", "")
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
@@ -271,12 +295,12 @@ func TestCustomTransactionDataDsvFileImporter_ParseInvalidTime(t *testing.T) {
|
|||||||
DefaultCurrency: "CNY",
|
DefaultCurrency: "CNY",
|
||||||
}
|
}
|
||||||
|
|
||||||
_, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte(
|
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(
|
||||||
"2024-09-01T12:34:56,E,123.45"), 0, nil, nil, nil, nil, nil)
|
"2024-09-01T12:34:56,E,123.45"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrTransactionTimeInvalid.Message)
|
assert.EqualError(t, err, errs.ErrTransactionTimeInvalid.Message)
|
||||||
|
|
||||||
_, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte(
|
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(
|
||||||
"09/01/2024 12:34:56,E,123.45"), 0, nil, nil, nil, nil, nil)
|
"09/01/2024 12:34:56,E,123.45"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrTransactionTimeInvalid.Message)
|
assert.EqualError(t, err, errs.ErrTransactionTimeInvalid.Message)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -292,7 +316,7 @@ func TestCustomTransactionDataDsvFileImporter_ParseTransactionWithoutType(t *tes
|
|||||||
"E": models.TRANSACTION_TYPE_EXPENSE,
|
"E": models.TRANSACTION_TYPE_EXPENSE,
|
||||||
"T": models.TRANSACTION_TYPE_TRANSFER,
|
"T": models.TRANSACTION_TYPE_TRANSFER,
|
||||||
}
|
}
|
||||||
converter, err := CreateNewCustomTransactionDataDsvFileImporter("custom_csv", "utf-8", columnIndexMapping, transactionTypeMapping, false, "YYYY-MM-DD HH:mm:ss", "", ".", "", "", "", "")
|
importer, err := CreateNewCustomTransactionDataDsvFileImporter("custom_csv", "utf-8", columnIndexMapping, transactionTypeMapping, false, "YYYY-MM-DD HH:mm:ss", "", ".", "", "", "", "")
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
@@ -302,8 +326,8 @@ func TestCustomTransactionDataDsvFileImporter_ParseTransactionWithoutType(t *tes
|
|||||||
DefaultCurrency: "CNY",
|
DefaultCurrency: "CNY",
|
||||||
}
|
}
|
||||||
|
|
||||||
_, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte(
|
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(
|
||||||
"2024-09-01 12:34:56,A,123.45"), 0, nil, nil, nil, nil, nil)
|
"2024-09-01 12:34:56,A,123.45"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrNotFoundTransactionDataInFile.Message)
|
assert.EqualError(t, err, errs.ErrNotFoundTransactionDataInFile.Message)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -316,7 +340,7 @@ func TestCustomTransactionDataDsvFileImporter_ParseInvalidType(t *testing.T) {
|
|||||||
transactionTypeMapping := map[string]models.TransactionType{
|
transactionTypeMapping := map[string]models.TransactionType{
|
||||||
"B": 0,
|
"B": 0,
|
||||||
}
|
}
|
||||||
converter, err := CreateNewCustomTransactionDataDsvFileImporter("custom_csv", "utf-8", columnIndexMapping, transactionTypeMapping, false, "YYYY-MM-DD HH:mm:ss", "", ".", "", "", "", "")
|
importer, err := CreateNewCustomTransactionDataDsvFileImporter("custom_csv", "utf-8", columnIndexMapping, transactionTypeMapping, false, "YYYY-MM-DD HH:mm:ss", "", ".", "", "", "", "")
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
@@ -326,8 +350,8 @@ func TestCustomTransactionDataDsvFileImporter_ParseInvalidType(t *testing.T) {
|
|||||||
DefaultCurrency: "CNY",
|
DefaultCurrency: "CNY",
|
||||||
}
|
}
|
||||||
|
|
||||||
_, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte(
|
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(
|
||||||
"2024-09-01 12:34:56,B,123.45"), 0, nil, nil, nil, nil, nil)
|
"2024-09-01 12:34:56,B,123.45"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrTransactionTypeInvalid.Message)
|
assert.EqualError(t, err, errs.ErrTransactionTypeInvalid.Message)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -340,7 +364,7 @@ func TestCustomTransactionDataDsvFileImporter_ParseTimeWithTimezone(t *testing.T
|
|||||||
transactionTypeMapping := map[string]models.TransactionType{
|
transactionTypeMapping := map[string]models.TransactionType{
|
||||||
"E": models.TRANSACTION_TYPE_EXPENSE,
|
"E": models.TRANSACTION_TYPE_EXPENSE,
|
||||||
}
|
}
|
||||||
converter, err := CreateNewCustomTransactionDataDsvFileImporter("custom_csv", "utf-8", columnIndexMapping, transactionTypeMapping, false, "YYYY-MM-DD HH:mm:ssZ", "", ".", "", "", "", "")
|
importer, err := CreateNewCustomTransactionDataDsvFileImporter("custom_csv", "utf-8", columnIndexMapping, transactionTypeMapping, false, "YYYY-MM-DD HH:mm:ssZ", "", ".", "", "", "", "")
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
@@ -350,20 +374,20 @@ func TestCustomTransactionDataDsvFileImporter_ParseTimeWithTimezone(t *testing.T
|
|||||||
DefaultCurrency: "CNY",
|
DefaultCurrency: "CNY",
|
||||||
}
|
}
|
||||||
|
|
||||||
allNewTransactions, _, _, _, _, _, err := converter.ParseImportedData(context, user, []byte(
|
allNewTransactions, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(
|
||||||
"2024-09-01 12:34:56-10:00,E,123.45"), 0, nil, nil, nil, nil, nil)
|
"2024-09-01 12:34:56-10:00,E,123.45"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
assert.Equal(t, 1, len(allNewTransactions))
|
assert.Equal(t, 1, len(allNewTransactions))
|
||||||
assert.Equal(t, int64(1725230096), utils.GetUnixTimeFromTransactionTime(allNewTransactions[0].TransactionTime))
|
assert.Equal(t, int64(1725230096), utils.GetUnixTimeFromTransactionTime(allNewTransactions[0].TransactionTime))
|
||||||
|
|
||||||
allNewTransactions, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte(
|
allNewTransactions, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(
|
||||||
"2024-09-01 12:34:56+00:00,E,123.45"), 0, nil, nil, nil, nil, nil)
|
"2024-09-01 12:34:56+00:00,E,123.45"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
assert.Equal(t, 1, len(allNewTransactions))
|
assert.Equal(t, 1, len(allNewTransactions))
|
||||||
assert.Equal(t, int64(1725194096), utils.GetUnixTimeFromTransactionTime(allNewTransactions[0].TransactionTime))
|
assert.Equal(t, int64(1725194096), utils.GetUnixTimeFromTransactionTime(allNewTransactions[0].TransactionTime))
|
||||||
|
|
||||||
allNewTransactions, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte(
|
allNewTransactions, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(
|
||||||
"2024-09-01 12:34:56+12:45,E,123.45"), 0, nil, nil, nil, nil, nil)
|
"2024-09-01 12:34:56+12:45,E,123.45"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
assert.Equal(t, 1, len(allNewTransactions))
|
assert.Equal(t, 1, len(allNewTransactions))
|
||||||
assert.Equal(t, int64(1725148196), utils.GetUnixTimeFromTransactionTime(allNewTransactions[0].TransactionTime))
|
assert.Equal(t, int64(1725148196), utils.GetUnixTimeFromTransactionTime(allNewTransactions[0].TransactionTime))
|
||||||
@@ -378,7 +402,7 @@ func TestCustomTransactionDataDsvFileImporter_ParseTimeWithTimezone2(t *testing.
|
|||||||
transactionTypeMapping := map[string]models.TransactionType{
|
transactionTypeMapping := map[string]models.TransactionType{
|
||||||
"E": models.TRANSACTION_TYPE_EXPENSE,
|
"E": models.TRANSACTION_TYPE_EXPENSE,
|
||||||
}
|
}
|
||||||
converter, err := CreateNewCustomTransactionDataDsvFileImporter("custom_csv", "utf-8", columnIndexMapping, transactionTypeMapping, false, "YYYY-MM-DD HH:mm:ssZZ", "", ".", "", "", "", "")
|
importer, err := CreateNewCustomTransactionDataDsvFileImporter("custom_csv", "utf-8", columnIndexMapping, transactionTypeMapping, false, "YYYY-MM-DD HH:mm:ssZZ", "", ".", "", "", "", "")
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
@@ -388,20 +412,20 @@ func TestCustomTransactionDataDsvFileImporter_ParseTimeWithTimezone2(t *testing.
|
|||||||
DefaultCurrency: "CNY",
|
DefaultCurrency: "CNY",
|
||||||
}
|
}
|
||||||
|
|
||||||
allNewTransactions, _, _, _, _, _, err := converter.ParseImportedData(context, user, []byte(
|
allNewTransactions, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(
|
||||||
"2024-09-01 12:34:56-1000,E,123.45"), 0, nil, nil, nil, nil, nil)
|
"2024-09-01 12:34:56-1000,E,123.45"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
assert.Equal(t, 1, len(allNewTransactions))
|
assert.Equal(t, 1, len(allNewTransactions))
|
||||||
assert.Equal(t, int64(1725230096), utils.GetUnixTimeFromTransactionTime(allNewTransactions[0].TransactionTime))
|
assert.Equal(t, int64(1725230096), utils.GetUnixTimeFromTransactionTime(allNewTransactions[0].TransactionTime))
|
||||||
|
|
||||||
allNewTransactions, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte(
|
allNewTransactions, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(
|
||||||
"2024-09-01 12:34:56+0000,E,123.45"), 0, nil, nil, nil, nil, nil)
|
"2024-09-01 12:34:56+0000,E,123.45"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
assert.Equal(t, 1, len(allNewTransactions))
|
assert.Equal(t, 1, len(allNewTransactions))
|
||||||
assert.Equal(t, int64(1725194096), utils.GetUnixTimeFromTransactionTime(allNewTransactions[0].TransactionTime))
|
assert.Equal(t, int64(1725194096), utils.GetUnixTimeFromTransactionTime(allNewTransactions[0].TransactionTime))
|
||||||
|
|
||||||
allNewTransactions, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte(
|
allNewTransactions, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(
|
||||||
"2024-09-01 12:34:56+1245,E,123.45"), 0, nil, nil, nil, nil, nil)
|
"2024-09-01 12:34:56+1245,E,123.45"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
assert.Equal(t, 1, len(allNewTransactions))
|
assert.Equal(t, 1, len(allNewTransactions))
|
||||||
assert.Equal(t, int64(1725148196), utils.GetUnixTimeFromTransactionTime(allNewTransactions[0].TransactionTime))
|
assert.Equal(t, int64(1725148196), utils.GetUnixTimeFromTransactionTime(allNewTransactions[0].TransactionTime))
|
||||||
@@ -417,7 +441,7 @@ func TestCustomTransactionDataDsvFileImporter_ParseValidTimezone(t *testing.T) {
|
|||||||
transactionTypeMapping := map[string]models.TransactionType{
|
transactionTypeMapping := map[string]models.TransactionType{
|
||||||
"E": models.TRANSACTION_TYPE_EXPENSE,
|
"E": models.TRANSACTION_TYPE_EXPENSE,
|
||||||
}
|
}
|
||||||
converter, err := CreateNewCustomTransactionDataDsvFileImporter("custom_csv", "utf-8", columnIndexMapping, transactionTypeMapping, false, "YYYY-MM-DD HH:mm:ss", "", ".", "", "", "", "")
|
importer, err := CreateNewCustomTransactionDataDsvFileImporter("custom_csv", "utf-8", columnIndexMapping, transactionTypeMapping, false, "YYYY-MM-DD HH:mm:ss", "", ".", "", "", "", "")
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
@@ -427,20 +451,20 @@ func TestCustomTransactionDataDsvFileImporter_ParseValidTimezone(t *testing.T) {
|
|||||||
DefaultCurrency: "CNY",
|
DefaultCurrency: "CNY",
|
||||||
}
|
}
|
||||||
|
|
||||||
allNewTransactions, _, _, _, _, _, err := converter.ParseImportedData(context, user, []byte(
|
allNewTransactions, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(
|
||||||
"2024-09-01 12:34:56,-10:00,E,123.45"), 0, nil, nil, nil, nil, nil)
|
"2024-09-01 12:34:56,-10:00,E,123.45"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
assert.Equal(t, 1, len(allNewTransactions))
|
assert.Equal(t, 1, len(allNewTransactions))
|
||||||
assert.Equal(t, int64(1725230096), utils.GetUnixTimeFromTransactionTime(allNewTransactions[0].TransactionTime))
|
assert.Equal(t, int64(1725230096), utils.GetUnixTimeFromTransactionTime(allNewTransactions[0].TransactionTime))
|
||||||
|
|
||||||
allNewTransactions, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte(
|
allNewTransactions, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(
|
||||||
"2024-09-01 12:34:56,+00:00,E,123.45"), 0, nil, nil, nil, nil, nil)
|
"2024-09-01 12:34:56,+00:00,E,123.45"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
assert.Equal(t, 1, len(allNewTransactions))
|
assert.Equal(t, 1, len(allNewTransactions))
|
||||||
assert.Equal(t, int64(1725194096), utils.GetUnixTimeFromTransactionTime(allNewTransactions[0].TransactionTime))
|
assert.Equal(t, int64(1725194096), utils.GetUnixTimeFromTransactionTime(allNewTransactions[0].TransactionTime))
|
||||||
|
|
||||||
allNewTransactions, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte(
|
allNewTransactions, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(
|
||||||
"2024-09-01 12:34:56,+12:45,E,123.45"), 0, nil, nil, nil, nil, nil)
|
"2024-09-01 12:34:56,+12:45,E,123.45"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
assert.Equal(t, 1, len(allNewTransactions))
|
assert.Equal(t, 1, len(allNewTransactions))
|
||||||
assert.Equal(t, int64(1725148196), utils.GetUnixTimeFromTransactionTime(allNewTransactions[0].TransactionTime))
|
assert.Equal(t, int64(1725148196), utils.GetUnixTimeFromTransactionTime(allNewTransactions[0].TransactionTime))
|
||||||
@@ -456,7 +480,7 @@ func TestCustomTransactionDataDsvFileImporter_ParseValidTimezone2(t *testing.T)
|
|||||||
transactionTypeMapping := map[string]models.TransactionType{
|
transactionTypeMapping := map[string]models.TransactionType{
|
||||||
"E": models.TRANSACTION_TYPE_EXPENSE,
|
"E": models.TRANSACTION_TYPE_EXPENSE,
|
||||||
}
|
}
|
||||||
converter, err := CreateNewCustomTransactionDataDsvFileImporter("custom_csv", "utf-8", columnIndexMapping, transactionTypeMapping, false, "YYYY-MM-DD HH:mm:ss", "ZZ", ".", "", "", "", "")
|
importer, err := CreateNewCustomTransactionDataDsvFileImporter("custom_csv", "utf-8", columnIndexMapping, transactionTypeMapping, false, "YYYY-MM-DD HH:mm:ss", "ZZ", ".", "", "", "", "")
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
@@ -466,20 +490,20 @@ func TestCustomTransactionDataDsvFileImporter_ParseValidTimezone2(t *testing.T)
|
|||||||
DefaultCurrency: "CNY",
|
DefaultCurrency: "CNY",
|
||||||
}
|
}
|
||||||
|
|
||||||
allNewTransactions, _, _, _, _, _, err := converter.ParseImportedData(context, user, []byte(
|
allNewTransactions, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(
|
||||||
"2024-09-01 12:34:56,-1000,E,123.45"), 0, nil, nil, nil, nil, nil)
|
"2024-09-01 12:34:56,-1000,E,123.45"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
assert.Equal(t, 1, len(allNewTransactions))
|
assert.Equal(t, 1, len(allNewTransactions))
|
||||||
assert.Equal(t, int64(1725230096), utils.GetUnixTimeFromTransactionTime(allNewTransactions[0].TransactionTime))
|
assert.Equal(t, int64(1725230096), utils.GetUnixTimeFromTransactionTime(allNewTransactions[0].TransactionTime))
|
||||||
|
|
||||||
allNewTransactions, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte(
|
allNewTransactions, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(
|
||||||
"2024-09-01 12:34:56,+0000,E,123.45"), 0, nil, nil, nil, nil, nil)
|
"2024-09-01 12:34:56,+0000,E,123.45"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
assert.Equal(t, 1, len(allNewTransactions))
|
assert.Equal(t, 1, len(allNewTransactions))
|
||||||
assert.Equal(t, int64(1725194096), utils.GetUnixTimeFromTransactionTime(allNewTransactions[0].TransactionTime))
|
assert.Equal(t, int64(1725194096), utils.GetUnixTimeFromTransactionTime(allNewTransactions[0].TransactionTime))
|
||||||
|
|
||||||
allNewTransactions, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte(
|
allNewTransactions, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(
|
||||||
"2024-09-01 12:34:56,+1245,E,123.45"), 0, nil, nil, nil, nil, nil)
|
"2024-09-01 12:34:56,+1245,E,123.45"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
assert.Equal(t, 1, len(allNewTransactions))
|
assert.Equal(t, 1, len(allNewTransactions))
|
||||||
assert.Equal(t, int64(1725148196), utils.GetUnixTimeFromTransactionTime(allNewTransactions[0].TransactionTime))
|
assert.Equal(t, int64(1725148196), utils.GetUnixTimeFromTransactionTime(allNewTransactions[0].TransactionTime))
|
||||||
@@ -495,7 +519,7 @@ func TestCustomTransactionDataDsvFileImporter_ParseInvalidTimezoneFormat(t *test
|
|||||||
transactionTypeMapping := map[string]models.TransactionType{
|
transactionTypeMapping := map[string]models.TransactionType{
|
||||||
"E": models.TRANSACTION_TYPE_EXPENSE,
|
"E": models.TRANSACTION_TYPE_EXPENSE,
|
||||||
}
|
}
|
||||||
converter, err := CreateNewCustomTransactionDataDsvFileImporter("custom_csv", "utf-8", columnIndexMapping, transactionTypeMapping, false, "YYYY-MM-DD HH:mm:ss", "z", ".", "", "", "", "")
|
importer, err := CreateNewCustomTransactionDataDsvFileImporter("custom_csv", "utf-8", columnIndexMapping, transactionTypeMapping, false, "YYYY-MM-DD HH:mm:ss", "z", ".", "", "", "", "")
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
@@ -505,8 +529,8 @@ func TestCustomTransactionDataDsvFileImporter_ParseInvalidTimezoneFormat(t *test
|
|||||||
DefaultCurrency: "CNY",
|
DefaultCurrency: "CNY",
|
||||||
}
|
}
|
||||||
|
|
||||||
_, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte(
|
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(
|
||||||
"2024-09-01 12:34:56,CST,E,123.45"), 0, nil, nil, nil, nil, nil)
|
"2024-09-01 12:34:56,CST,E,123.45"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrImportFileTransactionTimezoneFormatInvalid.Message)
|
assert.EqualError(t, err, errs.ErrImportFileTransactionTimezoneFormatInvalid.Message)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -520,7 +544,7 @@ func TestCustomTransactionDataDsvFileImporter_ParseInvalidTimezone(t *testing.T)
|
|||||||
transactionTypeMapping := map[string]models.TransactionType{
|
transactionTypeMapping := map[string]models.TransactionType{
|
||||||
"E": models.TRANSACTION_TYPE_EXPENSE,
|
"E": models.TRANSACTION_TYPE_EXPENSE,
|
||||||
}
|
}
|
||||||
converter, err := CreateNewCustomTransactionDataDsvFileImporter("custom_csv", "utf-8", columnIndexMapping, transactionTypeMapping, false, "YYYY-MM-DD HH:mm:ss", "", ".", "", "", "", "")
|
importer, err := CreateNewCustomTransactionDataDsvFileImporter("custom_csv", "utf-8", columnIndexMapping, transactionTypeMapping, false, "YYYY-MM-DD HH:mm:ss", "", ".", "", "", "", "")
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
@@ -530,12 +554,12 @@ func TestCustomTransactionDataDsvFileImporter_ParseInvalidTimezone(t *testing.T)
|
|||||||
DefaultCurrency: "CNY",
|
DefaultCurrency: "CNY",
|
||||||
}
|
}
|
||||||
|
|
||||||
_, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte(
|
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(
|
||||||
"2024-09-01 12:34:56,Asia/Shanghai,E,123.45"), 0, nil, nil, nil, nil, nil)
|
"2024-09-01 12:34:56,Asia/Shanghai,E,123.45"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrTransactionTimeZoneInvalid.Message)
|
assert.EqualError(t, err, errs.ErrTransactionTimeZoneInvalid.Message)
|
||||||
|
|
||||||
_, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte(
|
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(
|
||||||
"2024-09-01 12:34:56,-0700,E,123.45"), 0, nil, nil, nil, nil, nil)
|
"2024-09-01 12:34:56,-0700,E,123.45"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrTransactionTimeZoneInvalid.Message)
|
assert.EqualError(t, err, errs.ErrTransactionTimeZoneInvalid.Message)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -549,7 +573,7 @@ func TestCustomTransactionDataDsvFileImporter_ParseInvalidTimezone2(t *testing.T
|
|||||||
transactionTypeMapping := map[string]models.TransactionType{
|
transactionTypeMapping := map[string]models.TransactionType{
|
||||||
"E": models.TRANSACTION_TYPE_EXPENSE,
|
"E": models.TRANSACTION_TYPE_EXPENSE,
|
||||||
}
|
}
|
||||||
converter, err := CreateNewCustomTransactionDataDsvFileImporter("custom_csv", "utf-8", columnIndexMapping, transactionTypeMapping, false, "YYYY-MM-DD HH:mm:ss", "ZZ", ".", "", "", "", "")
|
importer, err := CreateNewCustomTransactionDataDsvFileImporter("custom_csv", "utf-8", columnIndexMapping, transactionTypeMapping, false, "YYYY-MM-DD HH:mm:ss", "ZZ", ".", "", "", "", "")
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
@@ -559,12 +583,12 @@ func TestCustomTransactionDataDsvFileImporter_ParseInvalidTimezone2(t *testing.T
|
|||||||
DefaultCurrency: "CNY",
|
DefaultCurrency: "CNY",
|
||||||
}
|
}
|
||||||
|
|
||||||
_, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte(
|
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(
|
||||||
"2024-09-01 12:34:56,Asia/Shanghai,E,123.45"), 0, nil, nil, nil, nil, nil)
|
"2024-09-01 12:34:56,Asia/Shanghai,E,123.45"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrTransactionTimeZoneInvalid.Message)
|
assert.EqualError(t, err, errs.ErrTransactionTimeZoneInvalid.Message)
|
||||||
|
|
||||||
_, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte(
|
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(
|
||||||
"2024-09-01 12:34:56,0700,E,123.45"), 0, nil, nil, nil, nil, nil)
|
"2024-09-01 12:34:56,0700,E,123.45"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrTransactionTimeZoneInvalid.Message)
|
assert.EqualError(t, err, errs.ErrTransactionTimeZoneInvalid.Message)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -577,7 +601,7 @@ func TestCustomTransactionDataDsvFileImporter_ParseAmountWithCustomFormat(t *tes
|
|||||||
transactionTypeMapping := map[string]models.TransactionType{
|
transactionTypeMapping := map[string]models.TransactionType{
|
||||||
"E": models.TRANSACTION_TYPE_EXPENSE,
|
"E": models.TRANSACTION_TYPE_EXPENSE,
|
||||||
}
|
}
|
||||||
converter, err := CreateNewCustomTransactionDataDsvFileImporter("custom_tsv", "utf-8", columnIndexMapping, transactionTypeMapping, false, "YYYY-MM-DD HH:mm:ss", "", ",", ".", "", "", "")
|
importer, err := CreateNewCustomTransactionDataDsvFileImporter("custom_tsv", "utf-8", columnIndexMapping, transactionTypeMapping, false, "YYYY-MM-DD HH:mm:ss", "", ",", ".", "", "", "")
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
@@ -587,8 +611,8 @@ func TestCustomTransactionDataDsvFileImporter_ParseAmountWithCustomFormat(t *tes
|
|||||||
DefaultCurrency: "CNY",
|
DefaultCurrency: "CNY",
|
||||||
}
|
}
|
||||||
|
|
||||||
allNewTransactions, _, _, _, _, _, err := converter.ParseImportedData(context, user, []byte(
|
allNewTransactions, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(
|
||||||
"2024-09-01 12:34:56\tE\t1.234,56"), 0, nil, nil, nil, nil, nil)
|
"2024-09-01 12:34:56\tE\t1.234,56"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
assert.Equal(t, 1, len(allNewTransactions))
|
assert.Equal(t, 1, len(allNewTransactions))
|
||||||
assert.Equal(t, int64(123456), allNewTransactions[0].Amount)
|
assert.Equal(t, int64(123456), allNewTransactions[0].Amount)
|
||||||
@@ -603,7 +627,7 @@ func TestCustomTransactionDataDsvFileImporter_ParseInvalidAmountWithCustomFormat
|
|||||||
transactionTypeMapping := map[string]models.TransactionType{
|
transactionTypeMapping := map[string]models.TransactionType{
|
||||||
"E": models.TRANSACTION_TYPE_EXPENSE,
|
"E": models.TRANSACTION_TYPE_EXPENSE,
|
||||||
}
|
}
|
||||||
converter, err := CreateNewCustomTransactionDataDsvFileImporter("custom_tsv", "utf-8", columnIndexMapping, transactionTypeMapping, false, "YYYY-MM-DD HH:mm:ss", "", ".", ",", "", "", "")
|
importer, err := CreateNewCustomTransactionDataDsvFileImporter("custom_tsv", "utf-8", columnIndexMapping, transactionTypeMapping, false, "YYYY-MM-DD HH:mm:ss", "", ".", ",", "", "", "")
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
@@ -613,8 +637,8 @@ func TestCustomTransactionDataDsvFileImporter_ParseInvalidAmountWithCustomFormat
|
|||||||
DefaultCurrency: "CNY",
|
DefaultCurrency: "CNY",
|
||||||
}
|
}
|
||||||
|
|
||||||
_, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte(
|
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(
|
||||||
"2024-09-01 12:34:56\tE\t1.234,56"), 0, nil, nil, nil, nil, nil)
|
"2024-09-01 12:34:56\tE\t1.234,56"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrAmountInvalid.Message)
|
assert.EqualError(t, err, errs.ErrAmountInvalid.Message)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -627,7 +651,7 @@ func TestCustomTransactionDataDsvFileImporter_ParseInvalidAmountWithCustomFormat
|
|||||||
transactionTypeMapping := map[string]models.TransactionType{
|
transactionTypeMapping := map[string]models.TransactionType{
|
||||||
"E": models.TRANSACTION_TYPE_EXPENSE,
|
"E": models.TRANSACTION_TYPE_EXPENSE,
|
||||||
}
|
}
|
||||||
converter, err := CreateNewCustomTransactionDataDsvFileImporter("custom_tsv", "utf-8", columnIndexMapping, transactionTypeMapping, false, "YYYY-MM-DD HH:mm:ss", "", ",", "", "", "", "")
|
importer, err := CreateNewCustomTransactionDataDsvFileImporter("custom_tsv", "utf-8", columnIndexMapping, transactionTypeMapping, false, "YYYY-MM-DD HH:mm:ss", "", ",", "", "", "", "")
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
@@ -637,8 +661,8 @@ func TestCustomTransactionDataDsvFileImporter_ParseInvalidAmountWithCustomFormat
|
|||||||
DefaultCurrency: "CNY",
|
DefaultCurrency: "CNY",
|
||||||
}
|
}
|
||||||
|
|
||||||
_, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte(
|
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(
|
||||||
"2024-09-01 12:34:56\tE\t1.234,56"), 0, nil, nil, nil, nil, nil)
|
"2024-09-01 12:34:56\tE\t1.234,56"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrAmountInvalid.Message)
|
assert.EqualError(t, err, errs.ErrAmountInvalid.Message)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -655,7 +679,7 @@ func TestCustomTransactionDataDsvFileImporter_ParsePrimaryCategory(t *testing.T)
|
|||||||
"E": models.TRANSACTION_TYPE_EXPENSE,
|
"E": models.TRANSACTION_TYPE_EXPENSE,
|
||||||
"T": models.TRANSACTION_TYPE_TRANSFER,
|
"T": models.TRANSACTION_TYPE_TRANSFER,
|
||||||
}
|
}
|
||||||
converter, err := CreateNewCustomTransactionDataDsvFileImporter("custom_csv", "utf-8", columnIndexMapping, transactionTypeMapping, false, "YYYY-MM-DD HH:mm:ss", "", ".", "", "", "", "")
|
importer, err := CreateNewCustomTransactionDataDsvFileImporter("custom_csv", "utf-8", columnIndexMapping, transactionTypeMapping, false, "YYYY-MM-DD HH:mm:ss", "", ".", "", "", "", "")
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
@@ -665,11 +689,11 @@ func TestCustomTransactionDataDsvFileImporter_ParsePrimaryCategory(t *testing.T)
|
|||||||
DefaultCurrency: "CNY",
|
DefaultCurrency: "CNY",
|
||||||
}
|
}
|
||||||
|
|
||||||
allNewTransactions, _, allNewSubExpenseCategories, allNewSubIncomeCategories, allNewSubTransferCategories, _, err := converter.ParseImportedData(context, user, []byte(
|
allNewTransactions, _, allNewSubExpenseCategories, allNewSubIncomeCategories, allNewSubTransferCategories, _, err := importer.ParseImportedData(context, user, []byte(
|
||||||
"2024-09-01 00:00:00,B,,123.45\n"+
|
"2024-09-01 00:00:00,B,,123.45\n"+
|
||||||
"2024-09-01 01:23:45,I,Test Category,0.12\n"+
|
"2024-09-01 01:23:45,I,Test Category,0.12\n"+
|
||||||
"2024-09-01 12:34:56,E,Test Category2,1.00\n"+
|
"2024-09-01 12:34:56,E,Test Category2,1.00\n"+
|
||||||
"2024-09-01 23:59:59,T,Test Category3,0.05"), 0, nil, nil, nil, nil, nil)
|
"2024-09-01 23:59:59,T,Test Category3,0.05"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
|
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
@@ -724,7 +748,7 @@ func TestCustomTransactionDataDsvFileImporter_ParseValidAccountCurrency(t *testi
|
|||||||
"B": models.TRANSACTION_TYPE_MODIFY_BALANCE,
|
"B": models.TRANSACTION_TYPE_MODIFY_BALANCE,
|
||||||
"T": models.TRANSACTION_TYPE_TRANSFER,
|
"T": models.TRANSACTION_TYPE_TRANSFER,
|
||||||
}
|
}
|
||||||
converter, err := CreateNewCustomTransactionDataDsvFileImporter("custom_csv", "utf-8", columnIndexMapping, transactionTypeMapping, false, "YYYY-MM-DD HH:mm:ss", "", ".", "", "", "", "")
|
importer, err := CreateNewCustomTransactionDataDsvFileImporter("custom_csv", "utf-8", columnIndexMapping, transactionTypeMapping, false, "YYYY-MM-DD HH:mm:ss", "", ".", "", "", "", "")
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
@@ -734,9 +758,9 @@ func TestCustomTransactionDataDsvFileImporter_ParseValidAccountCurrency(t *testi
|
|||||||
DefaultCurrency: "CNY",
|
DefaultCurrency: "CNY",
|
||||||
}
|
}
|
||||||
|
|
||||||
allNewTransactions, allNewAccounts, _, _, _, _, err := converter.ParseImportedData(context, user, []byte(
|
allNewTransactions, allNewAccounts, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(
|
||||||
"2024-09-01 01:23:45,B,Test Account,USD,123.45,,,\n"+
|
"2024-09-01 01:23:45,B,Test Account,USD,123.45,,,\n"+
|
||||||
"2024-09-01 12:34:56,T,Test Account,USD,1.23,Test Account2,EUR,1.10"), 0, nil, nil, nil, nil, nil)
|
"2024-09-01 12:34:56,T,Test Account,USD,1.23,Test Account2,EUR,1.10"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
|
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
@@ -767,7 +791,7 @@ func TestCustomTransactionDataDsvFileImporter_ParseInvalidAccountCurrency(t *tes
|
|||||||
"B": models.TRANSACTION_TYPE_MODIFY_BALANCE,
|
"B": models.TRANSACTION_TYPE_MODIFY_BALANCE,
|
||||||
"T": models.TRANSACTION_TYPE_TRANSFER,
|
"T": models.TRANSACTION_TYPE_TRANSFER,
|
||||||
}
|
}
|
||||||
converter, err := CreateNewCustomTransactionDataDsvFileImporter("custom_csv", "utf-8", columnIndexMapping, transactionTypeMapping, false, "YYYY-MM-DD HH:mm:ss", "", ".", "", "", "", "")
|
importer, err := CreateNewCustomTransactionDataDsvFileImporter("custom_csv", "utf-8", columnIndexMapping, transactionTypeMapping, false, "YYYY-MM-DD HH:mm:ss", "", ".", "", "", "", "")
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
@@ -777,14 +801,14 @@ func TestCustomTransactionDataDsvFileImporter_ParseInvalidAccountCurrency(t *tes
|
|||||||
DefaultCurrency: "CNY",
|
DefaultCurrency: "CNY",
|
||||||
}
|
}
|
||||||
|
|
||||||
_, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte(
|
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(
|
||||||
"2024-09-01 01:23:45,B,Test Account,USD,123.45,,,\n"+
|
"2024-09-01 01:23:45,B,Test Account,USD,123.45,,,\n"+
|
||||||
"2024-09-01 12:34:56,T,Test Account,CNY,1.23,Test Account2,EUR,1.10"), 0, nil, nil, nil, nil, nil)
|
"2024-09-01 12:34:56,T,Test Account,CNY,1.23,Test Account2,EUR,1.10"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrAccountCurrencyInvalid.Message)
|
assert.EqualError(t, err, errs.ErrAccountCurrencyInvalid.Message)
|
||||||
|
|
||||||
_, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte(
|
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(
|
||||||
"2024-09-01 01:23:45,B,Test Account,USD,123.45,,,\n"+
|
"2024-09-01 01:23:45,B,Test Account,USD,123.45,,,\n"+
|
||||||
"2024-09-01 12:34:56,T,Test Account2,CNY,1.23,Test Account,EUR,1.10"), 0, nil, nil, nil, nil, nil)
|
"2024-09-01 12:34:56,T,Test Account2,CNY,1.23,Test Account,EUR,1.10"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrAccountCurrencyInvalid.Message)
|
assert.EqualError(t, err, errs.ErrAccountCurrencyInvalid.Message)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -803,7 +827,7 @@ func TestCustomTransactionDataDsvFileImporter_ParseNotSupportedCurrency(t *testi
|
|||||||
"B": models.TRANSACTION_TYPE_MODIFY_BALANCE,
|
"B": models.TRANSACTION_TYPE_MODIFY_BALANCE,
|
||||||
"T": models.TRANSACTION_TYPE_TRANSFER,
|
"T": models.TRANSACTION_TYPE_TRANSFER,
|
||||||
}
|
}
|
||||||
converter, err := CreateNewCustomTransactionDataDsvFileImporter("custom_csv", "utf-8", columnIndexMapping, transactionTypeMapping, false, "YYYY-MM-DD HH:mm:ss", "", ".", "", "", "", "")
|
importer, err := CreateNewCustomTransactionDataDsvFileImporter("custom_csv", "utf-8", columnIndexMapping, transactionTypeMapping, false, "YYYY-MM-DD HH:mm:ss", "", ".", "", "", "", "")
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
@@ -813,12 +837,12 @@ func TestCustomTransactionDataDsvFileImporter_ParseNotSupportedCurrency(t *testi
|
|||||||
DefaultCurrency: "CNY",
|
DefaultCurrency: "CNY",
|
||||||
}
|
}
|
||||||
|
|
||||||
_, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte(
|
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(
|
||||||
"2024-09-01 01:23:45,B,Test Account,XXX,123.45,,,"), 0, nil, nil, nil, nil, nil)
|
"2024-09-01 01:23:45,B,Test Account,XXX,123.45,,,"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrAccountCurrencyInvalid.Message)
|
assert.EqualError(t, err, errs.ErrAccountCurrencyInvalid.Message)
|
||||||
|
|
||||||
_, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte(
|
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(
|
||||||
"2024-09-01 01:23:45,T,Test Account,USD,123.45,Test Account2,XXX,123.45"), 0, nil, nil, nil, nil, nil)
|
"2024-09-01 01:23:45,T,Test Account,USD,123.45,Test Account2,XXX,123.45"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrAccountCurrencyInvalid.Message)
|
assert.EqualError(t, err, errs.ErrAccountCurrencyInvalid.Message)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -835,7 +859,7 @@ func TestCustomTransactionDataDsvFileImporter_ParseValidAmount(t *testing.T) {
|
|||||||
"E": models.TRANSACTION_TYPE_EXPENSE,
|
"E": models.TRANSACTION_TYPE_EXPENSE,
|
||||||
"T": models.TRANSACTION_TYPE_TRANSFER,
|
"T": models.TRANSACTION_TYPE_TRANSFER,
|
||||||
}
|
}
|
||||||
converter, err := CreateNewCustomTransactionDataDsvFileImporter("custom_csv", "utf-8", columnIndexMapping, transactionTypeMapping, false, "YYYY-MM-DD HH:mm:ss", "", ".", "", "", "", "")
|
importer, err := CreateNewCustomTransactionDataDsvFileImporter("custom_csv", "utf-8", columnIndexMapping, transactionTypeMapping, false, "YYYY-MM-DD HH:mm:ss", "", ".", "", "", "", "")
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
@@ -845,11 +869,11 @@ func TestCustomTransactionDataDsvFileImporter_ParseValidAmount(t *testing.T) {
|
|||||||
DefaultCurrency: "CNY",
|
DefaultCurrency: "CNY",
|
||||||
}
|
}
|
||||||
|
|
||||||
allNewTransactions, _, _, _, _, _, err := converter.ParseImportedData(context, user, []byte(
|
allNewTransactions, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(
|
||||||
"2024-09-01 00:00:00,B,123.45000000,\n"+
|
"2024-09-01 00:00:00,B,123.45000000,\n"+
|
||||||
"2024-09-01 01:23:45,I,0.12000000,\n"+
|
"2024-09-01 01:23:45,I,0.12000000,\n"+
|
||||||
"2024-09-01 12:34:56,E,1.00000000,\n"+
|
"2024-09-01 12:34:56,E,1.00000000,\n"+
|
||||||
"2024-09-01 23:59:59,T,0.05000000,0.35000000"), 0, nil, nil, nil, nil, nil)
|
"2024-09-01 23:59:59,T,0.05000000,0.35000000"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
|
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
@@ -873,6 +897,62 @@ func TestCustomTransactionDataDsvFileImporter_ParseValidAmount(t *testing.T) {
|
|||||||
assert.Equal(t, int64(35), allNewTransactions[3].RelatedAccountAmount)
|
assert.Equal(t, int64(35), allNewTransactions[3].RelatedAccountAmount)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestCustomTransactionDataDsvFileImporter_ParseAmountWithSpaceDigitGroupingSymbol(t *testing.T) {
|
||||||
|
columnIndexMapping := map[datatable.TransactionDataTableColumn]int{
|
||||||
|
datatable.TRANSACTION_DATA_TABLE_TRANSACTION_TIME: 0,
|
||||||
|
datatable.TRANSACTION_DATA_TABLE_TRANSACTION_TYPE: 1,
|
||||||
|
datatable.TRANSACTION_DATA_TABLE_AMOUNT: 2,
|
||||||
|
}
|
||||||
|
transactionTypeMapping := map[string]models.TransactionType{
|
||||||
|
"E": models.TRANSACTION_TYPE_EXPENSE,
|
||||||
|
}
|
||||||
|
importer, err := CreateNewCustomTransactionDataDsvFileImporter("custom_csv", "utf-8", columnIndexMapping, transactionTypeMapping, false, "YYYY-MM-DD HH:mm:ss", "", ".", " ", "", "", "")
|
||||||
|
assert.Nil(t, err)
|
||||||
|
|
||||||
|
context := core.NewNullContext()
|
||||||
|
|
||||||
|
user := &models.User{
|
||||||
|
Uid: 1234567890,
|
||||||
|
DefaultCurrency: "CNY",
|
||||||
|
}
|
||||||
|
|
||||||
|
// normal space
|
||||||
|
allNewTransactions, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(
|
||||||
|
"2024-09-01 00:00:00,E,1 234,\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
|
|
||||||
|
assert.Nil(t, err)
|
||||||
|
|
||||||
|
assert.Equal(t, 1, len(allNewTransactions))
|
||||||
|
assert.Equal(t, int64(123400), allNewTransactions[0].Amount)
|
||||||
|
|
||||||
|
// no-break space (NBSP)
|
||||||
|
allNewTransactions, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(
|
||||||
|
"2024-09-01 00:00:00,E,1 234,\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
|
|
||||||
|
assert.Nil(t, err)
|
||||||
|
|
||||||
|
assert.Equal(t, 1, len(allNewTransactions))
|
||||||
|
assert.Equal(t, int64(123400), allNewTransactions[0].Amount)
|
||||||
|
|
||||||
|
// narrow no-break space (NNBSP)
|
||||||
|
allNewTransactions, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(
|
||||||
|
"2024-09-01 00:00:00,E,1 234,\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
|
|
||||||
|
assert.Nil(t, err)
|
||||||
|
|
||||||
|
assert.Equal(t, 1, len(allNewTransactions))
|
||||||
|
assert.Equal(t, int64(123400), allNewTransactions[0].Amount)
|
||||||
|
|
||||||
|
// figure space
|
||||||
|
allNewTransactions, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(
|
||||||
|
"2024-09-01 00:00:00,E,1 234,\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
|
|
||||||
|
assert.Nil(t, err)
|
||||||
|
|
||||||
|
assert.Equal(t, 1, len(allNewTransactions))
|
||||||
|
assert.Equal(t, int64(123400), allNewTransactions[0].Amount)
|
||||||
|
}
|
||||||
|
|
||||||
func TestCustomTransactionDataDsvFileImporter_ParseInvalidAmount(t *testing.T) {
|
func TestCustomTransactionDataDsvFileImporter_ParseInvalidAmount(t *testing.T) {
|
||||||
columnIndexMapping := map[datatable.TransactionDataTableColumn]int{
|
columnIndexMapping := map[datatable.TransactionDataTableColumn]int{
|
||||||
datatable.TRANSACTION_DATA_TABLE_TRANSACTION_TIME: 0,
|
datatable.TRANSACTION_DATA_TABLE_TRANSACTION_TIME: 0,
|
||||||
@@ -886,7 +966,7 @@ func TestCustomTransactionDataDsvFileImporter_ParseInvalidAmount(t *testing.T) {
|
|||||||
"E": models.TRANSACTION_TYPE_EXPENSE,
|
"E": models.TRANSACTION_TYPE_EXPENSE,
|
||||||
"T": models.TRANSACTION_TYPE_TRANSFER,
|
"T": models.TRANSACTION_TYPE_TRANSFER,
|
||||||
}
|
}
|
||||||
converter, err := CreateNewCustomTransactionDataDsvFileImporter("custom_csv", "utf-8", columnIndexMapping, transactionTypeMapping, false, "YYYY-MM-DD HH:mm:ss", "", ".", "", "", "", "")
|
importer, err := CreateNewCustomTransactionDataDsvFileImporter("custom_csv", "utf-8", columnIndexMapping, transactionTypeMapping, false, "YYYY-MM-DD HH:mm:ss", "", ".", "", "", "", "")
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
@@ -896,12 +976,12 @@ func TestCustomTransactionDataDsvFileImporter_ParseInvalidAmount(t *testing.T) {
|
|||||||
DefaultCurrency: "CNY",
|
DefaultCurrency: "CNY",
|
||||||
}
|
}
|
||||||
|
|
||||||
_, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte(
|
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(
|
||||||
"2024-09-01 12:34:56,E,Test Account,123 45,,"), 0, nil, nil, nil, nil, nil)
|
"2024-09-01 12:34:56,E,Test Account,123 45,,"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrAmountInvalid.Message)
|
assert.EqualError(t, err, errs.ErrAmountInvalid.Message)
|
||||||
|
|
||||||
_, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte(
|
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(
|
||||||
"2024-09-01 12:34:56,T,Test Account,123.45,Test Account2,123 45"), 0, nil, nil, nil, nil, nil)
|
"2024-09-01 12:34:56,T,Test Account,123.45,Test Account2,123 45"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrAmountInvalid.Message)
|
assert.EqualError(t, err, errs.ErrAmountInvalid.Message)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -917,7 +997,7 @@ func TestCustomTransactionDataDsvFileImporter_ParseNoAmount2(t *testing.T) {
|
|||||||
"E": models.TRANSACTION_TYPE_EXPENSE,
|
"E": models.TRANSACTION_TYPE_EXPENSE,
|
||||||
"T": models.TRANSACTION_TYPE_TRANSFER,
|
"T": models.TRANSACTION_TYPE_TRANSFER,
|
||||||
}
|
}
|
||||||
converter, err := CreateNewCustomTransactionDataDsvFileImporter("custom_csv", "utf-8", columnIndexMapping, transactionTypeMapping, false, "YYYY-MM-DD HH:mm:ss", "", ".", "", "", "", "")
|
importer, err := CreateNewCustomTransactionDataDsvFileImporter("custom_csv", "utf-8", columnIndexMapping, transactionTypeMapping, false, "YYYY-MM-DD HH:mm:ss", "", ".", "", "", "", "")
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
@@ -927,15 +1007,15 @@ func TestCustomTransactionDataDsvFileImporter_ParseNoAmount2(t *testing.T) {
|
|||||||
DefaultCurrency: "CNY",
|
DefaultCurrency: "CNY",
|
||||||
}
|
}
|
||||||
|
|
||||||
allNewTransactions, _, _, _, _, _, err := converter.ParseImportedData(context, user, []byte(
|
allNewTransactions, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(
|
||||||
"2024-09-01 12:34:56,E,Test Account,123.45,"), 0, nil, nil, nil, nil, nil)
|
"2024-09-01 12:34:56,E,Test Account,123.45,"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
|
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
assert.Equal(t, int64(12345), allNewTransactions[0].Amount)
|
assert.Equal(t, int64(12345), allNewTransactions[0].Amount)
|
||||||
assert.Equal(t, int64(0), allNewTransactions[0].RelatedAccountAmount)
|
assert.Equal(t, int64(0), allNewTransactions[0].RelatedAccountAmount)
|
||||||
|
|
||||||
allNewTransactions, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte(
|
allNewTransactions, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(
|
||||||
"2024-09-01 12:34:56,T,Test Account,123.45,Test Account2"), 0, nil, nil, nil, nil, nil)
|
"2024-09-01 12:34:56,T,Test Account,123.45,Test Account2"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
|
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
assert.Equal(t, int64(12345), allNewTransactions[0].Amount)
|
assert.Equal(t, int64(12345), allNewTransactions[0].Amount)
|
||||||
@@ -952,7 +1032,7 @@ func TestCustomTransactionDataDsvFileImporter_ParseValidGeographicLocation(t *te
|
|||||||
transactionTypeMapping := map[string]models.TransactionType{
|
transactionTypeMapping := map[string]models.TransactionType{
|
||||||
"E": models.TRANSACTION_TYPE_EXPENSE,
|
"E": models.TRANSACTION_TYPE_EXPENSE,
|
||||||
}
|
}
|
||||||
converter, err := CreateNewCustomTransactionDataDsvFileImporter("custom_csv", "utf-8", columnIndexMapping, transactionTypeMapping, false, "YYYY-MM-DD HH:mm:ss", "", ".", "", ";", "lonlat", "")
|
importer, err := CreateNewCustomTransactionDataDsvFileImporter("custom_csv", "utf-8", columnIndexMapping, transactionTypeMapping, false, "YYYY-MM-DD HH:mm:ss", "", ".", "", ";", "lonlat", "")
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
@@ -962,8 +1042,8 @@ func TestCustomTransactionDataDsvFileImporter_ParseValidGeographicLocation(t *te
|
|||||||
DefaultCurrency: "CNY",
|
DefaultCurrency: "CNY",
|
||||||
}
|
}
|
||||||
|
|
||||||
allNewTransactions, _, _, _, _, _, err := converter.ParseImportedData(context, user, []byte(
|
allNewTransactions, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(
|
||||||
"2024-09-01 12:34:56,E,123.45,123.45;45.56"), 0, nil, nil, nil, nil, nil)
|
"2024-09-01 12:34:56,E,123.45,123.45;45.56"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
|
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
assert.Equal(t, 1, len(allNewTransactions))
|
assert.Equal(t, 1, len(allNewTransactions))
|
||||||
@@ -981,7 +1061,7 @@ func TestCustomTransactionDataDsvFileImporter_ParseInvalidGeographicLocation(t *
|
|||||||
transactionTypeMapping := map[string]models.TransactionType{
|
transactionTypeMapping := map[string]models.TransactionType{
|
||||||
"E": models.TRANSACTION_TYPE_EXPENSE,
|
"E": models.TRANSACTION_TYPE_EXPENSE,
|
||||||
}
|
}
|
||||||
converter, err := CreateNewCustomTransactionDataDsvFileImporter("custom_csv", "utf-8", columnIndexMapping, transactionTypeMapping, false, "YYYY-MM-DD HH:mm:ss", "", ".", "", " ", "lonlat", "")
|
importer, err := CreateNewCustomTransactionDataDsvFileImporter("custom_csv", "utf-8", columnIndexMapping, transactionTypeMapping, false, "YYYY-MM-DD HH:mm:ss", "", ".", "", " ", "lonlat", "")
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
@@ -991,15 +1071,15 @@ func TestCustomTransactionDataDsvFileImporter_ParseInvalidGeographicLocation(t *
|
|||||||
DefaultCurrency: "CNY",
|
DefaultCurrency: "CNY",
|
||||||
}
|
}
|
||||||
|
|
||||||
allNewTransactions, _, _, _, _, _, err := converter.ParseImportedData(context, user, []byte(
|
allNewTransactions, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(
|
||||||
"2024-09-01 12:34:56,E,123.45,,,1"), 0, nil, nil, nil, nil, nil)
|
"2024-09-01 12:34:56,E,123.45,,,1"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
assert.Equal(t, 1, len(allNewTransactions))
|
assert.Equal(t, 1, len(allNewTransactions))
|
||||||
assert.Equal(t, float64(0), allNewTransactions[0].GeoLongitude)
|
assert.Equal(t, float64(0), allNewTransactions[0].GeoLongitude)
|
||||||
assert.Equal(t, float64(0), allNewTransactions[0].GeoLatitude)
|
assert.Equal(t, float64(0), allNewTransactions[0].GeoLatitude)
|
||||||
|
|
||||||
_, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte(
|
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(
|
||||||
"2024-09-01 12:34:56,E,123.45,a b"), 0, nil, nil, nil, nil, nil)
|
"2024-09-01 12:34:56,E,123.45,a b"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrGeographicLocationInvalid.Message)
|
assert.EqualError(t, err, errs.ErrGeographicLocationInvalid.Message)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1013,7 +1093,7 @@ func TestCustomTransactionDataDsvFileImporter_ParseTag(t *testing.T) {
|
|||||||
transactionTypeMapping := map[string]models.TransactionType{
|
transactionTypeMapping := map[string]models.TransactionType{
|
||||||
"E": models.TRANSACTION_TYPE_EXPENSE,
|
"E": models.TRANSACTION_TYPE_EXPENSE,
|
||||||
}
|
}
|
||||||
converter, err := CreateNewCustomTransactionDataDsvFileImporter("custom_csv", "utf-8", columnIndexMapping, transactionTypeMapping, false, "YYYY-MM-DD HH:mm:ss", "", ".", "", "", "", ";")
|
importer, err := CreateNewCustomTransactionDataDsvFileImporter("custom_csv", "utf-8", columnIndexMapping, transactionTypeMapping, false, "YYYY-MM-DD HH:mm:ss", "", ".", "", "", "", ";")
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
@@ -1023,8 +1103,8 @@ func TestCustomTransactionDataDsvFileImporter_ParseTag(t *testing.T) {
|
|||||||
DefaultCurrency: "CNY",
|
DefaultCurrency: "CNY",
|
||||||
}
|
}
|
||||||
|
|
||||||
_, _, _, _, _, allNewTags, err := converter.ParseImportedData(context, user, []byte(
|
_, _, _, _, _, allNewTags, err := importer.ParseImportedData(context, user, []byte(
|
||||||
"2024-09-01 00:00:00,E,123.45,foo;;bar.;#test;hello\tworld;;"), 0, nil, nil, nil, nil, nil)
|
"2024-09-01 00:00:00,E,123.45,foo;;bar.;#test;hello\tworld;;"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
|
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
@@ -1053,7 +1133,7 @@ func TestCustomTransactionDataDsvFileImporter_ParseTagWithoutSeparator(t *testin
|
|||||||
transactionTypeMapping := map[string]models.TransactionType{
|
transactionTypeMapping := map[string]models.TransactionType{
|
||||||
"E": models.TRANSACTION_TYPE_EXPENSE,
|
"E": models.TRANSACTION_TYPE_EXPENSE,
|
||||||
}
|
}
|
||||||
converter, err := CreateNewCustomTransactionDataDsvFileImporter("custom_csv", "utf-8", columnIndexMapping, transactionTypeMapping, false, "YYYY-MM-DD HH:mm:ss", "", ".", "", "", "", "")
|
importer, err := CreateNewCustomTransactionDataDsvFileImporter("custom_csv", "utf-8", columnIndexMapping, transactionTypeMapping, false, "YYYY-MM-DD HH:mm:ss", "", ".", "", "", "", "")
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
@@ -1063,8 +1143,8 @@ func TestCustomTransactionDataDsvFileImporter_ParseTagWithoutSeparator(t *testin
|
|||||||
DefaultCurrency: "CNY",
|
DefaultCurrency: "CNY",
|
||||||
}
|
}
|
||||||
|
|
||||||
_, _, _, _, _, allNewTags, err := converter.ParseImportedData(context, user, []byte(
|
_, _, _, _, _, allNewTags, err := importer.ParseImportedData(context, user, []byte(
|
||||||
"2024-09-01 00:00:00,E,123.45,foo;;bar.;#test;hello\tworld;;"), 0, nil, nil, nil, nil, nil)
|
"2024-09-01 00:00:00,E,123.45,foo;;bar.;#test;hello\tworld;;"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
|
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
@@ -1084,7 +1164,7 @@ func TestCustomTransactionDataDsvFileImporter_ParseDescription(t *testing.T) {
|
|||||||
transactionTypeMapping := map[string]models.TransactionType{
|
transactionTypeMapping := map[string]models.TransactionType{
|
||||||
"T": models.TRANSACTION_TYPE_TRANSFER,
|
"T": models.TRANSACTION_TYPE_TRANSFER,
|
||||||
}
|
}
|
||||||
converter, err := CreateNewCustomTransactionDataDsvFileImporter("custom_csv", "utf-8", columnIndexMapping, transactionTypeMapping, false, "YYYY-MM-DD HH:mm:ss", "", ".", "", "", "", "")
|
importer, err := CreateNewCustomTransactionDataDsvFileImporter("custom_csv", "utf-8", columnIndexMapping, transactionTypeMapping, false, "YYYY-MM-DD HH:mm:ss", "", ".", "", "", "", "")
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
@@ -1094,8 +1174,8 @@ func TestCustomTransactionDataDsvFileImporter_ParseDescription(t *testing.T) {
|
|||||||
DefaultCurrency: "CNY",
|
DefaultCurrency: "CNY",
|
||||||
}
|
}
|
||||||
|
|
||||||
allNewTransactions, _, _, _, _, _, err := converter.ParseImportedData(context, user, []byte(
|
allNewTransactions, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(
|
||||||
"2024-09-01 12:34:56,T,123.45,foo bar\t#test"), 0, nil, nil, nil, nil, nil)
|
"2024-09-01 12:34:56,T,123.45,foo bar\t#test"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
|
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
assert.Equal(t, 1, len(allNewTransactions))
|
assert.Equal(t, 1, len(allNewTransactions))
|
||||||
@@ -0,0 +1,137 @@
|
|||||||
|
package custom
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/mayswind/ezbookkeeping/pkg/converters/converter"
|
||||||
|
csvconverter "github.com/mayswind/ezbookkeeping/pkg/converters/csv"
|
||||||
|
"github.com/mayswind/ezbookkeeping/pkg/converters/datatable"
|
||||||
|
"github.com/mayswind/ezbookkeeping/pkg/converters/excel"
|
||||||
|
"github.com/mayswind/ezbookkeeping/pkg/core"
|
||||||
|
"github.com/mayswind/ezbookkeeping/pkg/errs"
|
||||||
|
"github.com/mayswind/ezbookkeeping/pkg/models"
|
||||||
|
)
|
||||||
|
|
||||||
|
const customOOXMLExcelFileType = "custom_xlsx"
|
||||||
|
const customMSCFBExcelFileType = "custom_xls"
|
||||||
|
|
||||||
|
// customTransactionDataExcelFileImporter defines the structure of custom excel importer for transaction data
|
||||||
|
type customTransactionDataExcelFileImporter struct {
|
||||||
|
fileType string
|
||||||
|
columnIndexMapping map[datatable.TransactionDataTableColumn]int
|
||||||
|
transactionTypeNameMapping map[string]models.TransactionType
|
||||||
|
hasHeaderLine bool
|
||||||
|
timeFormat string
|
||||||
|
timezoneFormat string
|
||||||
|
amountDecimalSeparator string
|
||||||
|
amountDigitGroupingSymbol string
|
||||||
|
geoLocationSeparator string
|
||||||
|
geoLocationOrder converter.TransactionGeoLocationOrder
|
||||||
|
transactionTagSeparator string
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseDataLines returns the parsed file lines for specified the excel file data
|
||||||
|
func (c *customTransactionDataExcelFileImporter) ParseDataLines(ctx core.Context, data []byte) ([][]string, error) {
|
||||||
|
var excelDataTable datatable.BasicDataTable
|
||||||
|
var err error
|
||||||
|
|
||||||
|
if c.fileType == customOOXMLExcelFileType {
|
||||||
|
excelDataTable, err = excel.CreateNewExcelOOXMLFileBasicDataTable(data, false)
|
||||||
|
} else if c.fileType == customMSCFBExcelFileType {
|
||||||
|
excelDataTable, err = excel.CreateNewExcelMSCFBFileBasicDataTable(data, false)
|
||||||
|
} else {
|
||||||
|
return nil, errs.ErrImportFileTypeNotSupported
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
iterator := excelDataTable.DataRowIterator()
|
||||||
|
allLines := make([][]string, 0)
|
||||||
|
|
||||||
|
for iterator.HasNext() {
|
||||||
|
row := iterator.Next()
|
||||||
|
items := make([]string, row.ColumnCount())
|
||||||
|
|
||||||
|
for i := 0; i < row.ColumnCount(); i++ {
|
||||||
|
items[i] = strings.Trim(row.GetData(i), " ")
|
||||||
|
}
|
||||||
|
|
||||||
|
allLines = append(allLines, items)
|
||||||
|
}
|
||||||
|
|
||||||
|
return allLines, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseImportedData returns the imported data by parsing the custom transaction dsv data
|
||||||
|
func (c *customTransactionDataExcelFileImporter) ParseImportedData(ctx core.Context, user *models.User, data []byte, defaultTimezone *time.Location, additionalOptions converter.TransactionDataImporterOptions, accountMap map[string]*models.Account, expenseCategoryMap map[string]map[string]*models.TransactionCategory, incomeCategoryMap map[string]map[string]*models.TransactionCategory, transferCategoryMap map[string]map[string]*models.TransactionCategory, tagMap map[string]*models.TransactionTag) (models.ImportedTransactionSlice, []*models.Account, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionTag, error) {
|
||||||
|
allLines, err := c.ParseDataLines(ctx, data)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, nil, nil, nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
dataTable := csvconverter.CreateNewCustomCsvBasicDataTable(allLines, c.hasHeaderLine)
|
||||||
|
transactionDataTable := CreateNewCustomPlainTextDataTable(dataTable, c.columnIndexMapping, c.transactionTypeNameMapping, c.timeFormat, c.timezoneFormat, c.amountDecimalSeparator, c.amountDigitGroupingSymbol)
|
||||||
|
dataTableImporter := converter.CreateNewImporterWithTypeNameMapping(customTransactionTypeNameMapping, c.geoLocationSeparator, c.geoLocationOrder, c.transactionTagSeparator)
|
||||||
|
|
||||||
|
return dataTableImporter.ParseImportedData(ctx, user, transactionDataTable, defaultTimezone, additionalOptions, accountMap, expenseCategoryMap, incomeCategoryMap, transferCategoryMap, tagMap)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsCustomExcelFileType returns whether the file type is the custom excel file type
|
||||||
|
func IsCustomExcelFileType(fileType string) bool {
|
||||||
|
return fileType == customOOXMLExcelFileType || fileType == customMSCFBExcelFileType
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateNewCustomTransactionDataExcelFileParser returns a new custom transaction data parser
|
||||||
|
func CreateNewCustomTransactionDataExcelFileParser(fileType string) (CustomTransactionDataParser, error) {
|
||||||
|
if fileType != customOOXMLExcelFileType && fileType != customMSCFBExcelFileType {
|
||||||
|
return nil, errs.ErrImportFileTypeNotSupported
|
||||||
|
}
|
||||||
|
|
||||||
|
return &customTransactionDataExcelFileImporter{
|
||||||
|
fileType: fileType,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateNewCustomTransactionDataExcelFileImporter returns a new custom excel importer for transaction data
|
||||||
|
func CreateNewCustomTransactionDataExcelFileImporter(fileType string, columnIndexMapping map[datatable.TransactionDataTableColumn]int, transactionTypeNameMapping map[string]models.TransactionType, hasHeaderLine bool, timeFormat string, timezoneFormat string, amountDecimalSeparator string, amountDigitGroupingSymbol string, geoLocationSeparator string, geoLocationOrder string, transactionTagSeparator string) (converter.TransactionDataImporter, error) {
|
||||||
|
if fileType != customOOXMLExcelFileType && fileType != customMSCFBExcelFileType {
|
||||||
|
return nil, errs.ErrImportFileTypeNotSupported
|
||||||
|
}
|
||||||
|
|
||||||
|
if geoLocationOrder == "" {
|
||||||
|
geoLocationOrder = string(converter.TRANSACTION_GEO_LOCATION_ORDER_LONGITUDE_LATITUDE)
|
||||||
|
} else if geoLocationOrder != string(converter.TRANSACTION_GEO_LOCATION_ORDER_LONGITUDE_LATITUDE) &&
|
||||||
|
geoLocationOrder != string(converter.TRANSACTION_GEO_LOCATION_ORDER_LATITUDE_LONGITUDE) {
|
||||||
|
return nil, errs.ErrImportFileTypeNotSupported
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, exists := columnIndexMapping[datatable.TRANSACTION_DATA_TABLE_TRANSACTION_TIME]; !exists {
|
||||||
|
return nil, errs.ErrMissingRequiredFieldInHeaderRow
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, exists := columnIndexMapping[datatable.TRANSACTION_DATA_TABLE_TRANSACTION_TYPE]; !exists {
|
||||||
|
return nil, errs.ErrMissingRequiredFieldInHeaderRow
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, exists := columnIndexMapping[datatable.TRANSACTION_DATA_TABLE_AMOUNT]; !exists {
|
||||||
|
return nil, errs.ErrMissingRequiredFieldInHeaderRow
|
||||||
|
}
|
||||||
|
|
||||||
|
return &customTransactionDataExcelFileImporter{
|
||||||
|
fileType: fileType,
|
||||||
|
columnIndexMapping: columnIndexMapping,
|
||||||
|
transactionTypeNameMapping: transactionTypeNameMapping,
|
||||||
|
hasHeaderLine: hasHeaderLine,
|
||||||
|
timeFormat: timeFormat,
|
||||||
|
timezoneFormat: timezoneFormat,
|
||||||
|
amountDecimalSeparator: amountDecimalSeparator,
|
||||||
|
amountDigitGroupingSymbol: amountDigitGroupingSymbol,
|
||||||
|
geoLocationSeparator: geoLocationSeparator,
|
||||||
|
geoLocationOrder: converter.TransactionGeoLocationOrder(geoLocationOrder),
|
||||||
|
transactionTagSeparator: transactionTagSeparator,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,254 @@
|
|||||||
|
package custom
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/mayswind/ezbookkeeping/pkg/core"
|
||||||
|
"github.com/mayswind/ezbookkeeping/pkg/errs"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestIsCustomExcelFileType(t *testing.T) {
|
||||||
|
assert.True(t, IsCustomExcelFileType("custom_xlsx"))
|
||||||
|
assert.True(t, IsCustomExcelFileType("custom_xls"))
|
||||||
|
|
||||||
|
assert.False(t, IsCustomExcelFileType("xlsx"))
|
||||||
|
assert.False(t, IsCustomExcelFileType("xls"))
|
||||||
|
assert.False(t, IsCustomExcelFileType("excel"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCustomTransactionDataParser_ParseOOXMLExcelDataLines_EmptyData(t *testing.T) {
|
||||||
|
importer, err := CreateNewCustomTransactionDataExcelFileParser("custom_xlsx")
|
||||||
|
assert.Nil(t, err)
|
||||||
|
|
||||||
|
context := core.NewNullContext()
|
||||||
|
|
||||||
|
testdata, err := os.ReadFile("../../../testdata/empty_excel_file.xlsx")
|
||||||
|
assert.Nil(t, err)
|
||||||
|
|
||||||
|
allLines, err := importer.ParseDataLines(context, testdata)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
|
||||||
|
assert.Equal(t, 0, len(allLines))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCustomTransactionDataParser_ParseOOXMLExcelDataLines_SingleSheet(t *testing.T) {
|
||||||
|
importer, err := CreateNewCustomTransactionDataExcelFileParser("custom_xlsx")
|
||||||
|
assert.Nil(t, err)
|
||||||
|
|
||||||
|
context := core.NewNullContext()
|
||||||
|
|
||||||
|
testdata, err := os.ReadFile("../../../testdata/simple_excel_file.xlsx")
|
||||||
|
assert.Nil(t, err)
|
||||||
|
|
||||||
|
allLines, err := importer.ParseDataLines(context, testdata)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
|
||||||
|
assert.Equal(t, 3, len(allLines))
|
||||||
|
|
||||||
|
assert.Equal(t, 3, len(allLines[0]))
|
||||||
|
assert.Equal(t, "A1", allLines[0][0])
|
||||||
|
assert.Equal(t, "B1", allLines[0][1])
|
||||||
|
assert.Equal(t, "C1", allLines[0][2])
|
||||||
|
|
||||||
|
assert.Equal(t, 3, len(allLines[1]))
|
||||||
|
assert.Equal(t, "A2", allLines[1][0])
|
||||||
|
assert.Equal(t, "B2", allLines[1][1])
|
||||||
|
assert.Equal(t, "C2", allLines[1][2])
|
||||||
|
|
||||||
|
assert.Equal(t, 3, len(allLines[2]))
|
||||||
|
assert.Equal(t, "A3", allLines[2][0])
|
||||||
|
assert.Equal(t, "B3", allLines[2][1])
|
||||||
|
assert.Equal(t, "C3", allLines[2][2])
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCustomTransactionDataParser_ParseOOXMLExcelDataLines_MultipleSheet(t *testing.T) {
|
||||||
|
importer, err := CreateNewCustomTransactionDataExcelFileParser("custom_xlsx")
|
||||||
|
assert.Nil(t, err)
|
||||||
|
|
||||||
|
context := core.NewNullContext()
|
||||||
|
|
||||||
|
testdata, err := os.ReadFile("../../../testdata/multiple_sheets_excel_file.xlsx")
|
||||||
|
assert.Nil(t, err)
|
||||||
|
|
||||||
|
allLines, err := importer.ParseDataLines(context, testdata)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
|
||||||
|
assert.Equal(t, 9, len(allLines))
|
||||||
|
|
||||||
|
assert.Equal(t, 3, len(allLines[0]))
|
||||||
|
assert.Equal(t, "A1", allLines[0][0])
|
||||||
|
assert.Equal(t, "B1", allLines[0][1])
|
||||||
|
assert.Equal(t, "C1", allLines[0][2])
|
||||||
|
|
||||||
|
assert.Equal(t, 3, len(allLines[1]))
|
||||||
|
assert.Equal(t, "1-A2", allLines[1][0])
|
||||||
|
assert.Equal(t, "1-B2", allLines[1][1])
|
||||||
|
assert.Equal(t, "1-C2", allLines[1][2])
|
||||||
|
|
||||||
|
assert.Equal(t, 3, len(allLines[2]))
|
||||||
|
assert.Equal(t, "1-A3", allLines[2][0])
|
||||||
|
assert.Equal(t, "1-B3", allLines[2][1])
|
||||||
|
assert.Equal(t, "1-C3", allLines[2][2])
|
||||||
|
|
||||||
|
assert.Equal(t, 3, len(allLines[3]))
|
||||||
|
assert.Equal(t, "A1", allLines[3][0])
|
||||||
|
assert.Equal(t, "B1", allLines[3][1])
|
||||||
|
assert.Equal(t, "C1", allLines[3][2])
|
||||||
|
|
||||||
|
assert.Equal(t, 2, len(allLines[4]))
|
||||||
|
assert.Equal(t, "3-A2", allLines[4][0])
|
||||||
|
assert.Equal(t, "3-B2", allLines[4][1])
|
||||||
|
|
||||||
|
assert.Equal(t, 3, len(allLines[5]))
|
||||||
|
assert.Equal(t, "A1", allLines[5][0])
|
||||||
|
assert.Equal(t, "B1", allLines[5][1])
|
||||||
|
assert.Equal(t, "C1", allLines[5][2])
|
||||||
|
|
||||||
|
assert.Equal(t, 3, len(allLines[6]))
|
||||||
|
assert.Equal(t, "A1", allLines[6][0])
|
||||||
|
assert.Equal(t, "B1", allLines[6][1])
|
||||||
|
assert.Equal(t, "C1", allLines[6][2])
|
||||||
|
|
||||||
|
assert.Equal(t, 3, len(allLines[7]))
|
||||||
|
assert.Equal(t, "5-A2", allLines[7][0])
|
||||||
|
assert.Equal(t, "5-B2", allLines[7][1])
|
||||||
|
assert.Equal(t, "5-C2", allLines[7][2])
|
||||||
|
|
||||||
|
assert.Equal(t, 3, len(allLines[8]))
|
||||||
|
assert.Equal(t, "5-A3", allLines[8][0])
|
||||||
|
assert.Equal(t, "5-B3", allLines[8][1])
|
||||||
|
assert.Equal(t, "5-C3", allLines[8][2])
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCustomTransactionDataParser_ParseOOXMLExcelDataLines_MultipleSheetWithDifferentColumnCount(t *testing.T) {
|
||||||
|
importer, err := CreateNewCustomTransactionDataExcelFileParser("custom_xlsx")
|
||||||
|
assert.Nil(t, err)
|
||||||
|
|
||||||
|
context := core.NewNullContext()
|
||||||
|
|
||||||
|
testdata, err := os.ReadFile("../../../testdata/multiple_sheets_with_different_header_row_excel_file.xlsx")
|
||||||
|
assert.Nil(t, err)
|
||||||
|
|
||||||
|
_, err = importer.ParseDataLines(context, testdata)
|
||||||
|
assert.EqualError(t, err, errs.ErrFieldsInMultiTableAreDifferent.Message)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCustomTransactionDataParser_ParseMSCFBExcelDataLines_EmptyData(t *testing.T) {
|
||||||
|
importer, err := CreateNewCustomTransactionDataExcelFileParser("custom_xls")
|
||||||
|
assert.Nil(t, err)
|
||||||
|
|
||||||
|
context := core.NewNullContext()
|
||||||
|
|
||||||
|
testdata, err := os.ReadFile("../../../testdata/empty_excel_file.xls")
|
||||||
|
assert.Nil(t, err)
|
||||||
|
|
||||||
|
allLines, err := importer.ParseDataLines(context, testdata)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
|
||||||
|
assert.Equal(t, 0, len(allLines))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCustomTransactionDataParser_ParseMSCFBExcelDataLines_SingleSheet(t *testing.T) {
|
||||||
|
importer, err := CreateNewCustomTransactionDataExcelFileParser("custom_xls")
|
||||||
|
assert.Nil(t, err)
|
||||||
|
|
||||||
|
context := core.NewNullContext()
|
||||||
|
|
||||||
|
testdata, err := os.ReadFile("../../../testdata/simple_excel_file.xls")
|
||||||
|
assert.Nil(t, err)
|
||||||
|
|
||||||
|
allLines, err := importer.ParseDataLines(context, testdata)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
|
||||||
|
assert.Equal(t, 3, len(allLines))
|
||||||
|
|
||||||
|
assert.Equal(t, 3, len(allLines[0]))
|
||||||
|
assert.Equal(t, "A1", allLines[0][0])
|
||||||
|
assert.Equal(t, "B1", allLines[0][1])
|
||||||
|
assert.Equal(t, "C1", allLines[0][2])
|
||||||
|
|
||||||
|
assert.Equal(t, 3, len(allLines[1]))
|
||||||
|
assert.Equal(t, "A2", allLines[1][0])
|
||||||
|
assert.Equal(t, "B2", allLines[1][1])
|
||||||
|
assert.Equal(t, "C2", allLines[1][2])
|
||||||
|
|
||||||
|
assert.Equal(t, 3, len(allLines[2]))
|
||||||
|
assert.Equal(t, "A3", allLines[2][0])
|
||||||
|
assert.Equal(t, "B3", allLines[2][1])
|
||||||
|
assert.Equal(t, "C3", allLines[2][2])
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCustomTransactionDataParser_ParseMSCFBExcelDataLines_MultipleSheet(t *testing.T) {
|
||||||
|
importer, err := CreateNewCustomTransactionDataExcelFileParser("custom_xls")
|
||||||
|
assert.Nil(t, err)
|
||||||
|
|
||||||
|
context := core.NewNullContext()
|
||||||
|
|
||||||
|
testdata, err := os.ReadFile("../../../testdata/multiple_sheets_excel_file.xls")
|
||||||
|
assert.Nil(t, err)
|
||||||
|
|
||||||
|
allLines, err := importer.ParseDataLines(context, testdata)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
|
||||||
|
assert.Equal(t, 9, len(allLines))
|
||||||
|
|
||||||
|
assert.Equal(t, 3, len(allLines[0]))
|
||||||
|
assert.Equal(t, "A1", allLines[0][0])
|
||||||
|
assert.Equal(t, "B1", allLines[0][1])
|
||||||
|
assert.Equal(t, "C1", allLines[0][2])
|
||||||
|
|
||||||
|
assert.Equal(t, 3, len(allLines[1]))
|
||||||
|
assert.Equal(t, "1-A2", allLines[1][0])
|
||||||
|
assert.Equal(t, "1-B2", allLines[1][1])
|
||||||
|
assert.Equal(t, "1-C2", allLines[1][2])
|
||||||
|
|
||||||
|
assert.Equal(t, 3, len(allLines[2]))
|
||||||
|
assert.Equal(t, "1-A3", allLines[2][0])
|
||||||
|
assert.Equal(t, "1-B3", allLines[2][1])
|
||||||
|
assert.Equal(t, "1-C3", allLines[2][2])
|
||||||
|
|
||||||
|
assert.Equal(t, 3, len(allLines[3]))
|
||||||
|
assert.Equal(t, "A1", allLines[3][0])
|
||||||
|
assert.Equal(t, "B1", allLines[3][1])
|
||||||
|
assert.Equal(t, "C1", allLines[3][2])
|
||||||
|
|
||||||
|
assert.Equal(t, 3, len(allLines[4]))
|
||||||
|
assert.Equal(t, "3-A2", allLines[4][0])
|
||||||
|
assert.Equal(t, "3-B2", allLines[4][1])
|
||||||
|
assert.Equal(t, "", allLines[4][2])
|
||||||
|
|
||||||
|
assert.Equal(t, 3, len(allLines[5]))
|
||||||
|
assert.Equal(t, "A1", allLines[5][0])
|
||||||
|
assert.Equal(t, "B1", allLines[5][1])
|
||||||
|
assert.Equal(t, "C1", allLines[5][2])
|
||||||
|
|
||||||
|
assert.Equal(t, 3, len(allLines[6]))
|
||||||
|
assert.Equal(t, "A1", allLines[6][0])
|
||||||
|
assert.Equal(t, "B1", allLines[6][1])
|
||||||
|
assert.Equal(t, "C1", allLines[6][2])
|
||||||
|
|
||||||
|
assert.Equal(t, 3, len(allLines[7]))
|
||||||
|
assert.Equal(t, "5-A2", allLines[7][0])
|
||||||
|
assert.Equal(t, "5-B2", allLines[7][1])
|
||||||
|
assert.Equal(t, "5-C2", allLines[7][2])
|
||||||
|
|
||||||
|
assert.Equal(t, 3, len(allLines[8]))
|
||||||
|
assert.Equal(t, "5-A3", allLines[8][0])
|
||||||
|
assert.Equal(t, "5-B3", allLines[8][1])
|
||||||
|
assert.Equal(t, "5-C3", allLines[8][2])
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCustomTransactionDataParser_ParseMSCFBExcelDataLines_MultipleSheetWithDifferentColumnCount(t *testing.T) {
|
||||||
|
importer, err := CreateNewCustomTransactionDataExcelFileParser("custom_xls")
|
||||||
|
assert.Nil(t, err)
|
||||||
|
|
||||||
|
context := core.NewNullContext()
|
||||||
|
|
||||||
|
testdata, err := os.ReadFile("../../../testdata/multiple_sheets_with_different_header_row_excel_file.xls")
|
||||||
|
assert.Nil(t, err)
|
||||||
|
|
||||||
|
_, err = importer.ParseDataLines(context, testdata)
|
||||||
|
assert.EqualError(t, err, errs.ErrFieldsInMultiTableAreDifferent.Message)
|
||||||
|
}
|
||||||
+23
-2
@@ -1,4 +1,4 @@
|
|||||||
package dsv
|
package custom
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"strings"
|
"strings"
|
||||||
@@ -107,6 +107,7 @@ func (t *customPlainTextDataRowIterator) Next(ctx core.Context, user *models.Use
|
|||||||
|
|
||||||
func (t *customPlainTextDataRowIterator) parseTransaction(ctx core.Context, user *models.User, row datatable.BasicDataTableRow) (map[datatable.TransactionDataTableColumn]string, bool, error) {
|
func (t *customPlainTextDataRowIterator) parseTransaction(ctx core.Context, user *models.User, row datatable.BasicDataTableRow) (map[datatable.TransactionDataTableColumn]string, bool, error) {
|
||||||
rowData := make(map[datatable.TransactionDataTableColumn]string, len(t.transactionDataTable.columnIndexMapping))
|
rowData := make(map[datatable.TransactionDataTableColumn]string, len(t.transactionDataTable.columnIndexMapping))
|
||||||
|
var transactionTime *time.Time = nil
|
||||||
|
|
||||||
for column, columnIndex := range t.transactionDataTable.columnIndexMapping {
|
for column, columnIndex := range t.transactionDataTable.columnIndexMapping {
|
||||||
if columnIndex < 0 || columnIndex >= row.ColumnCount() {
|
if columnIndex < 0 || columnIndex >= row.ColumnCount() {
|
||||||
@@ -144,10 +145,11 @@ func (t *customPlainTextDataRowIterator) parseTransaction(ctx core.Context, user
|
|||||||
return nil, false, errs.ErrTransactionTimeInvalid
|
return nil, false, errs.ErrTransactionTimeInvalid
|
||||||
}
|
}
|
||||||
|
|
||||||
|
transactionTime = &dateTime
|
||||||
rowData[datatable.TRANSACTION_DATA_TABLE_TRANSACTION_TIME] = utils.FormatUnixTimeToLongDateTime(dateTime.Unix(), dateTime.Location())
|
rowData[datatable.TRANSACTION_DATA_TABLE_TRANSACTION_TIME] = utils.FormatUnixTimeToLongDateTime(dateTime.Unix(), dateTime.Location())
|
||||||
|
|
||||||
if t.transactionDataTable.timeFormatIncludeTimezone {
|
if t.transactionDataTable.timeFormatIncludeTimezone {
|
||||||
rowData[datatable.TRANSACTION_DATA_TABLE_TRANSACTION_TIMEZONE] = utils.FormatTimezoneOffset(dateTime.Location())
|
rowData[datatable.TRANSACTION_DATA_TABLE_TRANSACTION_TIMEZONE] = utils.FormatTimezoneOffset(dateTime.Unix(), dateTime.Location())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -164,6 +166,19 @@ func (t *customPlainTextDataRowIterator) parseTransaction(ctx core.Context, user
|
|||||||
|
|
||||||
timezone = timezone[:3] + ":" + timezone[3:]
|
timezone = timezone[:3] + ":" + timezone[3:]
|
||||||
rowData[datatable.TRANSACTION_DATA_TABLE_TRANSACTION_TIMEZONE] = timezone
|
rowData[datatable.TRANSACTION_DATA_TABLE_TRANSACTION_TIMEZONE] = timezone
|
||||||
|
} else if t.transactionDataTable.timezoneFormat == "zzz" { // IANA Timezone Name
|
||||||
|
timezoneName := rowData[datatable.TRANSACTION_DATA_TABLE_TRANSACTION_TIMEZONE]
|
||||||
|
timezone, err := time.LoadLocation(timezoneName)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, false, errs.ErrTransactionTimeZoneInvalid
|
||||||
|
}
|
||||||
|
|
||||||
|
if transactionTime == nil {
|
||||||
|
return nil, false, errs.ErrTransactionTimeInvalid
|
||||||
|
}
|
||||||
|
|
||||||
|
rowData[datatable.TRANSACTION_DATA_TABLE_TRANSACTION_TIMEZONE] = utils.FormatTimezoneOffset(transactionTime.Unix(), timezone)
|
||||||
} else {
|
} else {
|
||||||
return nil, false, errs.ErrImportFileTransactionTimezoneFormatInvalid
|
return nil, false, errs.ErrImportFileTransactionTimezoneFormatInvalid
|
||||||
}
|
}
|
||||||
@@ -215,6 +230,12 @@ func (t *customPlainTextDataRowIterator) parseTransaction(ctx core.Context, user
|
|||||||
func (t *customPlainTextDataRowIterator) parseAmount(ctx core.Context, amountValue string) (string, error) {
|
func (t *customPlainTextDataRowIterator) parseAmount(ctx core.Context, amountValue string) (string, error) {
|
||||||
if t.transactionDataTable.amountDigitGroupingSymbol != "" {
|
if t.transactionDataTable.amountDigitGroupingSymbol != "" {
|
||||||
amountValue = strings.ReplaceAll(amountValue, t.transactionDataTable.amountDigitGroupingSymbol, "")
|
amountValue = strings.ReplaceAll(amountValue, t.transactionDataTable.amountDigitGroupingSymbol, "")
|
||||||
|
|
||||||
|
if t.transactionDataTable.amountDigitGroupingSymbol == " " {
|
||||||
|
amountValue = strings.ReplaceAll(amountValue, "\u00A0", "") // No-Break Space (NBSP)
|
||||||
|
amountValue = strings.ReplaceAll(amountValue, "\u202F", "") // Narrow No-Break Space (NNBSP)
|
||||||
|
amountValue = strings.ReplaceAll(amountValue, "\u2007", "") // Figure Space
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if t.transactionDataTable.amountDecimalSeparator != "" && t.transactionDataTable.amountDecimalSeparator != "." {
|
if t.transactionDataTable.amountDecimalSeparator != "" && t.transactionDataTable.amountDecimalSeparator != "." {
|
||||||
@@ -72,6 +72,10 @@ const (
|
|||||||
TRANSACTION_DATA_TABLE_GEOGRAPHIC_LOCATION TransactionDataTableColumn = 12
|
TRANSACTION_DATA_TABLE_GEOGRAPHIC_LOCATION TransactionDataTableColumn = 12
|
||||||
TRANSACTION_DATA_TABLE_TAGS TransactionDataTableColumn = 13
|
TRANSACTION_DATA_TABLE_TAGS TransactionDataTableColumn = 13
|
||||||
TRANSACTION_DATA_TABLE_DESCRIPTION TransactionDataTableColumn = 14
|
TRANSACTION_DATA_TABLE_DESCRIPTION TransactionDataTableColumn = 14
|
||||||
|
TRANSACTION_DATA_TABLE_PAYEE TransactionDataTableColumn = 101
|
||||||
|
TRANSACTION_DATA_TABLE_MEMBER TransactionDataTableColumn = 102
|
||||||
|
TRANSACTION_DATA_TABLE_PROJECT TransactionDataTableColumn = 103
|
||||||
|
TRANSACTION_DATA_TABLE_MERCHANT TransactionDataTableColumn = 104
|
||||||
)
|
)
|
||||||
|
|
||||||
// TRANSACTION_DATA_TABLE_TIMEZONE_NOT_AVAILABLE represents the constant for timezone not available
|
// TRANSACTION_DATA_TABLE_TIMEZONE_NOT_AVAILABLE represents the constant for timezone not available
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// ParseImportedData returns the imported data by parsing the transaction json data
|
// ParseImportedData returns the imported data by parsing the transaction json data
|
||||||
func (c *defaultTransactionDataJsonImporter) ParseImportedData(ctx core.Context, user *models.User, data []byte, defaultTimezoneOffset int16, accountMap map[string]*models.Account, expenseCategoryMap map[string]map[string]*models.TransactionCategory, incomeCategoryMap map[string]map[string]*models.TransactionCategory, transferCategoryMap map[string]map[string]*models.TransactionCategory, tagMap map[string]*models.TransactionTag) (models.ImportedTransactionSlice, []*models.Account, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionTag, error) {
|
func (c *defaultTransactionDataJsonImporter) ParseImportedData(ctx core.Context, user *models.User, data []byte, defaultTimezone *time.Location, additionalOptions converter.TransactionDataImporterOptions, accountMap map[string]*models.Account, expenseCategoryMap map[string]map[string]*models.TransactionCategory, incomeCategoryMap map[string]map[string]*models.TransactionCategory, transferCategoryMap map[string]map[string]*models.TransactionCategory, tagMap map[string]*models.TransactionTag) (models.ImportedTransactionSlice, []*models.Account, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionTag, error) {
|
||||||
var importRequest models.ImportTransactionRequest
|
var importRequest models.ImportTransactionRequest
|
||||||
|
|
||||||
if err := json.Unmarshal(data, &importRequest); err != nil {
|
if err := json.Unmarshal(data, &importRequest); err != nil {
|
||||||
@@ -55,7 +55,7 @@ func (c *defaultTransactionDataJsonImporter) ParseImportedData(ctx core.Context,
|
|||||||
ezbookkeepingTagSeparator,
|
ezbookkeepingTagSeparator,
|
||||||
)
|
)
|
||||||
|
|
||||||
return dataTableImporter.ParseImportedData(ctx, user, transactionDataTable, defaultTimezoneOffset, accountMap, expenseCategoryMap, incomeCategoryMap, transferCategoryMap, tagMap)
|
return dataTableImporter.ParseImportedData(ctx, user, transactionDataTable, defaultTimezone, additionalOptions, accountMap, expenseCategoryMap, incomeCategoryMap, transferCategoryMap, tagMap)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *defaultTransactionDataJsonImporter) createNewDefaultTransactionDataTable(importRequest models.ImportTransactionRequest) (datatable.TransactionDataTable, error) {
|
func (c *defaultTransactionDataJsonImporter) createNewDefaultTransactionDataTable(importRequest models.ImportTransactionRequest) (datatable.TransactionDataTable, error) {
|
||||||
@@ -75,10 +75,11 @@ func (c *defaultTransactionDataJsonImporter) createNewDefaultTransactionDataTabl
|
|||||||
}
|
}
|
||||||
|
|
||||||
timezone := time.FixedZone("Transaction Timezone", utcOffset*60)
|
timezone := time.FixedZone("Transaction Timezone", utcOffset*60)
|
||||||
|
timezoneOffset := utils.FormatTimezoneOffset(time.Now().Unix(), timezone)
|
||||||
|
|
||||||
row := make(map[datatable.TransactionDataTableColumn]string, len(allJsonDataSupportedColumns))
|
row := make(map[datatable.TransactionDataTableColumn]string, len(allJsonDataSupportedColumns))
|
||||||
row[datatable.TRANSACTION_DATA_TABLE_TRANSACTION_TIME] = transaction.Time
|
row[datatable.TRANSACTION_DATA_TABLE_TRANSACTION_TIME] = transaction.Time
|
||||||
row[datatable.TRANSACTION_DATA_TABLE_TRANSACTION_TIMEZONE] = utils.FormatTimezoneOffset(timezone)
|
row[datatable.TRANSACTION_DATA_TABLE_TRANSACTION_TIMEZONE] = timezoneOffset
|
||||||
row[datatable.TRANSACTION_DATA_TABLE_TRANSACTION_TYPE] = transaction.Type
|
row[datatable.TRANSACTION_DATA_TABLE_TRANSACTION_TYPE] = transaction.Type
|
||||||
row[datatable.TRANSACTION_DATA_TABLE_SUB_CATEGORY] = transaction.CategoryName
|
row[datatable.TRANSACTION_DATA_TABLE_SUB_CATEGORY] = transaction.CategoryName
|
||||||
row[datatable.TRANSACTION_DATA_TABLE_ACCOUNT_NAME] = transaction.SourceAccountName
|
row[datatable.TRANSACTION_DATA_TABLE_ACCOUNT_NAME] = transaction.SourceAccountName
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
package _default
|
package _default
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/converters/converter"
|
"github.com/mayswind/ezbookkeeping/pkg/converters/converter"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/converters/datatable"
|
"github.com/mayswind/ezbookkeeping/pkg/converters/datatable"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/core"
|
"github.com/mayswind/ezbookkeeping/pkg/core"
|
||||||
@@ -84,7 +86,7 @@ func (c *defaultTransactionDataPlainTextConverter) ToExportedContent(ctx core.Co
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ParseImportedData returns the imported data by parsing the transaction plain text data
|
// ParseImportedData returns the imported data by parsing the transaction plain text data
|
||||||
func (c *defaultTransactionDataPlainTextConverter) ParseImportedData(ctx core.Context, user *models.User, data []byte, defaultTimezoneOffset int16, accountMap map[string]*models.Account, expenseCategoryMap map[string]map[string]*models.TransactionCategory, incomeCategoryMap map[string]map[string]*models.TransactionCategory, transferCategoryMap map[string]map[string]*models.TransactionCategory, tagMap map[string]*models.TransactionTag) (models.ImportedTransactionSlice, []*models.Account, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionTag, error) {
|
func (c *defaultTransactionDataPlainTextConverter) ParseImportedData(ctx core.Context, user *models.User, data []byte, defaultTimezone *time.Location, additionalOptions converter.TransactionDataImporterOptions, accountMap map[string]*models.Account, expenseCategoryMap map[string]map[string]*models.TransactionCategory, incomeCategoryMap map[string]map[string]*models.TransactionCategory, transferCategoryMap map[string]map[string]*models.TransactionCategory, tagMap map[string]*models.TransactionTag) (models.ImportedTransactionSlice, []*models.Account, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionTag, error) {
|
||||||
dataTable, err := createNewDefaultPlainTextDataTable(
|
dataTable, err := createNewDefaultPlainTextDataTable(
|
||||||
string(data),
|
string(data),
|
||||||
c.columnSeparator,
|
c.columnSeparator,
|
||||||
@@ -104,5 +106,5 @@ func (c *defaultTransactionDataPlainTextConverter) ParseImportedData(ctx core.Co
|
|||||||
ezbookkeepingTagSeparator,
|
ezbookkeepingTagSeparator,
|
||||||
)
|
)
|
||||||
|
|
||||||
return dataTableImporter.ParseImportedData(ctx, user, transactionDataTable, defaultTimezoneOffset, accountMap, expenseCategoryMap, incomeCategoryMap, transferCategoryMap, tagMap)
|
return dataTableImporter.ParseImportedData(ctx, user, transactionDataTable, defaultTimezone, additionalOptions, accountMap, expenseCategoryMap, incomeCategoryMap, transferCategoryMap, tagMap)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,9 +2,11 @@ package _default
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
|
"github.com/mayswind/ezbookkeeping/pkg/converters/converter"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/core"
|
"github.com/mayswind/ezbookkeeping/pkg/core"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/errs"
|
"github.com/mayswind/ezbookkeeping/pkg/errs"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/models"
|
"github.com/mayswind/ezbookkeeping/pkg/models"
|
||||||
@@ -12,7 +14,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func TestDefaultTransactionDataCSVFileConverterToExportedContent(t *testing.T) {
|
func TestDefaultTransactionDataCSVFileConverterToExportedContent(t *testing.T) {
|
||||||
converter := DefaultTransactionDataCSVFileConverter
|
exporter := DefaultTransactionDataCSVFileConverter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
transactions := make([]*models.Transaction, 3)
|
transactions := make([]*models.Transaction, 3)
|
||||||
@@ -119,14 +121,14 @@ func TestDefaultTransactionDataCSVFileConverterToExportedContent(t *testing.T) {
|
|||||||
"2024-09-01 12:34:56,+08:00,Income,Test Category,Test Sub Category,Test Account,CNY,123.45,,,,123.450000 45.670000,Test Tag;Test Tag2,Hello World\n" +
|
"2024-09-01 12:34:56,+08:00,Income,Test Category,Test Sub Category,Test Account,CNY,123.45,,,,123.450000 45.670000,Test Tag;Test Tag2,Hello World\n" +
|
||||||
"2024-09-01 12:34:56,+00:00,Expense,Test Category2,Test Sub Category2,Test Account,CNY,-0.10,,,,,Test Tag,Foo#Bar\n" +
|
"2024-09-01 12:34:56,+00:00,Expense,Test Category2,Test Sub Category2,Test Account,CNY,-0.10,,,,,Test Tag,Foo#Bar\n" +
|
||||||
"2024-09-01 12:34:56,-05:00,Transfer,Test Category3,Test Sub Category3,Test Account,CNY,123.45,Test Account2,USD,17.35,,Test Tag2,T\te s t test\n"
|
"2024-09-01 12:34:56,-05:00,Transfer,Test Category3,Test Sub Category3,Test Account,CNY,123.45,Test Account2,USD,17.35,,Test Tag2,T\te s t test\n"
|
||||||
actualContent, err := converter.ToExportedContent(context, 123, transactions, accountMap, categoryMap, tagMap, allTagIndexes)
|
actualContent, err := exporter.ToExportedContent(context, 123, transactions, accountMap, categoryMap, tagMap, allTagIndexes)
|
||||||
|
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
assert.Equal(t, expectedContent, string(actualContent))
|
assert.Equal(t, expectedContent, string(actualContent))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDefaultTransactionDataCSVFileConverterParseImportedData_MinimumValidData(t *testing.T) {
|
func TestDefaultTransactionDataCSVFileConverterParseImportedData_MinimumValidData(t *testing.T) {
|
||||||
converter := DefaultTransactionDataCSVFileConverter
|
importer := DefaultTransactionDataCSVFileConverter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
@@ -134,11 +136,11 @@ func TestDefaultTransactionDataCSVFileConverterParseImportedData_MinimumValidDat
|
|||||||
DefaultCurrency: "CNY",
|
DefaultCurrency: "CNY",
|
||||||
}
|
}
|
||||||
|
|
||||||
allNewTransactions, allNewAccounts, allNewSubExpenseCategories, allNewSubIncomeCategories, allNewSubTransferCategories, allNewTags, err := converter.ParseImportedData(context, user, []byte("Time,Type,Sub Category,Account,Amount,Account2,Account2 Amount\n"+
|
allNewTransactions, allNewAccounts, allNewSubExpenseCategories, allNewSubIncomeCategories, allNewSubTransferCategories, allNewTags, err := importer.ParseImportedData(context, user, []byte("Time,Type,Sub Category,Account,Amount,Account2,Account2 Amount\n"+
|
||||||
"2024-09-01 00:00:00,Balance Modification,,Test Account,123.45,,\n"+
|
"2024-09-01 00:00:00,Balance Modification,,Test Account,123.45,,\n"+
|
||||||
"2024-09-01 01:23:45,Income,Test Category,Test Account,0.12,,\n"+
|
"2024-09-01 01:23:45,Income,Test Category,Test Account,0.12,,\n"+
|
||||||
"2024-09-01 12:34:56,Expense,Test Category2,Test Account,1.00,,\n"+
|
"2024-09-01 12:34:56,Expense,Test Category2,Test Account,1.00,,\n"+
|
||||||
"2024-09-01 23:59:59,Transfer,Test Category3,Test Account,0.05,Test Account2,0.05"), 0, nil, nil, nil, nil, nil)
|
"2024-09-01 23:59:59,Transfer,Test Category3,Test Account,0.05,Test Account2,0.05"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
|
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
@@ -197,7 +199,7 @@ func TestDefaultTransactionDataCSVFileConverterParseImportedData_MinimumValidDat
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestDefaultTransactionDataCSVFileConverterParseImportedData_ParseInvalidTime(t *testing.T) {
|
func TestDefaultTransactionDataCSVFileConverterParseImportedData_ParseInvalidTime(t *testing.T) {
|
||||||
converter := DefaultTransactionDataCSVFileConverter
|
importer := DefaultTransactionDataCSVFileConverter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
@@ -205,17 +207,17 @@ func TestDefaultTransactionDataCSVFileConverterParseImportedData_ParseInvalidTim
|
|||||||
DefaultCurrency: "CNY",
|
DefaultCurrency: "CNY",
|
||||||
}
|
}
|
||||||
|
|
||||||
_, _, _, _, _, _, err := converter.ParseImportedData(context, user, []byte("Time,Type,Sub Category,Account,Amount,Account2,Account2 Amount\n"+
|
_, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte("Time,Type,Sub Category,Account,Amount,Account2,Account2 Amount\n"+
|
||||||
"2024-09-01T12:34:56,Expense,Test Category,Test Account,123.45,,"), 0, nil, nil, nil, nil, nil)
|
"2024-09-01T12:34:56,Expense,Test Category,Test Account,123.45,,"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrTransactionTimeInvalid.Message)
|
assert.EqualError(t, err, errs.ErrTransactionTimeInvalid.Message)
|
||||||
|
|
||||||
_, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte("Time,Type,Sub Category,Account,Amount,Account2,Account2 Amount\n"+
|
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte("Time,Type,Sub Category,Account,Amount,Account2,Account2 Amount\n"+
|
||||||
"09/01/2024 12:34:56,Expense,Test Category,Test Account,123.45,,"), 0, nil, nil, nil, nil, nil)
|
"09/01/2024 12:34:56,Expense,Test Category,Test Account,123.45,,"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrTransactionTimeInvalid.Message)
|
assert.EqualError(t, err, errs.ErrTransactionTimeInvalid.Message)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDefaultTransactionDataCSVFileConverterParseImportedData_ParseInvalidType(t *testing.T) {
|
func TestDefaultTransactionDataCSVFileConverterParseImportedData_ParseInvalidType(t *testing.T) {
|
||||||
converter := DefaultTransactionDataCSVFileConverter
|
importer := DefaultTransactionDataCSVFileConverter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
@@ -223,13 +225,13 @@ func TestDefaultTransactionDataCSVFileConverterParseImportedData_ParseInvalidTyp
|
|||||||
DefaultCurrency: "CNY",
|
DefaultCurrency: "CNY",
|
||||||
}
|
}
|
||||||
|
|
||||||
_, _, _, _, _, _, err := converter.ParseImportedData(context, user, []byte("Time,Type,Sub Category,Account,Amount,Account2,Account2 Amount\n"+
|
_, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte("Time,Type,Sub Category,Account,Amount,Account2,Account2 Amount\n"+
|
||||||
"2024-09-01 12:34:56,Type,Test Category,Test Account,123.45,,"), 0, nil, nil, nil, nil, nil)
|
"2024-09-01 12:34:56,Type,Test Category,Test Account,123.45,,"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrTransactionTypeInvalid.Message)
|
assert.EqualError(t, err, errs.ErrTransactionTypeInvalid.Message)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDefaultTransactionDataCSVFileConverterParseImportedData_ParseValidTimezone(t *testing.T) {
|
func TestDefaultTransactionDataCSVFileConverterParseImportedData_ParseValidTimezone(t *testing.T) {
|
||||||
converter := DefaultTransactionDataCSVFileConverter
|
importer := DefaultTransactionDataCSVFileConverter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
@@ -237,27 +239,27 @@ func TestDefaultTransactionDataCSVFileConverterParseImportedData_ParseValidTimez
|
|||||||
DefaultCurrency: "CNY",
|
DefaultCurrency: "CNY",
|
||||||
}
|
}
|
||||||
|
|
||||||
allNewTransactions, _, _, _, _, _, err := converter.ParseImportedData(context, user, []byte("Time,Timezone,Type,Sub Category,Account,Amount,Account2,Account2 Amount\n"+
|
allNewTransactions, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte("Time,Timezone,Type,Sub Category,Account,Amount,Account2,Account2 Amount\n"+
|
||||||
"2024-09-01 12:34:56,-10:00,Expense,Test Category,Test Account,123.45,,"), 0, nil, nil, nil, nil, nil)
|
"2024-09-01 12:34:56,-10:00,Expense,Test Category,Test Account,123.45,,"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
assert.Equal(t, 1, len(allNewTransactions))
|
assert.Equal(t, 1, len(allNewTransactions))
|
||||||
assert.Equal(t, int64(1725230096), utils.GetUnixTimeFromTransactionTime(allNewTransactions[0].TransactionTime))
|
assert.Equal(t, int64(1725230096), utils.GetUnixTimeFromTransactionTime(allNewTransactions[0].TransactionTime))
|
||||||
|
|
||||||
allNewTransactions, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte("Time,Timezone,Type,Sub Category,Account,Amount,Account2,Account2 Amount\n"+
|
allNewTransactions, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte("Time,Timezone,Type,Sub Category,Account,Amount,Account2,Account2 Amount\n"+
|
||||||
"2024-09-01 12:34:56,+00:00,Expense,Test Category,Test Account,123.45,,"), 0, nil, nil, nil, nil, nil)
|
"2024-09-01 12:34:56,+00:00,Expense,Test Category,Test Account,123.45,,"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
assert.Equal(t, 1, len(allNewTransactions))
|
assert.Equal(t, 1, len(allNewTransactions))
|
||||||
assert.Equal(t, int64(1725194096), utils.GetUnixTimeFromTransactionTime(allNewTransactions[0].TransactionTime))
|
assert.Equal(t, int64(1725194096), utils.GetUnixTimeFromTransactionTime(allNewTransactions[0].TransactionTime))
|
||||||
|
|
||||||
allNewTransactions, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte("Time,Timezone,Type,Sub Category,Account,Amount,Account2,Account2 Amount\n"+
|
allNewTransactions, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte("Time,Timezone,Type,Sub Category,Account,Amount,Account2,Account2 Amount\n"+
|
||||||
"2024-09-01 12:34:56,+12:45,Expense,Test Category,Test Account,123.45,,"), 0, nil, nil, nil, nil, nil)
|
"2024-09-01 12:34:56,+12:45,Expense,Test Category,Test Account,123.45,,"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
assert.Equal(t, 1, len(allNewTransactions))
|
assert.Equal(t, 1, len(allNewTransactions))
|
||||||
assert.Equal(t, int64(1725148196), utils.GetUnixTimeFromTransactionTime(allNewTransactions[0].TransactionTime))
|
assert.Equal(t, int64(1725148196), utils.GetUnixTimeFromTransactionTime(allNewTransactions[0].TransactionTime))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDefaultTransactionDataCSVFileConverterParseImportedData_ParseInvalidTimezone(t *testing.T) {
|
func TestDefaultTransactionDataCSVFileConverterParseImportedData_ParseInvalidTimezone(t *testing.T) {
|
||||||
converter := DefaultTransactionDataCSVFileConverter
|
importer := DefaultTransactionDataCSVFileConverter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
@@ -265,13 +267,13 @@ func TestDefaultTransactionDataCSVFileConverterParseImportedData_ParseInvalidTim
|
|||||||
DefaultCurrency: "CNY",
|
DefaultCurrency: "CNY",
|
||||||
}
|
}
|
||||||
|
|
||||||
_, _, _, _, _, _, err := converter.ParseImportedData(context, user, []byte("Time,Timezone,Type,Sub Category,Account,Amount,Account2,Account2 Amount\n"+
|
_, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte("Time,Timezone,Type,Sub Category,Account,Amount,Account2,Account2 Amount\n"+
|
||||||
"2024-09-01 12:34:56,Asia/Shanghai,Expense,Test Category,Test Account,123.45,,"), 0, nil, nil, nil, nil, nil)
|
"2024-09-01 12:34:56,Asia/Shanghai,Expense,Test Category,Test Account,123.45,,"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrTransactionTimeZoneInvalid.Message)
|
assert.EqualError(t, err, errs.ErrTransactionTimeZoneInvalid.Message)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDefaultTransactionDataCSVFileConverterParseImportedData_ParseValidAccountCurrency(t *testing.T) {
|
func TestDefaultTransactionDataCSVFileConverterParseImportedData_ParseValidAccountCurrency(t *testing.T) {
|
||||||
converter := DefaultTransactionDataCSVFileConverter
|
importer := DefaultTransactionDataCSVFileConverter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
@@ -279,9 +281,9 @@ func TestDefaultTransactionDataCSVFileConverterParseImportedData_ParseValidAccou
|
|||||||
DefaultCurrency: "CNY",
|
DefaultCurrency: "CNY",
|
||||||
}
|
}
|
||||||
|
|
||||||
allNewTransactions, allNewAccounts, _, _, _, _, err := converter.ParseImportedData(context, user, []byte("Time,Type,Sub Category,Account,Account Currency,Amount,Account2,Account2 Currency,Account2 Amount\n"+
|
allNewTransactions, allNewAccounts, _, _, _, _, err := importer.ParseImportedData(context, user, []byte("Time,Type,Sub Category,Account,Account Currency,Amount,Account2,Account2 Currency,Account2 Amount\n"+
|
||||||
"2024-09-01 01:23:45,Balance Modification,,Test Account,USD,123.45,,,\n"+
|
"2024-09-01 01:23:45,Balance Modification,,Test Account,USD,123.45,,,\n"+
|
||||||
"2024-09-01 12:34:56,Transfer,Test Category2,Test Account,USD,1.23,Test Account2,EUR,1.10"), 0, nil, nil, nil, nil, nil)
|
"2024-09-01 12:34:56,Transfer,Test Category2,Test Account,USD,1.23,Test Account2,EUR,1.10"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
|
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
@@ -298,7 +300,7 @@ func TestDefaultTransactionDataCSVFileConverterParseImportedData_ParseValidAccou
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestDefaultTransactionDataCSVFileConverterParseImportedData_ParseInvalidAccountCurrency(t *testing.T) {
|
func TestDefaultTransactionDataCSVFileConverterParseImportedData_ParseInvalidAccountCurrency(t *testing.T) {
|
||||||
converter := DefaultTransactionDataCSVFileConverter
|
importer := DefaultTransactionDataCSVFileConverter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
@@ -306,19 +308,19 @@ func TestDefaultTransactionDataCSVFileConverterParseImportedData_ParseInvalidAcc
|
|||||||
DefaultCurrency: "CNY",
|
DefaultCurrency: "CNY",
|
||||||
}
|
}
|
||||||
|
|
||||||
_, _, _, _, _, _, err := converter.ParseImportedData(context, user, []byte("Time,Type,Sub Category,Account,Account Currency,Amount,Account2,Account2 Currency,Account2 Amount\n"+
|
_, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte("Time,Type,Sub Category,Account,Account Currency,Amount,Account2,Account2 Currency,Account2 Amount\n"+
|
||||||
"2024-09-01 01:23:45,Balance Modification,,Test Account,USD,123.45,,,\n"+
|
"2024-09-01 01:23:45,Balance Modification,,Test Account,USD,123.45,,,\n"+
|
||||||
"2024-09-01 12:34:56,Transfer,Test Category3,Test Account,CNY,1.23,Test Account2,EUR,1.10"), 0, nil, nil, nil, nil, nil)
|
"2024-09-01 12:34:56,Transfer,Test Category3,Test Account,CNY,1.23,Test Account2,EUR,1.10"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrAccountCurrencyInvalid.Message)
|
assert.EqualError(t, err, errs.ErrAccountCurrencyInvalid.Message)
|
||||||
|
|
||||||
_, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte("Time,Type,Sub Category,Account,Account Currency,Amount,Account2,Account2 Currency,Account2 Amount\n"+
|
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte("Time,Type,Sub Category,Account,Account Currency,Amount,Account2,Account2 Currency,Account2 Amount\n"+
|
||||||
"2024-09-01 01:23:45,Balance Modification,,Test Account,USD,123.45,,,\n"+
|
"2024-09-01 01:23:45,Balance Modification,,Test Account,USD,123.45,,,\n"+
|
||||||
"2024-09-01 12:34:56,Transfer,Test Category3,Test Account2,CNY,1.23,Test Account,EUR,1.10"), 0, nil, nil, nil, nil, nil)
|
"2024-09-01 12:34:56,Transfer,Test Category3,Test Account2,CNY,1.23,Test Account,EUR,1.10"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrAccountCurrencyInvalid.Message)
|
assert.EqualError(t, err, errs.ErrAccountCurrencyInvalid.Message)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDefaultTransactionDataCSVFileConverterParseImportedData_ParseNotSupportedCurrency(t *testing.T) {
|
func TestDefaultTransactionDataCSVFileConverterParseImportedData_ParseNotSupportedCurrency(t *testing.T) {
|
||||||
converter := DefaultTransactionDataCSVFileConverter
|
importer := DefaultTransactionDataCSVFileConverter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
@@ -326,17 +328,17 @@ func TestDefaultTransactionDataCSVFileConverterParseImportedData_ParseNotSupport
|
|||||||
DefaultCurrency: "CNY",
|
DefaultCurrency: "CNY",
|
||||||
}
|
}
|
||||||
|
|
||||||
_, _, _, _, _, _, err := converter.ParseImportedData(context, user, []byte("Time,Type,Sub Category,Account,Account Currency,Amount,Account2,Account2 Currency,Account2 Amount\n"+
|
_, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte("Time,Type,Sub Category,Account,Account Currency,Amount,Account2,Account2 Currency,Account2 Amount\n"+
|
||||||
"2024-09-01 01:23:45,Balance Modification,,Test Account,XXX,123.45,,,"), 0, nil, nil, nil, nil, nil)
|
"2024-09-01 01:23:45,Balance Modification,,Test Account,XXX,123.45,,,"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrAccountCurrencyInvalid.Message)
|
assert.EqualError(t, err, errs.ErrAccountCurrencyInvalid.Message)
|
||||||
|
|
||||||
_, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte("Time,Type,Sub Category,Account,Account Currency,Amount,Account2,Account2 Currency,Account2 Amount\n"+
|
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte("Time,Type,Sub Category,Account,Account Currency,Amount,Account2,Account2 Currency,Account2 Amount\n"+
|
||||||
"2024-09-01 01:23:45,Transfer,Test Category,Test Account,USD,123.45,Test Account2,XXX,123.45"), 0, nil, nil, nil, nil, nil)
|
"2024-09-01 01:23:45,Transfer,Test Category,Test Account,USD,123.45,Test Account2,XXX,123.45"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrAccountCurrencyInvalid.Message)
|
assert.EqualError(t, err, errs.ErrAccountCurrencyInvalid.Message)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDefaultTransactionDataCSVFileConverterParseImportedData_ParseInvalidAmount(t *testing.T) {
|
func TestDefaultTransactionDataCSVFileConverterParseImportedData_ParseInvalidAmount(t *testing.T) {
|
||||||
converter := DefaultTransactionDataCSVFileConverter
|
importer := DefaultTransactionDataCSVFileConverter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
@@ -344,17 +346,17 @@ func TestDefaultTransactionDataCSVFileConverterParseImportedData_ParseInvalidAmo
|
|||||||
DefaultCurrency: "CNY",
|
DefaultCurrency: "CNY",
|
||||||
}
|
}
|
||||||
|
|
||||||
_, _, _, _, _, _, err := converter.ParseImportedData(context, user, []byte("Time,Type,Sub Category,Account,Amount,Account2,Account2 Amount\n"+
|
_, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte("Time,Type,Sub Category,Account,Amount,Account2,Account2 Amount\n"+
|
||||||
"2024-09-01 12:34:56,Expense,Test Category,Test Account,123 45,,"), 0, nil, nil, nil, nil, nil)
|
"2024-09-01 12:34:56,Expense,Test Category,Test Account,123 45,,"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrAmountInvalid.Message)
|
assert.EqualError(t, err, errs.ErrAmountInvalid.Message)
|
||||||
|
|
||||||
_, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte("Time,Type,Sub Category,Account,Amount,Account2,Account2 Amount\n"+
|
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte("Time,Type,Sub Category,Account,Amount,Account2,Account2 Amount\n"+
|
||||||
"2024-09-01 12:34:56,Transfer,Test Category,Test Account,123.45,Test Account2,123 45"), 0, nil, nil, nil, nil, nil)
|
"2024-09-01 12:34:56,Transfer,Test Category,Test Account,123.45,Test Account2,123 45"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrAmountInvalid.Message)
|
assert.EqualError(t, err, errs.ErrAmountInvalid.Message)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDefaultTransactionDataCSVFileConverterParseImportedData_ParseNoAmount2(t *testing.T) {
|
func TestDefaultTransactionDataCSVFileConverterParseImportedData_ParseNoAmount2(t *testing.T) {
|
||||||
converter := DefaultTransactionDataCSVFileConverter
|
importer := DefaultTransactionDataCSVFileConverter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
@@ -362,15 +364,15 @@ func TestDefaultTransactionDataCSVFileConverterParseImportedData_ParseNoAmount2(
|
|||||||
DefaultCurrency: "CNY",
|
DefaultCurrency: "CNY",
|
||||||
}
|
}
|
||||||
|
|
||||||
allNewTransactions, _, _, _, _, _, err := converter.ParseImportedData(context, user, []byte("Time,Type,Sub Category,Account,Amount,Account2\n"+
|
allNewTransactions, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte("Time,Type,Sub Category,Account,Amount,Account2\n"+
|
||||||
"2024-09-01 12:34:56,Expense,Test Category,Test Account,123.45,"), 0, nil, nil, nil, nil, nil)
|
"2024-09-01 12:34:56,Expense,Test Category,Test Account,123.45,"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
|
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
assert.Equal(t, int64(12345), allNewTransactions[0].Amount)
|
assert.Equal(t, int64(12345), allNewTransactions[0].Amount)
|
||||||
assert.Equal(t, int64(0), allNewTransactions[0].RelatedAccountAmount)
|
assert.Equal(t, int64(0), allNewTransactions[0].RelatedAccountAmount)
|
||||||
|
|
||||||
allNewTransactions, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte("Time,Type,Sub Category,Account,Amount,Account2\n"+
|
allNewTransactions, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte("Time,Type,Sub Category,Account,Amount,Account2\n"+
|
||||||
"2024-09-01 12:34:56,Transfer,Test Category,Test Account,123.45,Test Account2"), 0, nil, nil, nil, nil, nil)
|
"2024-09-01 12:34:56,Transfer,Test Category,Test Account,123.45,Test Account2"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
|
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
assert.Equal(t, int64(12345), allNewTransactions[0].Amount)
|
assert.Equal(t, int64(12345), allNewTransactions[0].Amount)
|
||||||
@@ -378,7 +380,7 @@ func TestDefaultTransactionDataCSVFileConverterParseImportedData_ParseNoAmount2(
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestDefaultTransactionDataCSVFileConverterParseImportedData_ParseValidGeographicLocation(t *testing.T) {
|
func TestDefaultTransactionDataCSVFileConverterParseImportedData_ParseValidGeographicLocation(t *testing.T) {
|
||||||
converter := DefaultTransactionDataCSVFileConverter
|
importer := DefaultTransactionDataCSVFileConverter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
@@ -386,8 +388,8 @@ func TestDefaultTransactionDataCSVFileConverterParseImportedData_ParseValidGeogr
|
|||||||
DefaultCurrency: "CNY",
|
DefaultCurrency: "CNY",
|
||||||
}
|
}
|
||||||
|
|
||||||
allNewTransactions, _, _, _, _, _, err := converter.ParseImportedData(context, user, []byte("Time,Type,Sub Category,Account,Amount,Account2,Account2 Amount,Geographic Location\n"+
|
allNewTransactions, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte("Time,Type,Sub Category,Account,Amount,Account2,Account2 Amount,Geographic Location\n"+
|
||||||
"2024-09-01 12:34:56,Expense,Test Category,Test Account,123.45,,,123.45 45.56"), 0, nil, nil, nil, nil, nil)
|
"2024-09-01 12:34:56,Expense,Test Category,Test Account,123.45,,,123.45 45.56"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
|
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
assert.Equal(t, 1, len(allNewTransactions))
|
assert.Equal(t, 1, len(allNewTransactions))
|
||||||
@@ -396,7 +398,7 @@ func TestDefaultTransactionDataCSVFileConverterParseImportedData_ParseValidGeogr
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestDefaultTransactionDataCSVFileConverterParseImportedData_ParseInvalidGeographicLocation(t *testing.T) {
|
func TestDefaultTransactionDataCSVFileConverterParseImportedData_ParseInvalidGeographicLocation(t *testing.T) {
|
||||||
converter := DefaultTransactionDataCSVFileConverter
|
importer := DefaultTransactionDataCSVFileConverter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
@@ -404,24 +406,24 @@ func TestDefaultTransactionDataCSVFileConverterParseImportedData_ParseInvalidGeo
|
|||||||
DefaultCurrency: "CNY",
|
DefaultCurrency: "CNY",
|
||||||
}
|
}
|
||||||
|
|
||||||
allNewTransactions, _, _, _, _, _, err := converter.ParseImportedData(context, user, []byte("Time,Type,Sub Category,Account,Amount,Account2,Account2 Amount,Geographic Location\n"+
|
allNewTransactions, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte("Time,Type,Sub Category,Account,Amount,Account2,Account2 Amount,Geographic Location\n"+
|
||||||
"2024-09-01 12:34:56,Expense,Test Category,Test Account,123.45,,,1"), 0, nil, nil, nil, nil, nil)
|
"2024-09-01 12:34:56,Expense,Test Category,Test Account,123.45,,,1"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
assert.Equal(t, 1, len(allNewTransactions))
|
assert.Equal(t, 1, len(allNewTransactions))
|
||||||
assert.Equal(t, float64(0), allNewTransactions[0].GeoLongitude)
|
assert.Equal(t, float64(0), allNewTransactions[0].GeoLongitude)
|
||||||
assert.Equal(t, float64(0), allNewTransactions[0].GeoLatitude)
|
assert.Equal(t, float64(0), allNewTransactions[0].GeoLatitude)
|
||||||
|
|
||||||
_, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte("Time,Type,Sub Category,Account,Amount,Account2,Account2 Amount,Geographic Location\n"+
|
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte("Time,Type,Sub Category,Account,Amount,Account2,Account2 Amount,Geographic Location\n"+
|
||||||
"2024-09-01 12:34:56,Expense,Test Category,Test Account,123.45,,,a b"), 0, nil, nil, nil, nil, nil)
|
"2024-09-01 12:34:56,Expense,Test Category,Test Account,123.45,,,a b"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrGeographicLocationInvalid.Message)
|
assert.EqualError(t, err, errs.ErrGeographicLocationInvalid.Message)
|
||||||
|
|
||||||
_, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte("Time,Type,Sub Category,Account,Amount,Account2,Account2 Amount,Geographic Location\n"+
|
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte("Time,Type,Sub Category,Account,Amount,Account2,Account2 Amount,Geographic Location\n"+
|
||||||
"2024-09-01 12:34:56,Expense,Test Category,Test Account,123.45,,,1 "), 0, nil, nil, nil, nil, nil)
|
"2024-09-01 12:34:56,Expense,Test Category,Test Account,123.45,,,1 "), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrGeographicLocationInvalid.Message)
|
assert.EqualError(t, err, errs.ErrGeographicLocationInvalid.Message)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDefaultTransactionDataCSVFileConverterParseImportedData_ParseTag(t *testing.T) {
|
func TestDefaultTransactionDataCSVFileConverterParseImportedData_ParseTag(t *testing.T) {
|
||||||
converter := DefaultTransactionDataCSVFileConverter
|
importer := DefaultTransactionDataCSVFileConverter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
@@ -429,8 +431,8 @@ func TestDefaultTransactionDataCSVFileConverterParseImportedData_ParseTag(t *tes
|
|||||||
DefaultCurrency: "CNY",
|
DefaultCurrency: "CNY",
|
||||||
}
|
}
|
||||||
|
|
||||||
_, _, _, _, _, allNewTags, err := converter.ParseImportedData(context, user, []byte("Time,Type,Sub Category,Account,Amount,Account2,Account2 Amount,Tags\n"+
|
_, _, _, _, _, allNewTags, err := importer.ParseImportedData(context, user, []byte("Time,Type,Sub Category,Account,Amount,Account2,Account2 Amount,Tags\n"+
|
||||||
"2024-09-01 00:00:00,Balance Modification,,Test Account,123.45,,,foo;;bar.;#test;hello\tworld;;"), 0, nil, nil, nil, nil, nil)
|
"2024-09-01 00:00:00,Balance Modification,,Test Account,123.45,,,foo;;bar.;#test;hello\tworld;;"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
|
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
@@ -450,7 +452,7 @@ func TestDefaultTransactionDataCSVFileConverterParseImportedData_ParseTag(t *tes
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestDefaultTransactionDataCSVFileConverterParseImportedData_ParseDescription(t *testing.T) {
|
func TestDefaultTransactionDataCSVFileConverterParseImportedData_ParseDescription(t *testing.T) {
|
||||||
converter := DefaultTransactionDataCSVFileConverter
|
importer := DefaultTransactionDataCSVFileConverter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
@@ -458,8 +460,8 @@ func TestDefaultTransactionDataCSVFileConverterParseImportedData_ParseDescriptio
|
|||||||
DefaultCurrency: "CNY",
|
DefaultCurrency: "CNY",
|
||||||
}
|
}
|
||||||
|
|
||||||
allNewTransactions, _, _, _, _, _, err := converter.ParseImportedData(context, user, []byte("Time,Type,Sub Category,Account,Amount,Account2,Account2 Amount,Description\n"+
|
allNewTransactions, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte("Time,Type,Sub Category,Account,Amount,Account2,Account2 Amount,Description\n"+
|
||||||
"2024-09-01 12:34:56,Expense,Test Category,Test Account,123.45,,,foo bar\t#test"), 0, nil, nil, nil, nil, nil)
|
"2024-09-01 12:34:56,Expense,Test Category,Test Account,123.45,,,foo bar\t#test"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
|
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
assert.Equal(t, 1, len(allNewTransactions))
|
assert.Equal(t, 1, len(allNewTransactions))
|
||||||
@@ -467,7 +469,7 @@ func TestDefaultTransactionDataCSVFileConverterParseImportedData_ParseDescriptio
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestDefaultTransactionDataCSVFileConverterParseImportedData_MissingFileHeader(t *testing.T) {
|
func TestDefaultTransactionDataCSVFileConverterParseImportedData_MissingFileHeader(t *testing.T) {
|
||||||
converter := DefaultTransactionDataCSVFileConverter
|
importer := DefaultTransactionDataCSVFileConverter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
@@ -475,12 +477,12 @@ func TestDefaultTransactionDataCSVFileConverterParseImportedData_MissingFileHead
|
|||||||
DefaultCurrency: "CNY",
|
DefaultCurrency: "CNY",
|
||||||
}
|
}
|
||||||
|
|
||||||
_, _, _, _, _, _, err := converter.ParseImportedData(context, user, []byte(""), 0, nil, nil, nil, nil, nil)
|
_, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(""), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrNotFoundTransactionDataInFile.Message)
|
assert.EqualError(t, err, errs.ErrNotFoundTransactionDataInFile.Message)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDefaultTransactionDataCSVFileConverterParseImportedData_MissingRequiredColumn(t *testing.T) {
|
func TestDefaultTransactionDataCSVFileConverterParseImportedData_MissingRequiredColumn(t *testing.T) {
|
||||||
converter := DefaultTransactionDataCSVFileConverter
|
importer := DefaultTransactionDataCSVFileConverter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
@@ -489,32 +491,32 @@ func TestDefaultTransactionDataCSVFileConverterParseImportedData_MissingRequired
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Missing Time Column
|
// Missing Time Column
|
||||||
_, _, _, _, _, _, err := converter.ParseImportedData(context, user, []byte("Timezone,Type,Category,Sub Category,Account,Account Currency,Amount,Account2,Account2 Currency,Account2 Amount,Geographic Location,Tags,Description\n"+
|
_, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte("Timezone,Type,Category,Sub Category,Account,Account Currency,Amount,Account2,Account2 Currency,Account2 Amount,Geographic Location,Tags,Description\n"+
|
||||||
"+08:00,Balance Modification,,Test Sub Category,Test Account,CNY,123.45,,,,,,"), 0, nil, nil, nil, nil, nil)
|
"+08:00,Balance Modification,,Test Sub Category,Test Account,CNY,123.45,,,,,,"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrMissingRequiredFieldInHeaderRow.Message)
|
assert.EqualError(t, err, errs.ErrMissingRequiredFieldInHeaderRow.Message)
|
||||||
|
|
||||||
// Missing Type Column
|
// Missing Type Column
|
||||||
_, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte("Time,Category,Sub Category,Account,Account Currency,Amount,Account2,Account2 Currency,Account2 Amount,Geographic Location,Tags,Description\n"+
|
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte("Time,Category,Sub Category,Account,Account Currency,Amount,Account2,Account2 Currency,Account2 Amount,Geographic Location,Tags,Description\n"+
|
||||||
"2024-09-01 00:00:00,+08:00,Test Category,Test Sub Category,Test Account,CNY,123.45,,,,,,"), 0, nil, nil, nil, nil, nil)
|
"2024-09-01 00:00:00,+08:00,Test Category,Test Sub Category,Test Account,CNY,123.45,,,,,,"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrMissingRequiredFieldInHeaderRow.Message)
|
assert.EqualError(t, err, errs.ErrMissingRequiredFieldInHeaderRow.Message)
|
||||||
|
|
||||||
// Missing Sub Category Column
|
// Missing Sub Category Column
|
||||||
_, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte("Time,Type,Account,Account Currency,Amount,Account2,Account2 Currency,Account2 Amount,Geographic Location,Tags,Description\n"+
|
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte("Time,Type,Account,Account Currency,Amount,Account2,Account2 Currency,Account2 Amount,Geographic Location,Tags,Description\n"+
|
||||||
"2024-09-01 00:00:00,+08:00,Balance Modification,Test Account,CNY,123.45,,,,,,"), 0, nil, nil, nil, nil, nil)
|
"2024-09-01 00:00:00,+08:00,Balance Modification,Test Account,CNY,123.45,,,,,,"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrMissingRequiredFieldInHeaderRow.Message)
|
assert.EqualError(t, err, errs.ErrMissingRequiredFieldInHeaderRow.Message)
|
||||||
|
|
||||||
// Missing Account Name Column
|
// Missing Account Name Column
|
||||||
_, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte("Time,Timezone,Type,Category,Sub Category,Account Currency,Amount,Account2,Account2 Currency,Account2 Amount,Geographic Location,Tags,Description\n"+
|
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte("Time,Timezone,Type,Category,Sub Category,Account Currency,Amount,Account2,Account2 Currency,Account2 Amount,Geographic Location,Tags,Description\n"+
|
||||||
"2024-09-01 00:00:00,+08:00,Balance Modification,,Test Sub Category,CNY,123.45,,,,,,"), 0, nil, nil, nil, nil, nil)
|
"2024-09-01 00:00:00,+08:00,Balance Modification,,Test Sub Category,CNY,123.45,,,,,,"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrMissingRequiredFieldInHeaderRow.Message)
|
assert.EqualError(t, err, errs.ErrMissingRequiredFieldInHeaderRow.Message)
|
||||||
|
|
||||||
// Missing Amount Column
|
// Missing Amount Column
|
||||||
_, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte("Time,Timezone,Type,Category,Sub Category,Account,Account Currency,Account2,Account2 Currency,Account2 Amount,Geographic Location,Tags,Description\n"+
|
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte("Time,Timezone,Type,Category,Sub Category,Account,Account Currency,Account2,Account2 Currency,Account2 Amount,Geographic Location,Tags,Description\n"+
|
||||||
"2024-09-01 00:00:00,+08:00,Balance Modification,,Test Sub Category,Test Account,CNY,,,,,,"), 0, nil, nil, nil, nil, nil)
|
"2024-09-01 00:00:00,+08:00,Balance Modification,,Test Sub Category,Test Account,CNY,,,,,,"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrMissingRequiredFieldInHeaderRow.Message)
|
assert.EqualError(t, err, errs.ErrMissingRequiredFieldInHeaderRow.Message)
|
||||||
|
|
||||||
// Missing Account2 Name Column
|
// Missing Account2 Name Column
|
||||||
_, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte("Time,Timezone,Type,Category,Sub Category,Account,Account Currency,Amount,Account2 Currency,Account2 Amount,Geographic Location,Tags,Description\n"+
|
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte("Time,Timezone,Type,Category,Sub Category,Account,Account Currency,Amount,Account2 Currency,Account2 Amount,Geographic Location,Tags,Description\n"+
|
||||||
"2024-09-01 00:00:00,+08:00,Balance Modification,,Test Sub Category,Test Account,CNY,123.45,,,,,"), 0, nil, nil, nil, nil, nil)
|
"2024-09-01 00:00:00,+08:00,Balance Modification,,Test Sub Category,Test Account,CNY,123.45,,,,,"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrMissingRequiredFieldInHeaderRow.Message)
|
assert.EqualError(t, err, errs.ErrMissingRequiredFieldInHeaderRow.Message)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -86,7 +86,7 @@ func (t *ExcelMSCFBFileBasicDataTable) DataRowIterator() datatable.BasicDataTabl
|
|||||||
// ColumnCount returns the total count of column in this data row
|
// ColumnCount returns the total count of column in this data row
|
||||||
func (r *ExcelMSCFBFileBasicDataTableRow) ColumnCount() int {
|
func (r *ExcelMSCFBFileBasicDataTableRow) ColumnCount() int {
|
||||||
row := r.sheet.Row(r.rowIndex)
|
row := r.sheet.Row(r.rowIndex)
|
||||||
return row.LastCol() + 1
|
return row.LastCol()
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetData returns the data in the specified column index
|
// GetData returns the data in the specified column index
|
||||||
@@ -195,7 +195,10 @@ func CreateNewExcelMSCFBFileBasicDataTable(data []byte, hasTitleLine bool) (data
|
|||||||
}
|
}
|
||||||
|
|
||||||
if i == 0 {
|
if i == 0 {
|
||||||
for j := 0; j <= row.LastCol(); j++ {
|
// row.LastCol() returns "colMac" in the "Row" struct, that is an unsigned integer that specifies the one-based index of the last column.
|
||||||
|
// But row.FirstCol() returns "colMic" in the "Row" struct, that is an unsigned integer that specifies the zero-based index of the first column.
|
||||||
|
// Reference: https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-xls/4aab09eb-49ed-4d01-a3b1-1d726247d3c2
|
||||||
|
for j := 0; j < row.LastCol(); j++ {
|
||||||
headerItem := row.Col(j)
|
headerItem := row.Col(j)
|
||||||
|
|
||||||
if headerItem == "" {
|
if headerItem == "" {
|
||||||
@@ -205,7 +208,7 @@ func CreateNewExcelMSCFBFileBasicDataTable(data []byte, hasTitleLine bool) (data
|
|||||||
firstRowItems = append(firstRowItems, headerItem)
|
firstRowItems = append(firstRowItems, headerItem)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for j := 0; j <= min(row.LastCol(), len(firstRowItems)-1); j++ {
|
for j := 0; j < min(row.LastCol(), len(firstRowItems)); j++ {
|
||||||
headerItem := row.Col(j)
|
headerItem := row.Col(j)
|
||||||
|
|
||||||
if headerItem != firstRowItems[j] {
|
if headerItem != firstRowItems[j] {
|
||||||
|
|||||||
@@ -300,10 +300,10 @@ func TestExcelMSCFBFileBasicDataRowColumnCount(t *testing.T) {
|
|||||||
iterator := datatable.DataRowIterator()
|
iterator := datatable.DataRowIterator()
|
||||||
|
|
||||||
row1 := iterator.Next()
|
row1 := iterator.Next()
|
||||||
assert.EqualValues(t, 4, row1.ColumnCount())
|
assert.EqualValues(t, 3, row1.ColumnCount())
|
||||||
|
|
||||||
row2 := iterator.Next()
|
row2 := iterator.Next()
|
||||||
assert.EqualValues(t, 4, row2.ColumnCount())
|
assert.EqualValues(t, 3, row2.ColumnCount())
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestExcelMSCFBFileBasicDataRowGetData(t *testing.T) {
|
func TestExcelMSCFBFileBasicDataRowGetData(t *testing.T) {
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package feidee
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"golang.org/x/text/encoding/unicode"
|
"golang.org/x/text/encoding/unicode"
|
||||||
"golang.org/x/text/transform"
|
"golang.org/x/text/transform"
|
||||||
@@ -27,6 +28,9 @@ const feideeMymoneyAppTransactionAccountCurrencyColumnName = "账户币种"
|
|||||||
const feideeMymoneyAppTransactionAmountColumnName = "金额"
|
const feideeMymoneyAppTransactionAmountColumnName = "金额"
|
||||||
const feideeMymoneyAppTransactionDescriptionColumnName = "备注"
|
const feideeMymoneyAppTransactionDescriptionColumnName = "备注"
|
||||||
const feideeMymoneyAppTransactionRelatedIdColumnName = "关联Id"
|
const feideeMymoneyAppTransactionRelatedIdColumnName = "关联Id"
|
||||||
|
const feideeMymoneyAppTransactionMemberColumnName = "成员"
|
||||||
|
const feideeMymoneyAppTransactionProjectColumnName = "项目"
|
||||||
|
const feideeMymoneyAppTransactionMerchantColumnName = "商家"
|
||||||
|
|
||||||
const feideeMymoneyAppTransactionTypeModifyBalanceText = "余额变更"
|
const feideeMymoneyAppTransactionTypeModifyBalanceText = "余额变更"
|
||||||
const feideeMymoneyAppTransactionTypeModifyOutstandingBalanceText = "负债变更"
|
const feideeMymoneyAppTransactionTypeModifyOutstandingBalanceText = "负债变更"
|
||||||
@@ -44,6 +48,9 @@ var feideeMymoneyAppDataColumnNameMapping = map[datatable.TransactionDataTableCo
|
|||||||
datatable.TRANSACTION_DATA_TABLE_ACCOUNT_CURRENCY: feideeMymoneyAppTransactionAccountCurrencyColumnName,
|
datatable.TRANSACTION_DATA_TABLE_ACCOUNT_CURRENCY: feideeMymoneyAppTransactionAccountCurrencyColumnName,
|
||||||
datatable.TRANSACTION_DATA_TABLE_AMOUNT: feideeMymoneyAppTransactionAmountColumnName,
|
datatable.TRANSACTION_DATA_TABLE_AMOUNT: feideeMymoneyAppTransactionAmountColumnName,
|
||||||
datatable.TRANSACTION_DATA_TABLE_DESCRIPTION: feideeMymoneyAppTransactionDescriptionColumnName,
|
datatable.TRANSACTION_DATA_TABLE_DESCRIPTION: feideeMymoneyAppTransactionDescriptionColumnName,
|
||||||
|
datatable.TRANSACTION_DATA_TABLE_MEMBER: feideeMymoneyAppTransactionMemberColumnName,
|
||||||
|
datatable.TRANSACTION_DATA_TABLE_PROJECT: feideeMymoneyAppTransactionProjectColumnName,
|
||||||
|
datatable.TRANSACTION_DATA_TABLE_MERCHANT: feideeMymoneyAppTransactionMerchantColumnName,
|
||||||
}
|
}
|
||||||
|
|
||||||
// feideeMymoneyAppTransactionDataCsvFileImporter defines the structure of feidee mymoney app csv importer for transaction data
|
// feideeMymoneyAppTransactionDataCsvFileImporter defines the structure of feidee mymoney app csv importer for transaction data
|
||||||
@@ -55,7 +62,7 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// ParseImportedData returns the imported data by parsing the feidee mymoney app transaction csv data
|
// ParseImportedData returns the imported data by parsing the feidee mymoney app transaction csv data
|
||||||
func (c *feideeMymoneyAppTransactionDataCsvFileImporter) ParseImportedData(ctx core.Context, user *models.User, data []byte, defaultTimezoneOffset int16, accountMap map[string]*models.Account, expenseCategoryMap map[string]map[string]*models.TransactionCategory, incomeCategoryMap map[string]map[string]*models.TransactionCategory, transferCategoryMap map[string]map[string]*models.TransactionCategory, tagMap map[string]*models.TransactionTag) (models.ImportedTransactionSlice, []*models.Account, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionTag, error) {
|
func (c *feideeMymoneyAppTransactionDataCsvFileImporter) ParseImportedData(ctx core.Context, user *models.User, data []byte, defaultTimezone *time.Location, additionalOptions converter.TransactionDataImporterOptions, accountMap map[string]*models.Account, expenseCategoryMap map[string]map[string]*models.TransactionCategory, incomeCategoryMap map[string]map[string]*models.TransactionCategory, transferCategoryMap map[string]map[string]*models.TransactionCategory, tagMap map[string]*models.TransactionTag) (models.ImportedTransactionSlice, []*models.Account, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionTag, error) {
|
||||||
fallback := unicode.UTF8.NewDecoder()
|
fallback := unicode.UTF8.NewDecoder()
|
||||||
reader := transform.NewReader(bytes.NewReader(data), unicode.BOMOverride(fallback))
|
reader := transform.NewReader(bytes.NewReader(data), unicode.BOMOverride(fallback))
|
||||||
|
|
||||||
@@ -91,7 +98,7 @@ func (c *feideeMymoneyAppTransactionDataCsvFileImporter) ParseImportedData(ctx c
|
|||||||
|
|
||||||
dataTableImporter := converter.CreateNewSimpleImporterWithTypeNameMapping(feideeMymoneyTransactionTypeNameMapping)
|
dataTableImporter := converter.CreateNewSimpleImporterWithTypeNameMapping(feideeMymoneyTransactionTypeNameMapping)
|
||||||
|
|
||||||
return dataTableImporter.ParseImportedData(ctx, user, transactionDataTable, defaultTimezoneOffset, accountMap, expenseCategoryMap, incomeCategoryMap, transferCategoryMap, tagMap)
|
return dataTableImporter.ParseImportedData(ctx, user, transactionDataTable, defaultTimezone, additionalOptions, accountMap, expenseCategoryMap, incomeCategoryMap, transferCategoryMap, tagMap)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *feideeMymoneyAppTransactionDataCsvFileImporter) createNewFeideeMymoneyAppTransactionDataTable(ctx core.Context, commonDataTable datatable.CommonDataTable) (datatable.TransactionDataTable, error) {
|
func (c *feideeMymoneyAppTransactionDataCsvFileImporter) createNewFeideeMymoneyAppTransactionDataTable(ctx core.Context, commonDataTable datatable.CommonDataTable) (datatable.TransactionDataTable, error) {
|
||||||
@@ -123,6 +130,18 @@ func (c *feideeMymoneyAppTransactionDataCsvFileImporter) createNewFeideeMymoneyA
|
|||||||
newColumns = append(newColumns, datatable.TRANSACTION_DATA_TABLE_DESCRIPTION)
|
newColumns = append(newColumns, datatable.TRANSACTION_DATA_TABLE_DESCRIPTION)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if commonDataTable.HasColumn(feideeMymoneyAppTransactionMemberColumnName) {
|
||||||
|
newColumns = append(newColumns, datatable.TRANSACTION_DATA_TABLE_MEMBER)
|
||||||
|
}
|
||||||
|
|
||||||
|
if commonDataTable.HasColumn(feideeMymoneyAppTransactionProjectColumnName) {
|
||||||
|
newColumns = append(newColumns, datatable.TRANSACTION_DATA_TABLE_PROJECT)
|
||||||
|
}
|
||||||
|
|
||||||
|
if commonDataTable.HasColumn(feideeMymoneyAppTransactionMerchantColumnName) {
|
||||||
|
newColumns = append(newColumns, datatable.TRANSACTION_DATA_TABLE_MERCHANT)
|
||||||
|
}
|
||||||
|
|
||||||
transactionRowParser := createFeideeMymoneyTransactionDataRowParser()
|
transactionRowParser := createFeideeMymoneyTransactionDataRowParser()
|
||||||
transactionDataTable := datatable.CreateNewWritableTransactionDataTableWithRowParser(newColumns, transactionRowParser)
|
transactionDataTable := datatable.CreateNewWritableTransactionDataTableWithRowParser(newColumns, transactionRowParser)
|
||||||
transferTransactionsMap := make(map[string]map[datatable.TransactionDataTableColumn]string, 0)
|
transferTransactionsMap := make(map[string]map[datatable.TransactionDataTableColumn]string, 0)
|
||||||
|
|||||||
+96
-66
@@ -6,6 +6,7 @@ import (
|
|||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
|
"github.com/mayswind/ezbookkeeping/pkg/converters/converter"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/core"
|
"github.com/mayswind/ezbookkeeping/pkg/core"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/errs"
|
"github.com/mayswind/ezbookkeeping/pkg/errs"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/models"
|
"github.com/mayswind/ezbookkeeping/pkg/models"
|
||||||
@@ -13,7 +14,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func TestFeideeMymoneyCsvFileImporterParseImportedData_MinimumValidData(t *testing.T) {
|
func TestFeideeMymoneyCsvFileImporterParseImportedData_MinimumValidData(t *testing.T) {
|
||||||
converter := FeideeMymoneyAppTransactionDataCsvFileImporter
|
importer := FeideeMymoneyAppTransactionDataCsvFileImporter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
@@ -21,7 +22,7 @@ func TestFeideeMymoneyCsvFileImporterParseImportedData_MinimumValidData(t *testi
|
|||||||
DefaultCurrency: "CNY",
|
DefaultCurrency: "CNY",
|
||||||
}
|
}
|
||||||
|
|
||||||
allNewTransactions, allNewAccounts, allNewSubExpenseCategories, allNewSubIncomeCategories, allNewSubTransferCategories, allNewTags, err := converter.ParseImportedData(context, user, []byte("随手记导出文件(headers:v5;xxxxx)\n"+
|
allNewTransactions, allNewAccounts, allNewSubExpenseCategories, allNewSubIncomeCategories, allNewSubTransferCategories, allNewTags, err := importer.ParseImportedData(context, user, []byte("随手记导出文件(headers:v5;xxxxx)\n"+
|
||||||
"\"交易类型\",\"日期\",\"子类别\",\"账户\",\"金额\",\"备注\",\"关联Id\"\n"+
|
"\"交易类型\",\"日期\",\"子类别\",\"账户\",\"金额\",\"备注\",\"关联Id\"\n"+
|
||||||
"\"余额变更\",\"2024-09-01 00:00:00\",\"\",\"Test Account\",\"123.45\",\"\",\"\"\n"+
|
"\"余额变更\",\"2024-09-01 00:00:00\",\"\",\"Test Account\",\"123.45\",\"\",\"\"\n"+
|
||||||
"\"余额变更\",\"2024-09-01 01:00:00\",\"\",\"Test Account2\",\"-0.12\",\"\",\"\"\n"+
|
"\"余额变更\",\"2024-09-01 01:00:00\",\"\",\"Test Account2\",\"-0.12\",\"\",\"\"\n"+
|
||||||
@@ -30,7 +31,7 @@ func TestFeideeMymoneyCsvFileImporterParseImportedData_MinimumValidData(t *testi
|
|||||||
"\"转出\",\"2024-09-01 23:59:59\",\"Test Category3\",\"Test Account\",\"0.05\",\"\",\"00000000-0000-0000-0000-000000000001\"\n"+
|
"\"转出\",\"2024-09-01 23:59:59\",\"Test Category3\",\"Test Account\",\"0.05\",\"\",\"00000000-0000-0000-0000-000000000001\"\n"+
|
||||||
"\"转入\",\"2024-09-01 23:59:59\",\"Test Category3\",\"Test Account2\",\"0.05\",\"\",\"00000000-0000-0000-0000-000000000001\"\n"+
|
"\"转入\",\"2024-09-01 23:59:59\",\"Test Category3\",\"Test Account2\",\"0.05\",\"\",\"00000000-0000-0000-0000-000000000001\"\n"+
|
||||||
"\"转入\",\"2024-09-02 23:59:59\",\"Test Category3\",\"Test Account\",\"0.5\",\"\",\"00000000-0000-0000-0000-000000000002\"\n"+
|
"\"转入\",\"2024-09-02 23:59:59\",\"Test Category3\",\"Test Account\",\"0.5\",\"\",\"00000000-0000-0000-0000-000000000002\"\n"+
|
||||||
"\"转出\",\"2024-09-02 23:59:59\",\"Test Category3\",\"Test Account2\",\"0.5\",\"\",\"00000000-0000-0000-0000-000000000002\""), 0, nil, nil, nil, nil, nil)
|
"\"转出\",\"2024-09-02 23:59:59\",\"Test Category3\",\"Test Account2\",\"0.5\",\"\",\"00000000-0000-0000-0000-000000000002\""), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
|
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
@@ -110,7 +111,7 @@ func TestFeideeMymoneyCsvFileImporterParseImportedData_MinimumValidData(t *testi
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestFeideeMymoneyCsvFileImporterParseImportedData_ParseOutstandingBalanceModification(t *testing.T) {
|
func TestFeideeMymoneyCsvFileImporterParseImportedData_ParseOutstandingBalanceModification(t *testing.T) {
|
||||||
converter := FeideeMymoneyAppTransactionDataCsvFileImporter
|
importer := FeideeMymoneyAppTransactionDataCsvFileImporter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
@@ -118,10 +119,10 @@ func TestFeideeMymoneyCsvFileImporterParseImportedData_ParseOutstandingBalanceMo
|
|||||||
DefaultCurrency: "CNY",
|
DefaultCurrency: "CNY",
|
||||||
}
|
}
|
||||||
|
|
||||||
allNewTransactions, allNewAccounts, allNewSubExpenseCategories, allNewSubIncomeCategories, _, _, err := converter.ParseImportedData(context, user, []byte("随手记导出文件(headers:v5;xxxxx)\n"+
|
allNewTransactions, allNewAccounts, allNewSubExpenseCategories, allNewSubIncomeCategories, _, _, err := importer.ParseImportedData(context, user, []byte("随手记导出文件(headers:v5;xxxxx)\n"+
|
||||||
"\"交易类型\",\"日期\",\"子类别\",\"账户\",\"金额\",\"备注\",\"关联Id\"\n"+
|
"\"交易类型\",\"日期\",\"子类别\",\"账户\",\"金额\",\"备注\",\"关联Id\"\n"+
|
||||||
"\"负债变更\",\"2024-09-01 00:00:00\",\"\",\"Test Account\",\"123.45\",\"\",\"\"\n"+
|
"\"负债变更\",\"2024-09-01 00:00:00\",\"\",\"Test Account\",\"123.45\",\"\",\"\"\n"+
|
||||||
"\"负债变更\",\"2024-09-01 01:00:00\",\"\",\"Test Account2\",\"-0.12\",\"\",\"\"\n"), 0, nil, nil, nil, nil, nil)
|
"\"负债变更\",\"2024-09-01 01:00:00\",\"\",\"Test Account2\",\"-0.12\",\"\",\"\"\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
|
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
@@ -160,7 +161,7 @@ func TestFeideeMymoneyCsvFileImporterParseImportedData_ParseOutstandingBalanceMo
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestFeideeMymoneyCsvFileImporterParseImportedData_ParseInvalidTime(t *testing.T) {
|
func TestFeideeMymoneyCsvFileImporterParseImportedData_ParseInvalidTime(t *testing.T) {
|
||||||
converter := FeideeMymoneyAppTransactionDataCsvFileImporter
|
importer := FeideeMymoneyAppTransactionDataCsvFileImporter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
@@ -168,19 +169,19 @@ func TestFeideeMymoneyCsvFileImporterParseImportedData_ParseInvalidTime(t *testi
|
|||||||
DefaultCurrency: "CNY",
|
DefaultCurrency: "CNY",
|
||||||
}
|
}
|
||||||
|
|
||||||
_, _, _, _, _, _, err := converter.ParseImportedData(context, user, []byte("随手记导出文件(headers:v5;xxxxx)\n"+
|
_, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte("随手记导出文件(headers:v5;xxxxx)\n"+
|
||||||
"\"交易类型\",\"日期\",\"子类别\",\"账户\",\"金额\",\"备注\",\"关联Id\"\n"+
|
"\"交易类型\",\"日期\",\"子类别\",\"账户\",\"金额\",\"备注\",\"关联Id\"\n"+
|
||||||
"\"收入\",\"2024-09-01T12:34:56\",\"Test Category\",\"Test Account\",\"0.12\",\"\",\"\""), 0, nil, nil, nil, nil, nil)
|
"\"收入\",\"2024-09-01T12:34:56\",\"Test Category\",\"Test Account\",\"0.12\",\"\",\"\""), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrTransactionTimeInvalid.Message)
|
assert.EqualError(t, err, errs.ErrTransactionTimeInvalid.Message)
|
||||||
|
|
||||||
_, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte("随手记导出文件(headers:v5;xxxxx)\n"+
|
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte("随手记导出文件(headers:v5;xxxxx)\n"+
|
||||||
"\"交易类型\",\"日期\",\"子类别\",\"账户\",\"金额\",\"备注\",\"关联Id\"\n"+
|
"\"交易类型\",\"日期\",\"子类别\",\"账户\",\"金额\",\"备注\",\"关联Id\"\n"+
|
||||||
"\"收入\",\"09/01/2024 12:34:56\",\"Test Category\",\"Test Account\",\"0.12\",\"\",\"\""), 0, nil, nil, nil, nil, nil)
|
"\"收入\",\"09/01/2024 12:34:56\",\"Test Category\",\"Test Account\",\"0.12\",\"\",\"\""), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrTransactionTimeInvalid.Message)
|
assert.EqualError(t, err, errs.ErrTransactionTimeInvalid.Message)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestFeideeMymoneyCsvFileImporterParseImportedData_ParseInvalidType(t *testing.T) {
|
func TestFeideeMymoneyCsvFileImporterParseImportedData_ParseInvalidType(t *testing.T) {
|
||||||
converter := FeideeMymoneyAppTransactionDataCsvFileImporter
|
importer := FeideeMymoneyAppTransactionDataCsvFileImporter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
@@ -188,14 +189,14 @@ func TestFeideeMymoneyCsvFileImporterParseImportedData_ParseInvalidType(t *testi
|
|||||||
DefaultCurrency: "CNY",
|
DefaultCurrency: "CNY",
|
||||||
}
|
}
|
||||||
|
|
||||||
_, _, _, _, _, _, err := converter.ParseImportedData(context, user, []byte("随手记导出文件(headers:v5;xxxxx)\n"+
|
_, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte("随手记导出文件(headers:v5;xxxxx)\n"+
|
||||||
"\"交易类型\",\"日期\",\"子类别\",\"账户\",\"金额\",\"备注\",\"关联Id\"\n"+
|
"\"交易类型\",\"日期\",\"子类别\",\"账户\",\"金额\",\"备注\",\"关联Id\"\n"+
|
||||||
"\"Type\",\"2024-09-01 12:34:56\",\"Test Category\",\"Test Account\",\"0.12\",\"\",\"\""), 0, nil, nil, nil, nil, nil)
|
"\"Type\",\"2024-09-01 12:34:56\",\"Test Category\",\"Test Account\",\"0.12\",\"\",\"\""), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrTransactionTypeInvalid.Message)
|
assert.EqualError(t, err, errs.ErrTransactionTypeInvalid.Message)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestFeideeMymoneyCsvFileImporterParseImportedData_ParseValidAccountCurrency(t *testing.T) {
|
func TestFeideeMymoneyCsvFileImporterParseImportedData_ParseValidAccountCurrency(t *testing.T) {
|
||||||
converter := FeideeMymoneyAppTransactionDataCsvFileImporter
|
importer := FeideeMymoneyAppTransactionDataCsvFileImporter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
@@ -203,11 +204,11 @@ func TestFeideeMymoneyCsvFileImporterParseImportedData_ParseValidAccountCurrency
|
|||||||
DefaultCurrency: "CNY",
|
DefaultCurrency: "CNY",
|
||||||
}
|
}
|
||||||
|
|
||||||
allNewTransactions, allNewAccounts, _, _, _, _, err := converter.ParseImportedData(context, user, []byte("随手记导出文件(headers:v5;xxxxx)\n"+
|
allNewTransactions, allNewAccounts, _, _, _, _, err := importer.ParseImportedData(context, user, []byte("随手记导出文件(headers:v5;xxxxx)\n"+
|
||||||
"\"交易类型\",\"日期\",\"子类别\",\"账户\",\"账户币种\",\"金额\",\"备注\",\"关联Id\"\n"+
|
"\"交易类型\",\"日期\",\"子类别\",\"账户\",\"账户币种\",\"金额\",\"备注\",\"关联Id\"\n"+
|
||||||
"\"余额变更\",\"2024-09-01 01:23:45\",\"\",\"Test Account\",\"USD\",\"123.45\",\"\",\"\"\n"+
|
"\"余额变更\",\"2024-09-01 01:23:45\",\"\",\"Test Account\",\"USD\",\"123.45\",\"\",\"\"\n"+
|
||||||
"\"转出\",\"2024-09-01 12:34:56\",\"Test Category3\",\"Test Account\",\"USD\",\"1.23\",\"\",\"00000000-0000-0000-0000-000000000001\"\n"+
|
"\"转出\",\"2024-09-01 12:34:56\",\"Test Category3\",\"Test Account\",\"USD\",\"1.23\",\"\",\"00000000-0000-0000-0000-000000000001\"\n"+
|
||||||
"\"转入\",\"2024-09-01 12:34:56\",\"Test Category3\",\"Test Account2\",\"EUR\",\"1.10\",\"\",\"00000000-0000-0000-0000-000000000001\""), 0, nil, nil, nil, nil, nil)
|
"\"转入\",\"2024-09-01 12:34:56\",\"Test Category3\",\"Test Account2\",\"EUR\",\"1.10\",\"\",\"00000000-0000-0000-0000-000000000001\""), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
|
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
@@ -224,7 +225,7 @@ func TestFeideeMymoneyCsvFileImporterParseImportedData_ParseValidAccountCurrency
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestFeideeMymoneyCsvFileImporterParseImportedData_ParseInvalidAccountCurrency(t *testing.T) {
|
func TestFeideeMymoneyCsvFileImporterParseImportedData_ParseInvalidAccountCurrency(t *testing.T) {
|
||||||
converter := FeideeMymoneyAppTransactionDataCsvFileImporter
|
importer := FeideeMymoneyAppTransactionDataCsvFileImporter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
@@ -232,23 +233,23 @@ func TestFeideeMymoneyCsvFileImporterParseImportedData_ParseInvalidAccountCurren
|
|||||||
DefaultCurrency: "CNY",
|
DefaultCurrency: "CNY",
|
||||||
}
|
}
|
||||||
|
|
||||||
_, _, _, _, _, _, err := converter.ParseImportedData(context, user, []byte("随手记导出文件(headers:v5;xxxxx)\n"+
|
_, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte("随手记导出文件(headers:v5;xxxxx)\n"+
|
||||||
"\"交易类型\",\"日期\",\"子类别\",\"账户\",\"账户币种\",\"金额\",\"备注\",\"关联Id\"\n"+
|
"\"交易类型\",\"日期\",\"子类别\",\"账户\",\"账户币种\",\"金额\",\"备注\",\"关联Id\"\n"+
|
||||||
"\"余额变更\",\"2024-09-01 01:23:45\",\"\",\"Test Account\",\"USD\",\"123.45\",\"\",\"\"\n"+
|
"\"余额变更\",\"2024-09-01 01:23:45\",\"\",\"Test Account\",\"USD\",\"123.45\",\"\",\"\"\n"+
|
||||||
"\"转出\",\"2024-09-01 12:34:56\",\"Test Category3\",\"Test Account\",\"CNY\",\"1.23\",\"\",\"00000000-0000-0000-0000-000000000001\"\n"+
|
"\"转出\",\"2024-09-01 12:34:56\",\"Test Category3\",\"Test Account\",\"CNY\",\"1.23\",\"\",\"00000000-0000-0000-0000-000000000001\"\n"+
|
||||||
"\"转入\",\"2024-09-01 12:34:56\",\"Test Category3\",\"Test Account2\",\"EUR\",\"1.10\",\"\",\"00000000-0000-0000-0000-000000000001\""), 0, nil, nil, nil, nil, nil)
|
"\"转入\",\"2024-09-01 12:34:56\",\"Test Category3\",\"Test Account2\",\"EUR\",\"1.10\",\"\",\"00000000-0000-0000-0000-000000000001\""), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrAccountCurrencyInvalid.Message)
|
assert.EqualError(t, err, errs.ErrAccountCurrencyInvalid.Message)
|
||||||
|
|
||||||
_, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte("随手记导出文件(headers:v5;xxxxx)\n"+
|
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte("随手记导出文件(headers:v5;xxxxx)\n"+
|
||||||
"\"交易类型\",\"日期\",\"子类别\",\"账户\",\"账户币种\",\"金额\",\"备注\",\"关联Id\"\n"+
|
"\"交易类型\",\"日期\",\"子类别\",\"账户\",\"账户币种\",\"金额\",\"备注\",\"关联Id\"\n"+
|
||||||
"\"余额变更\",\"2024-09-01 01:23:45\",\"\",\"Test Account\",\"USD\",\"123.45\",\"\",\"\"\n"+
|
"\"余额变更\",\"2024-09-01 01:23:45\",\"\",\"Test Account\",\"USD\",\"123.45\",\"\",\"\"\n"+
|
||||||
"\"转出\",\"2024-09-01 12:34:56\",\"Test Category3\",\"Test Account2\",\"CNY\",\"1.23\",\"\",\"00000000-0000-0000-0000-000000000001\"\n"+
|
"\"转出\",\"2024-09-01 12:34:56\",\"Test Category3\",\"Test Account2\",\"CNY\",\"1.23\",\"\",\"00000000-0000-0000-0000-000000000001\"\n"+
|
||||||
"\"转入\",\"2024-09-01 12:34:56\",\"Test Category3\",\"Test Account\",\"EUR\",\"1.10\",\"\",\"00000000-0000-0000-0000-000000000001\""), 0, nil, nil, nil, nil, nil)
|
"\"转入\",\"2024-09-01 12:34:56\",\"Test Category3\",\"Test Account\",\"EUR\",\"1.10\",\"\",\"00000000-0000-0000-0000-000000000001\""), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrAccountCurrencyInvalid.Message)
|
assert.EqualError(t, err, errs.ErrAccountCurrencyInvalid.Message)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestFeideeMymoneyCsvFileImporterParseImportedData_ParseNotSupportedCurrency(t *testing.T) {
|
func TestFeideeMymoneyCsvFileImporterParseImportedData_ParseNotSupportedCurrency(t *testing.T) {
|
||||||
converter := FeideeMymoneyAppTransactionDataCsvFileImporter
|
importer := FeideeMymoneyAppTransactionDataCsvFileImporter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
@@ -256,26 +257,26 @@ func TestFeideeMymoneyCsvFileImporterParseImportedData_ParseNotSupportedCurrency
|
|||||||
DefaultCurrency: "CNY",
|
DefaultCurrency: "CNY",
|
||||||
}
|
}
|
||||||
|
|
||||||
_, _, _, _, _, _, err := converter.ParseImportedData(context, user, []byte("随手记导出文件(headers:v5;xxxxx)\n"+
|
_, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte("随手记导出文件(headers:v5;xxxxx)\n"+
|
||||||
"\"交易类型\",\"日期\",\"子类别\",\"账户\",\"账户币种\",\"金额\",\"备注\",\"关联Id\"\n"+
|
"\"交易类型\",\"日期\",\"子类别\",\"账户\",\"账户币种\",\"金额\",\"备注\",\"关联Id\"\n"+
|
||||||
"\"余额变更\",\"2024-09-01 01:23:45\",\"\",\"Test Account\",\"XXX\",\"123.45\",\"\",\"\""), 0, nil, nil, nil, nil, nil)
|
"\"余额变更\",\"2024-09-01 01:23:45\",\"\",\"Test Account\",\"XXX\",\"123.45\",\"\",\"\""), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrAccountCurrencyInvalid.Message)
|
assert.EqualError(t, err, errs.ErrAccountCurrencyInvalid.Message)
|
||||||
|
|
||||||
_, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte("随手记导出文件(headers:v5;xxxxx)\n"+
|
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte("随手记导出文件(headers:v5;xxxxx)\n"+
|
||||||
"\"交易类型\",\"日期\",\"子类别\",\"账户\",\"账户币种\",\"金额\",\"备注\",\"关联Id\"\n"+
|
"\"交易类型\",\"日期\",\"子类别\",\"账户\",\"账户币种\",\"金额\",\"备注\",\"关联Id\"\n"+
|
||||||
"\"转出\",\"2024-09-01 12:34:56\",\"Test Category\",\"Test Account\",\"USD\",\"123.45\",\"\",\"00000000-0000-0000-0000-000000000001\"\n"+
|
"\"转出\",\"2024-09-01 12:34:56\",\"Test Category\",\"Test Account\",\"USD\",\"123.45\",\"\",\"00000000-0000-0000-0000-000000000001\"\n"+
|
||||||
"\"转入\",\"2024-09-01 12:34:56\",\"Test Category\",\"Test Account2\",\"XXX\",\"123.45\",\"\",\"00000000-0000-0000-0000-000000000001\""), 0, nil, nil, nil, nil, nil)
|
"\"转入\",\"2024-09-01 12:34:56\",\"Test Category\",\"Test Account2\",\"XXX\",\"123.45\",\"\",\"00000000-0000-0000-0000-000000000001\""), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrAccountCurrencyInvalid.Message)
|
assert.EqualError(t, err, errs.ErrAccountCurrencyInvalid.Message)
|
||||||
|
|
||||||
_, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte("随手记导出文件(headers:v5;xxxxx)\n"+
|
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte("随手记导出文件(headers:v5;xxxxx)\n"+
|
||||||
"\"交易类型\",\"日期\",\"子类别\",\"账户\",\"账户币种\",\"金额\",\"备注\",\"关联Id\"\n"+
|
"\"交易类型\",\"日期\",\"子类别\",\"账户\",\"账户币种\",\"金额\",\"备注\",\"关联Id\"\n"+
|
||||||
"\"转出\",\"2024-09-01 12:34:56\",\"Test Category\",\"Test Account\",\"XXX\",\"123.45\",\"\",\"00000000-0000-0000-0000-000000000001\"\n"+
|
"\"转出\",\"2024-09-01 12:34:56\",\"Test Category\",\"Test Account\",\"XXX\",\"123.45\",\"\",\"00000000-0000-0000-0000-000000000001\"\n"+
|
||||||
"\"转入\",\"2024-09-01 12:34:56\",\"Test Category\",\"Test Account2\",\"USD\",\"123.45\",\"\",\"00000000-0000-0000-0000-000000000001\""), 0, nil, nil, nil, nil, nil)
|
"\"转入\",\"2024-09-01 12:34:56\",\"Test Category\",\"Test Account2\",\"USD\",\"123.45\",\"\",\"00000000-0000-0000-0000-000000000001\""), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrAccountCurrencyInvalid.Message)
|
assert.EqualError(t, err, errs.ErrAccountCurrencyInvalid.Message)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestFeideeMymoneyCsvFileImporterParseImportedData_ParseInvalidAmount(t *testing.T) {
|
func TestFeideeMymoneyCsvFileImporterParseImportedData_ParseInvalidAmount(t *testing.T) {
|
||||||
converter := FeideeMymoneyAppTransactionDataCsvFileImporter
|
importer := FeideeMymoneyAppTransactionDataCsvFileImporter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
@@ -283,31 +284,31 @@ func TestFeideeMymoneyCsvFileImporterParseImportedData_ParseInvalidAmount(t *tes
|
|||||||
DefaultCurrency: "CNY",
|
DefaultCurrency: "CNY",
|
||||||
}
|
}
|
||||||
|
|
||||||
_, _, _, _, _, _, err := converter.ParseImportedData(context, user, []byte("随手记导出文件(headers:v5;xxxxx)\n"+
|
_, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte("随手记导出文件(headers:v5;xxxxx)\n"+
|
||||||
"\"交易类型\",\"日期\",\"子类别\",\"账户\",\"金额\",\"备注\",\"关联Id\"\n"+
|
"\"交易类型\",\"日期\",\"子类别\",\"账户\",\"金额\",\"备注\",\"关联Id\"\n"+
|
||||||
"\"余额变更\",\"2024-09-01 01:23:45\",\"\",\"Test Account\",\"123 45\",\"\",\"\""), 0, nil, nil, nil, nil, nil)
|
"\"余额变更\",\"2024-09-01 01:23:45\",\"\",\"Test Account\",\"123 45\",\"\",\"\""), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrAmountInvalid.Message)
|
assert.EqualError(t, err, errs.ErrAmountInvalid.Message)
|
||||||
|
|
||||||
_, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte("随手记导出文件(headers:v5;xxxxx)\n"+
|
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte("随手记导出文件(headers:v5;xxxxx)\n"+
|
||||||
"\"交易类型\",\"日期\",\"子类别\",\"账户\",\"金额\",\"备注\",\"关联Id\"\n"+
|
"\"交易类型\",\"日期\",\"子类别\",\"账户\",\"金额\",\"备注\",\"关联Id\"\n"+
|
||||||
"\"负债变更\",\"2024-09-01 01:23:45\",\"\",\"Test Account\",\"123 45\",\"\",\"\""), 0, nil, nil, nil, nil, nil)
|
"\"负债变更\",\"2024-09-01 01:23:45\",\"\",\"Test Account\",\"123 45\",\"\",\"\""), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrAmountInvalid.Message)
|
assert.EqualError(t, err, errs.ErrAmountInvalid.Message)
|
||||||
|
|
||||||
_, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte("随手记导出文件(headers:v5;xxxxx)\n"+
|
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte("随手记导出文件(headers:v5;xxxxx)\n"+
|
||||||
"\"交易类型\",\"日期\",\"子类别\",\"账户\",\"金额\",\"备注\",\"关联Id\"\n"+
|
"\"交易类型\",\"日期\",\"子类别\",\"账户\",\"金额\",\"备注\",\"关联Id\"\n"+
|
||||||
"\"转出\",\"2024-09-01 12:34:56\",\"Test Category\",\"Test Account\",\"123 45\",\"\",\"00000000-0000-0000-0000-000000000001\"\n"+
|
"\"转出\",\"2024-09-01 12:34:56\",\"Test Category\",\"Test Account\",\"123 45\",\"\",\"00000000-0000-0000-0000-000000000001\"\n"+
|
||||||
"\"转入\",\"2024-09-01 12:34:56\",\"Test Category\",\"Test Account2\",\"123.45\",\"\",\"00000000-0000-0000-0000-000000000001\""), 0, nil, nil, nil, nil, nil)
|
"\"转入\",\"2024-09-01 12:34:56\",\"Test Category\",\"Test Account2\",\"123.45\",\"\",\"00000000-0000-0000-0000-000000000001\""), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrAmountInvalid.Message)
|
assert.EqualError(t, err, errs.ErrAmountInvalid.Message)
|
||||||
|
|
||||||
_, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte("随手记导出文件(headers:v5;xxxxx)\n"+
|
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte("随手记导出文件(headers:v5;xxxxx)\n"+
|
||||||
"\"交易类型\",\"日期\",\"子类别\",\"账户\",\"金额\",\"备注\",\"关联Id\"\n"+
|
"\"交易类型\",\"日期\",\"子类别\",\"账户\",\"金额\",\"备注\",\"关联Id\"\n"+
|
||||||
"\"转出\",\"2024-09-01 12:34:56\",\"Test Category\",\"Test Account\",\"123.45\",\"\",\"00000000-0000-0000-0000-000000000001\"\n"+
|
"\"转出\",\"2024-09-01 12:34:56\",\"Test Category\",\"Test Account\",\"123.45\",\"\",\"00000000-0000-0000-0000-000000000001\"\n"+
|
||||||
"\"转入\",\"2024-09-01 12:34:56\",\"Test Category\",\"Test Account2\",\"123 45\",\"\",\"00000000-0000-0000-0000-000000000001\""), 0, nil, nil, nil, nil, nil)
|
"\"转入\",\"2024-09-01 12:34:56\",\"Test Category\",\"Test Account2\",\"123 45\",\"\",\"00000000-0000-0000-0000-000000000001\""), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrAmountInvalid.Message)
|
assert.EqualError(t, err, errs.ErrAmountInvalid.Message)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestFeideeMymoneyCsvFileImporterParseImportedData_ParseDescription(t *testing.T) {
|
func TestFeideeMymoneyCsvFileImporterParseImportedData_ParseDescription(t *testing.T) {
|
||||||
converter := FeideeMymoneyAppTransactionDataCsvFileImporter
|
importer := FeideeMymoneyAppTransactionDataCsvFileImporter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
@@ -315,18 +316,18 @@ func TestFeideeMymoneyCsvFileImporterParseImportedData_ParseDescription(t *testi
|
|||||||
DefaultCurrency: "CNY",
|
DefaultCurrency: "CNY",
|
||||||
}
|
}
|
||||||
|
|
||||||
allNewTransactions, _, _, _, _, _, err := converter.ParseImportedData(context, user, []byte("随手记导出文件(headers:v5;xxxxx)\n"+
|
allNewTransactions, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte("随手记导出文件(headers:v5;xxxxx)\n"+
|
||||||
"\"交易类型\",\"日期\",\"子类别\",\"账户\",\"金额\",\"备注\",\"关联Id\"\n"+
|
"\"交易类型\",\"日期\",\"子类别\",\"账户\",\"金额\",\"备注\",\"关联Id\"\n"+
|
||||||
"\"支出\",\"2024-09-01 12:34:56\",\"Test Category\",\"Test Account\",\"123.45\",\"Test\n"+
|
"\"支出\",\"2024-09-01 12:34:56\",\"Test Category\",\"Test Account\",\"123.45\",\"Test\n"+
|
||||||
"A new line break\",\"\""), 0, nil, nil, nil, nil, nil)
|
"A new line break\",\"\""), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
|
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
assert.Equal(t, 1, len(allNewTransactions))
|
assert.Equal(t, 1, len(allNewTransactions))
|
||||||
assert.Equal(t, "Test\nA new line break", allNewTransactions[0].Comment)
|
assert.Equal(t, "Test\nA new line break", allNewTransactions[0].Comment)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestFeideeMymoneyCsvFileImporterParseImportedData_InvalidRelatedId(t *testing.T) {
|
func TestFeideeMymoneyCsvFileImporterParseImportedData_WithAdditionalOptions(t *testing.T) {
|
||||||
converter := FeideeMymoneyAppTransactionDataCsvFileImporter
|
importer := FeideeMymoneyAppTransactionDataCsvFileImporter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
@@ -334,41 +335,70 @@ func TestFeideeMymoneyCsvFileImporterParseImportedData_InvalidRelatedId(t *testi
|
|||||||
DefaultCurrency: "CNY",
|
DefaultCurrency: "CNY",
|
||||||
}
|
}
|
||||||
|
|
||||||
_, _, _, _, _, _, err := converter.ParseImportedData(context, user, []byte("随手记导出文件(headers:v5;xxxxx)\n"+
|
allNewTransactions, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte("随手记导出文件(headers:v5;xxxxx)\n"+
|
||||||
|
"\"交易类型\",\"日期\",\"子类别\",\"账户\",\"金额\",\"备注\",\"关联Id\",\"成员\",\"项目\",\"商家\"\n"+
|
||||||
|
"\"支出\",\"2024-09-01 12:34:56\",\"Test Category\",\"Test Account\",\"123.45\",\"\",\"\",\"test1\",\"test2\",\"test3\""), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
|
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.Equal(t, 1, len(allNewTransactions))
|
||||||
|
assert.Equal(t, 0, len(allNewTransactions[0].OriginalTagNames))
|
||||||
|
|
||||||
|
allNewTransactions, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte("随手记导出文件(headers:v5;xxxxx)\n"+
|
||||||
|
"\"交易类型\",\"日期\",\"子类别\",\"账户\",\"金额\",\"备注\",\"关联Id\",\"成员\",\"项目\",\"商家\"\n"+
|
||||||
|
"\"支出\",\"2024-09-01 12:34:56\",\"Test Category\",\"Test Account\",\"123.45\",\"\",\"\",\"test1\",\"test2\",\"test3\""), time.UTC, converter.DefaultImporterOptions.WithMemberAsTag().WithProjectAsTag().WithMerchantAsTag(), nil, nil, nil, nil, nil)
|
||||||
|
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.Equal(t, 1, len(allNewTransactions))
|
||||||
|
assert.Equal(t, 3, len(allNewTransactions[0].OriginalTagNames))
|
||||||
|
assert.Contains(t, allNewTransactions[0].OriginalTagNames, "test1")
|
||||||
|
assert.Contains(t, allNewTransactions[0].OriginalTagNames, "test2")
|
||||||
|
assert.Contains(t, allNewTransactions[0].OriginalTagNames, "test3")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFeideeMymoneyCsvFileImporterParseImportedData_InvalidRelatedId(t *testing.T) {
|
||||||
|
importer := FeideeMymoneyAppTransactionDataCsvFileImporter
|
||||||
|
context := core.NewNullContext()
|
||||||
|
|
||||||
|
user := &models.User{
|
||||||
|
Uid: 1234567890,
|
||||||
|
DefaultCurrency: "CNY",
|
||||||
|
}
|
||||||
|
|
||||||
|
_, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte("随手记导出文件(headers:v5;xxxxx)\n"+
|
||||||
"\"交易类型\",\"日期\",\"子类别\",\"账户\",\"金额\",\"备注\",\"关联Id\"\n"+
|
"\"交易类型\",\"日期\",\"子类别\",\"账户\",\"金额\",\"备注\",\"关联Id\"\n"+
|
||||||
"\"转出\",\"2024-09-01 23:59:59\",\"Test Category3\",\"Test Account\",\"0.05\",\"\",\"\""), 0, nil, nil, nil, nil, nil)
|
"\"转出\",\"2024-09-01 23:59:59\",\"Test Category3\",\"Test Account\",\"0.05\",\"\",\"\""), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrRelatedIdCannotBeBlank.Message)
|
assert.EqualError(t, err, errs.ErrRelatedIdCannotBeBlank.Message)
|
||||||
|
|
||||||
_, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte("随手记导出文件(headers:v5;xxxxx)\n"+
|
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte("随手记导出文件(headers:v5;xxxxx)\n"+
|
||||||
"\"交易类型\",\"日期\",\"子类别\",\"账户\",\"金额\",\"备注\",\"关联Id\"\n"+
|
"\"交易类型\",\"日期\",\"子类别\",\"账户\",\"金额\",\"备注\",\"关联Id\"\n"+
|
||||||
"\"转入\",\"2024-09-01 23:59:59\",\"Test Category3\",\"Test Account\",\"0.05\",\"\",\"\""), 0, nil, nil, nil, nil, nil)
|
"\"转入\",\"2024-09-01 23:59:59\",\"Test Category3\",\"Test Account\",\"0.05\",\"\",\"\""), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrRelatedIdCannotBeBlank.Message)
|
assert.EqualError(t, err, errs.ErrRelatedIdCannotBeBlank.Message)
|
||||||
|
|
||||||
_, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte("随手记导出文件(headers:v5;xxxxx)\n"+
|
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte("随手记导出文件(headers:v5;xxxxx)\n"+
|
||||||
"\"交易类型\",\"日期\",\"子类别\",\"账户\",\"金额\",\"备注\",\"关联Id\"\n"+
|
"\"交易类型\",\"日期\",\"子类别\",\"账户\",\"金额\",\"备注\",\"关联Id\"\n"+
|
||||||
"\"转出\",\"2024-09-01 23:59:59\",\"Test Category3\",\"Test Account\",\"0.05\",\"\",\"00000000-0000-0000-0000-000000000001\"\n"+
|
"\"转出\",\"2024-09-01 23:59:59\",\"Test Category3\",\"Test Account\",\"0.05\",\"\",\"00000000-0000-0000-0000-000000000001\"\n"+
|
||||||
"\"转入\",\"2024-09-02 23:59:59\",\"Test Category3\",\"Test Account\",\"0.5\",\"\",\"00000000-0000-0000-0000-000000000002\""), 0, nil, nil, nil, nil, nil)
|
"\"转入\",\"2024-09-02 23:59:59\",\"Test Category3\",\"Test Account\",\"0.5\",\"\",\"00000000-0000-0000-0000-000000000002\""), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrFoundRecordNotHasRelatedRecord.Message)
|
assert.EqualError(t, err, errs.ErrFoundRecordNotHasRelatedRecord.Message)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestFeideeMymoneyCsvFileImporterParseImportedData_MissingFileHeader(t *testing.T) {
|
func TestFeideeMymoneyCsvFileImporterParseImportedData_MissingFileHeader(t *testing.T) {
|
||||||
converter := FeideeMymoneyAppTransactionDataCsvFileImporter
|
importer := FeideeMymoneyAppTransactionDataCsvFileImporter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
Uid: 1,
|
Uid: 1,
|
||||||
DefaultCurrency: "CNY",
|
DefaultCurrency: "CNY",
|
||||||
}
|
}
|
||||||
_, _, _, _, _, _, err := converter.ParseImportedData(context, user, []byte("\"交易类型\",\"日期\",\"子类别\",\"账户\",\"金额\",\"备注\",\"关联Id\"\n"+
|
_, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte("\"交易类型\",\"日期\",\"子类别\",\"账户\",\"金额\",\"备注\",\"关联Id\"\n"+
|
||||||
"\"余额变更\",\"2024-09-01 00:00:00\",\"\",\"Test Account\",\"123.45\",\"\",\"\"\n"), 0, nil, nil, nil, nil, nil)
|
"\"余额变更\",\"2024-09-01 00:00:00\",\"\",\"Test Account\",\"123.45\",\"\",\"\"\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrInvalidFileHeader.Message)
|
assert.EqualError(t, err, errs.ErrInvalidFileHeader.Message)
|
||||||
|
|
||||||
_, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte(""), 0, nil, nil, nil, nil, nil)
|
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(""), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrInvalidFileHeader.Message)
|
assert.EqualError(t, err, errs.ErrInvalidFileHeader.Message)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestFeideeMymoneyCsvFileImporterParseImportedData_MissingRequiredColumn(t *testing.T) {
|
func TestFeideeMymoneyCsvFileImporterParseImportedData_MissingRequiredColumn(t *testing.T) {
|
||||||
converter := FeideeMymoneyAppTransactionDataCsvFileImporter
|
importer := FeideeMymoneyAppTransactionDataCsvFileImporter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
@@ -377,38 +407,38 @@ func TestFeideeMymoneyCsvFileImporterParseImportedData_MissingRequiredColumn(t *
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Missing Time Column
|
// Missing Time Column
|
||||||
_, _, _, _, _, _, err := converter.ParseImportedData(context, user, []byte("随手记导出文件(headers:v5;xxxxx)\n"+
|
_, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte("随手记导出文件(headers:v5;xxxxx)\n"+
|
||||||
"\"交易类型\",\"子类别\",\"账户\",\"金额\",\"备注\",\"关联Id\"\n"+
|
"\"交易类型\",\"子类别\",\"账户\",\"金额\",\"备注\",\"关联Id\"\n"+
|
||||||
"\"余额变更\",\"\",\"Test Account\",\"123.45\",\"\",\"\"\n"), 0, nil, nil, nil, nil, nil)
|
"\"余额变更\",\"\",\"Test Account\",\"123.45\",\"\",\"\"\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrMissingRequiredFieldInHeaderRow.Message)
|
assert.EqualError(t, err, errs.ErrMissingRequiredFieldInHeaderRow.Message)
|
||||||
|
|
||||||
// Missing Type Column
|
// Missing Type Column
|
||||||
_, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte("随手记导出文件(headers:v5;xxxxx)\n"+
|
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte("随手记导出文件(headers:v5;xxxxx)\n"+
|
||||||
"\"日期\",\"子类别\",\"账户\",\"金额\",\"备注\",\"关联Id\"\n"+
|
"\"日期\",\"子类别\",\"账户\",\"金额\",\"备注\",\"关联Id\"\n"+
|
||||||
"\"2024-09-01 00:00:00\",\"Test Category\",\"Test Account\",\"123.45\",\"\",\"\"\n"), 0, nil, nil, nil, nil, nil)
|
"\"2024-09-01 00:00:00\",\"Test Category\",\"Test Account\",\"123.45\",\"\",\"\"\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrMissingRequiredFieldInHeaderRow.Message)
|
assert.EqualError(t, err, errs.ErrMissingRequiredFieldInHeaderRow.Message)
|
||||||
|
|
||||||
// Missing Sub Category Column
|
// Missing Sub Category Column
|
||||||
_, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte("随手记导出文件(headers:v5;xxxxx)\n"+
|
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte("随手记导出文件(headers:v5;xxxxx)\n"+
|
||||||
"\"交易类型\",\"日期\",\"账户\",\"金额\",\"备注\",\"关联Id\"\n"+
|
"\"交易类型\",\"日期\",\"账户\",\"金额\",\"备注\",\"关联Id\"\n"+
|
||||||
"\"余额变更\",\"2024-09-01 00:00:00\",\"Test Account\",\"123.45\",\"\",\"\"\n"), 0, nil, nil, nil, nil, nil)
|
"\"余额变更\",\"2024-09-01 00:00:00\",\"Test Account\",\"123.45\",\"\",\"\"\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrMissingRequiredFieldInHeaderRow.Message)
|
assert.EqualError(t, err, errs.ErrMissingRequiredFieldInHeaderRow.Message)
|
||||||
|
|
||||||
// Missing Account Name Column
|
// Missing Account Name Column
|
||||||
_, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte("随手记导出文件(headers:v5;xxxxx)\n"+
|
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte("随手记导出文件(headers:v5;xxxxx)\n"+
|
||||||
"\"交易类型\",\"日期\",\"子类别\",\"金额\",\"备注\",\"关联Id\"\n"+
|
"\"交易类型\",\"日期\",\"子类别\",\"金额\",\"备注\",\"关联Id\"\n"+
|
||||||
"\"余额变更\",\"2024-09-01 00:00:00\",\"\",\"123.45\",\"\",\"\"\n"), 0, nil, nil, nil, nil, nil)
|
"\"余额变更\",\"2024-09-01 00:00:00\",\"\",\"123.45\",\"\",\"\"\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrMissingRequiredFieldInHeaderRow.Message)
|
assert.EqualError(t, err, errs.ErrMissingRequiredFieldInHeaderRow.Message)
|
||||||
|
|
||||||
// Missing Amount Column
|
// Missing Amount Column
|
||||||
_, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte("随手记导出文件(headers:v5;xxxxx)\n"+
|
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte("随手记导出文件(headers:v5;xxxxx)\n"+
|
||||||
"\"交易类型\",\"日期\",\"子类别\",\"账户\",\"备注\",\"关联Id\"\n"+
|
"\"交易类型\",\"日期\",\"子类别\",\"账户\",\"备注\",\"关联Id\"\n"+
|
||||||
"\"余额变更\",\"2024-09-01 00:00:00\",\"\",\"Test Account\",\"\",\"\"\n"), 0, nil, nil, nil, nil, nil)
|
"\"余额变更\",\"2024-09-01 00:00:00\",\"\",\"Test Account\",\"\",\"\"\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrMissingRequiredFieldInHeaderRow.Message)
|
assert.EqualError(t, err, errs.ErrMissingRequiredFieldInHeaderRow.Message)
|
||||||
|
|
||||||
// Missing Related ID Column
|
// Missing Related ID Column
|
||||||
_, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte("随手记导出文件(headers:v5;xxxxx)\n"+
|
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte("随手记导出文件(headers:v5;xxxxx)\n"+
|
||||||
"\"交易类型\",\"日期\",\"子类别\",\"账户\",\"金额\",\"备注\"\n"+
|
"\"交易类型\",\"日期\",\"子类别\",\"账户\",\"金额\",\"备注\"\n"+
|
||||||
"\"余额变更\",\"2024-09-01 00:00:00\",\"\",\"Test Account\",\"123.45\",\"\"\n"), 0, nil, nil, nil, nil, nil)
|
"\"余额变更\",\"2024-09-01 00:00:00\",\"\",\"Test Account\",\"123.45\",\"\"\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrMissingRequiredFieldInHeaderRow.Message)
|
assert.EqualError(t, err, errs.ErrMissingRequiredFieldInHeaderRow.Message)
|
||||||
}
|
}
|
||||||
|
|||||||
+7
-2
@@ -1,6 +1,8 @@
|
|||||||
package feidee
|
package feidee
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/converters/converter"
|
"github.com/mayswind/ezbookkeeping/pkg/converters/converter"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/converters/datatable"
|
"github.com/mayswind/ezbookkeeping/pkg/converters/datatable"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/converters/excel"
|
"github.com/mayswind/ezbookkeeping/pkg/converters/excel"
|
||||||
@@ -18,6 +20,9 @@ var feideeMymoneyElecloudDataColumnNameMapping = map[datatable.TransactionDataTa
|
|||||||
datatable.TRANSACTION_DATA_TABLE_AMOUNT: "金额",
|
datatable.TRANSACTION_DATA_TABLE_AMOUNT: "金额",
|
||||||
datatable.TRANSACTION_DATA_TABLE_RELATED_ACCOUNT_NAME: "账户2",
|
datatable.TRANSACTION_DATA_TABLE_RELATED_ACCOUNT_NAME: "账户2",
|
||||||
datatable.TRANSACTION_DATA_TABLE_DESCRIPTION: "备注",
|
datatable.TRANSACTION_DATA_TABLE_DESCRIPTION: "备注",
|
||||||
|
datatable.TRANSACTION_DATA_TABLE_MEMBER: "成员",
|
||||||
|
datatable.TRANSACTION_DATA_TABLE_PROJECT: "项目",
|
||||||
|
datatable.TRANSACTION_DATA_TABLE_MERCHANT: "商家",
|
||||||
}
|
}
|
||||||
|
|
||||||
// feideeMymoneyElecloudTransactionDataXlsxFileImporter defines the structure of feidee mymoney (elecloud) xlsx importer for transaction data
|
// feideeMymoneyElecloudTransactionDataXlsxFileImporter defines the structure of feidee mymoney (elecloud) xlsx importer for transaction data
|
||||||
@@ -31,7 +36,7 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// ParseImportedData returns the imported data by parsing the feidee mymoney (elecloud) transaction xlsx data
|
// ParseImportedData returns the imported data by parsing the feidee mymoney (elecloud) transaction xlsx data
|
||||||
func (c *feideeMymoneyElecloudTransactionDataXlsxFileImporter) ParseImportedData(ctx core.Context, user *models.User, data []byte, defaultTimezoneOffset int16, accountMap map[string]*models.Account, expenseCategoryMap map[string]map[string]*models.TransactionCategory, incomeCategoryMap map[string]map[string]*models.TransactionCategory, transferCategoryMap map[string]map[string]*models.TransactionCategory, tagMap map[string]*models.TransactionTag) (models.ImportedTransactionSlice, []*models.Account, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionTag, error) {
|
func (c *feideeMymoneyElecloudTransactionDataXlsxFileImporter) ParseImportedData(ctx core.Context, user *models.User, data []byte, defaultTimezone *time.Location, additionalOptions converter.TransactionDataImporterOptions, accountMap map[string]*models.Account, expenseCategoryMap map[string]map[string]*models.TransactionCategory, incomeCategoryMap map[string]map[string]*models.TransactionCategory, transferCategoryMap map[string]map[string]*models.TransactionCategory, tagMap map[string]*models.TransactionTag) (models.ImportedTransactionSlice, []*models.Account, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionTag, error) {
|
||||||
dataTable, err := excel.CreateNewExcelOOXMLFileBasicDataTable(data, true)
|
dataTable, err := excel.CreateNewExcelOOXMLFileBasicDataTable(data, true)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -42,5 +47,5 @@ func (c *feideeMymoneyElecloudTransactionDataXlsxFileImporter) ParseImportedData
|
|||||||
transactionDataTable := datatable.CreateNewTransactionDataTableFromBasicDataTableWithRowParser(dataTable, feideeMymoneyElecloudDataColumnNameMapping, transactionRowParser)
|
transactionDataTable := datatable.CreateNewTransactionDataTableFromBasicDataTableWithRowParser(dataTable, feideeMymoneyElecloudDataColumnNameMapping, transactionRowParser)
|
||||||
dataTableImporter := converter.CreateNewSimpleImporter(feideeMymoneyElecloudTransactionTypeNameMapping)
|
dataTableImporter := converter.CreateNewSimpleImporter(feideeMymoneyElecloudTransactionTypeNameMapping)
|
||||||
|
|
||||||
return dataTableImporter.ParseImportedData(ctx, user, transactionDataTable, defaultTimezoneOffset, accountMap, expenseCategoryMap, incomeCategoryMap, transferCategoryMap, tagMap)
|
return dataTableImporter.ParseImportedData(ctx, user, transactionDataTable, defaultTimezone, additionalOptions, accountMap, expenseCategoryMap, incomeCategoryMap, transferCategoryMap, tagMap)
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-2
@@ -7,13 +7,14 @@ import (
|
|||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
|
"github.com/mayswind/ezbookkeeping/pkg/converters/converter"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/core"
|
"github.com/mayswind/ezbookkeeping/pkg/core"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/models"
|
"github.com/mayswind/ezbookkeeping/pkg/models"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/utils"
|
"github.com/mayswind/ezbookkeeping/pkg/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestFeideeMymoneyElecloudTransactionDataXlsxImporterParseImportedData_MinimumValidData(t *testing.T) {
|
func TestFeideeMymoneyElecloudTransactionDataXlsxImporterParseImportedData_MinimumValidData(t *testing.T) {
|
||||||
converter := FeideeMymoneyElecloudTransactionDataXlsxFileImporter
|
importer := FeideeMymoneyElecloudTransactionDataXlsxFileImporter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
@@ -24,7 +25,7 @@ func TestFeideeMymoneyElecloudTransactionDataXlsxImporterParseImportedData_Minim
|
|||||||
testdata, err := os.ReadFile("../../../testdata/feidee_mymoney_elecloud_test_file.xlsx")
|
testdata, err := os.ReadFile("../../../testdata/feidee_mymoney_elecloud_test_file.xlsx")
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
allNewTransactions, allNewAccounts, allNewSubExpenseCategories, allNewSubIncomeCategories, allNewSubTransferCategories, allNewTags, err := converter.ParseImportedData(context, user, testdata, 0, nil, nil, nil, nil, nil)
|
allNewTransactions, allNewAccounts, allNewSubExpenseCategories, allNewSubIncomeCategories, allNewSubTransferCategories, allNewTags, err := importer.ParseImportedData(context, user, testdata, time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
assert.Equal(t, 7, len(allNewTransactions))
|
assert.Equal(t, 7, len(allNewTransactions))
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
package feidee
|
package feidee
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/converters/converter"
|
"github.com/mayswind/ezbookkeeping/pkg/converters/converter"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/converters/datatable"
|
"github.com/mayswind/ezbookkeeping/pkg/converters/datatable"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/converters/excel"
|
"github.com/mayswind/ezbookkeeping/pkg/converters/excel"
|
||||||
@@ -17,6 +19,9 @@ var feideeMymoneyWebDataColumnNameMapping = map[datatable.TransactionDataTableCo
|
|||||||
datatable.TRANSACTION_DATA_TABLE_AMOUNT: "金额",
|
datatable.TRANSACTION_DATA_TABLE_AMOUNT: "金额",
|
||||||
datatable.TRANSACTION_DATA_TABLE_RELATED_ACCOUNT_NAME: "账户2",
|
datatable.TRANSACTION_DATA_TABLE_RELATED_ACCOUNT_NAME: "账户2",
|
||||||
datatable.TRANSACTION_DATA_TABLE_DESCRIPTION: "备注",
|
datatable.TRANSACTION_DATA_TABLE_DESCRIPTION: "备注",
|
||||||
|
datatable.TRANSACTION_DATA_TABLE_MEMBER: "成员",
|
||||||
|
datatable.TRANSACTION_DATA_TABLE_PROJECT: "项目",
|
||||||
|
datatable.TRANSACTION_DATA_TABLE_MERCHANT: "商家",
|
||||||
}
|
}
|
||||||
|
|
||||||
// feideeMymoneyWebTransactionDataXlsFileImporter defines the structure of feidee mymoney (web) xls importer for transaction data
|
// feideeMymoneyWebTransactionDataXlsFileImporter defines the structure of feidee mymoney (web) xls importer for transaction data
|
||||||
@@ -30,7 +35,7 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// ParseImportedData returns the imported data by parsing the feidee mymoney (web) transaction xls data
|
// ParseImportedData returns the imported data by parsing the feidee mymoney (web) transaction xls data
|
||||||
func (c *feideeMymoneyWebTransactionDataXlsFileImporter) ParseImportedData(ctx core.Context, user *models.User, data []byte, defaultTimezoneOffset int16, accountMap map[string]*models.Account, expenseCategoryMap map[string]map[string]*models.TransactionCategory, incomeCategoryMap map[string]map[string]*models.TransactionCategory, transferCategoryMap map[string]map[string]*models.TransactionCategory, tagMap map[string]*models.TransactionTag) (models.ImportedTransactionSlice, []*models.Account, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionTag, error) {
|
func (c *feideeMymoneyWebTransactionDataXlsFileImporter) ParseImportedData(ctx core.Context, user *models.User, data []byte, defaultTimezone *time.Location, additionalOptions converter.TransactionDataImporterOptions, accountMap map[string]*models.Account, expenseCategoryMap map[string]map[string]*models.TransactionCategory, incomeCategoryMap map[string]map[string]*models.TransactionCategory, transferCategoryMap map[string]map[string]*models.TransactionCategory, tagMap map[string]*models.TransactionTag) (models.ImportedTransactionSlice, []*models.Account, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionTag, error) {
|
||||||
dataTable, err := excel.CreateNewExcelMSCFBFileBasicDataTable(data, true)
|
dataTable, err := excel.CreateNewExcelMSCFBFileBasicDataTable(data, true)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -41,5 +46,5 @@ func (c *feideeMymoneyWebTransactionDataXlsFileImporter) ParseImportedData(ctx c
|
|||||||
transactionDataTable := datatable.CreateNewTransactionDataTableFromBasicDataTableWithRowParser(dataTable, feideeMymoneyWebDataColumnNameMapping, transactionRowParser)
|
transactionDataTable := datatable.CreateNewTransactionDataTableFromBasicDataTableWithRowParser(dataTable, feideeMymoneyWebDataColumnNameMapping, transactionRowParser)
|
||||||
dataTableImporter := converter.CreateNewSimpleImporterWithTypeNameMapping(feideeMymoneyTransactionTypeNameMapping)
|
dataTableImporter := converter.CreateNewSimpleImporterWithTypeNameMapping(feideeMymoneyTransactionTypeNameMapping)
|
||||||
|
|
||||||
return dataTableImporter.ParseImportedData(ctx, user, transactionDataTable, defaultTimezoneOffset, accountMap, expenseCategoryMap, incomeCategoryMap, transferCategoryMap, tagMap)
|
return dataTableImporter.ParseImportedData(ctx, user, transactionDataTable, defaultTimezone, additionalOptions, accountMap, expenseCategoryMap, incomeCategoryMap, transferCategoryMap, tagMap)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,13 +7,14 @@ import (
|
|||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
|
"github.com/mayswind/ezbookkeeping/pkg/converters/converter"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/core"
|
"github.com/mayswind/ezbookkeeping/pkg/core"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/models"
|
"github.com/mayswind/ezbookkeeping/pkg/models"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/utils"
|
"github.com/mayswind/ezbookkeeping/pkg/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestFeideeMymoneyTransactionDataXlsImporterParseImportedData_MinimumValidData(t *testing.T) {
|
func TestFeideeMymoneyTransactionDataXlsImporterParseImportedData_MinimumValidData(t *testing.T) {
|
||||||
converter := FeideeMymoneyWebTransactionDataXlsFileImporter
|
importer := FeideeMymoneyWebTransactionDataXlsFileImporter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
@@ -24,7 +25,7 @@ func TestFeideeMymoneyTransactionDataXlsImporterParseImportedData_MinimumValidDa
|
|||||||
testdata, err := os.ReadFile("../../../testdata/feidee_mymoney_test_file.xls")
|
testdata, err := os.ReadFile("../../../testdata/feidee_mymoney_test_file.xls")
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
allNewTransactions, allNewAccounts, allNewSubExpenseCategories, allNewSubIncomeCategories, allNewSubTransferCategories, allNewTags, err := converter.ParseImportedData(context, user, testdata, 0, nil, nil, nil, nil, nil)
|
allNewTransactions, allNewAccounts, allNewSubExpenseCategories, allNewSubIncomeCategories, allNewSubTransferCategories, allNewTags, err := importer.ParseImportedData(context, user, testdata, time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
assert.Equal(t, 7, len(allNewTransactions))
|
assert.Equal(t, 7, len(allNewTransactions))
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package fireflyIII
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/converters/converter"
|
"github.com/mayswind/ezbookkeeping/pkg/converters/converter"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/converters/csv"
|
"github.com/mayswind/ezbookkeeping/pkg/converters/csv"
|
||||||
@@ -40,7 +41,7 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// ParseImportedData returns the imported data by parsing the firefly III transaction csv data
|
// ParseImportedData returns the imported data by parsing the firefly III transaction csv data
|
||||||
func (c *fireflyIIITransactionDataCsvFileImporter) ParseImportedData(ctx core.Context, user *models.User, data []byte, defaultTimezoneOffset int16, accountMap map[string]*models.Account, expenseCategoryMap map[string]map[string]*models.TransactionCategory, incomeCategoryMap map[string]map[string]*models.TransactionCategory, transferCategoryMap map[string]map[string]*models.TransactionCategory, tagMap map[string]*models.TransactionTag) (models.ImportedTransactionSlice, []*models.Account, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionTag, error) {
|
func (c *fireflyIIITransactionDataCsvFileImporter) ParseImportedData(ctx core.Context, user *models.User, data []byte, defaultTimezone *time.Location, additionalOptions converter.TransactionDataImporterOptions, accountMap map[string]*models.Account, expenseCategoryMap map[string]map[string]*models.TransactionCategory, incomeCategoryMap map[string]map[string]*models.TransactionCategory, transferCategoryMap map[string]map[string]*models.TransactionCategory, tagMap map[string]*models.TransactionTag) (models.ImportedTransactionSlice, []*models.Account, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionTag, error) {
|
||||||
reader := bytes.NewReader(data)
|
reader := bytes.NewReader(data)
|
||||||
dataTable, err := csv.CreateNewCsvBasicDataTable(ctx, reader, true)
|
dataTable, err := csv.CreateNewCsvBasicDataTable(ctx, reader, true)
|
||||||
|
|
||||||
@@ -52,5 +53,5 @@ func (c *fireflyIIITransactionDataCsvFileImporter) ParseImportedData(ctx core.Co
|
|||||||
transactionDataTable := datatable.CreateNewTransactionDataTableFromBasicDataTableWithRowParser(dataTable, fireflyIIITransactionDataColumnNameMapping, transactionRowParser)
|
transactionDataTable := datatable.CreateNewTransactionDataTableFromBasicDataTableWithRowParser(dataTable, fireflyIIITransactionDataColumnNameMapping, transactionRowParser)
|
||||||
dataTableImporter := converter.CreateNewImporterWithTypeNameMapping(fireflyIIITransactionTypeNameMapping, "", "", ",")
|
dataTableImporter := converter.CreateNewImporterWithTypeNameMapping(fireflyIIITransactionTypeNameMapping, "", "", ",")
|
||||||
|
|
||||||
return dataTableImporter.ParseImportedData(ctx, user, transactionDataTable, defaultTimezoneOffset, accountMap, expenseCategoryMap, incomeCategoryMap, transferCategoryMap, tagMap)
|
return dataTableImporter.ParseImportedData(ctx, user, transactionDataTable, defaultTimezone, additionalOptions, accountMap, expenseCategoryMap, incomeCategoryMap, transferCategoryMap, tagMap)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,17 +2,19 @@ package fireflyIII
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
|
"github.com/mayswind/ezbookkeeping/pkg/converters/converter"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/core"
|
"github.com/mayswind/ezbookkeeping/pkg/core"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/errs"
|
"github.com/mayswind/ezbookkeeping/pkg/errs"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/models"
|
"github.com/mayswind/ezbookkeeping/pkg/models"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/utils"
|
"github.com/mayswind/ezbookkeeping/pkg/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestFireFlyIIICsvFileConverterParseImportedData_MinimumValidData(t *testing.T) {
|
func TestFireFlyIIICsvFileimporterParseImportedData_MinimumValidData(t *testing.T) {
|
||||||
converter := FireflyIIITransactionDataCsvFileImporter
|
importer := FireflyIIITransactionDataCsvFileImporter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
@@ -20,11 +22,11 @@ func TestFireFlyIIICsvFileConverterParseImportedData_MinimumValidData(t *testing
|
|||||||
DefaultCurrency: "CNY",
|
DefaultCurrency: "CNY",
|
||||||
}
|
}
|
||||||
|
|
||||||
allNewTransactions, allNewAccounts, allNewSubExpenseCategories, allNewSubIncomeCategories, allNewSubTransferCategories, allNewTags, err := converter.ParseImportedData(context, user, []byte("type,amount,date,source_name,destination_name,category\n"+
|
allNewTransactions, allNewAccounts, allNewSubExpenseCategories, allNewSubIncomeCategories, allNewSubTransferCategories, allNewTags, err := importer.ParseImportedData(context, user, []byte("type,amount,date,source_name,destination_name,category\n"+
|
||||||
"\"Opening balance\",123.45,2024-09-01T00:00:00+08:00,\"Initial balance for \"\"Test Account\"\"\",\"Test Account\",\n"+
|
"\"Opening balance\",123.45,2024-09-01T00:00:00+08:00,\"Initial balance for \"\"Test Account\"\"\",\"Test Account\",\n"+
|
||||||
"Deposit,0.12,2024-09-01T01:23:45+08:00,\"A revenue account\",\"Test Account\",\"Test Category\"\n"+
|
"Deposit,0.12,2024-09-01T01:23:45+08:00,\"A revenue account\",\"Test Account\",\"Test Category\"\n"+
|
||||||
"Withdrawal,-1.00,2024-09-01T12:34:56+08:00,\"Test Account\",\"A expense account\",\"Test Category2\"\n"+
|
"Withdrawal,-1.00,2024-09-01T12:34:56+08:00,\"Test Account\",\"A expense account\",\"Test Category2\"\n"+
|
||||||
"Transfer,0.05,2024-09-01T23:59:59+08:00,\"Test Account\",\"Test Account2\",\"Test Category3\""), 0, nil, nil, nil, nil, nil)
|
"Transfer,0.05,2024-09-01T23:59:59+08:00,\"Test Account\",\"Test Account2\",\"Test Category3\""), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
|
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
@@ -82,8 +84,8 @@ func TestFireFlyIIICsvFileConverterParseImportedData_MinimumValidData(t *testing
|
|||||||
assert.Equal(t, "Test Category3", allNewSubTransferCategories[0].Name)
|
assert.Equal(t, "Test Category3", allNewSubTransferCategories[0].Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestFireFlyIIICsvFileConverterParseImportedData_ParseInvalidTime(t *testing.T) {
|
func TestFireFlyIIICsvFileimporterParseImportedData_ParseInvalidTime(t *testing.T) {
|
||||||
converter := FireflyIIITransactionDataCsvFileImporter
|
importer := FireflyIIITransactionDataCsvFileImporter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
@@ -91,17 +93,17 @@ func TestFireFlyIIICsvFileConverterParseImportedData_ParseInvalidTime(t *testing
|
|||||||
DefaultCurrency: "CNY",
|
DefaultCurrency: "CNY",
|
||||||
}
|
}
|
||||||
|
|
||||||
_, _, _, _, _, _, err := converter.ParseImportedData(context, user, []byte("type,amount,date,source_name,destination_name,category\n"+
|
_, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte("type,amount,date,source_name,destination_name,category\n"+
|
||||||
"Withdrawal,-1.00,2024-09-01T12:34:56,\"Test Account\",\"A expense account\",\"Test Category\""), 0, nil, nil, nil, nil, nil)
|
"Withdrawal,-1.00,2024-09-01T12:34:56,\"Test Account\",\"A expense account\",\"Test Category\""), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrTransactionTimeInvalid.Message)
|
assert.EqualError(t, err, errs.ErrTransactionTimeInvalid.Message)
|
||||||
|
|
||||||
_, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte("type,amount,date,source_name,destination_name,category\n"+
|
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte("type,amount,date,source_name,destination_name,category\n"+
|
||||||
"Withdrawal,-1.00,2024-09-01 12:34:56+08:00,\"Test Account\",\"A expense account\",\"Test Category\""), 0, nil, nil, nil, nil, nil)
|
"Withdrawal,-1.00,2024-09-01 12:34:56+08:00,\"Test Account\",\"A expense account\",\"Test Category\""), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrTransactionTimeInvalid.Message)
|
assert.EqualError(t, err, errs.ErrTransactionTimeInvalid.Message)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestFireFlyIIICsvFileConverterParseImportedData_ParseInvalidType(t *testing.T) {
|
func TestFireFlyIIICsvFileimporterParseImportedData_ParseInvalidType(t *testing.T) {
|
||||||
converter := FireflyIIITransactionDataCsvFileImporter
|
importer := FireflyIIITransactionDataCsvFileImporter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
@@ -109,13 +111,13 @@ func TestFireFlyIIICsvFileConverterParseImportedData_ParseInvalidType(t *testing
|
|||||||
DefaultCurrency: "CNY",
|
DefaultCurrency: "CNY",
|
||||||
}
|
}
|
||||||
|
|
||||||
_, _, _, _, _, _, err := converter.ParseImportedData(context, user, []byte("type,amount,date,source_name,destination_name,category\n"+
|
_, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte("type,amount,date,source_name,destination_name,category\n"+
|
||||||
"Type,123.45,2024-09-01T12:34:56+08:00,\"Test Account\",\"A expense account\",\"Test Category\""), 0, nil, nil, nil, nil, nil)
|
"Type,123.45,2024-09-01T12:34:56+08:00,\"Test Account\",\"A expense account\",\"Test Category\""), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrTransactionTypeInvalid.Message)
|
assert.EqualError(t, err, errs.ErrTransactionTypeInvalid.Message)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestFireFlyIIICsvFileConverterParseImportedData_ParseAccountNameAsCategoryName(t *testing.T) {
|
func TestFireFlyIIICsvFileimporterParseImportedData_ParseAccountNameAsCategoryName(t *testing.T) {
|
||||||
converter := FireflyIIITransactionDataCsvFileImporter
|
importer := FireflyIIITransactionDataCsvFileImporter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
@@ -123,23 +125,23 @@ func TestFireFlyIIICsvFileConverterParseImportedData_ParseAccountNameAsCategoryN
|
|||||||
DefaultCurrency: "CNY",
|
DefaultCurrency: "CNY",
|
||||||
}
|
}
|
||||||
|
|
||||||
allNewTransactions, _, _, _, _, _, err := converter.ParseImportedData(context, user, []byte("type,amount,date,source_name,destination_name,category\n"+
|
allNewTransactions, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte("type,amount,date,source_name,destination_name,category\n"+
|
||||||
"Withdrawal,-1.00,2024-09-01T12:34:56+08:00,\"Test Account\",\"A expense account\",\"\""), 0, nil, nil, nil, nil, nil)
|
"Withdrawal,-1.00,2024-09-01T12:34:56+08:00,\"Test Account\",\"A expense account\",\"\""), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
|
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
assert.Equal(t, 1, len(allNewTransactions))
|
assert.Equal(t, 1, len(allNewTransactions))
|
||||||
assert.Equal(t, "A expense account", allNewTransactions[0].OriginalCategoryName)
|
assert.Equal(t, "A expense account", allNewTransactions[0].OriginalCategoryName)
|
||||||
|
|
||||||
allNewTransactions, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte("type,amount,date,source_name,destination_name,category\n"+
|
allNewTransactions, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte("type,amount,date,source_name,destination_name,category\n"+
|
||||||
"Deposit,10.00,2024-09-01T12:34:56+08:00,\"A revenue account\",\"Test Account\",\"\""), 0, nil, nil, nil, nil, nil)
|
"Deposit,10.00,2024-09-01T12:34:56+08:00,\"A revenue account\",\"Test Account\",\"\""), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
|
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
assert.Equal(t, 1, len(allNewTransactions))
|
assert.Equal(t, 1, len(allNewTransactions))
|
||||||
assert.Equal(t, "A revenue account", allNewTransactions[0].OriginalCategoryName)
|
assert.Equal(t, "A revenue account", allNewTransactions[0].OriginalCategoryName)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestFireFlyIIICsvFileConverterParseImportedData_ParseValidTimezone(t *testing.T) {
|
func TestFireFlyIIICsvFileimporterParseImportedData_ParseValidTimezone(t *testing.T) {
|
||||||
converter := FireflyIIITransactionDataCsvFileImporter
|
importer := FireflyIIITransactionDataCsvFileImporter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
@@ -147,27 +149,27 @@ func TestFireFlyIIICsvFileConverterParseImportedData_ParseValidTimezone(t *testi
|
|||||||
DefaultCurrency: "CNY",
|
DefaultCurrency: "CNY",
|
||||||
}
|
}
|
||||||
|
|
||||||
allNewTransactions, _, _, _, _, _, err := converter.ParseImportedData(context, user, []byte("type,amount,date,source_name,destination_name,category\n"+
|
allNewTransactions, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte("type,amount,date,source_name,destination_name,category\n"+
|
||||||
"Withdrawal,-1.00,2024-09-01T12:34:56-10:00,\"Test Account\",\"A expense account\",\"Test Category\""), 0, nil, nil, nil, nil, nil)
|
"Withdrawal,-1.00,2024-09-01T12:34:56-10:00,\"Test Account\",\"A expense account\",\"Test Category\""), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
assert.Equal(t, 1, len(allNewTransactions))
|
assert.Equal(t, 1, len(allNewTransactions))
|
||||||
assert.Equal(t, int64(1725230096), utils.GetUnixTimeFromTransactionTime(allNewTransactions[0].TransactionTime))
|
assert.Equal(t, int64(1725230096), utils.GetUnixTimeFromTransactionTime(allNewTransactions[0].TransactionTime))
|
||||||
|
|
||||||
allNewTransactions, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte("type,amount,date,source_name,destination_name,category\n"+
|
allNewTransactions, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte("type,amount,date,source_name,destination_name,category\n"+
|
||||||
"Withdrawal,-1.00,2024-09-01T12:34:56+00:00,\"Test Account\",\"A expense account\",\"Test Category\""), 0, nil, nil, nil, nil, nil)
|
"Withdrawal,-1.00,2024-09-01T12:34:56+00:00,\"Test Account\",\"A expense account\",\"Test Category\""), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
assert.Equal(t, 1, len(allNewTransactions))
|
assert.Equal(t, 1, len(allNewTransactions))
|
||||||
assert.Equal(t, int64(1725194096), utils.GetUnixTimeFromTransactionTime(allNewTransactions[0].TransactionTime))
|
assert.Equal(t, int64(1725194096), utils.GetUnixTimeFromTransactionTime(allNewTransactions[0].TransactionTime))
|
||||||
|
|
||||||
allNewTransactions, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte("type,amount,date,source_name,destination_name,category\n"+
|
allNewTransactions, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte("type,amount,date,source_name,destination_name,category\n"+
|
||||||
"Withdrawal,-1.00,2024-09-01T12:34:56+12:45,\"Test Account\",\"A expense account\",\"Test Category\""), 0, nil, nil, nil, nil, nil)
|
"Withdrawal,-1.00,2024-09-01T12:34:56+12:45,\"Test Account\",\"A expense account\",\"Test Category\""), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
assert.Equal(t, 1, len(allNewTransactions))
|
assert.Equal(t, 1, len(allNewTransactions))
|
||||||
assert.Equal(t, int64(1725148196), utils.GetUnixTimeFromTransactionTime(allNewTransactions[0].TransactionTime))
|
assert.Equal(t, int64(1725148196), utils.GetUnixTimeFromTransactionTime(allNewTransactions[0].TransactionTime))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestFireFlyIIICsvFileConverterParseImportedData_ParseValidAccountCurrency(t *testing.T) {
|
func TestFireFlyIIICsvFileimporterParseImportedData_ParseValidAccountCurrency(t *testing.T) {
|
||||||
converter := FireflyIIITransactionDataCsvFileImporter
|
importer := FireflyIIITransactionDataCsvFileImporter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
@@ -175,9 +177,9 @@ func TestFireFlyIIICsvFileConverterParseImportedData_ParseValidAccountCurrency(t
|
|||||||
DefaultCurrency: "CNY",
|
DefaultCurrency: "CNY",
|
||||||
}
|
}
|
||||||
|
|
||||||
allNewTransactions, allNewAccounts, _, _, _, _, err := converter.ParseImportedData(context, user, []byte("type,amount,foreign_amount,date,currency_code,foreign_currency_code,source_name,destination_name,category\n"+
|
allNewTransactions, allNewAccounts, _, _, _, _, err := importer.ParseImportedData(context, user, []byte("type,amount,foreign_amount,date,currency_code,foreign_currency_code,source_name,destination_name,category\n"+
|
||||||
"\"Opening balance\",123.45,,2024-09-01T00:00:00+08:00,USD,,\"Initial balance for \"\"Test Account\"\"\",\"Test Account\",\n"+
|
"\"Opening balance\",123.45,,2024-09-01T00:00:00+08:00,USD,,\"Initial balance for \"\"Test Account\"\"\",\"Test Account\",\n"+
|
||||||
"Transfer,1.23,1.10,2024-09-01T23:59:59+08:00,USD,EUR,\"Test Account\",\"Test Account2\",\"Test Category2\""), 0, nil, nil, nil, nil, nil)
|
"Transfer,1.23,1.10,2024-09-01T23:59:59+08:00,USD,EUR,\"Test Account\",\"Test Account2\",\"Test Category2\""), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
|
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
@@ -193,8 +195,8 @@ func TestFireFlyIIICsvFileConverterParseImportedData_ParseValidAccountCurrency(t
|
|||||||
assert.Equal(t, "EUR", allNewAccounts[1].Currency)
|
assert.Equal(t, "EUR", allNewAccounts[1].Currency)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestFireFlyIIICsvFileConverterParseImportedData_ParseValidForeignAmountAndCurrency(t *testing.T) {
|
func TestFireFlyIIICsvFileimporterParseImportedData_ParseValidForeignAmountAndCurrency(t *testing.T) {
|
||||||
converter := FireflyIIITransactionDataCsvFileImporter
|
importer := FireflyIIITransactionDataCsvFileImporter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
@@ -202,8 +204,8 @@ func TestFireFlyIIICsvFileConverterParseImportedData_ParseValidForeignAmountAndC
|
|||||||
DefaultCurrency: "CNY",
|
DefaultCurrency: "CNY",
|
||||||
}
|
}
|
||||||
|
|
||||||
allNewTransactions, _, _, _, _, _, err := converter.ParseImportedData(context, user, []byte("type,amount,foreign_amount,date,currency_code,foreign_currency_code,source_name,destination_name,category\n"+
|
allNewTransactions, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte("type,amount,foreign_amount,date,currency_code,foreign_currency_code,source_name,destination_name,category\n"+
|
||||||
"Transfer,10.00,15.00,2024-09-01T12:34:56+08:00,USD,EUR,\"Test Account\",\"Test Account2\",\"Test Category\""), 0, nil, nil, nil, nil, nil)
|
"Transfer,10.00,15.00,2024-09-01T12:34:56+08:00,USD,EUR,\"Test Account\",\"Test Account2\",\"Test Category\""), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
|
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
assert.Equal(t, 1, len(allNewTransactions))
|
assert.Equal(t, 1, len(allNewTransactions))
|
||||||
@@ -213,8 +215,8 @@ func TestFireFlyIIICsvFileConverterParseImportedData_ParseValidForeignAmountAndC
|
|||||||
assert.Equal(t, "USD", allNewTransactions[0].OriginalSourceAccountCurrency)
|
assert.Equal(t, "USD", allNewTransactions[0].OriginalSourceAccountCurrency)
|
||||||
assert.Equal(t, "EUR", allNewTransactions[0].OriginalDestinationAccountCurrency)
|
assert.Equal(t, "EUR", allNewTransactions[0].OriginalDestinationAccountCurrency)
|
||||||
|
|
||||||
allNewTransactions, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte("type,amount,date,currency_code,foreign_currency_code,source_name,destination_name,category\n"+
|
allNewTransactions, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte("type,amount,date,currency_code,foreign_currency_code,source_name,destination_name,category\n"+
|
||||||
"Transfer,10.00,2024-09-01T12:34:56+08:00,USD,EUR,\"Test Account\",\"Test Account2\",\"Test Category\""), 0, nil, nil, nil, nil, nil)
|
"Transfer,10.00,2024-09-01T12:34:56+08:00,USD,EUR,\"Test Account\",\"Test Account2\",\"Test Category\""), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
|
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
assert.Equal(t, 1, len(allNewTransactions))
|
assert.Equal(t, 1, len(allNewTransactions))
|
||||||
@@ -223,8 +225,8 @@ func TestFireFlyIIICsvFileConverterParseImportedData_ParseValidForeignAmountAndC
|
|||||||
assert.Equal(t, "USD", allNewTransactions[0].OriginalSourceAccountCurrency)
|
assert.Equal(t, "USD", allNewTransactions[0].OriginalSourceAccountCurrency)
|
||||||
assert.Equal(t, "EUR", allNewTransactions[0].OriginalDestinationAccountCurrency)
|
assert.Equal(t, "EUR", allNewTransactions[0].OriginalDestinationAccountCurrency)
|
||||||
|
|
||||||
allNewTransactions, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte("type,amount,date,currency_code,foreign_currency_code,source_name,destination_name,category\n"+
|
allNewTransactions, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte("type,amount,date,currency_code,foreign_currency_code,source_name,destination_name,category\n"+
|
||||||
"Transfer,10.00,2024-09-01T12:34:56+08:00,USD,,\"Test Account\",\"Test Account2\",\"Test Category\""), 0, nil, nil, nil, nil, nil)
|
"Transfer,10.00,2024-09-01T12:34:56+08:00,USD,,\"Test Account\",\"Test Account2\",\"Test Category\""), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
|
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
assert.Equal(t, 1, len(allNewTransactions))
|
assert.Equal(t, 1, len(allNewTransactions))
|
||||||
@@ -232,8 +234,8 @@ func TestFireFlyIIICsvFileConverterParseImportedData_ParseValidForeignAmountAndC
|
|||||||
assert.Equal(t, "USD", allNewTransactions[0].OriginalDestinationAccountCurrency)
|
assert.Equal(t, "USD", allNewTransactions[0].OriginalDestinationAccountCurrency)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestFireFlyIIICsvFileConverterParseImportedData_ParseInvalidAccountCurrency(t *testing.T) {
|
func TestFireFlyIIICsvFileimporterParseImportedData_ParseInvalidAccountCurrency(t *testing.T) {
|
||||||
converter := FireflyIIITransactionDataCsvFileImporter
|
importer := FireflyIIITransactionDataCsvFileImporter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
@@ -241,19 +243,19 @@ func TestFireFlyIIICsvFileConverterParseImportedData_ParseInvalidAccountCurrency
|
|||||||
DefaultCurrency: "CNY",
|
DefaultCurrency: "CNY",
|
||||||
}
|
}
|
||||||
|
|
||||||
_, _, _, _, _, _, err := converter.ParseImportedData(context, user, []byte("type,amount,foreign_amount,date,currency_code,foreign_currency_code,source_name,destination_name,category\n"+
|
_, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte("type,amount,foreign_amount,date,currency_code,foreign_currency_code,source_name,destination_name,category\n"+
|
||||||
"\"Opening balance\",123.45,,2024-09-01T00:00:00+08:00,USD,,\"Initial balance for \"\"Test Account\"\"\",\"Test Account\",\n"+
|
"\"Opening balance\",123.45,,2024-09-01T00:00:00+08:00,USD,,\"Initial balance for \"\"Test Account\"\"\",\"Test Account\",\n"+
|
||||||
"Transfer,1.23,1.10,2024-09-01T23:59:59+08:00,CNY,EUR,\"Test Account\",\"Test Account2\",\"Test Category3\""), 0, nil, nil, nil, nil, nil)
|
"Transfer,1.23,1.10,2024-09-01T23:59:59+08:00,CNY,EUR,\"Test Account\",\"Test Account2\",\"Test Category3\""), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrAccountCurrencyInvalid.Message)
|
assert.EqualError(t, err, errs.ErrAccountCurrencyInvalid.Message)
|
||||||
|
|
||||||
_, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte("type,amount,foreign_amount,date,currency_code,foreign_currency_code,source_name,destination_name,category\n"+
|
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte("type,amount,foreign_amount,date,currency_code,foreign_currency_code,source_name,destination_name,category\n"+
|
||||||
"\"Opening balance\",123.45,,2024-09-01T00:00:00+08:00,USD,,\"Initial balance for \"\"Test Account\"\"\",\"Test Account\",\n"+
|
"\"Opening balance\",123.45,,2024-09-01T00:00:00+08:00,USD,,\"Initial balance for \"\"Test Account\"\"\",\"Test Account\",\n"+
|
||||||
"Transfer,1.23,1.10,2024-09-01T23:59:59+08:00,CNY,EUR,\"Test Account2\",\"Test Account\",\"Test Category3\""), 0, nil, nil, nil, nil, nil)
|
"Transfer,1.23,1.10,2024-09-01T23:59:59+08:00,CNY,EUR,\"Test Account2\",\"Test Account\",\"Test Category3\""), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrAccountCurrencyInvalid.Message)
|
assert.EqualError(t, err, errs.ErrAccountCurrencyInvalid.Message)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestFireFlyIIICsvFileConverterParseImportedData_ParseNotSupportedCurrency(t *testing.T) {
|
func TestFireFlyIIICsvFileimporterParseImportedData_ParseNotSupportedCurrency(t *testing.T) {
|
||||||
converter := FireflyIIITransactionDataCsvFileImporter
|
importer := FireflyIIITransactionDataCsvFileImporter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
@@ -261,17 +263,17 @@ func TestFireFlyIIICsvFileConverterParseImportedData_ParseNotSupportedCurrency(t
|
|||||||
DefaultCurrency: "CNY",
|
DefaultCurrency: "CNY",
|
||||||
}
|
}
|
||||||
|
|
||||||
_, _, _, _, _, _, err := converter.ParseImportedData(context, user, []byte("type,amount,foreign_amount,date,currency_code,foreign_currency_code,source_name,destination_name,category\n"+
|
_, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte("type,amount,foreign_amount,date,currency_code,foreign_currency_code,source_name,destination_name,category\n"+
|
||||||
"\"Opening balance\",123.45,,2024-09-01T00:00:00+08:00,XXX,,\"Initial balance for \"\"Test Account\"\"\",\"Test Account\",\n"), 0, nil, nil, nil, nil, nil)
|
"\"Opening balance\",123.45,,2024-09-01T00:00:00+08:00,XXX,,\"Initial balance for \"\"Test Account\"\"\",\"Test Account\",\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrAccountCurrencyInvalid.Message)
|
assert.EqualError(t, err, errs.ErrAccountCurrencyInvalid.Message)
|
||||||
|
|
||||||
_, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte("type,amount,foreign_amount,date,currency_code,foreign_currency_code,source_name,destination_name,category\n"+
|
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte("type,amount,foreign_amount,date,currency_code,foreign_currency_code,source_name,destination_name,category\n"+
|
||||||
"Transfer,123.45,123.45,2024-09-01T23:59:59+08:00,USD,XXX,\"Test Account\",\"Test Account2\",\"Test Category2\""), 0, nil, nil, nil, nil, nil)
|
"Transfer,123.45,123.45,2024-09-01T23:59:59+08:00,USD,XXX,\"Test Account\",\"Test Account2\",\"Test Category2\""), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrAccountCurrencyInvalid.Message)
|
assert.EqualError(t, err, errs.ErrAccountCurrencyInvalid.Message)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestFireFlyIIICsvFileConverterParseImportedData_ParseInvalidAmount(t *testing.T) {
|
func TestFireFlyIIICsvFileimporterParseImportedData_ParseInvalidAmount(t *testing.T) {
|
||||||
converter := FireflyIIITransactionDataCsvFileImporter
|
importer := FireflyIIITransactionDataCsvFileImporter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
@@ -279,17 +281,17 @@ func TestFireFlyIIICsvFileConverterParseImportedData_ParseInvalidAmount(t *testi
|
|||||||
DefaultCurrency: "CNY",
|
DefaultCurrency: "CNY",
|
||||||
}
|
}
|
||||||
|
|
||||||
_, _, _, _, _, _, err := converter.ParseImportedData(context, user, []byte("type,amount,date,source_name,destination_name,category\n"+
|
_, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte("type,amount,date,source_name,destination_name,category\n"+
|
||||||
"Withdrawal,-123 45,2024-09-01T12:34:56+08:00,\"Test Account\",\"A expense account\",\"Test Category\"\n"), 0, nil, nil, nil, nil, nil)
|
"Withdrawal,-123 45,2024-09-01T12:34:56+08:00,\"Test Account\",\"A expense account\",\"Test Category\"\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrAmountInvalid.Message)
|
assert.EqualError(t, err, errs.ErrAmountInvalid.Message)
|
||||||
|
|
||||||
_, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte("type,amount,foreign_amount,date,source_name,destination_name,category\n"+
|
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte("type,amount,foreign_amount,date,source_name,destination_name,category\n"+
|
||||||
"Transfer,123.45,123 45,2024-09-01T23:59:59+08:00,\"Test Account\",\"Test Account2\",\"Test Category2\""), 0, nil, nil, nil, nil, nil)
|
"Transfer,123.45,123 45,2024-09-01T23:59:59+08:00,\"Test Account\",\"Test Account2\",\"Test Category2\""), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrAmountInvalid.Message)
|
assert.EqualError(t, err, errs.ErrAmountInvalid.Message)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestFireFlyIIICsvFileConverterParseImportedData_ParseDescription(t *testing.T) {
|
func TestFireFlyIIICsvFileimporterParseImportedData_ParseDescription(t *testing.T) {
|
||||||
converter := FireflyIIITransactionDataCsvFileImporter
|
importer := FireflyIIITransactionDataCsvFileImporter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
@@ -297,16 +299,16 @@ func TestFireFlyIIICsvFileConverterParseImportedData_ParseDescription(t *testing
|
|||||||
DefaultCurrency: "CNY",
|
DefaultCurrency: "CNY",
|
||||||
}
|
}
|
||||||
|
|
||||||
allNewTransactions, _, _, _, _, _, err := converter.ParseImportedData(context, user, []byte("type,amount,description,date,source_name,destination_name,category\n"+
|
allNewTransactions, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte("type,amount,description,date,source_name,destination_name,category\n"+
|
||||||
"Withdrawal,-123.45,\"foo bar\t#test\",2024-09-01T12:34:56+08:00,\"Test Account\",\"A expense account\",\"Test Category\"\n"), 0, nil, nil, nil, nil, nil)
|
"Withdrawal,-123.45,\"foo bar\t#test\",2024-09-01T12:34:56+08:00,\"Test Account\",\"A expense account\",\"Test Category\"\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
|
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
assert.Equal(t, 1, len(allNewTransactions))
|
assert.Equal(t, 1, len(allNewTransactions))
|
||||||
assert.Equal(t, "foo bar\t#test", allNewTransactions[0].Comment)
|
assert.Equal(t, "foo bar\t#test", allNewTransactions[0].Comment)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestFireFlyIIICsvFileConverterParseImportedData_ParseTags(t *testing.T) {
|
func TestFireFlyIIICsvFileimporterParseImportedData_ParseTags(t *testing.T) {
|
||||||
converter := FireflyIIITransactionDataCsvFileImporter
|
importer := FireflyIIITransactionDataCsvFileImporter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
@@ -314,8 +316,8 @@ func TestFireFlyIIICsvFileConverterParseImportedData_ParseTags(t *testing.T) {
|
|||||||
DefaultCurrency: "CNY",
|
DefaultCurrency: "CNY",
|
||||||
}
|
}
|
||||||
|
|
||||||
allNewTransactions, _, _, _, _, allNewTags, err := converter.ParseImportedData(context, user, []byte("type,amount,tags,date,source_name,destination_name,category\n"+
|
allNewTransactions, _, _, _, _, allNewTags, err := importer.ParseImportedData(context, user, []byte("type,amount,tags,date,source_name,destination_name,category\n"+
|
||||||
"Withdrawal,-123.45,\"tag1,tag2,tag3\",2024-09-01T12:34:56+08:00,\"Test Account\",\"A expense account\",\"Test Category\"\n"), 0, nil, nil, nil, nil, nil)
|
"Withdrawal,-123.45,\"tag1,tag2,tag3\",2024-09-01T12:34:56+08:00,\"Test Account\",\"A expense account\",\"Test Category\"\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
|
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
assert.Equal(t, 1, len(allNewTransactions))
|
assert.Equal(t, 1, len(allNewTransactions))
|
||||||
@@ -328,8 +330,8 @@ func TestFireFlyIIICsvFileConverterParseImportedData_ParseTags(t *testing.T) {
|
|||||||
assert.Equal(t, "tag3", allNewTags[2].Name)
|
assert.Equal(t, "tag3", allNewTags[2].Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestFireFlyIIICsvFileConverterParseImportedData_MissingFileHeader(t *testing.T) {
|
func TestFireFlyIIICsvFileimporterParseImportedData_MissingFileHeader(t *testing.T) {
|
||||||
converter := FireflyIIITransactionDataCsvFileImporter
|
importer := FireflyIIITransactionDataCsvFileImporter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
@@ -337,12 +339,12 @@ func TestFireFlyIIICsvFileConverterParseImportedData_MissingFileHeader(t *testin
|
|||||||
DefaultCurrency: "CNY",
|
DefaultCurrency: "CNY",
|
||||||
}
|
}
|
||||||
|
|
||||||
_, _, _, _, _, _, err := converter.ParseImportedData(context, user, []byte(""), 0, nil, nil, nil, nil, nil)
|
_, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(""), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrNotFoundTransactionDataInFile.Message)
|
assert.EqualError(t, err, errs.ErrNotFoundTransactionDataInFile.Message)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestFireFlyIIICsvFileConverterParseImportedData_MissingRequiredColumn(t *testing.T) {
|
func TestFireFlyIIICsvFileimporterParseImportedData_MissingRequiredColumn(t *testing.T) {
|
||||||
converter := FireflyIIITransactionDataCsvFileImporter
|
importer := FireflyIIITransactionDataCsvFileImporter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
@@ -351,32 +353,32 @@ func TestFireFlyIIICsvFileConverterParseImportedData_MissingRequiredColumn(t *te
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Missing Time Column
|
// Missing Time Column
|
||||||
_, _, _, _, _, _, err := converter.ParseImportedData(context, user, []byte("type,amount,source_name,destination_name,category\n"+
|
_, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte("type,amount,source_name,destination_name,category\n"+
|
||||||
"\"Opening balance\",123.45,\"Initial balance for \"\"Test Account\"\"\",\"Test Account\",\n"), 0, nil, nil, nil, nil, nil)
|
"\"Opening balance\",123.45,\"Initial balance for \"\"Test Account\"\"\",\"Test Account\",\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrMissingRequiredFieldInHeaderRow.Message)
|
assert.EqualError(t, err, errs.ErrMissingRequiredFieldInHeaderRow.Message)
|
||||||
|
|
||||||
// Missing Type Column
|
// Missing Type Column
|
||||||
_, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte("amount,date,source_name,destination_name,category\n"+
|
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte("amount,date,source_name,destination_name,category\n"+
|
||||||
"123.45,2024-09-01T00:00:00+08:00,\"Initial balance for \"\"Test Account\"\"\",\"Test Account\",\n"), 0, nil, nil, nil, nil, nil)
|
"123.45,2024-09-01T00:00:00+08:00,\"Initial balance for \"\"Test Account\"\"\",\"Test Account\",\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrMissingRequiredFieldInHeaderRow.Message)
|
assert.EqualError(t, err, errs.ErrMissingRequiredFieldInHeaderRow.Message)
|
||||||
|
|
||||||
// Missing Sub Category Column
|
// Missing Sub Category Column
|
||||||
_, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte("type,amount,date,source_name,destination_name\n"+
|
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte("type,amount,date,source_name,destination_name\n"+
|
||||||
"\"Opening balance\",123.45,2024-09-01T00:00:00+08:00,\"Initial balance for \"\"Test Account\"\"\",\"Test Account\"\n"), 0, nil, nil, nil, nil, nil)
|
"\"Opening balance\",123.45,2024-09-01T00:00:00+08:00,\"Initial balance for \"\"Test Account\"\"\",\"Test Account\"\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrMissingRequiredFieldInHeaderRow.Message)
|
assert.EqualError(t, err, errs.ErrMissingRequiredFieldInHeaderRow.Message)
|
||||||
|
|
||||||
// Missing Account Name Column
|
// Missing Account Name Column
|
||||||
_, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte("type,amount,date,destination_name,category\n"+
|
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte("type,amount,date,destination_name,category\n"+
|
||||||
"\"Opening balance\",123.45,2024-09-01T00:00:00+08:00,\"Test Account\",\n"), 0, nil, nil, nil, nil, nil)
|
"\"Opening balance\",123.45,2024-09-01T00:00:00+08:00,\"Test Account\",\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrMissingRequiredFieldInHeaderRow.Message)
|
assert.EqualError(t, err, errs.ErrMissingRequiredFieldInHeaderRow.Message)
|
||||||
|
|
||||||
// Missing Amount Column
|
// Missing Amount Column
|
||||||
_, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte("type,date,source_name,destination_name,category\n"+
|
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte("type,date,source_name,destination_name,category\n"+
|
||||||
"\"Opening balance\",2024-09-01T00:00:00+08:00,\"Initial balance for \"\"Test Account\"\"\",\"Test Account\",\n"), 0, nil, nil, nil, nil, nil)
|
"\"Opening balance\",2024-09-01T00:00:00+08:00,\"Initial balance for \"\"Test Account\"\"\",\"Test Account\",\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrMissingRequiredFieldInHeaderRow.Message)
|
assert.EqualError(t, err, errs.ErrMissingRequiredFieldInHeaderRow.Message)
|
||||||
|
|
||||||
// Missing Account2 Name Column
|
// Missing Account2 Name Column
|
||||||
_, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte("type,amount,date,source_name,category\n"+
|
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte("type,amount,date,source_name,category\n"+
|
||||||
"\"Opening balance\",123.45,2024-09-01T00:00:00+08:00,\"Initial balance for \"\"Test Account\"\"\",\n"), 0, nil, nil, nil, nil, nil)
|
"\"Opening balance\",123.45,2024-09-01T00:00:00+08:00,\"Initial balance for \"\"Test Account\"\"\",\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrMissingRequiredFieldInHeaderRow.Message)
|
assert.EqualError(t, err, errs.ErrMissingRequiredFieldInHeaderRow.Message)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ func (p *fireflyIIITransactionDataRowParser) Parse(data map[datatable.Transactio
|
|||||||
}
|
}
|
||||||
|
|
||||||
rowData[datatable.TRANSACTION_DATA_TABLE_TRANSACTION_TIME] = utils.FormatUnixTimeToLongDateTime(dateTime.Unix(), dateTime.Location())
|
rowData[datatable.TRANSACTION_DATA_TABLE_TRANSACTION_TIME] = utils.FormatUnixTimeToLongDateTime(dateTime.Unix(), dateTime.Location())
|
||||||
rowData[datatable.TRANSACTION_DATA_TABLE_TRANSACTION_TIMEZONE] = utils.FormatTimezoneOffset(dateTime.Location())
|
rowData[datatable.TRANSACTION_DATA_TABLE_TRANSACTION_TIMEZONE] = utils.FormatTimezoneOffset(dateTime.Unix(), dateTime.Location())
|
||||||
}
|
}
|
||||||
|
|
||||||
// trim trailing zero in decimal
|
// trim trailing zero in decimal
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
package gnucash
|
package gnucash
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/converters/converter"
|
"github.com/mayswind/ezbookkeeping/pkg/converters/converter"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/core"
|
"github.com/mayswind/ezbookkeeping/pkg/core"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/models"
|
"github.com/mayswind/ezbookkeeping/pkg/models"
|
||||||
@@ -24,7 +26,7 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// ParseImportedData returns the imported data by parsing the gnucash transaction data
|
// ParseImportedData returns the imported data by parsing the gnucash transaction data
|
||||||
func (c *gnucashTransactionDataImporter) ParseImportedData(ctx core.Context, user *models.User, data []byte, defaultTimezoneOffset int16, accountMap map[string]*models.Account, expenseCategoryMap map[string]map[string]*models.TransactionCategory, incomeCategoryMap map[string]map[string]*models.TransactionCategory, transferCategoryMap map[string]map[string]*models.TransactionCategory, tagMap map[string]*models.TransactionTag) (models.ImportedTransactionSlice, []*models.Account, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionTag, error) {
|
func (c *gnucashTransactionDataImporter) ParseImportedData(ctx core.Context, user *models.User, data []byte, defaultTimezone *time.Location, additionalOptions converter.TransactionDataImporterOptions, accountMap map[string]*models.Account, expenseCategoryMap map[string]map[string]*models.TransactionCategory, incomeCategoryMap map[string]map[string]*models.TransactionCategory, transferCategoryMap map[string]map[string]*models.TransactionCategory, tagMap map[string]*models.TransactionTag) (models.ImportedTransactionSlice, []*models.Account, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionTag, error) {
|
||||||
gnucashDataReader, err := createNewGnuCashDatabaseReader(data)
|
gnucashDataReader, err := createNewGnuCashDatabaseReader(data)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -45,5 +47,5 @@ func (c *gnucashTransactionDataImporter) ParseImportedData(ctx core.Context, use
|
|||||||
|
|
||||||
dataTableImporter := converter.CreateNewSimpleImporterWithTypeNameMapping(gnucashTransactionTypeNameMapping)
|
dataTableImporter := converter.CreateNewSimpleImporterWithTypeNameMapping(gnucashTransactionTypeNameMapping)
|
||||||
|
|
||||||
return dataTableImporter.ParseImportedData(ctx, user, transactionDataTable, defaultTimezoneOffset, accountMap, expenseCategoryMap, incomeCategoryMap, transferCategoryMap, tagMap)
|
return dataTableImporter.ParseImportedData(ctx, user, transactionDataTable, defaultTimezone, additionalOptions, accountMap, expenseCategoryMap, incomeCategoryMap, transferCategoryMap, tagMap)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,9 +4,11 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"compress/gzip"
|
"compress/gzip"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
|
"github.com/mayswind/ezbookkeeping/pkg/converters/converter"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/core"
|
"github.com/mayswind/ezbookkeeping/pkg/core"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/errs"
|
"github.com/mayswind/ezbookkeeping/pkg/errs"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/models"
|
"github.com/mayswind/ezbookkeeping/pkg/models"
|
||||||
@@ -185,7 +187,7 @@ const gnucashCommonValidDataCaseFooter = "</gnc:book>\n" +
|
|||||||
"</gnc-v2>\n"
|
"</gnc-v2>\n"
|
||||||
|
|
||||||
func TestGnuCashTransactionDatabaseFileParseImportedData_MinimumValidData(t *testing.T) {
|
func TestGnuCashTransactionDatabaseFileParseImportedData_MinimumValidData(t *testing.T) {
|
||||||
converter := GnuCashTransactionDataImporter
|
importer := GnuCashTransactionDataImporter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
@@ -193,14 +195,14 @@ func TestGnuCashTransactionDatabaseFileParseImportedData_MinimumValidData(t *tes
|
|||||||
DefaultCurrency: "CNY",
|
DefaultCurrency: "CNY",
|
||||||
}
|
}
|
||||||
|
|
||||||
allNewTransactions, allNewAccounts, allNewSubExpenseCategories, allNewSubIncomeCategories, allNewSubTransferCategories, allNewTags, err := converter.ParseImportedData(context, user, []byte(gnucashMinimumValidDataCase), 0, nil, nil, nil, nil, nil)
|
allNewTransactions, allNewAccounts, allNewSubExpenseCategories, allNewSubIncomeCategories, allNewSubTransferCategories, allNewTags, err := importer.ParseImportedData(context, user, []byte(gnucashMinimumValidDataCase), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
|
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
checkParsedMinimumValidData(t, allNewTransactions, allNewAccounts, allNewSubExpenseCategories, allNewSubIncomeCategories, allNewSubTransferCategories, allNewTags)
|
checkParsedMinimumValidData(t, allNewTransactions, allNewAccounts, allNewSubExpenseCategories, allNewSubIncomeCategories, allNewSubTransferCategories, allNewTags)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGnuCashTransactionDatabaseFileParseImportedData_GzippedMinimumValidData(t *testing.T) {
|
func TestGnuCashTransactionDatabaseFileParseImportedData_GzippedMinimumValidData(t *testing.T) {
|
||||||
converter := GnuCashTransactionDataImporter
|
importer := GnuCashTransactionDataImporter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
@@ -217,14 +219,14 @@ func TestGnuCashTransactionDatabaseFileParseImportedData_GzippedMinimumValidData
|
|||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
gzippedData := buffer.Bytes()
|
gzippedData := buffer.Bytes()
|
||||||
allNewTransactions, allNewAccounts, allNewSubExpenseCategories, allNewSubIncomeCategories, allNewSubTransferCategories, allNewTags, err := converter.ParseImportedData(context, user, gzippedData, 0, nil, nil, nil, nil, nil)
|
allNewTransactions, allNewAccounts, allNewSubExpenseCategories, allNewSubIncomeCategories, allNewSubTransferCategories, allNewTags, err := importer.ParseImportedData(context, user, gzippedData, time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
|
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
checkParsedMinimumValidData(t, allNewTransactions, allNewAccounts, allNewSubExpenseCategories, allNewSubIncomeCategories, allNewSubTransferCategories, allNewTags)
|
checkParsedMinimumValidData(t, allNewTransactions, allNewAccounts, allNewSubExpenseCategories, allNewSubIncomeCategories, allNewSubTransferCategories, allNewTags)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGnuCashTransactionDatabaseFileParseImportedData_MinimumValidDataWithReversedSplitOrder(t *testing.T) {
|
func TestGnuCashTransactionDatabaseFileParseImportedData_MinimumValidDataWithReversedSplitOrder(t *testing.T) {
|
||||||
converter := GnuCashTransactionDataImporter
|
importer := GnuCashTransactionDataImporter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
@@ -232,7 +234,7 @@ func TestGnuCashTransactionDatabaseFileParseImportedData_MinimumValidDataWithRev
|
|||||||
DefaultCurrency: "CNY",
|
DefaultCurrency: "CNY",
|
||||||
}
|
}
|
||||||
|
|
||||||
allNewTransactions, allNewAccounts, allNewSubExpenseCategories, allNewSubIncomeCategories, allNewSubTransferCategories, allNewTags, err := converter.ParseImportedData(context, user, []byte("<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n"+
|
allNewTransactions, allNewAccounts, allNewSubExpenseCategories, allNewSubIncomeCategories, allNewSubTransferCategories, allNewTags, err := importer.ParseImportedData(context, user, []byte("<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n"+
|
||||||
"<gnc-v2\n"+
|
"<gnc-v2\n"+
|
||||||
" xmlns:gnc=\"http://www.gnucash.org/XML/gnc\"\n"+
|
" xmlns:gnc=\"http://www.gnucash.org/XML/gnc\"\n"+
|
||||||
" xmlns:act=\"http://www.gnucash.org/XML/act\"\n"+
|
" xmlns:act=\"http://www.gnucash.org/XML/act\"\n"+
|
||||||
@@ -356,14 +358,14 @@ func TestGnuCashTransactionDatabaseFileParseImportedData_MinimumValidDataWithRev
|
|||||||
" </trn:splits>\n"+
|
" </trn:splits>\n"+
|
||||||
"</gnc:transaction>\n"+
|
"</gnc:transaction>\n"+
|
||||||
"</gnc:book>\n"+
|
"</gnc:book>\n"+
|
||||||
"</gnc-v2>\n"), 0, nil, nil, nil, nil, nil)
|
"</gnc-v2>\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
|
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
checkParsedMinimumValidData(t, allNewTransactions, allNewAccounts, allNewSubExpenseCategories, allNewSubIncomeCategories, allNewSubTransferCategories, allNewTags)
|
checkParsedMinimumValidData(t, allNewTransactions, allNewAccounts, allNewSubExpenseCategories, allNewSubIncomeCategories, allNewSubTransferCategories, allNewTags)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGnuCashTransactionDatabaseFileParseImportedData_ParseInvalidTime(t *testing.T) {
|
func TestGnuCashTransactionDatabaseFileParseImportedData_ParseInvalidTime(t *testing.T) {
|
||||||
converter := GnuCashTransactionDataImporter
|
importer := GnuCashTransactionDataImporter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
@@ -371,7 +373,7 @@ func TestGnuCashTransactionDatabaseFileParseImportedData_ParseInvalidTime(t *tes
|
|||||||
DefaultCurrency: "CNY",
|
DefaultCurrency: "CNY",
|
||||||
}
|
}
|
||||||
|
|
||||||
_, _, _, _, _, _, err := converter.ParseImportedData(context, user, []byte(
|
_, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(
|
||||||
gnucashCommonValidDataCaseHeader+
|
gnucashCommonValidDataCaseHeader+
|
||||||
"<gnc:transaction version=\"2.0.0\">\n"+
|
"<gnc:transaction version=\"2.0.0\">\n"+
|
||||||
" <trn:date-posted>\n"+
|
" <trn:date-posted>\n"+
|
||||||
@@ -388,10 +390,10 @@ func TestGnuCashTransactionDatabaseFileParseImportedData_ParseInvalidTime(t *tes
|
|||||||
" </trn:split>\n"+
|
" </trn:split>\n"+
|
||||||
" </trn:splits>\n"+
|
" </trn:splits>\n"+
|
||||||
"</gnc:transaction>\n"+
|
"</gnc:transaction>\n"+
|
||||||
gnucashCommonValidDataCaseFooter), 0, nil, nil, nil, nil, nil)
|
gnucashCommonValidDataCaseFooter), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrTransactionTimeInvalid.Message)
|
assert.EqualError(t, err, errs.ErrTransactionTimeInvalid.Message)
|
||||||
|
|
||||||
_, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte(
|
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(
|
||||||
gnucashCommonValidDataCaseHeader+
|
gnucashCommonValidDataCaseHeader+
|
||||||
"<gnc:transaction version=\"2.0.0\">\n"+
|
"<gnc:transaction version=\"2.0.0\">\n"+
|
||||||
" <trn:date-posted>\n"+
|
" <trn:date-posted>\n"+
|
||||||
@@ -408,12 +410,12 @@ func TestGnuCashTransactionDatabaseFileParseImportedData_ParseInvalidTime(t *tes
|
|||||||
" </trn:split>\n"+
|
" </trn:split>\n"+
|
||||||
" </trn:splits>\n"+
|
" </trn:splits>\n"+
|
||||||
"</gnc:transaction>\n"+
|
"</gnc:transaction>\n"+
|
||||||
gnucashCommonValidDataCaseFooter), 0, nil, nil, nil, nil, nil)
|
gnucashCommonValidDataCaseFooter), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrTransactionTimeInvalid.Message)
|
assert.EqualError(t, err, errs.ErrTransactionTimeInvalid.Message)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGnuCashTransactionDatabaseFileParseImportedData_ParseValidTimezone(t *testing.T) {
|
func TestGnuCashTransactionDatabaseFileParseImportedData_ParseValidTimezone(t *testing.T) {
|
||||||
converter := GnuCashTransactionDataImporter
|
importer := GnuCashTransactionDataImporter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
@@ -421,7 +423,7 @@ func TestGnuCashTransactionDatabaseFileParseImportedData_ParseValidTimezone(t *t
|
|||||||
DefaultCurrency: "CNY",
|
DefaultCurrency: "CNY",
|
||||||
}
|
}
|
||||||
|
|
||||||
allNewTransactions, _, _, _, _, _, err := converter.ParseImportedData(context, user, []byte(
|
allNewTransactions, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(
|
||||||
gnucashCommonValidDataCaseHeader+
|
gnucashCommonValidDataCaseHeader+
|
||||||
"<gnc:transaction version=\"2.0.0\">\n"+
|
"<gnc:transaction version=\"2.0.0\">\n"+
|
||||||
" <trn:date-posted>\n"+
|
" <trn:date-posted>\n"+
|
||||||
@@ -438,12 +440,12 @@ func TestGnuCashTransactionDatabaseFileParseImportedData_ParseValidTimezone(t *t
|
|||||||
" </trn:split>\n"+
|
" </trn:split>\n"+
|
||||||
" </trn:splits>\n"+
|
" </trn:splits>\n"+
|
||||||
"</gnc:transaction>\n"+
|
"</gnc:transaction>\n"+
|
||||||
gnucashCommonValidDataCaseFooter), 0, nil, nil, nil, nil, nil)
|
gnucashCommonValidDataCaseFooter), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
assert.Equal(t, 1, len(allNewTransactions))
|
assert.Equal(t, 1, len(allNewTransactions))
|
||||||
assert.Equal(t, int64(1725230096), utils.GetUnixTimeFromTransactionTime(allNewTransactions[0].TransactionTime))
|
assert.Equal(t, int64(1725230096), utils.GetUnixTimeFromTransactionTime(allNewTransactions[0].TransactionTime))
|
||||||
|
|
||||||
allNewTransactions, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte(
|
allNewTransactions, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(
|
||||||
gnucashCommonValidDataCaseHeader+
|
gnucashCommonValidDataCaseHeader+
|
||||||
"<gnc:transaction version=\"2.0.0\">\n"+
|
"<gnc:transaction version=\"2.0.0\">\n"+
|
||||||
" <trn:date-posted>\n"+
|
" <trn:date-posted>\n"+
|
||||||
@@ -460,14 +462,14 @@ func TestGnuCashTransactionDatabaseFileParseImportedData_ParseValidTimezone(t *t
|
|||||||
" </trn:split>\n"+
|
" </trn:split>\n"+
|
||||||
" </trn:splits>\n"+
|
" </trn:splits>\n"+
|
||||||
"</gnc:transaction>\n"+
|
"</gnc:transaction>\n"+
|
||||||
gnucashCommonValidDataCaseFooter), 0, nil, nil, nil, nil, nil)
|
gnucashCommonValidDataCaseFooter), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
assert.Equal(t, 1, len(allNewTransactions))
|
assert.Equal(t, 1, len(allNewTransactions))
|
||||||
assert.Equal(t, int64(1725148196), utils.GetUnixTimeFromTransactionTime(allNewTransactions[0].TransactionTime))
|
assert.Equal(t, int64(1725148196), utils.GetUnixTimeFromTransactionTime(allNewTransactions[0].TransactionTime))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGnuCashTransactionDatabaseFileParseImportedData_ParseValidAccountCurrency(t *testing.T) {
|
func TestGnuCashTransactionDatabaseFileParseImportedData_ParseValidAccountCurrency(t *testing.T) {
|
||||||
converter := GnuCashTransactionDataImporter
|
importer := GnuCashTransactionDataImporter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
@@ -475,7 +477,7 @@ func TestGnuCashTransactionDatabaseFileParseImportedData_ParseValidAccountCurren
|
|||||||
DefaultCurrency: "CNY",
|
DefaultCurrency: "CNY",
|
||||||
}
|
}
|
||||||
|
|
||||||
allNewTransactions, allNewAccounts, _, _, _, _, err := converter.ParseImportedData(context, user, []byte("<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n"+
|
allNewTransactions, allNewAccounts, _, _, _, _, err := importer.ParseImportedData(context, user, []byte("<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n"+
|
||||||
"<gnc-v2\n"+
|
"<gnc-v2\n"+
|
||||||
" xmlns:gnc=\"http://www.gnucash.org/XML/gnc\"\n"+
|
" xmlns:gnc=\"http://www.gnucash.org/XML/gnc\"\n"+
|
||||||
" xmlns:act=\"http://www.gnucash.org/XML/act\"\n"+
|
" xmlns:act=\"http://www.gnucash.org/XML/act\"\n"+
|
||||||
@@ -557,7 +559,7 @@ func TestGnuCashTransactionDatabaseFileParseImportedData_ParseValidAccountCurren
|
|||||||
" </trn:splits>\n"+
|
" </trn:splits>\n"+
|
||||||
"</gnc:transaction>\n"+
|
"</gnc:transaction>\n"+
|
||||||
"</gnc:book>\n"+
|
"</gnc:book>\n"+
|
||||||
"</gnc-v2>\n"), 0, nil, nil, nil, nil, nil)
|
"</gnc-v2>\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
|
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
@@ -574,7 +576,7 @@ func TestGnuCashTransactionDatabaseFileParseImportedData_ParseValidAccountCurren
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestGnuCashTransactionDatabaseFileParseImportedData_ParseValidAmount(t *testing.T) {
|
func TestGnuCashTransactionDatabaseFileParseImportedData_ParseValidAmount(t *testing.T) {
|
||||||
converter := GnuCashTransactionDataImporter
|
importer := GnuCashTransactionDataImporter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
@@ -582,7 +584,7 @@ func TestGnuCashTransactionDatabaseFileParseImportedData_ParseValidAmount(t *tes
|
|||||||
DefaultCurrency: "CNY",
|
DefaultCurrency: "CNY",
|
||||||
}
|
}
|
||||||
|
|
||||||
allNewTransactions, _, _, _, _, _, err := converter.ParseImportedData(context, user, []byte(
|
allNewTransactions, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(
|
||||||
gnucashCommonValidDataCaseHeader+
|
gnucashCommonValidDataCaseHeader+
|
||||||
"<gnc:transaction version=\"2.0.0\">\n"+
|
"<gnc:transaction version=\"2.0.0\">\n"+
|
||||||
" <trn:date-posted>\n"+
|
" <trn:date-posted>\n"+
|
||||||
@@ -599,13 +601,13 @@ func TestGnuCashTransactionDatabaseFileParseImportedData_ParseValidAmount(t *tes
|
|||||||
" </trn:split>\n"+
|
" </trn:split>\n"+
|
||||||
" </trn:splits>\n"+
|
" </trn:splits>\n"+
|
||||||
"</gnc:transaction>\n"+
|
"</gnc:transaction>\n"+
|
||||||
gnucashCommonValidDataCaseFooter), 0, nil, nil, nil, nil, nil)
|
gnucashCommonValidDataCaseFooter), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
|
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
assert.Equal(t, 1, len(allNewTransactions))
|
assert.Equal(t, 1, len(allNewTransactions))
|
||||||
assert.Equal(t, int64(1234500), allNewTransactions[0].Amount)
|
assert.Equal(t, int64(1234500), allNewTransactions[0].Amount)
|
||||||
|
|
||||||
allNewTransactions, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte(
|
allNewTransactions, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(
|
||||||
gnucashCommonValidDataCaseHeader+
|
gnucashCommonValidDataCaseHeader+
|
||||||
"<gnc:transaction version=\"2.0.0\">\n"+
|
"<gnc:transaction version=\"2.0.0\">\n"+
|
||||||
" <trn:date-posted>\n"+
|
" <trn:date-posted>\n"+
|
||||||
@@ -622,7 +624,7 @@ func TestGnuCashTransactionDatabaseFileParseImportedData_ParseValidAmount(t *tes
|
|||||||
" </trn:split>\n"+
|
" </trn:split>\n"+
|
||||||
" </trn:splits>\n"+
|
" </trn:splits>\n"+
|
||||||
"</gnc:transaction>\n"+
|
"</gnc:transaction>\n"+
|
||||||
gnucashCommonValidDataCaseFooter), 0, nil, nil, nil, nil, nil)
|
gnucashCommonValidDataCaseFooter), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
|
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
assert.Equal(t, 1, len(allNewTransactions))
|
assert.Equal(t, 1, len(allNewTransactions))
|
||||||
@@ -630,7 +632,7 @@ func TestGnuCashTransactionDatabaseFileParseImportedData_ParseValidAmount(t *tes
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestGnuCashTransactionDatabaseFileParseImportedData_ParseInvalidAmount(t *testing.T) {
|
func TestGnuCashTransactionDatabaseFileParseImportedData_ParseInvalidAmount(t *testing.T) {
|
||||||
converter := GnuCashTransactionDataImporter
|
importer := GnuCashTransactionDataImporter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
@@ -638,7 +640,7 @@ func TestGnuCashTransactionDatabaseFileParseImportedData_ParseInvalidAmount(t *t
|
|||||||
DefaultCurrency: "CNY",
|
DefaultCurrency: "CNY",
|
||||||
}
|
}
|
||||||
|
|
||||||
_, _, _, _, _, _, err := converter.ParseImportedData(context, user, []byte(
|
_, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(
|
||||||
gnucashCommonValidDataCaseHeader+
|
gnucashCommonValidDataCaseHeader+
|
||||||
"<gnc:transaction version=\"2.0.0\">\n"+
|
"<gnc:transaction version=\"2.0.0\">\n"+
|
||||||
" <trn:date-posted>\n"+
|
" <trn:date-posted>\n"+
|
||||||
@@ -655,10 +657,10 @@ func TestGnuCashTransactionDatabaseFileParseImportedData_ParseInvalidAmount(t *t
|
|||||||
" </trn:split>\n"+
|
" </trn:split>\n"+
|
||||||
" </trn:splits>\n"+
|
" </trn:splits>\n"+
|
||||||
"</gnc:transaction>\n"+
|
"</gnc:transaction>\n"+
|
||||||
gnucashCommonValidDataCaseFooter), 0, nil, nil, nil, nil, nil)
|
gnucashCommonValidDataCaseFooter), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrAmountInvalid.Message)
|
assert.EqualError(t, err, errs.ErrAmountInvalid.Message)
|
||||||
|
|
||||||
_, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte(
|
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(
|
||||||
gnucashCommonValidDataCaseHeader+
|
gnucashCommonValidDataCaseHeader+
|
||||||
"<gnc:transaction version=\"2.0.0\">\n"+
|
"<gnc:transaction version=\"2.0.0\">\n"+
|
||||||
" <trn:date-posted>\n"+
|
" <trn:date-posted>\n"+
|
||||||
@@ -675,10 +677,10 @@ func TestGnuCashTransactionDatabaseFileParseImportedData_ParseInvalidAmount(t *t
|
|||||||
" </trn:split>\n"+
|
" </trn:split>\n"+
|
||||||
" </trn:splits>\n"+
|
" </trn:splits>\n"+
|
||||||
"</gnc:transaction>\n"+
|
"</gnc:transaction>\n"+
|
||||||
gnucashCommonValidDataCaseFooter), 0, nil, nil, nil, nil, nil)
|
gnucashCommonValidDataCaseFooter), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrAmountInvalid.Message)
|
assert.EqualError(t, err, errs.ErrAmountInvalid.Message)
|
||||||
|
|
||||||
_, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte(
|
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(
|
||||||
gnucashCommonValidDataCaseHeader+
|
gnucashCommonValidDataCaseHeader+
|
||||||
"<gnc:transaction version=\"2.0.0\">\n"+
|
"<gnc:transaction version=\"2.0.0\">\n"+
|
||||||
" <trn:date-posted>\n"+
|
" <trn:date-posted>\n"+
|
||||||
@@ -695,12 +697,12 @@ func TestGnuCashTransactionDatabaseFileParseImportedData_ParseInvalidAmount(t *t
|
|||||||
" </trn:split>\n"+
|
" </trn:split>\n"+
|
||||||
" </trn:splits>\n"+
|
" </trn:splits>\n"+
|
||||||
"</gnc:transaction>\n"+
|
"</gnc:transaction>\n"+
|
||||||
gnucashCommonValidDataCaseFooter), 0, nil, nil, nil, nil, nil)
|
gnucashCommonValidDataCaseFooter), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrAmountInvalid.Message)
|
assert.EqualError(t, err, errs.ErrAmountInvalid.Message)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGnuCashTransactionDatabaseFileParseImportedData_ParseDescription(t *testing.T) {
|
func TestGnuCashTransactionDatabaseFileParseImportedData_ParseDescription(t *testing.T) {
|
||||||
converter := GnuCashTransactionDataImporter
|
importer := GnuCashTransactionDataImporter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
@@ -708,7 +710,7 @@ func TestGnuCashTransactionDatabaseFileParseImportedData_ParseDescription(t *tes
|
|||||||
DefaultCurrency: "CNY",
|
DefaultCurrency: "CNY",
|
||||||
}
|
}
|
||||||
|
|
||||||
allNewTransactions, _, _, _, _, _, err := converter.ParseImportedData(context, user, []byte(
|
allNewTransactions, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(
|
||||||
gnucashCommonValidDataCaseHeader+
|
gnucashCommonValidDataCaseHeader+
|
||||||
"<gnc:transaction version=\"2.0.0\">\n"+
|
"<gnc:transaction version=\"2.0.0\">\n"+
|
||||||
" <trn:date-posted>\n"+
|
" <trn:date-posted>\n"+
|
||||||
@@ -726,7 +728,7 @@ func TestGnuCashTransactionDatabaseFileParseImportedData_ParseDescription(t *tes
|
|||||||
" </trn:split>\n"+
|
" </trn:split>\n"+
|
||||||
" </trn:splits>\n"+
|
" </trn:splits>\n"+
|
||||||
"</gnc:transaction>\n"+
|
"</gnc:transaction>\n"+
|
||||||
gnucashCommonValidDataCaseFooter), 0, nil, nil, nil, nil, nil)
|
gnucashCommonValidDataCaseFooter), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
|
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
assert.Equal(t, 1, len(allNewTransactions))
|
assert.Equal(t, 1, len(allNewTransactions))
|
||||||
@@ -734,7 +736,7 @@ func TestGnuCashTransactionDatabaseFileParseImportedData_ParseDescription(t *tes
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestGnuCashTransactionDatabaseFileParseImportedData_SkipZeroAmountTransaction(t *testing.T) {
|
func TestGnuCashTransactionDatabaseFileParseImportedData_SkipZeroAmountTransaction(t *testing.T) {
|
||||||
converter := GnuCashTransactionDataImporter
|
importer := GnuCashTransactionDataImporter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
@@ -742,7 +744,7 @@ func TestGnuCashTransactionDatabaseFileParseImportedData_SkipZeroAmountTransacti
|
|||||||
DefaultCurrency: "CNY",
|
DefaultCurrency: "CNY",
|
||||||
}
|
}
|
||||||
|
|
||||||
_, _, _, _, _, _, err := converter.ParseImportedData(context, user, []byte(
|
_, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(
|
||||||
gnucashCommonValidDataCaseHeader+
|
gnucashCommonValidDataCaseHeader+
|
||||||
"<gnc:transaction version=\"2.0.0\">\n"+
|
"<gnc:transaction version=\"2.0.0\">\n"+
|
||||||
" <trn:date-posted>\n"+
|
" <trn:date-posted>\n"+
|
||||||
@@ -755,12 +757,12 @@ func TestGnuCashTransactionDatabaseFileParseImportedData_SkipZeroAmountTransacti
|
|||||||
" </trn:split>\n"+
|
" </trn:split>\n"+
|
||||||
" </trn:splits>\n"+
|
" </trn:splits>\n"+
|
||||||
"</gnc:transaction>\n"+
|
"</gnc:transaction>\n"+
|
||||||
gnucashCommonValidDataCaseFooter), 0, nil, nil, nil, nil, nil)
|
gnucashCommonValidDataCaseFooter), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrNotFoundTransactionDataInFile.Message)
|
assert.EqualError(t, err, errs.ErrNotFoundTransactionDataInFile.Message)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGnuCashTransactionDatabaseFileParseImportedData_NotSupportedToParseSplitTransaction(t *testing.T) {
|
func TestGnuCashTransactionDatabaseFileParseImportedData_NotSupportedToParseSplitTransaction(t *testing.T) {
|
||||||
converter := GnuCashTransactionDataImporter
|
importer := GnuCashTransactionDataImporter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
@@ -768,7 +770,7 @@ func TestGnuCashTransactionDatabaseFileParseImportedData_NotSupportedToParseSpli
|
|||||||
DefaultCurrency: "CNY",
|
DefaultCurrency: "CNY",
|
||||||
}
|
}
|
||||||
|
|
||||||
_, _, _, _, _, _, err := converter.ParseImportedData(context, user, []byte(
|
_, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(
|
||||||
gnucashCommonValidDataCaseHeader+
|
gnucashCommonValidDataCaseHeader+
|
||||||
"<gnc:account version=\"2.0.0\">\n"+
|
"<gnc:account version=\"2.0.0\">\n"+
|
||||||
" <act:name>Test Category2</act:name>\n"+
|
" <act:name>Test Category2</act:name>\n"+
|
||||||
@@ -805,12 +807,12 @@ func TestGnuCashTransactionDatabaseFileParseImportedData_NotSupportedToParseSpli
|
|||||||
" </trn:split>\n"+
|
" </trn:split>\n"+
|
||||||
" </trn:splits>\n"+
|
" </trn:splits>\n"+
|
||||||
"</gnc:transaction>\n"+
|
"</gnc:transaction>\n"+
|
||||||
gnucashCommonValidDataCaseFooter), 0, nil, nil, nil, nil, nil)
|
gnucashCommonValidDataCaseFooter), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrNotSupportedSplitTransactions.Message)
|
assert.EqualError(t, err, errs.ErrNotSupportedSplitTransactions.Message)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGnuCashTransactionDatabaseFileParseImportedData_MissingAccountRequiredNode(t *testing.T) {
|
func TestGnuCashTransactionDatabaseFileParseImportedData_MissingAccountRequiredNode(t *testing.T) {
|
||||||
converter := GnuCashTransactionDataImporter
|
importer := GnuCashTransactionDataImporter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
@@ -819,7 +821,7 @@ func TestGnuCashTransactionDatabaseFileParseImportedData_MissingAccountRequiredN
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Missing Account Currency Node
|
// Missing Account Currency Node
|
||||||
_, _, _, _, _, _, err := converter.ParseImportedData(context, user, []byte(
|
_, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(
|
||||||
"<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n"+
|
"<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n"+
|
||||||
"<gnc-v2\n"+
|
"<gnc-v2\n"+
|
||||||
" xmlns:gnc=\"http://www.gnucash.org/XML/gnc\"\n"+
|
" xmlns:gnc=\"http://www.gnucash.org/XML/gnc\"\n"+
|
||||||
@@ -872,12 +874,12 @@ func TestGnuCashTransactionDatabaseFileParseImportedData_MissingAccountRequiredN
|
|||||||
" </trn:split>\n"+
|
" </trn:split>\n"+
|
||||||
" </trn:splits>\n"+
|
" </trn:splits>\n"+
|
||||||
"</gnc:transaction>\n"+
|
"</gnc:transaction>\n"+
|
||||||
gnucashCommonValidDataCaseFooter), 0, nil, nil, nil, nil, nil)
|
gnucashCommonValidDataCaseFooter), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrAccountCurrencyInvalid.Message)
|
assert.EqualError(t, err, errs.ErrAccountCurrencyInvalid.Message)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGnuCashTransactionDatabaseFileParseImportedData_MissingTransactionRequiredNode(t *testing.T) {
|
func TestGnuCashTransactionDatabaseFileParseImportedData_MissingTransactionRequiredNode(t *testing.T) {
|
||||||
converter := GnuCashTransactionDataImporter
|
importer := GnuCashTransactionDataImporter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
@@ -886,7 +888,7 @@ func TestGnuCashTransactionDatabaseFileParseImportedData_MissingTransactionRequi
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Missing Transaction Time Node
|
// Missing Transaction Time Node
|
||||||
_, _, _, _, _, _, err := converter.ParseImportedData(context, user, []byte(
|
_, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(
|
||||||
gnucashCommonValidDataCaseHeader+
|
gnucashCommonValidDataCaseHeader+
|
||||||
"<gnc:transaction version=\"2.0.0\">\n"+
|
"<gnc:transaction version=\"2.0.0\">\n"+
|
||||||
" <trn:splits>\n"+
|
" <trn:splits>\n"+
|
||||||
@@ -900,22 +902,22 @@ func TestGnuCashTransactionDatabaseFileParseImportedData_MissingTransactionRequi
|
|||||||
" </trn:split>\n"+
|
" </trn:split>\n"+
|
||||||
" </trn:splits>\n"+
|
" </trn:splits>\n"+
|
||||||
"</gnc:transaction>\n"+
|
"</gnc:transaction>\n"+
|
||||||
gnucashCommonValidDataCaseFooter), 0, nil, nil, nil, nil, nil)
|
gnucashCommonValidDataCaseFooter), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrMissingTransactionTime.Message)
|
assert.EqualError(t, err, errs.ErrMissingTransactionTime.Message)
|
||||||
|
|
||||||
// Missing Transaction Splits Node
|
// Missing Transaction Splits Node
|
||||||
_, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte(
|
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(
|
||||||
gnucashCommonValidDataCaseHeader+
|
gnucashCommonValidDataCaseHeader+
|
||||||
"<gnc:transaction version=\"2.0.0\">\n"+
|
"<gnc:transaction version=\"2.0.0\">\n"+
|
||||||
" <trn:date-posted>\n"+
|
" <trn:date-posted>\n"+
|
||||||
" <ts:date>2024-09-01 00:00:00 +0000</ts:date>\n"+
|
" <ts:date>2024-09-01 00:00:00 +0000</ts:date>\n"+
|
||||||
" </trn:date-posted>\n"+
|
" </trn:date-posted>\n"+
|
||||||
"</gnc:transaction>\n"+
|
"</gnc:transaction>\n"+
|
||||||
gnucashCommonValidDataCaseFooter), 0, nil, nil, nil, nil, nil)
|
gnucashCommonValidDataCaseFooter), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrInvalidGnuCashFile.Message)
|
assert.EqualError(t, err, errs.ErrInvalidGnuCashFile.Message)
|
||||||
|
|
||||||
// Missing Transaction Split Quantity Node
|
// Missing Transaction Split Quantity Node
|
||||||
_, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte(
|
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(
|
||||||
gnucashCommonValidDataCaseHeader+
|
gnucashCommonValidDataCaseHeader+
|
||||||
"<gnc:transaction version=\"2.0.0\">\n"+
|
"<gnc:transaction version=\"2.0.0\">\n"+
|
||||||
" <trn:date-posted>\n"+
|
" <trn:date-posted>\n"+
|
||||||
@@ -930,11 +932,11 @@ func TestGnuCashTransactionDatabaseFileParseImportedData_MissingTransactionRequi
|
|||||||
" </trn:split>\n"+
|
" </trn:split>\n"+
|
||||||
" </trn:splits>\n"+
|
" </trn:splits>\n"+
|
||||||
"</gnc:transaction>\n"+
|
"</gnc:transaction>\n"+
|
||||||
gnucashCommonValidDataCaseFooter), 0, nil, nil, nil, nil, nil)
|
gnucashCommonValidDataCaseFooter), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrAmountInvalid.Message)
|
assert.EqualError(t, err, errs.ErrAmountInvalid.Message)
|
||||||
|
|
||||||
// Missing Transaction Split Account Node
|
// Missing Transaction Split Account Node
|
||||||
_, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte(
|
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(
|
||||||
gnucashCommonValidDataCaseHeader+
|
gnucashCommonValidDataCaseHeader+
|
||||||
"<gnc:transaction version=\"2.0.0\">\n"+
|
"<gnc:transaction version=\"2.0.0\">\n"+
|
||||||
" <trn:date-posted>\n"+
|
" <trn:date-posted>\n"+
|
||||||
@@ -950,7 +952,7 @@ func TestGnuCashTransactionDatabaseFileParseImportedData_MissingTransactionRequi
|
|||||||
" </trn:split>\n"+
|
" </trn:split>\n"+
|
||||||
" </trn:splits>\n"+
|
" </trn:splits>\n"+
|
||||||
"</gnc:transaction>\n"+
|
"</gnc:transaction>\n"+
|
||||||
gnucashCommonValidDataCaseFooter), 0, nil, nil, nil, nil, nil)
|
gnucashCommonValidDataCaseFooter), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrMissingAccountData.Message)
|
assert.EqualError(t, err, errs.ErrMissingAccountData.Message)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -124,7 +124,7 @@ func (t *gnucashTransactionDataRowIterator) parseTransaction(ctx core.Context, u
|
|||||||
}
|
}
|
||||||
|
|
||||||
data[datatable.TRANSACTION_DATA_TABLE_TRANSACTION_TIME] = utils.FormatUnixTimeToLongDateTime(dateTime.Unix(), dateTime.Location())
|
data[datatable.TRANSACTION_DATA_TABLE_TRANSACTION_TIME] = utils.FormatUnixTimeToLongDateTime(dateTime.Unix(), dateTime.Location())
|
||||||
data[datatable.TRANSACTION_DATA_TABLE_TRANSACTION_TIMEZONE] = utils.FormatTimezoneOffset(dateTime.Location())
|
data[datatable.TRANSACTION_DATA_TABLE_TRANSACTION_TIMEZONE] = utils.FormatTimezoneOffset(dateTime.Unix(), dateTime.Location())
|
||||||
|
|
||||||
if len(gnucashTransaction.Splits) == 2 {
|
if len(gnucashTransaction.Splits) == 2 {
|
||||||
splitData1 := gnucashTransaction.Splits[0]
|
splitData1 := gnucashTransaction.Splits[0]
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
package iif
|
package iif
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/converters/converter"
|
"github.com/mayswind/ezbookkeeping/pkg/converters/converter"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/core"
|
"github.com/mayswind/ezbookkeeping/pkg/core"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/models"
|
"github.com/mayswind/ezbookkeeping/pkg/models"
|
||||||
@@ -23,7 +25,7 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// ParseImportedData returns the imported data by parsing the intuit interchange format (iif) data
|
// ParseImportedData returns the imported data by parsing the intuit interchange format (iif) data
|
||||||
func (c *iifTransactionDataFileImporter) ParseImportedData(ctx core.Context, user *models.User, data []byte, defaultTimezoneOffset int16, accountMap map[string]*models.Account, expenseCategoryMap map[string]map[string]*models.TransactionCategory, incomeCategoryMap map[string]map[string]*models.TransactionCategory, transferCategoryMap map[string]map[string]*models.TransactionCategory, tagMap map[string]*models.TransactionTag) (models.ImportedTransactionSlice, []*models.Account, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionTag, error) {
|
func (c *iifTransactionDataFileImporter) ParseImportedData(ctx core.Context, user *models.User, data []byte, defaultTimezone *time.Location, additionalOptions converter.TransactionDataImporterOptions, accountMap map[string]*models.Account, expenseCategoryMap map[string]map[string]*models.TransactionCategory, incomeCategoryMap map[string]map[string]*models.TransactionCategory, transferCategoryMap map[string]map[string]*models.TransactionCategory, tagMap map[string]*models.TransactionTag) (models.ImportedTransactionSlice, []*models.Account, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionTag, error) {
|
||||||
iifDataReader := createNewIifDataReader(data)
|
iifDataReader := createNewIifDataReader(data)
|
||||||
accountDatasets, transactionDatasets, err := iifDataReader.read(ctx)
|
accountDatasets, transactionDatasets, err := iifDataReader.read(ctx)
|
||||||
|
|
||||||
@@ -39,5 +41,5 @@ func (c *iifTransactionDataFileImporter) ParseImportedData(ctx core.Context, use
|
|||||||
|
|
||||||
dataTableImporter := converter.CreateNewSimpleImporterWithTypeNameMapping(iifTransactionTypeNameMapping)
|
dataTableImporter := converter.CreateNewSimpleImporterWithTypeNameMapping(iifTransactionTypeNameMapping)
|
||||||
|
|
||||||
return dataTableImporter.ParseImportedData(ctx, user, transactionDataTable, defaultTimezoneOffset, accountMap, expenseCategoryMap, incomeCategoryMap, transferCategoryMap, tagMap)
|
return dataTableImporter.ParseImportedData(ctx, user, transactionDataTable, defaultTimezone, additionalOptions, accountMap, expenseCategoryMap, incomeCategoryMap, transferCategoryMap, tagMap)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,9 +2,11 @@ package iif
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
|
"github.com/mayswind/ezbookkeeping/pkg/converters/converter"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/core"
|
"github.com/mayswind/ezbookkeeping/pkg/core"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/errs"
|
"github.com/mayswind/ezbookkeeping/pkg/errs"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/models"
|
"github.com/mayswind/ezbookkeeping/pkg/models"
|
||||||
@@ -12,7 +14,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func TestIIFTransactionDataFileParseImportedData_MinimumValidData(t *testing.T) {
|
func TestIIFTransactionDataFileParseImportedData_MinimumValidData(t *testing.T) {
|
||||||
converter := IifTransactionDataFileImporter
|
importer := IifTransactionDataFileImporter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
@@ -20,7 +22,7 @@ func TestIIFTransactionDataFileParseImportedData_MinimumValidData(t *testing.T)
|
|||||||
DefaultCurrency: "CNY",
|
DefaultCurrency: "CNY",
|
||||||
}
|
}
|
||||||
|
|
||||||
allNewTransactions, allNewAccounts, allNewSubExpenseCategories, allNewSubIncomeCategories, allNewSubTransferCategories, allNewTags, err := converter.ParseImportedData(context, user, []byte(
|
allNewTransactions, allNewAccounts, allNewSubExpenseCategories, allNewSubIncomeCategories, allNewSubTransferCategories, allNewTags, err := importer.ParseImportedData(context, user, []byte(
|
||||||
"!ACCNT\tNAME\tACCNTTYPE\n"+
|
"!ACCNT\tNAME\tACCNTTYPE\n"+
|
||||||
"ACCNT\tTest Account\tBANK\n"+
|
"ACCNT\tTest Account\tBANK\n"+
|
||||||
"ACCNT\tTest Account2\tBANK\n"+
|
"ACCNT\tTest Account2\tBANK\n"+
|
||||||
@@ -49,7 +51,7 @@ func TestIIFTransactionDataFileParseImportedData_MinimumValidData(t *testing.T)
|
|||||||
"ENDTRNS\t\t\t\t\n"+
|
"ENDTRNS\t\t\t\t\n"+
|
||||||
"TRNS\tCREDIT CARD\t09/07/2024\tTest Category2\t34.56\n"+
|
"TRNS\tCREDIT CARD\t09/07/2024\tTest Category2\t34.56\n"+
|
||||||
"SPL\tCREDIT CARD\t09/07/2024\tTest Account2\t-34.56\n"+
|
"SPL\tCREDIT CARD\t09/07/2024\tTest Account2\t-34.56\n"+
|
||||||
"ENDTRNS\t\t\t\t\n"), 0, nil, nil, nil, nil, nil)
|
"ENDTRNS\t\t\t\t\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
|
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
@@ -132,7 +134,7 @@ func TestIIFTransactionDataFileParseImportedData_MinimumValidData(t *testing.T)
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestIIFTransactionDataFileParseImportedData_MinimumValidDataWithoutAccountData(t *testing.T) {
|
func TestIIFTransactionDataFileParseImportedData_MinimumValidDataWithoutAccountData(t *testing.T) {
|
||||||
converter := IifTransactionDataFileImporter
|
importer := IifTransactionDataFileImporter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
@@ -140,13 +142,13 @@ func TestIIFTransactionDataFileParseImportedData_MinimumValidDataWithoutAccountD
|
|||||||
DefaultCurrency: "CNY",
|
DefaultCurrency: "CNY",
|
||||||
}
|
}
|
||||||
|
|
||||||
allNewTransactions, _, _, _, _, _, err := converter.ParseImportedData(context, user, []byte(
|
allNewTransactions, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(
|
||||||
"!TRNS\tDATE\tACCNT\tAMOUNT\n"+
|
"!TRNS\tDATE\tACCNT\tAMOUNT\n"+
|
||||||
"!SPL\tDATE\tACCNT\tAMOUNT\n"+
|
"!SPL\tDATE\tACCNT\tAMOUNT\n"+
|
||||||
"!ENDTRNS\t\t\t\n"+
|
"!ENDTRNS\t\t\t\n"+
|
||||||
"TRNS\t09/01/2024\tTest Account\t123.45\n"+
|
"TRNS\t09/01/2024\tTest Account\t123.45\n"+
|
||||||
"SPL\t09/01/2024\tTest Category\t-123.45\n"+
|
"SPL\t09/01/2024\tTest Category\t-123.45\n"+
|
||||||
"ENDTRNS\t\t\t\n"), 0, nil, nil, nil, nil, nil)
|
"ENDTRNS\t\t\t\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
|
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
@@ -162,7 +164,7 @@ func TestIIFTransactionDataFileParseImportedData_MinimumValidDataWithoutAccountD
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestIIFTransactionDataFileParseImportedData_MultipleDataset(t *testing.T) {
|
func TestIIFTransactionDataFileParseImportedData_MultipleDataset(t *testing.T) {
|
||||||
converter := IifTransactionDataFileImporter
|
importer := IifTransactionDataFileImporter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
@@ -170,7 +172,7 @@ func TestIIFTransactionDataFileParseImportedData_MultipleDataset(t *testing.T) {
|
|||||||
DefaultCurrency: "CNY",
|
DefaultCurrency: "CNY",
|
||||||
}
|
}
|
||||||
|
|
||||||
allNewTransactions, _, _, _, _, _, err := converter.ParseImportedData(context, user, []byte(
|
allNewTransactions, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(
|
||||||
"!ACCNT\tNAME\tACCNTTYPE\n"+
|
"!ACCNT\tNAME\tACCNTTYPE\n"+
|
||||||
"ACCNT\tTest Account3\tBANK\n"+
|
"ACCNT\tTest Account3\tBANK\n"+
|
||||||
"ACCNT\tTest Account4\tBANK\n"+
|
"ACCNT\tTest Account4\tBANK\n"+
|
||||||
@@ -202,7 +204,7 @@ func TestIIFTransactionDataFileParseImportedData_MultipleDataset(t *testing.T) {
|
|||||||
"ENDTRNS\t\t\t\t\n"+
|
"ENDTRNS\t\t\t\t\n"+
|
||||||
"!ACCNT\tTEST\tNAME\tACCNTTYPE\n"+
|
"!ACCNT\tTEST\tNAME\tACCNTTYPE\n"+
|
||||||
"ACCNT\t\tTest Category\tINC\n"+
|
"ACCNT\t\tTest Category\tINC\n"+
|
||||||
"ACCNT\t\tTest Category2\tEXP\n"), 0, nil, nil, nil, nil, nil)
|
"ACCNT\t\tTest Category2\tEXP\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
|
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
@@ -247,7 +249,7 @@ func TestIIFTransactionDataFileParseImportedData_MultipleDataset(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestIIFTransactionDataFileParseImportedData_ParseCategoryAndSubCategory(t *testing.T) {
|
func TestIIFTransactionDataFileParseImportedData_ParseCategoryAndSubCategory(t *testing.T) {
|
||||||
converter := IifTransactionDataFileImporter
|
importer := IifTransactionDataFileImporter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
@@ -255,7 +257,7 @@ func TestIIFTransactionDataFileParseImportedData_ParseCategoryAndSubCategory(t *
|
|||||||
DefaultCurrency: "CNY",
|
DefaultCurrency: "CNY",
|
||||||
}
|
}
|
||||||
|
|
||||||
allNewTransactions, _, allNewSubExpenseCategories, allNewSubIncomeCategories, _, _, err := converter.ParseImportedData(context, user, []byte(
|
allNewTransactions, _, allNewSubExpenseCategories, allNewSubIncomeCategories, _, _, err := importer.ParseImportedData(context, user, []byte(
|
||||||
"!ACCNT\tNAME\tACCNTTYPE\n"+
|
"!ACCNT\tNAME\tACCNTTYPE\n"+
|
||||||
"ACCNT\tTest Parent Category:Test Category\tINC\n"+
|
"ACCNT\tTest Parent Category:Test Category\tINC\n"+
|
||||||
"ACCNT\tTest Parent Category2:Test Category2\tEXP\n"+
|
"ACCNT\tTest Parent Category2:Test Category2\tEXP\n"+
|
||||||
@@ -267,7 +269,7 @@ func TestIIFTransactionDataFileParseImportedData_ParseCategoryAndSubCategory(t *
|
|||||||
"ENDTRNS\t\t\t\n"+
|
"ENDTRNS\t\t\t\n"+
|
||||||
"TRNS\t09/02/2024\tTest Account2\t-123.45\n"+
|
"TRNS\t09/02/2024\tTest Account2\t-123.45\n"+
|
||||||
"SPL\t09/02/2024\tTest Parent Category2:Test Category2\t123.45\n"+
|
"SPL\t09/02/2024\tTest Parent Category2:Test Category2\t123.45\n"+
|
||||||
"ENDTRNS\t\t\t\n"), 0, nil, nil, nil, nil, nil)
|
"ENDTRNS\t\t\t\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
|
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
@@ -299,7 +301,7 @@ func TestIIFTransactionDataFileParseImportedData_ParseCategoryAndSubCategory(t *
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestIIFTransactionDataFileParseImportedData_ParseYearMonthDayFormatTime(t *testing.T) {
|
func TestIIFTransactionDataFileParseImportedData_ParseYearMonthDayFormatTime(t *testing.T) {
|
||||||
converter := IifTransactionDataFileImporter
|
importer := IifTransactionDataFileImporter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
@@ -307,7 +309,7 @@ func TestIIFTransactionDataFileParseImportedData_ParseYearMonthDayFormatTime(t *
|
|||||||
DefaultCurrency: "CNY",
|
DefaultCurrency: "CNY",
|
||||||
}
|
}
|
||||||
|
|
||||||
allNewTransactions, _, _, _, _, _, err := converter.ParseImportedData(context, user, []byte(
|
allNewTransactions, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(
|
||||||
"!TRNS\tDATE\tACCNT\tAMOUNT\n"+
|
"!TRNS\tDATE\tACCNT\tAMOUNT\n"+
|
||||||
"!SPL\tDATE\tACCNT\tAMOUNT\n"+
|
"!SPL\tDATE\tACCNT\tAMOUNT\n"+
|
||||||
"!ENDTRNS\t\t\t\n"+
|
"!ENDTRNS\t\t\t\n"+
|
||||||
@@ -322,7 +324,7 @@ func TestIIFTransactionDataFileParseImportedData_ParseYearMonthDayFormatTime(t *
|
|||||||
"ENDTRNS\t\t\t\n"+
|
"ENDTRNS\t\t\t\n"+
|
||||||
"TRNS\t2024/9/4\tTest Account\t123.45\n"+
|
"TRNS\t2024/9/4\tTest Account\t123.45\n"+
|
||||||
"SPL\t2024/9/4\tTest Account2\t-123.45\n"+
|
"SPL\t2024/9/4\tTest Account2\t-123.45\n"+
|
||||||
"ENDTRNS\t\t\t\n"), 0, nil, nil, nil, nil, nil)
|
"ENDTRNS\t\t\t\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
|
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
@@ -334,7 +336,7 @@ func TestIIFTransactionDataFileParseImportedData_ParseYearMonthDayFormatTime(t *
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestIIFTransactionDataFileParseImportedData_ParseShortMonthDayYearFormatTime(t *testing.T) {
|
func TestIIFTransactionDataFileParseImportedData_ParseShortMonthDayYearFormatTime(t *testing.T) {
|
||||||
converter := IifTransactionDataFileImporter
|
importer := IifTransactionDataFileImporter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
@@ -342,7 +344,7 @@ func TestIIFTransactionDataFileParseImportedData_ParseShortMonthDayYearFormatTim
|
|||||||
DefaultCurrency: "CNY",
|
DefaultCurrency: "CNY",
|
||||||
}
|
}
|
||||||
|
|
||||||
allNewTransactions, _, _, _, _, _, err := converter.ParseImportedData(context, user, []byte(
|
allNewTransactions, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(
|
||||||
"!TRNS\tDATE\tACCNT\tAMOUNT\n"+
|
"!TRNS\tDATE\tACCNT\tAMOUNT\n"+
|
||||||
"!SPL\tDATE\tACCNT\tAMOUNT\n"+
|
"!SPL\tDATE\tACCNT\tAMOUNT\n"+
|
||||||
"!ENDTRNS\t\t\t\n"+
|
"!ENDTRNS\t\t\t\n"+
|
||||||
@@ -354,7 +356,7 @@ func TestIIFTransactionDataFileParseImportedData_ParseShortMonthDayYearFormatTim
|
|||||||
"ENDTRNS\t\t\t\n"+
|
"ENDTRNS\t\t\t\n"+
|
||||||
"TRNS\t9/3/2024\tTest Account\t123.45\n"+
|
"TRNS\t9/3/2024\tTest Account\t123.45\n"+
|
||||||
"SPL\t9/3/2024\tTest Account2\t-123.45\n"+
|
"SPL\t9/3/2024\tTest Account2\t-123.45\n"+
|
||||||
"ENDTRNS\t\t\t\n"), 0, nil, nil, nil, nil, nil)
|
"ENDTRNS\t\t\t\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
|
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
@@ -365,7 +367,7 @@ func TestIIFTransactionDataFileParseImportedData_ParseShortMonthDayYearFormatTim
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestIIFTransactionDataFileParseImportedData_ParseShortMonthDayTwoDigitsYearFormatTime(t *testing.T) {
|
func TestIIFTransactionDataFileParseImportedData_ParseShortMonthDayTwoDigitsYearFormatTime(t *testing.T) {
|
||||||
converter := IifTransactionDataFileImporter
|
importer := IifTransactionDataFileImporter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
@@ -373,7 +375,7 @@ func TestIIFTransactionDataFileParseImportedData_ParseShortMonthDayTwoDigitsYear
|
|||||||
DefaultCurrency: "CNY",
|
DefaultCurrency: "CNY",
|
||||||
}
|
}
|
||||||
|
|
||||||
allNewTransactions, _, _, _, _, _, err := converter.ParseImportedData(context, user, []byte(
|
allNewTransactions, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(
|
||||||
"!TRNS\tDATE\tACCNT\tAMOUNT\n"+
|
"!TRNS\tDATE\tACCNT\tAMOUNT\n"+
|
||||||
"!SPL\tDATE\tACCNT\tAMOUNT\n"+
|
"!SPL\tDATE\tACCNT\tAMOUNT\n"+
|
||||||
"!ENDTRNS\t\t\t\n"+
|
"!ENDTRNS\t\t\t\n"+
|
||||||
@@ -385,7 +387,7 @@ func TestIIFTransactionDataFileParseImportedData_ParseShortMonthDayTwoDigitsYear
|
|||||||
"ENDTRNS\t\t\t\n"+
|
"ENDTRNS\t\t\t\n"+
|
||||||
"TRNS\t24/9/3\tTest Account\t123.45\n"+
|
"TRNS\t24/9/3\tTest Account\t123.45\n"+
|
||||||
"SPL\t24/9/3\tTest Account2\t-123.45\n"+
|
"SPL\t24/9/3\tTest Account2\t-123.45\n"+
|
||||||
"ENDTRNS\t\t\t\n"), 0, nil, nil, nil, nil, nil)
|
"ENDTRNS\t\t\t\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
|
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
@@ -396,7 +398,7 @@ func TestIIFTransactionDataFileParseImportedData_ParseShortMonthDayTwoDigitsYear
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestIIFTransactionDataFileParseImportedData_ParseInvalidTime(t *testing.T) {
|
func TestIIFTransactionDataFileParseImportedData_ParseInvalidTime(t *testing.T) {
|
||||||
converter := IifTransactionDataFileImporter
|
importer := IifTransactionDataFileImporter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
@@ -404,36 +406,36 @@ func TestIIFTransactionDataFileParseImportedData_ParseInvalidTime(t *testing.T)
|
|||||||
DefaultCurrency: "CNY",
|
DefaultCurrency: "CNY",
|
||||||
}
|
}
|
||||||
|
|
||||||
_, _, _, _, _, _, err := converter.ParseImportedData(context, user, []byte(
|
_, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(
|
||||||
"!TRNS\tDATE\tACCNT\tAMOUNT\n"+
|
"!TRNS\tDATE\tACCNT\tAMOUNT\n"+
|
||||||
"!SPL\tDATE\tACCNT\tAMOUNT\n"+
|
"!SPL\tDATE\tACCNT\tAMOUNT\n"+
|
||||||
"!ENDTRNS\t\t\t\n"+
|
"!ENDTRNS\t\t\t\n"+
|
||||||
"TRNS\t09-01-2024\tTest Account\t123.45\n"+
|
"TRNS\t09-01-2024\tTest Account\t123.45\n"+
|
||||||
"SPL\t09-01-2024\tTest Account2\t-123.45\n"+
|
"SPL\t09-01-2024\tTest Account2\t-123.45\n"+
|
||||||
"ENDTRNS\t\t\t\n"), 0, nil, nil, nil, nil, nil)
|
"ENDTRNS\t\t\t\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrTransactionTimeInvalid.Message)
|
assert.EqualError(t, err, errs.ErrTransactionTimeInvalid.Message)
|
||||||
|
|
||||||
_, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte(
|
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(
|
||||||
"!TRNS\tDATE\tACCNT\tAMOUNT\n"+
|
"!TRNS\tDATE\tACCNT\tAMOUNT\n"+
|
||||||
"!SPL\tDATE\tACCNT\tAMOUNT\n"+
|
"!SPL\tDATE\tACCNT\tAMOUNT\n"+
|
||||||
"!ENDTRNS\t\t\t\n"+
|
"!ENDTRNS\t\t\t\n"+
|
||||||
"TRNS\t2024-09-01\tTest Account\t123.45\n"+
|
"TRNS\t2024-09-01\tTest Account\t123.45\n"+
|
||||||
"SPL\t2024-09-01\tTest Account2\t-123.45\n"+
|
"SPL\t2024-09-01\tTest Account2\t-123.45\n"+
|
||||||
"ENDTRNS\t\t\t\n"), 0, nil, nil, nil, nil, nil)
|
"ENDTRNS\t\t\t\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrTransactionTimeInvalid.Message)
|
assert.EqualError(t, err, errs.ErrTransactionTimeInvalid.Message)
|
||||||
|
|
||||||
_, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte(
|
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(
|
||||||
"!TRNS\tDATE\tACCNT\tAMOUNT\n"+
|
"!TRNS\tDATE\tACCNT\tAMOUNT\n"+
|
||||||
"!SPL\tDATE\tACCNT\tAMOUNT\n"+
|
"!SPL\tDATE\tACCNT\tAMOUNT\n"+
|
||||||
"!ENDTRNS\t\t\t\n"+
|
"!ENDTRNS\t\t\t\n"+
|
||||||
"TRNS\t9/24\tTest Account\t123.45\n"+
|
"TRNS\t9/24\tTest Account\t123.45\n"+
|
||||||
"SPL\t9/24\tTest Account2\t-123.45\n"+
|
"SPL\t9/24\tTest Account2\t-123.45\n"+
|
||||||
"ENDTRNS\t\t\t\n"), 0, nil, nil, nil, nil, nil)
|
"ENDTRNS\t\t\t\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrTransactionTimeInvalid.Message)
|
assert.EqualError(t, err, errs.ErrTransactionTimeInvalid.Message)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestIIFTransactionDataFileParseImportedData_ParseAmountWithThousandsSeparator(t *testing.T) {
|
func TestIIFTransactionDataFileParseImportedData_ParseAmountWithThousandsSeparator(t *testing.T) {
|
||||||
converter := IifTransactionDataFileImporter
|
importer := IifTransactionDataFileImporter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
@@ -441,13 +443,13 @@ func TestIIFTransactionDataFileParseImportedData_ParseAmountWithThousandsSeparat
|
|||||||
DefaultCurrency: "CNY",
|
DefaultCurrency: "CNY",
|
||||||
}
|
}
|
||||||
|
|
||||||
allNewTransactions, _, _, _, _, _, err := converter.ParseImportedData(context, user, []byte(
|
allNewTransactions, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(
|
||||||
"!TRNS\tDATE\tACCNT\tAMOUNT\n"+
|
"!TRNS\tDATE\tACCNT\tAMOUNT\n"+
|
||||||
"!SPL\tDATE\tACCNT\tAMOUNT\n"+
|
"!SPL\tDATE\tACCNT\tAMOUNT\n"+
|
||||||
"!ENDTRNS\t\t\t\n"+
|
"!ENDTRNS\t\t\t\n"+
|
||||||
"TRNS\t9/01/2024\tTest Account\t123,456.78\n"+
|
"TRNS\t9/01/2024\tTest Account\t123,456.78\n"+
|
||||||
"SPL\t9/01/2024\tTest Account2\t-123,456.78\n"+
|
"SPL\t9/01/2024\tTest Account2\t-123,456.78\n"+
|
||||||
"ENDTRNS\t\t\t\n"), 0, nil, nil, nil, nil, nil)
|
"ENDTRNS\t\t\t\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
|
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
@@ -456,7 +458,7 @@ func TestIIFTransactionDataFileParseImportedData_ParseAmountWithThousandsSeparat
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestIIFTransactionDataFileParseImportedData_ParseInvalidAmount(t *testing.T) {
|
func TestIIFTransactionDataFileParseImportedData_ParseInvalidAmount(t *testing.T) {
|
||||||
converter := IifTransactionDataFileImporter
|
importer := IifTransactionDataFileImporter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
@@ -464,27 +466,27 @@ func TestIIFTransactionDataFileParseImportedData_ParseInvalidAmount(t *testing.T
|
|||||||
DefaultCurrency: "CNY",
|
DefaultCurrency: "CNY",
|
||||||
}
|
}
|
||||||
|
|
||||||
_, _, _, _, _, _, err := converter.ParseImportedData(context, user, []byte(
|
_, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(
|
||||||
"!TRNS\tDATE\tACCNT\tAMOUNT\n"+
|
"!TRNS\tDATE\tACCNT\tAMOUNT\n"+
|
||||||
"!SPL\tDATE\tACCNT\tAMOUNT\n"+
|
"!SPL\tDATE\tACCNT\tAMOUNT\n"+
|
||||||
"!ENDTRNS\t\t\t\n"+
|
"!ENDTRNS\t\t\t\n"+
|
||||||
"TRNS\t09/01/2024\tTest Account\t123 45\n"+
|
"TRNS\t09/01/2024\tTest Account\t123 45\n"+
|
||||||
"SPL\t09/01/2024\tTest Account2\t-123.45\n"+
|
"SPL\t09/01/2024\tTest Account2\t-123.45\n"+
|
||||||
"ENDTRNS\t\t\t\n"), 0, nil, nil, nil, nil, nil)
|
"ENDTRNS\t\t\t\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrAmountInvalid.Message)
|
assert.EqualError(t, err, errs.ErrAmountInvalid.Message)
|
||||||
|
|
||||||
_, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte(
|
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(
|
||||||
"!TRNS\tDATE\tACCNT\tAMOUNT\n"+
|
"!TRNS\tDATE\tACCNT\tAMOUNT\n"+
|
||||||
"!SPL\tDATE\tACCNT\tAMOUNT\n"+
|
"!SPL\tDATE\tACCNT\tAMOUNT\n"+
|
||||||
"!ENDTRNS\t\t\t\n"+
|
"!ENDTRNS\t\t\t\n"+
|
||||||
"TRNS\t09/01/2024\tTest Account\t123.45\n"+
|
"TRNS\t09/01/2024\tTest Account\t123.45\n"+
|
||||||
"SPL\t09/01/2024\tTest Account2\t-123 45\n"+
|
"SPL\t09/01/2024\tTest Account2\t-123 45\n"+
|
||||||
"ENDTRNS\t\t\t\n"), 0, nil, nil, nil, nil, nil)
|
"ENDTRNS\t\t\t\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrAmountInvalid.Message)
|
assert.EqualError(t, err, errs.ErrAmountInvalid.Message)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestIIFTransactionDataFileParseImportedData_ParseDescription(t *testing.T) {
|
func TestIIFTransactionDataFileParseImportedData_ParseDescription(t *testing.T) {
|
||||||
converter := IifTransactionDataFileImporter
|
importer := IifTransactionDataFileImporter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
@@ -492,25 +494,25 @@ func TestIIFTransactionDataFileParseImportedData_ParseDescription(t *testing.T)
|
|||||||
DefaultCurrency: "CNY",
|
DefaultCurrency: "CNY",
|
||||||
}
|
}
|
||||||
|
|
||||||
allNewTransactions, _, _, _, _, _, err := converter.ParseImportedData(context, user, []byte(
|
allNewTransactions, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(
|
||||||
"!TRNS\tDATE\tACCNT\tNAME\tAMOUNT\tMEMO\n"+
|
"!TRNS\tDATE\tACCNT\tNAME\tAMOUNT\tMEMO\n"+
|
||||||
"!SPL\tDATE\tACCNT\tNAME\tAMOUNT\tMEMO\n"+
|
"!SPL\tDATE\tACCNT\tNAME\tAMOUNT\tMEMO\n"+
|
||||||
"!ENDTRNS\t\t\t\t\t\n"+
|
"!ENDTRNS\t\t\t\t\t\n"+
|
||||||
"TRNS\t09/01/2024\tTest Account\t\"Test\"\t123.45\t\"foo bar\t#test\"\n"+
|
"TRNS\t09/01/2024\tTest Account\t\"Test\"\t123.45\t\"foo bar\t#test\"\n"+
|
||||||
"SPL\t09/01/2024\tTest Account2\t\t-123.45\t\n"+
|
"SPL\t09/01/2024\tTest Account2\t\t-123.45\t\n"+
|
||||||
"ENDTRNS\t\t\t\t\t\n"), 0, nil, nil, nil, nil, nil)
|
"ENDTRNS\t\t\t\t\t\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
|
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
assert.Equal(t, 1, len(allNewTransactions))
|
assert.Equal(t, 1, len(allNewTransactions))
|
||||||
assert.Equal(t, "foo bar\t#test", allNewTransactions[0].Comment)
|
assert.Equal(t, "foo bar\t#test", allNewTransactions[0].Comment)
|
||||||
|
|
||||||
allNewTransactions, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte(
|
allNewTransactions, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(
|
||||||
"!TRNS\tDATE\tACCNT\tNAME\tAMOUNT\tMEMO\n"+
|
"!TRNS\tDATE\tACCNT\tNAME\tAMOUNT\tMEMO\n"+
|
||||||
"!SPL\tDATE\tACCNT\tNAME\tAMOUNT\tMEMO\n"+
|
"!SPL\tDATE\tACCNT\tNAME\tAMOUNT\tMEMO\n"+
|
||||||
"!ENDTRNS\t\t\t\t\t\n"+
|
"!ENDTRNS\t\t\t\t\t\n"+
|
||||||
"TRNS\t09/01/2024\tTest Account\tTest\t123.45\t\n"+
|
"TRNS\t09/01/2024\tTest Account\tTest\t123.45\t\n"+
|
||||||
"SPL\t09/01/2024\tTest Account2\t\t-123.45\t\n"+
|
"SPL\t09/01/2024\tTest Account2\t\t-123.45\t\n"+
|
||||||
"ENDTRNS\t\t\t\t\t\n"), 0, nil, nil, nil, nil, nil)
|
"ENDTRNS\t\t\t\t\t\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
|
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
assert.Equal(t, 1, len(allNewTransactions))
|
assert.Equal(t, 1, len(allNewTransactions))
|
||||||
@@ -518,7 +520,7 @@ func TestIIFTransactionDataFileParseImportedData_ParseDescription(t *testing.T)
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestIIFTransactionDataFileParseImportedData_ParseSplitTransaction(t *testing.T) {
|
func TestIIFTransactionDataFileParseImportedData_ParseSplitTransaction(t *testing.T) {
|
||||||
converter := IifTransactionDataFileImporter
|
importer := IifTransactionDataFileImporter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
@@ -526,7 +528,7 @@ func TestIIFTransactionDataFileParseImportedData_ParseSplitTransaction(t *testin
|
|||||||
DefaultCurrency: "CNY",
|
DefaultCurrency: "CNY",
|
||||||
}
|
}
|
||||||
|
|
||||||
allNewTransactions, allNewAccounts, _, _, _, _, err := converter.ParseImportedData(context, user, []byte(
|
allNewTransactions, allNewAccounts, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(
|
||||||
"!ACCNT\tNAME\tACCNTTYPE\n"+
|
"!ACCNT\tNAME\tACCNTTYPE\n"+
|
||||||
"ACCNT\tTest Category\tINC\n"+
|
"ACCNT\tTest Category\tINC\n"+
|
||||||
"ACCNT\tTest Category2\tEXP\n"+
|
"ACCNT\tTest Category2\tEXP\n"+
|
||||||
@@ -552,7 +554,7 @@ func TestIIFTransactionDataFileParseImportedData_ParseSplitTransaction(t *testin
|
|||||||
"TRNS\t09/05/2024\tTest Category2\t100.00\n"+
|
"TRNS\t09/05/2024\tTest Category2\t100.00\n"+
|
||||||
"SPL\t09/05/2024\tTest Account3\t-40.00\n"+
|
"SPL\t09/05/2024\tTest Account3\t-40.00\n"+
|
||||||
"SPL\t09/05/2024\tTest Account4\t-60.00\n"+
|
"SPL\t09/05/2024\tTest Account4\t-60.00\n"+
|
||||||
"ENDTRNS\t\t\t\n"), 0, nil, nil, nil, nil, nil)
|
"ENDTRNS\t\t\t\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
|
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
@@ -651,7 +653,7 @@ func TestIIFTransactionDataFileParseImportedData_ParseSplitTransaction(t *testin
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestIIFTransactionDataFileParseImportedData_ParseSplitTransactionDescription(t *testing.T) {
|
func TestIIFTransactionDataFileParseImportedData_ParseSplitTransactionDescription(t *testing.T) {
|
||||||
converter := IifTransactionDataFileImporter
|
importer := IifTransactionDataFileImporter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
@@ -659,21 +661,21 @@ func TestIIFTransactionDataFileParseImportedData_ParseSplitTransactionDescriptio
|
|||||||
DefaultCurrency: "CNY",
|
DefaultCurrency: "CNY",
|
||||||
}
|
}
|
||||||
|
|
||||||
allNewTransactions, _, _, _, _, _, err := converter.ParseImportedData(context, user, []byte(
|
allNewTransactions, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(
|
||||||
"!TRNS\tDATE\tACCNT\tNAME\tAMOUNT\tMEMO\n"+
|
"!TRNS\tDATE\tACCNT\tNAME\tAMOUNT\tMEMO\n"+
|
||||||
"!SPL\tDATE\tACCNT\tNAME\tAMOUNT\tMEMO\n"+
|
"!SPL\tDATE\tACCNT\tNAME\tAMOUNT\tMEMO\n"+
|
||||||
"!ENDTRNS\t\t\t\t\t\n"+
|
"!ENDTRNS\t\t\t\t\t\n"+
|
||||||
"TRNS\t09/01/2024\tTest Account\t\"Test\"\t123.45\t\"foo bar\t#test\"\n"+
|
"TRNS\t09/01/2024\tTest Account\t\"Test\"\t123.45\t\"foo bar\t#test\"\n"+
|
||||||
"SPL\t09/01/2024\tTest Account2\t\t-100.00\t\"foo\ttest#bar\"\n"+
|
"SPL\t09/01/2024\tTest Account2\t\t-100.00\t\"foo\ttest#bar\"\n"+
|
||||||
"SPL\t09/01/2024\tTest Account3\t\t-23.45\t\n"+
|
"SPL\t09/01/2024\tTest Account3\t\t-23.45\t\n"+
|
||||||
"ENDTRNS\t\t\t\t\t\n"), 0, nil, nil, nil, nil, nil)
|
"ENDTRNS\t\t\t\t\t\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
|
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
assert.Equal(t, 2, len(allNewTransactions))
|
assert.Equal(t, 2, len(allNewTransactions))
|
||||||
assert.Equal(t, "foo\ttest#bar", allNewTransactions[0].Comment)
|
assert.Equal(t, "foo\ttest#bar", allNewTransactions[0].Comment)
|
||||||
assert.Equal(t, "foo bar\t#test", allNewTransactions[1].Comment)
|
assert.Equal(t, "foo bar\t#test", allNewTransactions[1].Comment)
|
||||||
|
|
||||||
allNewTransactions, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte(
|
allNewTransactions, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(
|
||||||
"!TRNS\tDATE\tACCNT\tNAME\tAMOUNT\tMEMO\n"+
|
"!TRNS\tDATE\tACCNT\tNAME\tAMOUNT\tMEMO\n"+
|
||||||
"!SPL\tDATE\tACCNT\tNAME\tAMOUNT\tMEMO\n"+
|
"!SPL\tDATE\tACCNT\tNAME\tAMOUNT\tMEMO\n"+
|
||||||
"!ENDTRNS\t\t\t\t\t\n"+
|
"!ENDTRNS\t\t\t\t\t\n"+
|
||||||
@@ -681,7 +683,7 @@ func TestIIFTransactionDataFileParseImportedData_ParseSplitTransactionDescriptio
|
|||||||
"SPL\t09/01/2024\tTest Account2\t\t-100.00\t\"test\"\n"+
|
"SPL\t09/01/2024\tTest Account2\t\t-100.00\t\"test\"\n"+
|
||||||
"SPL\t09/01/2024\tTest Account3\tfoo\t-12.34\t\n"+
|
"SPL\t09/01/2024\tTest Account3\tfoo\t-12.34\t\n"+
|
||||||
"SPL\t09/01/2024\tTest Account4\t\t-11.11\t\n"+
|
"SPL\t09/01/2024\tTest Account4\t\t-11.11\t\n"+
|
||||||
"ENDTRNS\t\t\t\t\t\n"), 0, nil, nil, nil, nil, nil)
|
"ENDTRNS\t\t\t\t\t\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
|
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
assert.Equal(t, 3, len(allNewTransactions))
|
assert.Equal(t, 3, len(allNewTransactions))
|
||||||
@@ -691,7 +693,7 @@ func TestIIFTransactionDataFileParseImportedData_ParseSplitTransactionDescriptio
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestIIFTransactionDataFileParseImportedData_NotSupportedSplitTransaction(t *testing.T) {
|
func TestIIFTransactionDataFileParseImportedData_NotSupportedSplitTransaction(t *testing.T) {
|
||||||
converter := IifTransactionDataFileImporter
|
importer := IifTransactionDataFileImporter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
@@ -700,52 +702,52 @@ func TestIIFTransactionDataFileParseImportedData_NotSupportedSplitTransaction(t
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Opening balance transaction
|
// Opening balance transaction
|
||||||
_, _, _, _, _, _, err := converter.ParseImportedData(context, user, []byte(
|
_, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(
|
||||||
"!TRNS\tTRNSTYPE\tDATE\tACCNT\tAMOUNT\n"+
|
"!TRNS\tTRNSTYPE\tDATE\tACCNT\tAMOUNT\n"+
|
||||||
"!SPL\tTRNSTYPE\tDATE\tACCNT\tAMOUNT\n"+
|
"!SPL\tTRNSTYPE\tDATE\tACCNT\tAMOUNT\n"+
|
||||||
"!ENDTRNS\t\t\t\t\n"+
|
"!ENDTRNS\t\t\t\t\n"+
|
||||||
"TRNS\tBEGINBALCHECK\t09/01/2024\tTest Account\t123.45\n"+
|
"TRNS\tBEGINBALCHECK\t09/01/2024\tTest Account\t123.45\n"+
|
||||||
"SPL\tBEGINBALCHECK\t09/01/2024\tTest Account2\t-100.00\n"+
|
"SPL\tBEGINBALCHECK\t09/01/2024\tTest Account2\t-100.00\n"+
|
||||||
"SPL\tBEGINBALCHECK\t09/01/2024\tTest Account3\t-23.45\n"+
|
"SPL\tBEGINBALCHECK\t09/01/2024\tTest Account3\t-23.45\n"+
|
||||||
"ENDTRNS\t\t\t\n"), 0, nil, nil, nil, nil, nil)
|
"ENDTRNS\t\t\t\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrNotSupportedSplitTransactions.Message)
|
assert.EqualError(t, err, errs.ErrNotSupportedSplitTransactions.Message)
|
||||||
|
|
||||||
// Transaction with invalid amount
|
// Transaction with invalid amount
|
||||||
_, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte(
|
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(
|
||||||
"!TRNS\tDATE\tACCNT\tAMOUNT\n"+
|
"!TRNS\tDATE\tACCNT\tAMOUNT\n"+
|
||||||
"!SPL\tDATE\tACCNT\tAMOUNT\n"+
|
"!SPL\tDATE\tACCNT\tAMOUNT\n"+
|
||||||
"!ENDTRNS\t\t\t\n"+
|
"!ENDTRNS\t\t\t\n"+
|
||||||
"TRNS\t09/01/2024\tTest Account\t123 45\n"+
|
"TRNS\t09/01/2024\tTest Account\t123 45\n"+
|
||||||
"SPL\t09/01/2024\tTest Account2\t-100.00\n"+
|
"SPL\t09/01/2024\tTest Account2\t-100.00\n"+
|
||||||
"SPL\t09/01/2024\tTest Account3\t-23.45\n"+
|
"SPL\t09/01/2024\tTest Account3\t-23.45\n"+
|
||||||
"ENDTRNS\t\t\t\n"), 0, nil, nil, nil, nil, nil)
|
"ENDTRNS\t\t\t\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrAmountInvalid.Message)
|
assert.EqualError(t, err, errs.ErrAmountInvalid.Message)
|
||||||
|
|
||||||
// Transaction split data with invalid amount
|
// Transaction split data with invalid amount
|
||||||
_, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte(
|
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(
|
||||||
"!TRNS\tDATE\tACCNT\tAMOUNT\n"+
|
"!TRNS\tDATE\tACCNT\tAMOUNT\n"+
|
||||||
"!SPL\tDATE\tACCNT\tAMOUNT\n"+
|
"!SPL\tDATE\tACCNT\tAMOUNT\n"+
|
||||||
"!ENDTRNS\t\t\t\n"+
|
"!ENDTRNS\t\t\t\n"+
|
||||||
"TRNS\t09/01/2024\tTest Account\t123.45\n"+
|
"TRNS\t09/01/2024\tTest Account\t123.45\n"+
|
||||||
"SPL\t09/01/2024\tTest Account2\t-100 00\n"+
|
"SPL\t09/01/2024\tTest Account2\t-100 00\n"+
|
||||||
"SPL\t09/01/2024\tTest Account3\t-23.45\n"+
|
"SPL\t09/01/2024\tTest Account3\t-23.45\n"+
|
||||||
"ENDTRNS\t\t\t\n"), 0, nil, nil, nil, nil, nil)
|
"ENDTRNS\t\t\t\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrAmountInvalid.Message)
|
assert.EqualError(t, err, errs.ErrAmountInvalid.Message)
|
||||||
|
|
||||||
// Transaction amount not equal to sum of split data amount
|
// Transaction amount not equal to sum of split data amount
|
||||||
_, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte(
|
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(
|
||||||
"!TRNS\tDATE\tACCNT\tAMOUNT\n"+
|
"!TRNS\tDATE\tACCNT\tAMOUNT\n"+
|
||||||
"!SPL\tDATE\tACCNT\tAMOUNT\n"+
|
"!SPL\tDATE\tACCNT\tAMOUNT\n"+
|
||||||
"!ENDTRNS\t\t\t\n"+
|
"!ENDTRNS\t\t\t\n"+
|
||||||
"TRNS\t09/01/2024\tTest Account\t123.00\n"+
|
"TRNS\t09/01/2024\tTest Account\t123.00\n"+
|
||||||
"SPL\t09/01/2024\tTest Account2\t-100.00\n"+
|
"SPL\t09/01/2024\tTest Account2\t-100.00\n"+
|
||||||
"SPL\t09/01/2024\tTest Account3\t-23.45\n"+
|
"SPL\t09/01/2024\tTest Account3\t-23.45\n"+
|
||||||
"ENDTRNS\t\t\t\n"), 0, nil, nil, nil, nil, nil)
|
"ENDTRNS\t\t\t\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrNotSupportedSplitTransactions.Message)
|
assert.EqualError(t, err, errs.ErrNotSupportedSplitTransactions.Message)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestIIFTransactionDataFileParseImportedData_InvalidDataLines(t *testing.T) {
|
func TestIIFTransactionDataFileParseImportedData_InvalidDataLines(t *testing.T) {
|
||||||
converter := IifTransactionDataFileImporter
|
importer := IifTransactionDataFileImporter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
@@ -754,75 +756,75 @@ func TestIIFTransactionDataFileParseImportedData_InvalidDataLines(t *testing.T)
|
|||||||
}
|
}
|
||||||
|
|
||||||
//Missing Transaction Line
|
//Missing Transaction Line
|
||||||
_, _, _, _, _, _, err := converter.ParseImportedData(context, user, []byte(
|
_, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(
|
||||||
"!TRNS\tDATE\tACCNT\tAMOUNT\n"+
|
"!TRNS\tDATE\tACCNT\tAMOUNT\n"+
|
||||||
"!SPL\tDATE\tACCNT\tAMOUNT\n"+
|
"!SPL\tDATE\tACCNT\tAMOUNT\n"+
|
||||||
"!ENDTRNS\t\t\t\n"+
|
"!ENDTRNS\t\t\t\n"+
|
||||||
"SPL\t09/01/2024\tTest Account2\t-123.45\n"+
|
"SPL\t09/01/2024\tTest Account2\t-123.45\n"+
|
||||||
"ENDTRNS\t\t\t\n"), 0, nil, nil, nil, nil, nil)
|
"ENDTRNS\t\t\t\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrInvalidIIFFile.Message)
|
assert.EqualError(t, err, errs.ErrInvalidIIFFile.Message)
|
||||||
|
|
||||||
// Missing Transaction And Split Line
|
// Missing Transaction And Split Line
|
||||||
_, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte(
|
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(
|
||||||
"!TRNS\tDATE\tACCNT\tAMOUNT\n"+
|
"!TRNS\tDATE\tACCNT\tAMOUNT\n"+
|
||||||
"!SPL\tDATE\tACCNT\tAMOUNT\n"+
|
"!SPL\tDATE\tACCNT\tAMOUNT\n"+
|
||||||
"!ENDTRNS\t\t\t\n"+
|
"!ENDTRNS\t\t\t\n"+
|
||||||
"ENDTRNS\t\t\t\n"), 0, nil, nil, nil, nil, nil)
|
"ENDTRNS\t\t\t\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrInvalidIIFFile.Message)
|
assert.EqualError(t, err, errs.ErrInvalidIIFFile.Message)
|
||||||
|
|
||||||
// Missing Split Line
|
// Missing Split Line
|
||||||
_, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte(
|
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(
|
||||||
"!TRNS\tDATE\tACCNT\tAMOUNT\n"+
|
"!TRNS\tDATE\tACCNT\tAMOUNT\n"+
|
||||||
"!SPL\tDATE\tACCNT\tAMOUNT\n"+
|
"!SPL\tDATE\tACCNT\tAMOUNT\n"+
|
||||||
"!ENDTRNS\t\t\t\n"+
|
"!ENDTRNS\t\t\t\n"+
|
||||||
"TRNS\t09/01/2024\tTest Account\t123.45\n"+
|
"TRNS\t09/01/2024\tTest Account\t123.45\n"+
|
||||||
"ENDTRNS\t\t\t\n"), 0, nil, nil, nil, nil, nil)
|
"ENDTRNS\t\t\t\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrInvalidIIFFile.Message)
|
assert.EqualError(t, err, errs.ErrInvalidIIFFile.Message)
|
||||||
|
|
||||||
// Missing Transaction End Line
|
// Missing Transaction End Line
|
||||||
_, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte(
|
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(
|
||||||
"!TRNS\tDATE\tACCNT\tAMOUNT\n"+
|
"!TRNS\tDATE\tACCNT\tAMOUNT\n"+
|
||||||
"!SPL\tDATE\tACCNT\tAMOUNT\n"+
|
"!SPL\tDATE\tACCNT\tAMOUNT\n"+
|
||||||
"!ENDTRNS\t\t\t\n"+
|
"!ENDTRNS\t\t\t\n"+
|
||||||
"TRNS\t09/01/2024\tTest Account\t123.45\n"+
|
"TRNS\t09/01/2024\tTest Account\t123.45\n"+
|
||||||
"SPL\t09/01/2024\tTest Account2\t-123.45\n"), 0, nil, nil, nil, nil, nil)
|
"SPL\t09/01/2024\tTest Account2\t-123.45\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrInvalidIIFFile.Message)
|
assert.EqualError(t, err, errs.ErrInvalidIIFFile.Message)
|
||||||
|
|
||||||
// Missing Transaction End Line (following is another header)
|
// Missing Transaction End Line (following is another header)
|
||||||
_, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte(
|
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(
|
||||||
"!TRNS\tDATE\tACCNT\tAMOUNT\n"+
|
"!TRNS\tDATE\tACCNT\tAMOUNT\n"+
|
||||||
"!SPL\tDATE\tACCNT\tAMOUNT\n"+
|
"!SPL\tDATE\tACCNT\tAMOUNT\n"+
|
||||||
"!ENDTRNS\t\t\t\n"+
|
"!ENDTRNS\t\t\t\n"+
|
||||||
"TRNS\t09/01/2024\tTest Account\t123.45\n"+
|
"TRNS\t09/01/2024\tTest Account\t123.45\n"+
|
||||||
"SPL\t09/01/2024\tTest Account2\t-123.45\n"+
|
"SPL\t09/01/2024\tTest Account2\t-123.45\n"+
|
||||||
"!ACCNT\tNAME\tACCNTTYPE\n"+
|
"!ACCNT\tNAME\tACCNTTYPE\n"+
|
||||||
"ACCNT\tTest Account\tBANK\n"), 0, nil, nil, nil, nil, nil)
|
"ACCNT\tTest Account\tBANK\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrInvalidIIFFile.Message)
|
assert.EqualError(t, err, errs.ErrInvalidIIFFile.Message)
|
||||||
|
|
||||||
// Invalid Line
|
// Invalid Line
|
||||||
_, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte(
|
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(
|
||||||
"!TRNS\tDATE\tACCNT\tAMOUNT\n"+
|
"!TRNS\tDATE\tACCNT\tAMOUNT\n"+
|
||||||
"!SPL\tDATE\tACCNT\tAMOUNT\n"+
|
"!SPL\tDATE\tACCNT\tAMOUNT\n"+
|
||||||
"!ENDTRNS\t\t\t\n"+
|
"!ENDTRNS\t\t\t\n"+
|
||||||
"TRNS\t09/01/2024\tTest Account\t123.45\n"+
|
"TRNS\t09/01/2024\tTest Account\t123.45\n"+
|
||||||
"SPL\t09/01/2024\tTest Account2\t-123.45\n"+
|
"SPL\t09/01/2024\tTest Account2\t-123.45\n"+
|
||||||
"TEST\t\t\t\n"+
|
"TEST\t\t\t\n"+
|
||||||
"ENDTRNS\t\t\t\n"), 0, nil, nil, nil, nil, nil)
|
"ENDTRNS\t\t\t\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrInvalidIIFFile.Message)
|
assert.EqualError(t, err, errs.ErrInvalidIIFFile.Message)
|
||||||
|
|
||||||
// Repeat Transaction Line
|
// Repeat Transaction Line
|
||||||
_, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte(
|
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(
|
||||||
"!TRNS\tDATE\tACCNT\tAMOUNT\n"+
|
"!TRNS\tDATE\tACCNT\tAMOUNT\n"+
|
||||||
"!SPL\tDATE\tACCNT\tAMOUNT\n"+
|
"!SPL\tDATE\tACCNT\tAMOUNT\n"+
|
||||||
"!ENDTRNS\t\t\t\n"+
|
"!ENDTRNS\t\t\t\n"+
|
||||||
"TRNS\t09/01/2024\tTest Account\t123.45\n"+
|
"TRNS\t09/01/2024\tTest Account\t123.45\n"+
|
||||||
"TRNS\t09/01/2024\tTest Account\t123.45\n"+
|
"TRNS\t09/01/2024\tTest Account\t123.45\n"+
|
||||||
"SPL\t09/01/2024\tTest Account2\t-123.45\n"+
|
"SPL\t09/01/2024\tTest Account2\t-123.45\n"+
|
||||||
"ENDTRNS\t\t\t\n"), 0, nil, nil, nil, nil, nil)
|
"ENDTRNS\t\t\t\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrInvalidIIFFile.Message)
|
assert.EqualError(t, err, errs.ErrInvalidIIFFile.Message)
|
||||||
|
|
||||||
// Repeat Transaction End Line
|
// Repeat Transaction End Line
|
||||||
_, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte(
|
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(
|
||||||
"!TRNS\tDATE\tACCNT\tAMOUNT\t\n"+
|
"!TRNS\tDATE\tACCNT\tAMOUNT\t\n"+
|
||||||
"!SPL\tDATE\tACCNT\tAMOUNT\t\n"+
|
"!SPL\tDATE\tACCNT\tAMOUNT\t\n"+
|
||||||
"!ENDTRNS\t\t\t\n"+
|
"!ENDTRNS\t\t\t\n"+
|
||||||
@@ -830,12 +832,12 @@ func TestIIFTransactionDataFileParseImportedData_InvalidDataLines(t *testing.T)
|
|||||||
"TRNS\t09/01/2024\tTest Account\t123.45\n"+
|
"TRNS\t09/01/2024\tTest Account\t123.45\n"+
|
||||||
"SPL\t09/01/2024\tTest Account2\t-123.45\n"+
|
"SPL\t09/01/2024\tTest Account2\t-123.45\n"+
|
||||||
"ENDTRNS\t\t\t\n"+
|
"ENDTRNS\t\t\t\n"+
|
||||||
"ENDTRNS\t\t\t\n"), 0, nil, nil, nil, nil, nil)
|
"ENDTRNS\t\t\t\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrInvalidIIFFile.Message)
|
assert.EqualError(t, err, errs.ErrInvalidIIFFile.Message)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestIIFTransactionDataFileParseImportedData_InvalidHeaderLines(t *testing.T) {
|
func TestIIFTransactionDataFileParseImportedData_InvalidHeaderLines(t *testing.T) {
|
||||||
converter := IifTransactionDataFileImporter
|
importer := IifTransactionDataFileImporter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
@@ -844,49 +846,49 @@ func TestIIFTransactionDataFileParseImportedData_InvalidHeaderLines(t *testing.T
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Missing All Sample Lines
|
// Missing All Sample Lines
|
||||||
_, _, _, _, _, _, err := converter.ParseImportedData(context, user, []byte(
|
_, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(
|
||||||
"TRNS\t09/01/2024\tTest Account\t123.45\n"+
|
"TRNS\t09/01/2024\tTest Account\t123.45\n"+
|
||||||
"SPL\t09/01/2024\tTest Account2\t-123.45\n"+
|
"SPL\t09/01/2024\tTest Account2\t-123.45\n"+
|
||||||
"ENDTRNS\t\t\t\n"), 0, nil, nil, nil, nil, nil)
|
"ENDTRNS\t\t\t\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrInvalidIIFFile.Message)
|
assert.EqualError(t, err, errs.ErrInvalidIIFFile.Message)
|
||||||
|
|
||||||
// Missing Transaction Sample Line
|
// Missing Transaction Sample Line
|
||||||
_, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte(
|
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(
|
||||||
"!SPL\tDATE\tACCNT\tAMOUNT\n"+
|
"!SPL\tDATE\tACCNT\tAMOUNT\n"+
|
||||||
"!ENDTRNS\t\t\t\n"), 0, nil, nil, nil, nil, nil)
|
"!ENDTRNS\t\t\t\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrInvalidIIFFile.Message)
|
assert.EqualError(t, err, errs.ErrInvalidIIFFile.Message)
|
||||||
|
|
||||||
// Missing Split Sample Line
|
// Missing Split Sample Line
|
||||||
_, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte(
|
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(
|
||||||
"!TRNS\tDATE\tACCNT\tAMOUNT\n"+
|
"!TRNS\tDATE\tACCNT\tAMOUNT\n"+
|
||||||
"!ENDTRNS\t\t\t\n"), 0, nil, nil, nil, nil, nil)
|
"!ENDTRNS\t\t\t\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrInvalidIIFFile.Message)
|
assert.EqualError(t, err, errs.ErrInvalidIIFFile.Message)
|
||||||
|
|
||||||
// Missing Transaction End Sample Line
|
// Missing Transaction End Sample Line
|
||||||
_, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte(
|
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(
|
||||||
"!TRNS\tDATE\tACCNT\tAMOUNT\n"+
|
"!TRNS\tDATE\tACCNT\tAMOUNT\n"+
|
||||||
"!SPL\tDATE\tACCNT\tAMOUNT\n"), 0, nil, nil, nil, nil, nil)
|
"!SPL\tDATE\tACCNT\tAMOUNT\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrInvalidIIFFile.Message)
|
assert.EqualError(t, err, errs.ErrInvalidIIFFile.Message)
|
||||||
|
|
||||||
// Missing Transaction End Sample Line (following is data line)
|
// Missing Transaction End Sample Line (following is data line)
|
||||||
_, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte(
|
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(
|
||||||
"!TRNS\tDATE\tACCNT\tAMOUNT\n"+
|
"!TRNS\tDATE\tACCNT\tAMOUNT\n"+
|
||||||
"!SPL\tDATE\tACCNT\tAMOUNT\n"+
|
"!SPL\tDATE\tACCNT\tAMOUNT\n"+
|
||||||
"TRNS\t09/01/2024\tTest Account\t123.45\n"+
|
"TRNS\t09/01/2024\tTest Account\t123.45\n"+
|
||||||
"SPL\t09/01/2024\tTest Account2\t-123.45\n"+
|
"SPL\t09/01/2024\tTest Account2\t-123.45\n"+
|
||||||
"ENDTRNS\t\t\t\n"), 0, nil, nil, nil, nil, nil)
|
"ENDTRNS\t\t\t\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrInvalidIIFFile.Message)
|
assert.EqualError(t, err, errs.ErrInvalidIIFFile.Message)
|
||||||
|
|
||||||
// Invalid Sample Line
|
// Invalid Sample Line
|
||||||
_, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte(
|
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(
|
||||||
"!TRNS\tDATE\tACCNT\tAMOUNT\n"+
|
"!TRNS\tDATE\tACCNT\tAMOUNT\n"+
|
||||||
"!TEST\tDATE\tACCNT\tAMOUNT\n"+
|
"!TEST\tDATE\tACCNT\tAMOUNT\n"+
|
||||||
"!ENDTRNS\t\t\t\n"), 0, nil, nil, nil, nil, nil)
|
"!ENDTRNS\t\t\t\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrInvalidIIFFile.Message)
|
assert.EqualError(t, err, errs.ErrInvalidIIFFile.Message)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestIIFTransactionDataFileParseImportedData_MissingRequiredColumn(t *testing.T) {
|
func TestIIFTransactionDataFileParseImportedData_MissingRequiredColumn(t *testing.T) {
|
||||||
converter := IifTransactionDataFileImporter
|
importer := IifTransactionDataFileImporter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
@@ -895,32 +897,32 @@ func TestIIFTransactionDataFileParseImportedData_MissingRequiredColumn(t *testin
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Missing Date Column
|
// Missing Date Column
|
||||||
_, _, _, _, _, _, err := converter.ParseImportedData(context, user, []byte(
|
_, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(
|
||||||
"!TRNS\tACCNT\tAMOUNT\t\n"+
|
"!TRNS\tACCNT\tAMOUNT\t\n"+
|
||||||
"!SPL\tACCNT\tAMOUNT\t\n"+
|
"!SPL\tACCNT\tAMOUNT\t\n"+
|
||||||
"!ENDTRNS\t\t\t\n"+
|
"!ENDTRNS\t\t\t\n"+
|
||||||
"TRNS\tTest Account\t123.45\n"+
|
"TRNS\tTest Account\t123.45\n"+
|
||||||
"SPL\tTest Account2\t-123.45\n"+
|
"SPL\tTest Account2\t-123.45\n"+
|
||||||
"ENDTRNS\t\t\t\t\n"), 0, nil, nil, nil, nil, nil)
|
"ENDTRNS\t\t\t\t\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrMissingRequiredFieldInHeaderRow.Message)
|
assert.EqualError(t, err, errs.ErrMissingRequiredFieldInHeaderRow.Message)
|
||||||
|
|
||||||
// Missing Account Column
|
// Missing Account Column
|
||||||
_, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte(
|
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(
|
||||||
"!TRNS\tDATE\tAMOUNT\t\n"+
|
"!TRNS\tDATE\tAMOUNT\t\n"+
|
||||||
"!SPL\tDATE\tAMOUNT\t\n"+
|
"!SPL\tDATE\tAMOUNT\t\n"+
|
||||||
"!ENDTRNS\t\t\t\n"+
|
"!ENDTRNS\t\t\t\n"+
|
||||||
"TRNS\t09/01/2024\t123.45\n"+
|
"TRNS\t09/01/2024\t123.45\n"+
|
||||||
"SPL\t09/01/2024\t-123.45\n"+
|
"SPL\t09/01/2024\t-123.45\n"+
|
||||||
"ENDTRNS\t\t\t\t\n"), 0, nil, nil, nil, nil, nil)
|
"ENDTRNS\t\t\t\t\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrMissingRequiredFieldInHeaderRow.Message)
|
assert.EqualError(t, err, errs.ErrMissingRequiredFieldInHeaderRow.Message)
|
||||||
|
|
||||||
// Missing Amount Column
|
// Missing Amount Column
|
||||||
_, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte(
|
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(
|
||||||
"!TRNS\tDATE\tACCNT\t\n"+
|
"!TRNS\tDATE\tACCNT\t\n"+
|
||||||
"!SPL\tDATE\tACCNT\t\n"+
|
"!SPL\tDATE\tACCNT\t\n"+
|
||||||
"!ENDTRNS\t\t\t\n"+
|
"!ENDTRNS\t\t\t\n"+
|
||||||
"TRNS\t09/01/2024\tTest Account\n"+
|
"TRNS\t09/01/2024\tTest Account\n"+
|
||||||
"SPL\t09/01/2024\tTest Account2\n"+
|
"SPL\t09/01/2024\tTest Account2\n"+
|
||||||
"ENDTRNS\t\t\t\t\n"), 0, nil, nil, nil, nil, nil)
|
"ENDTRNS\t\t\t\t\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrMissingRequiredFieldInHeaderRow.Message)
|
assert.EqualError(t, err, errs.ErrMissingRequiredFieldInHeaderRow.Message)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package jdcom
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"time"
|
||||||
|
|
||||||
"golang.org/x/text/encoding/unicode"
|
"golang.org/x/text/encoding/unicode"
|
||||||
"golang.org/x/text/transform"
|
"golang.org/x/text/transform"
|
||||||
@@ -27,7 +28,7 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// ParseImportedData returns the imported data by parsing the jd.com finance transaction csv data
|
// ParseImportedData returns the imported data by parsing the jd.com finance transaction csv data
|
||||||
func (c *jdComFinanceTransactionDataCsvFileImporter) ParseImportedData(ctx core.Context, user *models.User, data []byte, defaultTimezoneOffset int16, accountMap map[string]*models.Account, expenseCategoryMap map[string]map[string]*models.TransactionCategory, incomeCategoryMap map[string]map[string]*models.TransactionCategory, transferCategoryMap map[string]map[string]*models.TransactionCategory, tagMap map[string]*models.TransactionTag) (models.ImportedTransactionSlice, []*models.Account, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionTag, error) {
|
func (c *jdComFinanceTransactionDataCsvFileImporter) ParseImportedData(ctx core.Context, user *models.User, data []byte, defaultTimezone *time.Location, additionalOptions converter.TransactionDataImporterOptions, accountMap map[string]*models.Account, expenseCategoryMap map[string]map[string]*models.TransactionCategory, incomeCategoryMap map[string]map[string]*models.TransactionCategory, transferCategoryMap map[string]map[string]*models.TransactionCategory, tagMap map[string]*models.TransactionTag) (models.ImportedTransactionSlice, []*models.Account, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionTag, error) {
|
||||||
fallback := unicode.UTF8.NewDecoder()
|
fallback := unicode.UTF8.NewDecoder()
|
||||||
reader := transform.NewReader(bytes.NewReader(data), unicode.BOMOverride(fallback))
|
reader := transform.NewReader(bytes.NewReader(data), unicode.BOMOverride(fallback))
|
||||||
|
|
||||||
@@ -60,5 +61,5 @@ func (c *jdComFinanceTransactionDataCsvFileImporter) ParseImportedData(ctx core.
|
|||||||
transactionDataTable := datatable.CreateNewTransactionDataTableFromCommonDataTable(commonDataTable, jdComFinanceTransactionSupportedColumns, transactionRowParser)
|
transactionDataTable := datatable.CreateNewTransactionDataTableFromCommonDataTable(commonDataTable, jdComFinanceTransactionSupportedColumns, transactionRowParser)
|
||||||
dataTableImporter := converter.CreateNewSimpleImporterWithTypeNameMapping(jdComFinanceTransactionTypeNameMapping)
|
dataTableImporter := converter.CreateNewSimpleImporterWithTypeNameMapping(jdComFinanceTransactionTypeNameMapping)
|
||||||
|
|
||||||
return dataTableImporter.ParseImportedData(ctx, user, transactionDataTable, defaultTimezoneOffset, accountMap, expenseCategoryMap, incomeCategoryMap, transferCategoryMap, tagMap)
|
return dataTableImporter.ParseImportedData(ctx, user, transactionDataTable, defaultTimezone, additionalOptions, accountMap, expenseCategoryMap, incomeCategoryMap, transferCategoryMap, tagMap)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import (
|
|||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
|
"github.com/mayswind/ezbookkeeping/pkg/converters/converter"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/core"
|
"github.com/mayswind/ezbookkeeping/pkg/core"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/errs"
|
"github.com/mayswind/ezbookkeeping/pkg/errs"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/models"
|
"github.com/mayswind/ezbookkeeping/pkg/models"
|
||||||
@@ -13,7 +14,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func TestJDComFinanceCsvFileImporterParseImportedData_MinimumValidData(t *testing.T) {
|
func TestJDComFinanceCsvFileImporterParseImportedData_MinimumValidData(t *testing.T) {
|
||||||
converter := JDComFinanceTransactionDataCsvFileImporter
|
importer := JDComFinanceTransactionDataCsvFileImporter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
@@ -30,7 +31,7 @@ func TestJDComFinanceCsvFileImporterParseImportedData_MinimumValidData(t *testin
|
|||||||
"2025-09-01 12:34:56,xxx,xxx,123.45,银行卡,交易成功,支出,其他网购\n" +
|
"2025-09-01 12:34:56,xxx,xxx,123.45,银行卡,交易成功,支出,其他网购\n" +
|
||||||
"2025-09-01 23:59:59,xxx,京东钱包余额充值,0.05,银行卡,交易成功,不计收支,余额\n" +
|
"2025-09-01 23:59:59,xxx,京东钱包余额充值,0.05,银行卡,交易成功,不计收支,余额\n" +
|
||||||
"2025-09-02 23:59:59,xxx,京东余额提现,0.03,银行卡,交易成功,不计收支,余额\n"
|
"2025-09-02 23:59:59,xxx,京东余额提现,0.03,银行卡,交易成功,不计收支,余额\n"
|
||||||
allNewTransactions, allNewAccounts, allNewSubExpenseCategories, allNewSubIncomeCategories, allNewSubTransferCategories, allNewTags, err := converter.ParseImportedData(context, user, []byte(data), 0, nil, nil, nil, nil, nil)
|
allNewTransactions, allNewAccounts, allNewSubExpenseCategories, allNewSubIncomeCategories, allNewSubTransferCategories, allNewTags, err := importer.ParseImportedData(context, user, []byte(data), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
assert.Equal(t, 4, len(allNewTransactions))
|
assert.Equal(t, 4, len(allNewTransactions))
|
||||||
@@ -93,7 +94,7 @@ func TestJDComFinanceCsvFileImporterParseImportedData_MinimumValidData(t *testin
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestJDComFinanceCsvFileImporterParseImportedData_ParseRefundTransaction(t *testing.T) {
|
func TestJDComFinanceCsvFileImporterParseImportedData_ParseRefundTransaction(t *testing.T) {
|
||||||
converter := JDComFinanceTransactionDataCsvFileImporter
|
importer := JDComFinanceTransactionDataCsvFileImporter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
@@ -110,7 +111,7 @@ func TestJDComFinanceCsvFileImporterParseImportedData_ParseRefundTransaction(t *
|
|||||||
"2025-09-01 02:34:56,xxx,xxx,0.12(已全额退款),银行卡,交易成功,不计收支\n" +
|
"2025-09-01 02:34:56,xxx,xxx,0.12(已全额退款),银行卡,交易成功,不计收支\n" +
|
||||||
"2025-09-02 01:23:45,xxx,xxx,3.45,银行卡,退款成功,不计收支\n" +
|
"2025-09-02 01:23:45,xxx,xxx,3.45,银行卡,退款成功,不计收支\n" +
|
||||||
"2025-09-02 02:34:56,xxx,xxx,123.45(已退款3.45),银行卡,交易成功,支出\n"
|
"2025-09-02 02:34:56,xxx,xxx,123.45(已退款3.45),银行卡,交易成功,支出\n"
|
||||||
allNewTransactions, _, _, _, _, _, err := converter.ParseImportedData(context, user, []byte(data1), 0, nil, nil, nil, nil, nil)
|
allNewTransactions, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(data1), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
assert.Equal(t, int64(1234567890), allNewTransactions[0].Uid)
|
assert.Equal(t, int64(1234567890), allNewTransactions[0].Uid)
|
||||||
@@ -139,7 +140,7 @@ func TestJDComFinanceCsvFileImporterParseImportedData_ParseRefundTransaction(t *
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestJDComFinanceCsvFileImporterParseImportedData_ParseInvalidTime(t *testing.T) {
|
func TestJDComFinanceCsvFileImporterParseImportedData_ParseInvalidTime(t *testing.T) {
|
||||||
converter := JDComFinanceTransactionDataCsvFileImporter
|
importer := JDComFinanceTransactionDataCsvFileImporter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
@@ -153,7 +154,7 @@ func TestJDComFinanceCsvFileImporterParseImportedData_ParseInvalidTime(t *testin
|
|||||||
"\n" +
|
"\n" +
|
||||||
"交易时间,商户名称,交易说明,金额,收/付款方式,交易状态,收/支\n" +
|
"交易时间,商户名称,交易说明,金额,收/付款方式,交易状态,收/支\n" +
|
||||||
"2025-09-01T01:23:45,xxx,xxx,0.12,银行卡,交易成功,支出\n"
|
"2025-09-01T01:23:45,xxx,xxx,0.12,银行卡,交易成功,支出\n"
|
||||||
_, _, _, _, _, _, err := converter.ParseImportedData(context, user, []byte(data1), 0, nil, nil, nil, nil, nil)
|
_, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(data1), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrTransactionTimeInvalid.Message)
|
assert.EqualError(t, err, errs.ErrTransactionTimeInvalid.Message)
|
||||||
|
|
||||||
data2 := "导出信息:\n" +
|
data2 := "导出信息:\n" +
|
||||||
@@ -162,12 +163,12 @@ func TestJDComFinanceCsvFileImporterParseImportedData_ParseInvalidTime(t *testin
|
|||||||
"\n" +
|
"\n" +
|
||||||
"交易时间,商户名称,交易说明,金额,收/付款方式,交易状态,收/支\n" +
|
"交易时间,商户名称,交易说明,金额,收/付款方式,交易状态,收/支\n" +
|
||||||
"09/01/2025 01:23:45,xxx,xxx,0.12,银行卡,交易成功,支出\n"
|
"09/01/2025 01:23:45,xxx,xxx,0.12,银行卡,交易成功,支出\n"
|
||||||
_, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte(data2), 0, nil, nil, nil, nil, nil)
|
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(data2), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrTransactionTimeInvalid.Message)
|
assert.EqualError(t, err, errs.ErrTransactionTimeInvalid.Message)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestJDComFinanceCsvFileImporterParseImportedData_ParseInvalidType(t *testing.T) {
|
func TestJDComFinanceCsvFileImporterParseImportedData_ParseInvalidType(t *testing.T) {
|
||||||
converter := JDComFinanceTransactionDataCsvFileImporter
|
importer := JDComFinanceTransactionDataCsvFileImporter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
@@ -181,12 +182,12 @@ func TestJDComFinanceCsvFileImporterParseImportedData_ParseInvalidType(t *testin
|
|||||||
"\n" +
|
"\n" +
|
||||||
"交易时间,商户名称,交易说明,金额,收/付款方式,交易状态,收/支\n" +
|
"交易时间,商户名称,交易说明,金额,收/付款方式,交易状态,收/支\n" +
|
||||||
"2025-09-01 01:23:45,xxx,xxx,0.12,银行卡,交易成功,转账\n"
|
"2025-09-01 01:23:45,xxx,xxx,0.12,银行卡,交易成功,转账\n"
|
||||||
_, _, _, _, _, _, err := converter.ParseImportedData(context, user, []byte(data), 0, nil, nil, nil, nil, nil)
|
_, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(data), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrNotFoundTransactionDataInFile.Message)
|
assert.EqualError(t, err, errs.ErrNotFoundTransactionDataInFile.Message)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestJDComFinanceCsvFileImporterParseImportedData_ParseInvalidAmount(t *testing.T) {
|
func TestJDComFinanceCsvFileImporterParseImportedData_ParseInvalidAmount(t *testing.T) {
|
||||||
converter := JDComFinanceTransactionDataCsvFileImporter
|
importer := JDComFinanceTransactionDataCsvFileImporter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
@@ -200,12 +201,12 @@ func TestJDComFinanceCsvFileImporterParseImportedData_ParseInvalidAmount(t *test
|
|||||||
"\n" +
|
"\n" +
|
||||||
"交易时间,商户名称,交易说明,金额,收/付款方式,交易状态,收/支\n" +
|
"交易时间,商户名称,交易说明,金额,收/付款方式,交易状态,收/支\n" +
|
||||||
"2025-09-01 01:23:45,xxx,xxx,¥0.12,银行卡,交易成功,支出\n"
|
"2025-09-01 01:23:45,xxx,xxx,¥0.12,银行卡,交易成功,支出\n"
|
||||||
_, _, _, _, _, _, err := converter.ParseImportedData(context, user, []byte(data), 0, nil, nil, nil, nil, nil)
|
_, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(data), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrAmountInvalid.Message)
|
assert.EqualError(t, err, errs.ErrAmountInvalid.Message)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestJDComFinanceCsvFileImporterParseImportedData_ParseAccountName(t *testing.T) {
|
func TestJDComFinanceCsvFileImporterParseImportedData_ParseAccountName(t *testing.T) {
|
||||||
converter := JDComFinanceTransactionDataCsvFileImporter
|
importer := JDComFinanceTransactionDataCsvFileImporter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
@@ -220,7 +221,7 @@ func TestJDComFinanceCsvFileImporterParseImportedData_ParseAccountName(t *testin
|
|||||||
"\n" +
|
"\n" +
|
||||||
"交易时间,商户名称,交易说明,金额,收/付款方式,交易状态,收/支,交易分类\n" +
|
"交易时间,商户名称,交易说明,金额,收/付款方式,交易状态,收/支,交易分类\n" +
|
||||||
"2025-09-01 01:23:45,xxx,京东钱包余额充值,0.05,银行卡,交易成功,不计收支,余额\n"
|
"2025-09-01 01:23:45,xxx,京东钱包余额充值,0.05,银行卡,交易成功,不计收支,余额\n"
|
||||||
allNewTransactions, _, _, _, _, _, err := converter.ParseImportedData(context, user, []byte(data1), 0, nil, nil, nil, nil, nil)
|
allNewTransactions, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(data1), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
assert.Equal(t, 1, len(allNewTransactions))
|
assert.Equal(t, 1, len(allNewTransactions))
|
||||||
@@ -235,7 +236,7 @@ func TestJDComFinanceCsvFileImporterParseImportedData_ParseAccountName(t *testin
|
|||||||
"\n" +
|
"\n" +
|
||||||
"交易时间,商户名称,交易说明,金额,收/付款方式,交易状态,收/支,交易分类\n" +
|
"交易时间,商户名称,交易说明,金额,收/付款方式,交易状态,收/支,交易分类\n" +
|
||||||
"2025-09-01 01:23:45,xxx,京东余额提现,0.05,银行卡,交易成功,不计收支,余额\n"
|
"2025-09-01 01:23:45,xxx,京东余额提现,0.05,银行卡,交易成功,不计收支,余额\n"
|
||||||
allNewTransactions, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte(data2), 0, nil, nil, nil, nil, nil)
|
allNewTransactions, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(data2), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
assert.Equal(t, 1, len(allNewTransactions))
|
assert.Equal(t, 1, len(allNewTransactions))
|
||||||
@@ -252,7 +253,7 @@ func TestJDComFinanceCsvFileImporterParseImportedData_ParseAccountName(t *testin
|
|||||||
"2025-09-01 01:23:45,xxx,京东小金库-转入,0.05,余额,交易成功,不计收支,小金库\n"
|
"2025-09-01 01:23:45,xxx,京东小金库-转入,0.05,余额,交易成功,不计收支,小金库\n"
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
allNewTransactions, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte(data3), 0, nil, nil, nil, nil, nil)
|
allNewTransactions, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(data3), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
assert.Equal(t, 1, len(allNewTransactions))
|
assert.Equal(t, 1, len(allNewTransactions))
|
||||||
@@ -269,7 +270,7 @@ func TestJDComFinanceCsvFileImporterParseImportedData_ParseAccountName(t *testin
|
|||||||
"2025-09-01 01:23:45,xxx,京东小金库-转出,0.05,余额,交易成功,不计收支,小金库\n"
|
"2025-09-01 01:23:45,xxx,京东小金库-转出,0.05,余额,交易成功,不计收支,小金库\n"
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
allNewTransactions, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte(data4), 0, nil, nil, nil, nil, nil)
|
allNewTransactions, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(data4), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
assert.Equal(t, 1, len(allNewTransactions))
|
assert.Equal(t, 1, len(allNewTransactions))
|
||||||
@@ -286,7 +287,7 @@ func TestJDComFinanceCsvFileImporterParseImportedData_ParseAccountName(t *testin
|
|||||||
"2025-09-01 01:23:45,xxx,价保退款,0.05,银行卡,交易成功,不计收支,其他\n"
|
"2025-09-01 01:23:45,xxx,价保退款,0.05,银行卡,交易成功,不计收支,其他\n"
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
allNewTransactions, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte(data5), 0, nil, nil, nil, nil, nil)
|
allNewTransactions, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(data5), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
assert.Equal(t, 1, len(allNewTransactions))
|
assert.Equal(t, 1, len(allNewTransactions))
|
||||||
@@ -302,7 +303,7 @@ func TestJDComFinanceCsvFileImporterParseImportedData_ParseAccountName(t *testin
|
|||||||
"2025-09-01 01:23:45,xxx,白条主动还款,0.05,银行卡,交易成功,不计收支,白条\n"
|
"2025-09-01 01:23:45,xxx,白条主动还款,0.05,银行卡,交易成功,不计收支,白条\n"
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
allNewTransactions, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte(data6), 0, nil, nil, nil, nil, nil)
|
allNewTransactions, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(data6), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
assert.Equal(t, 1, len(allNewTransactions))
|
assert.Equal(t, 1, len(allNewTransactions))
|
||||||
@@ -312,7 +313,7 @@ func TestJDComFinanceCsvFileImporterParseImportedData_ParseAccountName(t *testin
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestJDComFinanceCsvFileImporterParseImportedData_ParseDescription(t *testing.T) {
|
func TestJDComFinanceCsvFileImporterParseImportedData_ParseDescription(t *testing.T) {
|
||||||
converter := JDComFinanceTransactionDataCsvFileImporter
|
importer := JDComFinanceTransactionDataCsvFileImporter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
@@ -326,7 +327,7 @@ func TestJDComFinanceCsvFileImporterParseImportedData_ParseDescription(t *testin
|
|||||||
"\n" +
|
"\n" +
|
||||||
"交易时间,商户名称,交易说明,金额,收/付款方式,交易状态,收/支\n" +
|
"交易时间,商户名称,交易说明,金额,收/付款方式,交易状态,收/支\n" +
|
||||||
"2025-09-01 01:23:45,xxx,,0.12,银行卡,交易成功,支出\n"
|
"2025-09-01 01:23:45,xxx,,0.12,银行卡,交易成功,支出\n"
|
||||||
allNewTransactions, _, _, _, _, _, err := converter.ParseImportedData(context, user, []byte(data1), 0, nil, nil, nil, nil, nil)
|
allNewTransactions, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(data1), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
assert.Equal(t, 1, len(allNewTransactions))
|
assert.Equal(t, 1, len(allNewTransactions))
|
||||||
@@ -338,7 +339,7 @@ func TestJDComFinanceCsvFileImporterParseImportedData_ParseDescription(t *testin
|
|||||||
"\n" +
|
"\n" +
|
||||||
"交易时间,商户名称,交易说明,交易说明,金额,收/付款方式,交易状态,收/支,备注\n" +
|
"交易时间,商户名称,交易说明,交易说明,金额,收/付款方式,交易状态,收/支,备注\n" +
|
||||||
"2025-09-01 01:23:45,xxx,xxx,Test,0.12,银行卡,交易成功,支出,\"foo\"\"bar,\ntest\"\n"
|
"2025-09-01 01:23:45,xxx,xxx,Test,0.12,银行卡,交易成功,支出,\"foo\"\"bar,\ntest\"\n"
|
||||||
allNewTransactions, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte(data2), 0, nil, nil, nil, nil, nil)
|
allNewTransactions, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(data2), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.Equal(t, 1, len(allNewTransactions))
|
assert.Equal(t, 1, len(allNewTransactions))
|
||||||
assert.Equal(t, "foo\"bar,\ntest", allNewTransactions[0].Comment)
|
assert.Equal(t, "foo\"bar,\ntest", allNewTransactions[0].Comment)
|
||||||
|
|
||||||
@@ -348,13 +349,13 @@ func TestJDComFinanceCsvFileImporterParseImportedData_ParseDescription(t *testin
|
|||||||
"\n" +
|
"\n" +
|
||||||
"交易时间,商户名称,交易说明,交易说明,金额,收/付款方式,交易状态,收/支,备注\n" +
|
"交易时间,商户名称,交易说明,交易说明,金额,收/付款方式,交易状态,收/支,备注\n" +
|
||||||
"2025-09-01 01:23:45,xxx,xxx,Test,0.12,银行卡,交易成功,支出,\n"
|
"2025-09-01 01:23:45,xxx,xxx,Test,0.12,银行卡,交易成功,支出,\n"
|
||||||
allNewTransactions, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte(data3), 0, nil, nil, nil, nil, nil)
|
allNewTransactions, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(data3), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.Equal(t, 1, len(allNewTransactions))
|
assert.Equal(t, 1, len(allNewTransactions))
|
||||||
assert.Equal(t, "Test", allNewTransactions[0].Comment)
|
assert.Equal(t, "Test", allNewTransactions[0].Comment)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestJDComFinanceCsvFileImporterParseImportedData_SkipUnknownStatusTransaction(t *testing.T) {
|
func TestJDComFinanceCsvFileImporterParseImportedData_SkipUnknownStatusTransaction(t *testing.T) {
|
||||||
converter := JDComFinanceTransactionDataCsvFileImporter
|
importer := JDComFinanceTransactionDataCsvFileImporter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
@@ -368,12 +369,12 @@ func TestJDComFinanceCsvFileImporterParseImportedData_SkipUnknownStatusTransacti
|
|||||||
"\n" +
|
"\n" +
|
||||||
"交易时间,商户名称,交易说明,金额,收/付款方式,交易状态,收/支\n" +
|
"交易时间,商户名称,交易说明,金额,收/付款方式,交易状态,收/支\n" +
|
||||||
"2025-09-01 01:23:45,xxx,xxx,0.12,银行卡,xxxx,支出\n"
|
"2025-09-01 01:23:45,xxx,xxx,0.12,银行卡,xxxx,支出\n"
|
||||||
_, _, _, _, _, _, err := converter.ParseImportedData(context, user, []byte(data), 0, nil, nil, nil, nil, nil)
|
_, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(data), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrNotFoundTransactionDataInFile.Message)
|
assert.EqualError(t, err, errs.ErrNotFoundTransactionDataInFile.Message)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestJDComFinanceCsvFileImporterParseImportedData_SkipUnknownMemoTransferTransaction(t *testing.T) {
|
func TestJDComFinanceCsvFileImporterParseImportedData_SkipUnknownMemoTransferTransaction(t *testing.T) {
|
||||||
converter := JDComFinanceTransactionDataCsvFileImporter
|
importer := JDComFinanceTransactionDataCsvFileImporter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
@@ -387,12 +388,12 @@ func TestJDComFinanceCsvFileImporterParseImportedData_SkipUnknownMemoTransferTra
|
|||||||
"\n" +
|
"\n" +
|
||||||
"交易时间,商户名称,交易说明,金额,收/付款方式,交易状态,收/支\n" +
|
"交易时间,商户名称,交易说明,金额,收/付款方式,交易状态,收/支\n" +
|
||||||
"2025-09-01 01:23:45,xxx,xxx,0.12,银行卡,交易成功,不计收支\n"
|
"2025-09-01 01:23:45,xxx,xxx,0.12,银行卡,交易成功,不计收支\n"
|
||||||
_, _, _, _, _, _, err := converter.ParseImportedData(context, user, []byte(data), 0, nil, nil, nil, nil, nil)
|
_, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(data), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrNotFoundTransactionDataInFile.Message)
|
assert.EqualError(t, err, errs.ErrNotFoundTransactionDataInFile.Message)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestJDComFinanceCsvFileImporterParseImportedData_MissingFileHeader(t *testing.T) {
|
func TestJDComFinanceCsvFileImporterParseImportedData_MissingFileHeader(t *testing.T) {
|
||||||
converter := JDComFinanceTransactionDataCsvFileImporter
|
importer := JDComFinanceTransactionDataCsvFileImporter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
@@ -402,15 +403,15 @@ func TestJDComFinanceCsvFileImporterParseImportedData_MissingFileHeader(t *testi
|
|||||||
|
|
||||||
data := "交易时间,商户名称,交易说明,金额,收/付款方式,交易状态,收/支\n" +
|
data := "交易时间,商户名称,交易说明,金额,收/付款方式,交易状态,收/支\n" +
|
||||||
"2025-09-01 01:23:45,xxx,xxx,0.12,银行卡,交易成功,支出\n"
|
"2025-09-01 01:23:45,xxx,xxx,0.12,银行卡,交易成功,支出\n"
|
||||||
_, _, _, _, _, _, err := converter.ParseImportedData(context, user, []byte(data), 0, nil, nil, nil, nil, nil)
|
_, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(data), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrInvalidFileHeader.Message)
|
assert.EqualError(t, err, errs.ErrInvalidFileHeader.Message)
|
||||||
|
|
||||||
_, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte(""), 0, nil, nil, nil, nil, nil)
|
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(""), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrInvalidFileHeader.Message)
|
assert.EqualError(t, err, errs.ErrInvalidFileHeader.Message)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestJDComFinanceCsvFileImporterParseImportedData_MissingRequiredColumn(t *testing.T) {
|
func TestJDComFinanceCsvFileImporterParseImportedData_MissingRequiredColumn(t *testing.T) {
|
||||||
converter := JDComFinanceTransactionDataCsvFileImporter
|
importer := JDComFinanceTransactionDataCsvFileImporter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
@@ -425,7 +426,7 @@ func TestJDComFinanceCsvFileImporterParseImportedData_MissingRequiredColumn(t *t
|
|||||||
"\n" +
|
"\n" +
|
||||||
"商户名称,交易说明,金额,收/付款方式,交易状态,收/支\n" +
|
"商户名称,交易说明,金额,收/付款方式,交易状态,收/支\n" +
|
||||||
"xxx,xxx,0.12,银行卡,交易成功,支出\n"
|
"xxx,xxx,0.12,银行卡,交易成功,支出\n"
|
||||||
_, _, _, _, _, _, err := converter.ParseImportedData(context, user, []byte(data1), 0, nil, nil, nil, nil, nil)
|
_, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(data1), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrInvalidFileHeader.Message)
|
assert.EqualError(t, err, errs.ErrInvalidFileHeader.Message)
|
||||||
|
|
||||||
// Missing Merchant Name Column
|
// Missing Merchant Name Column
|
||||||
@@ -435,7 +436,7 @@ func TestJDComFinanceCsvFileImporterParseImportedData_MissingRequiredColumn(t *t
|
|||||||
"\n" +
|
"\n" +
|
||||||
"交易时间,交易说明,金额,收/付款方式,交易状态,收/支\n" +
|
"交易时间,交易说明,金额,收/付款方式,交易状态,收/支\n" +
|
||||||
"2025-09-01 01:23:45,xxx,0.12,银行卡,交易成功,支出\n"
|
"2025-09-01 01:23:45,xxx,0.12,银行卡,交易成功,支出\n"
|
||||||
_, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte(data2), 0, nil, nil, nil, nil, nil)
|
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(data2), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrMissingRequiredFieldInHeaderRow.Message)
|
assert.EqualError(t, err, errs.ErrMissingRequiredFieldInHeaderRow.Message)
|
||||||
|
|
||||||
// Missing Transaction Memo Column
|
// Missing Transaction Memo Column
|
||||||
@@ -445,7 +446,7 @@ func TestJDComFinanceCsvFileImporterParseImportedData_MissingRequiredColumn(t *t
|
|||||||
"\n" +
|
"\n" +
|
||||||
"交易时间,商户名称,金额,收/付款方式,交易状态,收/支\n" +
|
"交易时间,商户名称,金额,收/付款方式,交易状态,收/支\n" +
|
||||||
"2025-09-01 01:23:45,xxx,0.12,银行卡,交易成功,支出\n"
|
"2025-09-01 01:23:45,xxx,0.12,银行卡,交易成功,支出\n"
|
||||||
_, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte(data3), 0, nil, nil, nil, nil, nil)
|
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(data3), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrMissingRequiredFieldInHeaderRow.Message)
|
assert.EqualError(t, err, errs.ErrMissingRequiredFieldInHeaderRow.Message)
|
||||||
|
|
||||||
// Missing Amount Column
|
// Missing Amount Column
|
||||||
@@ -455,7 +456,7 @@ func TestJDComFinanceCsvFileImporterParseImportedData_MissingRequiredColumn(t *t
|
|||||||
"\n" +
|
"\n" +
|
||||||
"交易时间,商户名称,交易说明,收/付款方式,交易状态,收/支\n" +
|
"交易时间,商户名称,交易说明,收/付款方式,交易状态,收/支\n" +
|
||||||
"2025-09-01 01:23:45,xxx,xxx,银行卡,交易成功,支出\n"
|
"2025-09-01 01:23:45,xxx,xxx,银行卡,交易成功,支出\n"
|
||||||
_, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte(data4), 0, nil, nil, nil, nil, nil)
|
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(data4), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrMissingRequiredFieldInHeaderRow.Message)
|
assert.EqualError(t, err, errs.ErrMissingRequiredFieldInHeaderRow.Message)
|
||||||
|
|
||||||
// Missing Related Account Column
|
// Missing Related Account Column
|
||||||
@@ -465,7 +466,7 @@ func TestJDComFinanceCsvFileImporterParseImportedData_MissingRequiredColumn(t *t
|
|||||||
"\n" +
|
"\n" +
|
||||||
"交易时间,商户名称,交易说明,金额,交易状态,收/支\n" +
|
"交易时间,商户名称,交易说明,金额,交易状态,收/支\n" +
|
||||||
"2025-09-01 01:23:45,xxx,xxx,0.12,交易成功,支出\n"
|
"2025-09-01 01:23:45,xxx,xxx,0.12,交易成功,支出\n"
|
||||||
_, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte(data5), 0, nil, nil, nil, nil, nil)
|
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(data5), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrMissingRequiredFieldInHeaderRow.Message)
|
assert.EqualError(t, err, errs.ErrMissingRequiredFieldInHeaderRow.Message)
|
||||||
|
|
||||||
// Missing Status Column
|
// Missing Status Column
|
||||||
@@ -475,7 +476,7 @@ func TestJDComFinanceCsvFileImporterParseImportedData_MissingRequiredColumn(t *t
|
|||||||
"\n" +
|
"\n" +
|
||||||
"交易时间,商户名称,交易说明,金额,收/付款方式,收/支\n" +
|
"交易时间,商户名称,交易说明,金额,收/付款方式,收/支\n" +
|
||||||
"2025-09-01 01:23:45,xxx,xxx,0.12,银行卡,支出\n"
|
"2025-09-01 01:23:45,xxx,xxx,0.12,银行卡,支出\n"
|
||||||
_, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte(data6), 0, nil, nil, nil, nil, nil)
|
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(data6), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrMissingRequiredFieldInHeaderRow.Message)
|
assert.EqualError(t, err, errs.ErrMissingRequiredFieldInHeaderRow.Message)
|
||||||
|
|
||||||
// Missing Type Column
|
// Missing Type Column
|
||||||
@@ -485,12 +486,12 @@ func TestJDComFinanceCsvFileImporterParseImportedData_MissingRequiredColumn(t *t
|
|||||||
"\n" +
|
"\n" +
|
||||||
"交易时间,商户名称,交易说明,金额,收/付款方式,交易状态\n" +
|
"交易时间,商户名称,交易说明,金额,收/付款方式,交易状态\n" +
|
||||||
"2025-09-01 01:23:45,xxx,xxx,0.12,银行卡,交易成功\n"
|
"2025-09-01 01:23:45,xxx,xxx,0.12,银行卡,交易成功\n"
|
||||||
_, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte(data7), 0, nil, nil, nil, nil, nil)
|
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(data7), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrMissingRequiredFieldInHeaderRow.Message)
|
assert.EqualError(t, err, errs.ErrMissingRequiredFieldInHeaderRow.Message)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestJDComFinanceCsvFileImporterParseImportedData_NoTransactionData(t *testing.T) {
|
func TestJDComFinanceCsvFileImporterParseImportedData_NoTransactionData(t *testing.T) {
|
||||||
converter := JDComFinanceTransactionDataCsvFileImporter
|
importer := JDComFinanceTransactionDataCsvFileImporter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
@@ -503,6 +504,6 @@ func TestJDComFinanceCsvFileImporterParseImportedData_NoTransactionData(t *testi
|
|||||||
"日期区间:2025-01-01 至 2025-09-01\n" +
|
"日期区间:2025-01-01 至 2025-09-01\n" +
|
||||||
"\n" +
|
"\n" +
|
||||||
"交易时间,商户名称,交易说明,金额,收/付款方式,交易状态,收/支\n"
|
"交易时间,商户名称,交易说明,金额,收/付款方式,交易状态,收/支\n"
|
||||||
_, _, _, _, _, _, err := converter.ParseImportedData(context, user, []byte(data), 0, nil, nil, nil, nil, nil)
|
_, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(data), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrNotFoundTransactionDataInFile.Message)
|
assert.EqualError(t, err, errs.ErrNotFoundTransactionDataInFile.Message)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
package mt
|
package mt
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/converters/converter"
|
"github.com/mayswind/ezbookkeeping/pkg/converters/converter"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/core"
|
"github.com/mayswind/ezbookkeeping/pkg/core"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/models"
|
"github.com/mayswind/ezbookkeeping/pkg/models"
|
||||||
@@ -22,7 +24,7 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// ParseImportedData returns the imported data by parsing the mt940 file statement data
|
// ParseImportedData returns the imported data by parsing the mt940 file statement data
|
||||||
func (c *mt940TransactionDataFileImporter) ParseImportedData(ctx core.Context, user *models.User, data []byte, defaultTimezoneOffset int16, accountMap map[string]*models.Account, expenseCategoryMap map[string]map[string]*models.TransactionCategory, incomeCategoryMap map[string]map[string]*models.TransactionCategory, transferCategoryMap map[string]map[string]*models.TransactionCategory, tagMap map[string]*models.TransactionTag) (models.ImportedTransactionSlice, []*models.Account, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionTag, error) {
|
func (c *mt940TransactionDataFileImporter) ParseImportedData(ctx core.Context, user *models.User, data []byte, defaultTimezone *time.Location, additionalOptions converter.TransactionDataImporterOptions, accountMap map[string]*models.Account, expenseCategoryMap map[string]map[string]*models.TransactionCategory, incomeCategoryMap map[string]map[string]*models.TransactionCategory, transferCategoryMap map[string]map[string]*models.TransactionCategory, tagMap map[string]*models.TransactionTag) (models.ImportedTransactionSlice, []*models.Account, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionTag, error) {
|
||||||
mt940DataReader := createNewMT940FileReader(data)
|
mt940DataReader := createNewMT940FileReader(data)
|
||||||
mt940Data, err := mt940DataReader.read(ctx)
|
mt940Data, err := mt940DataReader.read(ctx)
|
||||||
|
|
||||||
@@ -38,5 +40,5 @@ func (c *mt940TransactionDataFileImporter) ParseImportedData(ctx core.Context, u
|
|||||||
|
|
||||||
dataTableImporter := converter.CreateNewSimpleImporterWithTypeNameMapping(mt940TransactionTypeNameMapping)
|
dataTableImporter := converter.CreateNewSimpleImporterWithTypeNameMapping(mt940TransactionTypeNameMapping)
|
||||||
|
|
||||||
return dataTableImporter.ParseImportedData(ctx, user, transactionDataTable, defaultTimezoneOffset, accountMap, expenseCategoryMap, incomeCategoryMap, transferCategoryMap, tagMap)
|
return dataTableImporter.ParseImportedData(ctx, user, transactionDataTable, defaultTimezone, additionalOptions, accountMap, expenseCategoryMap, incomeCategoryMap, transferCategoryMap, tagMap)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,9 +2,11 @@ package mt
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
|
"github.com/mayswind/ezbookkeeping/pkg/converters/converter"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/core"
|
"github.com/mayswind/ezbookkeeping/pkg/core"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/errs"
|
"github.com/mayswind/ezbookkeeping/pkg/errs"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/models"
|
"github.com/mayswind/ezbookkeeping/pkg/models"
|
||||||
@@ -12,7 +14,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func TestMT940TransactionDataFileParseImportedData_MinimumValidData(t *testing.T) {
|
func TestMT940TransactionDataFileParseImportedData_MinimumValidData(t *testing.T) {
|
||||||
converter := MT940TransactionDataFileImporter
|
importer := MT940TransactionDataFileImporter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
@@ -20,7 +22,7 @@ func TestMT940TransactionDataFileParseImportedData_MinimumValidData(t *testing.T
|
|||||||
DefaultCurrency: "CNY",
|
DefaultCurrency: "CNY",
|
||||||
}
|
}
|
||||||
|
|
||||||
allNewTransactions, allNewAccounts, allNewSubExpenseCategories, allNewSubIncomeCategories, allNewSubTransferCategories, allNewTags, err := converter.ParseImportedData(context, user, []byte(
|
allNewTransactions, allNewAccounts, allNewSubExpenseCategories, allNewSubIncomeCategories, allNewSubTransferCategories, allNewTags, err := importer.ParseImportedData(context, user, []byte(
|
||||||
`{1:F01TESTBANK123456789}{2:I940TESTBANK}{4:
|
`{1:F01TESTBANK123456789}{2:I940TESTBANK}{4:
|
||||||
:20:123456789
|
:20:123456789
|
||||||
:25:12345678
|
:25:12345678
|
||||||
@@ -31,7 +33,7 @@ func TestMT940TransactionDataFileParseImportedData_MinimumValidData(t *testing.T
|
|||||||
:61:2506020603D234,56NTRFFOOBAR
|
:61:2506020603D234,56NTRFFOOBAR
|
||||||
:86:Transaction 2
|
:86:Transaction 2
|
||||||
:62F:C250601CNY123,45
|
:62F:C250601CNY123,45
|
||||||
-}`), 0, nil, nil, nil, nil, nil)
|
-}`), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
|
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
@@ -70,7 +72,7 @@ func TestMT940TransactionDataFileParseImportedData_MinimumValidData(t *testing.T
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestMT940TransactionDataFileParseImportedData_ParseTransactionValidAmountAndCurrency(t *testing.T) {
|
func TestMT940TransactionDataFileParseImportedData_ParseTransactionValidAmountAndCurrency(t *testing.T) {
|
||||||
converter := MT940TransactionDataFileImporter
|
importer := MT940TransactionDataFileImporter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
@@ -78,7 +80,7 @@ func TestMT940TransactionDataFileParseImportedData_ParseTransactionValidAmountAn
|
|||||||
DefaultCurrency: "CNY",
|
DefaultCurrency: "CNY",
|
||||||
}
|
}
|
||||||
|
|
||||||
allNewTransactions, _, _, _, _, _, err := converter.ParseImportedData(context, user, []byte(
|
allNewTransactions, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(
|
||||||
`{1:F01TESTBANK123456789}{2:I940TESTBANK}{4:
|
`{1:F01TESTBANK123456789}{2:I940TESTBANK}{4:
|
||||||
:20:123456789
|
:20:123456789
|
||||||
:25:12345678
|
:25:12345678
|
||||||
@@ -91,7 +93,7 @@ func TestMT940TransactionDataFileParseImportedData_ParseTransactionValidAmountAn
|
|||||||
:61:250603C1,NTRFTEST
|
:61:250603C1,NTRFTEST
|
||||||
:86:Transaction 3
|
:86:Transaction 3
|
||||||
:62F:C250601CNY123,45
|
:62F:C250601CNY123,45
|
||||||
-}`), 0, nil, nil, nil, nil, nil)
|
-}`), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
|
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
assert.Equal(t, 3, len(allNewTransactions))
|
assert.Equal(t, 3, len(allNewTransactions))
|
||||||
@@ -101,7 +103,7 @@ func TestMT940TransactionDataFileParseImportedData_ParseTransactionValidAmountAn
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestMT940TransactionDataFileParseImportedData_ParseTransactionInvalidAmountAndCurrency(t *testing.T) {
|
func TestMT940TransactionDataFileParseImportedData_ParseTransactionInvalidAmountAndCurrency(t *testing.T) {
|
||||||
converter := MT940TransactionDataFileImporter
|
importer := MT940TransactionDataFileImporter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
@@ -109,7 +111,7 @@ func TestMT940TransactionDataFileParseImportedData_ParseTransactionInvalidAmount
|
|||||||
DefaultCurrency: "CNY",
|
DefaultCurrency: "CNY",
|
||||||
}
|
}
|
||||||
|
|
||||||
_, _, _, _, _, _, err := converter.ParseImportedData(context, user, []byte(
|
_, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(
|
||||||
`{1:F01TESTBANK123456789}{2:I940TESTBANK}{4:
|
`{1:F01TESTBANK123456789}{2:I940TESTBANK}{4:
|
||||||
:20:123456789
|
:20:123456789
|
||||||
:25:12345678
|
:25:12345678
|
||||||
@@ -117,10 +119,10 @@ func TestMT940TransactionDataFileParseImportedData_ParseTransactionInvalidAmount
|
|||||||
:60F:C250601CNY123,45
|
:60F:C250601CNY123,45
|
||||||
:61:2506010602C123 45NTRFTEST
|
:61:2506010602C123 45NTRFTEST
|
||||||
:62F:C250601CNY123,45
|
:62F:C250601CNY123,45
|
||||||
-}`), 0, nil, nil, nil, nil, nil)
|
-}`), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrInvalidMT940File.Message)
|
assert.EqualError(t, err, errs.ErrInvalidMT940File.Message)
|
||||||
|
|
||||||
_, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte(
|
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(
|
||||||
`{1:F01TESTBANK123456789}{2:I940TESTBANK}{4:
|
`{1:F01TESTBANK123456789}{2:I940TESTBANK}{4:
|
||||||
:20:123456789
|
:20:123456789
|
||||||
:25:12345678
|
:25:12345678
|
||||||
@@ -128,12 +130,12 @@ func TestMT940TransactionDataFileParseImportedData_ParseTransactionInvalidAmount
|
|||||||
:60F:C250601CNY123,45
|
:60F:C250601CNY123,45
|
||||||
:61:2506010602C12.45NTRFTEST
|
:61:2506010602C12.45NTRFTEST
|
||||||
:62F:C250601CNY123,45
|
:62F:C250601CNY123,45
|
||||||
-}`), 0, nil, nil, nil, nil, nil)
|
-}`), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrInvalidMT940File.Message)
|
assert.EqualError(t, err, errs.ErrInvalidMT940File.Message)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMT940TransactionDataFileParseImportedData_ParseTransactionType(t *testing.T) {
|
func TestMT940TransactionDataFileParseImportedData_ParseTransactionType(t *testing.T) {
|
||||||
converter := MT940TransactionDataFileImporter
|
importer := MT940TransactionDataFileImporter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
@@ -141,7 +143,7 @@ func TestMT940TransactionDataFileParseImportedData_ParseTransactionType(t *testi
|
|||||||
DefaultCurrency: "CNY",
|
DefaultCurrency: "CNY",
|
||||||
}
|
}
|
||||||
|
|
||||||
allNewTransactions, _, _, _, _, _, err := converter.ParseImportedData(context, user, []byte(
|
allNewTransactions, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(
|
||||||
`{1:F01TESTBANK123456789}{2:I940TESTBANK}{4:
|
`{1:F01TESTBANK123456789}{2:I940TESTBANK}{4:
|
||||||
:20:123456789
|
:20:123456789
|
||||||
:25:12345678
|
:25:12345678
|
||||||
@@ -156,7 +158,7 @@ func TestMT940TransactionDataFileParseImportedData_ParseTransactionType(t *testi
|
|||||||
:61:250604RD123,45NTRFTEST
|
:61:250604RD123,45NTRFTEST
|
||||||
:86:Transaction 4
|
:86:Transaction 4
|
||||||
:62F:C250601CNY123,45
|
:62F:C250601CNY123,45
|
||||||
-}`), 0, nil, nil, nil, nil, nil)
|
-}`), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
|
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
assert.Equal(t, 4, len(allNewTransactions))
|
assert.Equal(t, 4, len(allNewTransactions))
|
||||||
@@ -167,7 +169,7 @@ func TestMT940TransactionDataFileParseImportedData_ParseTransactionType(t *testi
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestMT940TransactionDataFileParseImportedData_ParseDescription(t *testing.T) {
|
func TestMT940TransactionDataFileParseImportedData_ParseDescription(t *testing.T) {
|
||||||
converter := MT940TransactionDataFileImporter
|
importer := MT940TransactionDataFileImporter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
@@ -175,7 +177,7 @@ func TestMT940TransactionDataFileParseImportedData_ParseDescription(t *testing.T
|
|||||||
DefaultCurrency: "CNY",
|
DefaultCurrency: "CNY",
|
||||||
}
|
}
|
||||||
|
|
||||||
allNewTransactions, _, _, _, _, _, err := converter.ParseImportedData(context, user, []byte(
|
allNewTransactions, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(
|
||||||
`{1:F01TESTBANK123456789}{2:I940TESTBANK}{4:
|
`{1:F01TESTBANK123456789}{2:I940TESTBANK}{4:
|
||||||
:20:123456789
|
:20:123456789
|
||||||
:25:12345678
|
:25:12345678
|
||||||
@@ -186,7 +188,7 @@ func TestMT940TransactionDataFileParseImportedData_ParseDescription(t *testing.T
|
|||||||
Part 2
|
Part 2
|
||||||
Part 3
|
Part 3
|
||||||
:62F:C250601CNY123,45
|
:62F:C250601CNY123,45
|
||||||
-}`), 0, nil, nil, nil, nil, nil)
|
-}`), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
|
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
assert.Equal(t, 1, len(allNewTransactions))
|
assert.Equal(t, 1, len(allNewTransactions))
|
||||||
@@ -194,7 +196,7 @@ func TestMT940TransactionDataFileParseImportedData_ParseDescription(t *testing.T
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestMT940TransactionDataFileParseImportedData_MissingRequiredField(t *testing.T) {
|
func TestMT940TransactionDataFileParseImportedData_MissingRequiredField(t *testing.T) {
|
||||||
converter := MT940TransactionDataFileImporter
|
importer := MT940TransactionDataFileImporter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
@@ -203,12 +205,12 @@ func TestMT940TransactionDataFileParseImportedData_MissingRequiredField(t *testi
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Missing opening balance and closing balance
|
// Missing opening balance and closing balance
|
||||||
_, _, _, _, _, _, err := converter.ParseImportedData(context, user, []byte(
|
_, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(
|
||||||
`{1:F01TESTBANK123456789}{2:I940TESTBANK}{4:
|
`{1:F01TESTBANK123456789}{2:I940TESTBANK}{4:
|
||||||
:20:123456789
|
:20:123456789
|
||||||
:28C:123/1
|
:28C:123/1
|
||||||
:61:250601C123,45NTRFTEST
|
:61:250601C123,45NTRFTEST
|
||||||
:86:Transaction 1
|
:86:Transaction 1
|
||||||
-}`), 0, nil, nil, nil, nil, nil)
|
-}`), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrAccountCurrencyInvalid.Message)
|
assert.EqualError(t, err, errs.ErrAccountCurrencyInvalid.Message)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ import (
|
|||||||
|
|
||||||
const ofx1USAsciiEncoding = "usascii"
|
const ofx1USAsciiEncoding = "usascii"
|
||||||
const ofx1UnicodeEncoding = "unicode"
|
const ofx1UnicodeEncoding = "unicode"
|
||||||
const ofx1UTF8Encoding = "utf8" // non-standard ofx 1.x encoding, used by some banks (https://github.com/mayswind/ezbookkeeping/issues/48)
|
const ofx1UTF8Encoding = "utf-8" // non-standard ofx 1.x encoding, used by some banks (https://github.com/mayswind/ezbookkeeping/issues/48)
|
||||||
const ofx1SGMLDataFormat = "OFXSGML"
|
const ofx1SGMLDataFormat = "OFXSGML"
|
||||||
|
|
||||||
var ofx2HeaderPattern = regexp.MustCompile("<\\?OFX( +[A-Z]+=\"[^=]*\")* *\\?>")
|
var ofx2HeaderPattern = regexp.MustCompile("<\\?OFX( +[A-Z]+=\"[^=]*\")* *\\?>")
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
package ofx
|
package ofx
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/converters/converter"
|
"github.com/mayswind/ezbookkeeping/pkg/converters/converter"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/core"
|
"github.com/mayswind/ezbookkeeping/pkg/core"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/models"
|
"github.com/mayswind/ezbookkeeping/pkg/models"
|
||||||
@@ -23,7 +25,7 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// ParseImportedData returns the imported data by parsing the open financial exchange (ofx) file transaction data
|
// ParseImportedData returns the imported data by parsing the open financial exchange (ofx) file transaction data
|
||||||
func (c *ofxTransactionDataImporter) ParseImportedData(ctx core.Context, user *models.User, data []byte, defaultTimezoneOffset int16, accountMap map[string]*models.Account, expenseCategoryMap map[string]map[string]*models.TransactionCategory, incomeCategoryMap map[string]map[string]*models.TransactionCategory, transferCategoryMap map[string]map[string]*models.TransactionCategory, tagMap map[string]*models.TransactionTag) (models.ImportedTransactionSlice, []*models.Account, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionTag, error) {
|
func (c *ofxTransactionDataImporter) ParseImportedData(ctx core.Context, user *models.User, data []byte, defaultTimezone *time.Location, additionalOptions converter.TransactionDataImporterOptions, accountMap map[string]*models.Account, expenseCategoryMap map[string]map[string]*models.TransactionCategory, incomeCategoryMap map[string]map[string]*models.TransactionCategory, transferCategoryMap map[string]map[string]*models.TransactionCategory, tagMap map[string]*models.TransactionTag) (models.ImportedTransactionSlice, []*models.Account, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionTag, error) {
|
||||||
ofxDataReader, err := createNewOFXFileReader(ctx, data)
|
ofxDataReader, err := createNewOFXFileReader(ctx, data)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -44,5 +46,5 @@ func (c *ofxTransactionDataImporter) ParseImportedData(ctx core.Context, user *m
|
|||||||
|
|
||||||
dataTableImporter := converter.CreateNewSimpleImporterWithTypeNameMapping(ofxTransactionTypeNameMapping)
|
dataTableImporter := converter.CreateNewSimpleImporterWithTypeNameMapping(ofxTransactionTypeNameMapping)
|
||||||
|
|
||||||
return dataTableImporter.ParseImportedData(ctx, user, transactionDataTable, defaultTimezoneOffset, accountMap, expenseCategoryMap, incomeCategoryMap, transferCategoryMap, tagMap)
|
return dataTableImporter.ParseImportedData(ctx, user, transactionDataTable, defaultTimezone, additionalOptions, accountMap, expenseCategoryMap, incomeCategoryMap, transferCategoryMap, tagMap)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,9 +2,11 @@ package ofx
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
|
"github.com/mayswind/ezbookkeeping/pkg/converters/converter"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/core"
|
"github.com/mayswind/ezbookkeeping/pkg/core"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/errs"
|
"github.com/mayswind/ezbookkeeping/pkg/errs"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/models"
|
"github.com/mayswind/ezbookkeeping/pkg/models"
|
||||||
@@ -12,7 +14,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func TestOFXTransactionDataFileParseImportedData_MinimumValidData(t *testing.T) {
|
func TestOFXTransactionDataFileParseImportedData_MinimumValidData(t *testing.T) {
|
||||||
converter := OFXTransactionDataImporter
|
importer := OFXTransactionDataImporter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
@@ -20,7 +22,7 @@ func TestOFXTransactionDataFileParseImportedData_MinimumValidData(t *testing.T)
|
|||||||
DefaultCurrency: "CNY",
|
DefaultCurrency: "CNY",
|
||||||
}
|
}
|
||||||
|
|
||||||
allNewTransactions, allNewAccounts, allNewSubExpenseCategories, allNewSubIncomeCategories, allNewSubTransferCategories, allNewTags, err := converter.ParseImportedData(context, user, []byte(
|
allNewTransactions, allNewAccounts, allNewSubExpenseCategories, allNewSubIncomeCategories, allNewSubTransferCategories, allNewTags, err := importer.ParseImportedData(context, user, []byte(
|
||||||
"<OFX>\n"+
|
"<OFX>\n"+
|
||||||
" <BANKMSGSRSV1>\n"+
|
" <BANKMSGSRSV1>\n"+
|
||||||
" <STMTTRNRS>\n"+
|
" <STMTTRNRS>\n"+
|
||||||
@@ -76,7 +78,7 @@ func TestOFXTransactionDataFileParseImportedData_MinimumValidData(t *testing.T)
|
|||||||
" </CCSTMTRS>\n"+
|
" </CCSTMTRS>\n"+
|
||||||
" </CCSTMTTRNRS>\n"+
|
" </CCSTMTTRNRS>\n"+
|
||||||
" </CREDITCARDMSGSRSV1>\n"+
|
" </CREDITCARDMSGSRSV1>\n"+
|
||||||
"</OFX>"), 0, nil, nil, nil, nil, nil)
|
"</OFX>"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
|
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
@@ -160,7 +162,7 @@ func TestOFXTransactionDataFileParseImportedData_MinimumValidData(t *testing.T)
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestOFXTransactionDataFileParseImportedData_ParseAccountTo(t *testing.T) {
|
func TestOFXTransactionDataFileParseImportedData_ParseAccountTo(t *testing.T) {
|
||||||
converter := OFXTransactionDataImporter
|
importer := OFXTransactionDataImporter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
@@ -168,7 +170,7 @@ func TestOFXTransactionDataFileParseImportedData_ParseAccountTo(t *testing.T) {
|
|||||||
DefaultCurrency: "CNY",
|
DefaultCurrency: "CNY",
|
||||||
}
|
}
|
||||||
|
|
||||||
allNewTransactions, allNewAccounts, _, _, _, _, err := converter.ParseImportedData(context, user, []byte(
|
allNewTransactions, allNewAccounts, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(
|
||||||
"<OFX>\n"+
|
"<OFX>\n"+
|
||||||
" <BANKMSGSRSV1>\n"+
|
" <BANKMSGSRSV1>\n"+
|
||||||
" <STMTTRNRS>\n"+
|
" <STMTTRNRS>\n"+
|
||||||
@@ -210,7 +212,7 @@ func TestOFXTransactionDataFileParseImportedData_ParseAccountTo(t *testing.T) {
|
|||||||
" </CCSTMTRS>\n"+
|
" </CCSTMTRS>\n"+
|
||||||
" </CCSTMTTRNRS>\n"+
|
" </CCSTMTTRNRS>\n"+
|
||||||
" </CREDITCARDMSGSRSV1>\n"+
|
" </CREDITCARDMSGSRSV1>\n"+
|
||||||
"</OFX>"), 0, nil, nil, nil, nil, nil)
|
"</OFX>"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
|
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
@@ -243,7 +245,7 @@ func TestOFXTransactionDataFileParseImportedData_ParseAccountTo(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestOFXTransactionDataFileParseImportedData_ParseValidTransactionTime(t *testing.T) {
|
func TestOFXTransactionDataFileParseImportedData_ParseValidTransactionTime(t *testing.T) {
|
||||||
converter := OFXTransactionDataImporter
|
importer := OFXTransactionDataImporter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
@@ -251,7 +253,7 @@ func TestOFXTransactionDataFileParseImportedData_ParseValidTransactionTime(t *te
|
|||||||
DefaultCurrency: "CNY",
|
DefaultCurrency: "CNY",
|
||||||
}
|
}
|
||||||
|
|
||||||
allNewTransactions, _, _, _, _, _, err := converter.ParseImportedData(context, user, []byte(
|
allNewTransactions, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(
|
||||||
"<OFX>\n"+
|
"<OFX>\n"+
|
||||||
" <BANKMSGSRSV1>\n"+
|
" <BANKMSGSRSV1>\n"+
|
||||||
" <STMTTRNRS>\n"+
|
" <STMTTRNRS>\n"+
|
||||||
@@ -295,7 +297,7 @@ func TestOFXTransactionDataFileParseImportedData_ParseValidTransactionTime(t *te
|
|||||||
" </STMTRS>\n"+
|
" </STMTRS>\n"+
|
||||||
" </STMTTRNRS>\n"+
|
" </STMTTRNRS>\n"+
|
||||||
" </BANKMSGSRSV1>\n"+
|
" </BANKMSGSRSV1>\n"+
|
||||||
"</OFX>"), 0, nil, nil, nil, nil, nil)
|
"</OFX>"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
|
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
@@ -310,7 +312,7 @@ func TestOFXTransactionDataFileParseImportedData_ParseValidTransactionTime(t *te
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestOFXTransactionDataFileParseImportedData_ParseInvalidTransactionTime(t *testing.T) {
|
func TestOFXTransactionDataFileParseImportedData_ParseInvalidTransactionTime(t *testing.T) {
|
||||||
converter := OFXTransactionDataImporter
|
importer := OFXTransactionDataImporter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
@@ -318,7 +320,7 @@ func TestOFXTransactionDataFileParseImportedData_ParseInvalidTransactionTime(t *
|
|||||||
DefaultCurrency: "CNY",
|
DefaultCurrency: "CNY",
|
||||||
}
|
}
|
||||||
|
|
||||||
_, _, _, _, _, _, err := converter.ParseImportedData(context, user, []byte(
|
_, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(
|
||||||
"<OFX>\n"+
|
"<OFX>\n"+
|
||||||
" <BANKMSGSRSV1>\n"+
|
" <BANKMSGSRSV1>\n"+
|
||||||
" <STMTTRNRS>\n"+
|
" <STMTTRNRS>\n"+
|
||||||
@@ -337,10 +339,10 @@ func TestOFXTransactionDataFileParseImportedData_ParseInvalidTransactionTime(t *
|
|||||||
" </STMTRS>\n"+
|
" </STMTRS>\n"+
|
||||||
" </STMTTRNRS>\n"+
|
" </STMTTRNRS>\n"+
|
||||||
" </BANKMSGSRSV1>\n"+
|
" </BANKMSGSRSV1>\n"+
|
||||||
"</OFX>"), 0, nil, nil, nil, nil, nil)
|
"</OFX>"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrTransactionTimeInvalid.Message)
|
assert.EqualError(t, err, errs.ErrTransactionTimeInvalid.Message)
|
||||||
|
|
||||||
_, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte(
|
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(
|
||||||
"<OFX>\n"+
|
"<OFX>\n"+
|
||||||
" <BANKMSGSRSV1>\n"+
|
" <BANKMSGSRSV1>\n"+
|
||||||
" <STMTTRNRS>\n"+
|
" <STMTTRNRS>\n"+
|
||||||
@@ -359,10 +361,10 @@ func TestOFXTransactionDataFileParseImportedData_ParseInvalidTransactionTime(t *
|
|||||||
" </STMTRS>\n"+
|
" </STMTRS>\n"+
|
||||||
" </STMTTRNRS>\n"+
|
" </STMTTRNRS>\n"+
|
||||||
" </BANKMSGSRSV1>\n"+
|
" </BANKMSGSRSV1>\n"+
|
||||||
"</OFX>"), 0, nil, nil, nil, nil, nil)
|
"</OFX>"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrTransactionTimeInvalid.Message)
|
assert.EqualError(t, err, errs.ErrTransactionTimeInvalid.Message)
|
||||||
|
|
||||||
_, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte(
|
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(
|
||||||
"<OFX>\n"+
|
"<OFX>\n"+
|
||||||
" <BANKMSGSRSV1>\n"+
|
" <BANKMSGSRSV1>\n"+
|
||||||
" <STMTTRNRS>\n"+
|
" <STMTTRNRS>\n"+
|
||||||
@@ -381,10 +383,10 @@ func TestOFXTransactionDataFileParseImportedData_ParseInvalidTransactionTime(t *
|
|||||||
" </STMTRS>\n"+
|
" </STMTRS>\n"+
|
||||||
" </STMTTRNRS>\n"+
|
" </STMTTRNRS>\n"+
|
||||||
" </BANKMSGSRSV1>\n"+
|
" </BANKMSGSRSV1>\n"+
|
||||||
"</OFX>"), 0, nil, nil, nil, nil, nil)
|
"</OFX>"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrTransactionTimeInvalid.Message)
|
assert.EqualError(t, err, errs.ErrTransactionTimeInvalid.Message)
|
||||||
|
|
||||||
_, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte(
|
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(
|
||||||
"<OFX>\n"+
|
"<OFX>\n"+
|
||||||
" <BANKMSGSRSV1>\n"+
|
" <BANKMSGSRSV1>\n"+
|
||||||
" <STMTTRNRS>\n"+
|
" <STMTTRNRS>\n"+
|
||||||
@@ -403,12 +405,12 @@ func TestOFXTransactionDataFileParseImportedData_ParseInvalidTransactionTime(t *
|
|||||||
" </STMTRS>\n"+
|
" </STMTRS>\n"+
|
||||||
" </STMTTRNRS>\n"+
|
" </STMTTRNRS>\n"+
|
||||||
" </BANKMSGSRSV1>\n"+
|
" </BANKMSGSRSV1>\n"+
|
||||||
"</OFX>"), 0, nil, nil, nil, nil, nil)
|
"</OFX>"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrTransactionTimeInvalid.Message)
|
assert.EqualError(t, err, errs.ErrTransactionTimeInvalid.Message)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestOFXTransactionDataFileParseImportedData_ParseAmount_CommaAsDecimalPoint(t *testing.T) {
|
func TestOFXTransactionDataFileParseImportedData_ParseAmount_CommaAsDecimalPoint(t *testing.T) {
|
||||||
converter := OFXTransactionDataImporter
|
importer := OFXTransactionDataImporter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
@@ -416,7 +418,7 @@ func TestOFXTransactionDataFileParseImportedData_ParseAmount_CommaAsDecimalPoint
|
|||||||
DefaultCurrency: "CNY",
|
DefaultCurrency: "CNY",
|
||||||
}
|
}
|
||||||
|
|
||||||
allNewTransactions, _, _, _, _, _, err := converter.ParseImportedData(context, user, []byte(
|
allNewTransactions, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(
|
||||||
"<OFX>\n"+
|
"<OFX>\n"+
|
||||||
" <BANKMSGSRSV1>\n"+
|
" <BANKMSGSRSV1>\n"+
|
||||||
" <STMTTRNRS>\n"+
|
" <STMTTRNRS>\n"+
|
||||||
@@ -435,7 +437,7 @@ func TestOFXTransactionDataFileParseImportedData_ParseAmount_CommaAsDecimalPoint
|
|||||||
" </STMTRS>\n"+
|
" </STMTRS>\n"+
|
||||||
" </STMTTRNRS>\n"+
|
" </STMTTRNRS>\n"+
|
||||||
" </BANKMSGSRSV1>\n"+
|
" </BANKMSGSRSV1>\n"+
|
||||||
"</OFX>"), 0, nil, nil, nil, nil, nil)
|
"</OFX>"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
|
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
@@ -444,7 +446,7 @@ func TestOFXTransactionDataFileParseImportedData_ParseAmount_CommaAsDecimalPoint
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestOFXTransactionDataFileParseImportedData_ParseInvalidAmount(t *testing.T) {
|
func TestOFXTransactionDataFileParseImportedData_ParseInvalidAmount(t *testing.T) {
|
||||||
converter := OFXTransactionDataImporter
|
importer := OFXTransactionDataImporter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
@@ -452,7 +454,7 @@ func TestOFXTransactionDataFileParseImportedData_ParseInvalidAmount(t *testing.T
|
|||||||
DefaultCurrency: "CNY",
|
DefaultCurrency: "CNY",
|
||||||
}
|
}
|
||||||
|
|
||||||
_, _, _, _, _, _, err := converter.ParseImportedData(context, user, []byte(
|
_, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(
|
||||||
"<OFX>\n"+
|
"<OFX>\n"+
|
||||||
" <BANKMSGSRSV1>\n"+
|
" <BANKMSGSRSV1>\n"+
|
||||||
" <STMTTRNRS>\n"+
|
" <STMTTRNRS>\n"+
|
||||||
@@ -471,12 +473,12 @@ func TestOFXTransactionDataFileParseImportedData_ParseInvalidAmount(t *testing.T
|
|||||||
" </STMTRS>\n"+
|
" </STMTRS>\n"+
|
||||||
" </STMTTRNRS>\n"+
|
" </STMTTRNRS>\n"+
|
||||||
" </BANKMSGSRSV1>\n"+
|
" </BANKMSGSRSV1>\n"+
|
||||||
"</OFX>"), 0, nil, nil, nil, nil, nil)
|
"</OFX>"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrAmountInvalid.Message)
|
assert.EqualError(t, err, errs.ErrAmountInvalid.Message)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestOFXTransactionDataFileParseImportedData_ParseTransactionCurrency(t *testing.T) {
|
func TestOFXTransactionDataFileParseImportedData_ParseTransactionCurrency(t *testing.T) {
|
||||||
converter := OFXTransactionDataImporter
|
importer := OFXTransactionDataImporter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
@@ -484,7 +486,7 @@ func TestOFXTransactionDataFileParseImportedData_ParseTransactionCurrency(t *tes
|
|||||||
DefaultCurrency: "CNY",
|
DefaultCurrency: "CNY",
|
||||||
}
|
}
|
||||||
|
|
||||||
allNewTransactions, _, _, _, _, _, err := converter.ParseImportedData(context, user, []byte(
|
allNewTransactions, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(
|
||||||
"<OFX>\n"+
|
"<OFX>\n"+
|
||||||
" <BANKMSGSRSV1>\n"+
|
" <BANKMSGSRSV1>\n"+
|
||||||
" <STMTTRNRS>\n"+
|
" <STMTTRNRS>\n"+
|
||||||
@@ -504,7 +506,7 @@ func TestOFXTransactionDataFileParseImportedData_ParseTransactionCurrency(t *tes
|
|||||||
" </STMTRS>\n"+
|
" </STMTRS>\n"+
|
||||||
" </STMTTRNRS>\n"+
|
" </STMTTRNRS>\n"+
|
||||||
" </BANKMSGSRSV1>\n"+
|
" </BANKMSGSRSV1>\n"+
|
||||||
"</OFX>"), 0, nil, nil, nil, nil, nil)
|
"</OFX>"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
|
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
assert.Equal(t, 1, len(allNewTransactions))
|
assert.Equal(t, 1, len(allNewTransactions))
|
||||||
@@ -512,7 +514,7 @@ func TestOFXTransactionDataFileParseImportedData_ParseTransactionCurrency(t *tes
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestOFXTransactionDataFileParseImportedData_ParseDescription(t *testing.T) {
|
func TestOFXTransactionDataFileParseImportedData_ParseDescription(t *testing.T) {
|
||||||
converter := OFXTransactionDataImporter
|
importer := OFXTransactionDataImporter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
@@ -520,7 +522,7 @@ func TestOFXTransactionDataFileParseImportedData_ParseDescription(t *testing.T)
|
|||||||
DefaultCurrency: "CNY",
|
DefaultCurrency: "CNY",
|
||||||
}
|
}
|
||||||
|
|
||||||
allNewTransactions, _, _, _, _, _, err := converter.ParseImportedData(context, user, []byte(
|
allNewTransactions, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(
|
||||||
"<OFX>\n"+
|
"<OFX>\n"+
|
||||||
" <BANKMSGSRSV1>\n"+
|
" <BANKMSGSRSV1>\n"+
|
||||||
" <STMTTRNRS>\n"+
|
" <STMTTRNRS>\n"+
|
||||||
@@ -541,13 +543,13 @@ func TestOFXTransactionDataFileParseImportedData_ParseDescription(t *testing.T)
|
|||||||
" </STMTRS>\n"+
|
" </STMTRS>\n"+
|
||||||
" </STMTTRNRS>\n"+
|
" </STMTTRNRS>\n"+
|
||||||
" </BANKMSGSRSV1>\n"+
|
" </BANKMSGSRSV1>\n"+
|
||||||
"</OFX>"), 0, nil, nil, nil, nil, nil)
|
"</OFX>"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
|
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
assert.Equal(t, 1, len(allNewTransactions))
|
assert.Equal(t, 1, len(allNewTransactions))
|
||||||
assert.Equal(t, "foo bar\t#test", allNewTransactions[0].Comment)
|
assert.Equal(t, "foo bar\t#test", allNewTransactions[0].Comment)
|
||||||
|
|
||||||
allNewTransactions, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte(
|
allNewTransactions, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(
|
||||||
"<OFX>\n"+
|
"<OFX>\n"+
|
||||||
" <BANKMSGSRSV1>\n"+
|
" <BANKMSGSRSV1>\n"+
|
||||||
" <STMTTRNRS>\n"+
|
" <STMTTRNRS>\n"+
|
||||||
@@ -567,13 +569,13 @@ func TestOFXTransactionDataFileParseImportedData_ParseDescription(t *testing.T)
|
|||||||
" </STMTRS>\n"+
|
" </STMTRS>\n"+
|
||||||
" </STMTTRNRS>\n"+
|
" </STMTTRNRS>\n"+
|
||||||
" </BANKMSGSRSV1>\n"+
|
" </BANKMSGSRSV1>\n"+
|
||||||
"</OFX>"), 0, nil, nil, nil, nil, nil)
|
"</OFX>"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
|
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
assert.Equal(t, 1, len(allNewTransactions))
|
assert.Equal(t, 1, len(allNewTransactions))
|
||||||
assert.Equal(t, "Test", allNewTransactions[0].Comment)
|
assert.Equal(t, "Test", allNewTransactions[0].Comment)
|
||||||
|
|
||||||
allNewTransactions, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte(
|
allNewTransactions, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(
|
||||||
"<OFX>\n"+
|
"<OFX>\n"+
|
||||||
" <BANKMSGSRSV1>\n"+
|
" <BANKMSGSRSV1>\n"+
|
||||||
" <STMTTRNRS>\n"+
|
" <STMTTRNRS>\n"+
|
||||||
@@ -595,7 +597,7 @@ func TestOFXTransactionDataFileParseImportedData_ParseDescription(t *testing.T)
|
|||||||
" </STMTRS>\n"+
|
" </STMTRS>\n"+
|
||||||
" </STMTTRNRS>\n"+
|
" </STMTTRNRS>\n"+
|
||||||
" </BANKMSGSRSV1>\n"+
|
" </BANKMSGSRSV1>\n"+
|
||||||
"</OFX>"), 0, nil, nil, nil, nil, nil)
|
"</OFX>"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
|
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
assert.Equal(t, 1, len(allNewTransactions))
|
assert.Equal(t, 1, len(allNewTransactions))
|
||||||
@@ -603,7 +605,7 @@ func TestOFXTransactionDataFileParseImportedData_ParseDescription(t *testing.T)
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestOFXTransactionDataFileParseImportedData_MissingAccountFromNode(t *testing.T) {
|
func TestOFXTransactionDataFileParseImportedData_MissingAccountFromNode(t *testing.T) {
|
||||||
converter := OFXTransactionDataImporter
|
importer := OFXTransactionDataImporter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
@@ -612,7 +614,7 @@ func TestOFXTransactionDataFileParseImportedData_MissingAccountFromNode(t *testi
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Missing Posted Date Node
|
// Missing Posted Date Node
|
||||||
_, _, _, _, _, _, err := converter.ParseImportedData(context, user, []byte(
|
_, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(
|
||||||
"<OFX>\n"+
|
"<OFX>\n"+
|
||||||
" <BANKMSGSRSV1>\n"+
|
" <BANKMSGSRSV1>\n"+
|
||||||
" <STMTTRNRS>\n"+
|
" <STMTTRNRS>\n"+
|
||||||
@@ -628,12 +630,12 @@ func TestOFXTransactionDataFileParseImportedData_MissingAccountFromNode(t *testi
|
|||||||
" </STMTRS>\n"+
|
" </STMTRS>\n"+
|
||||||
" </STMTTRNRS>\n"+
|
" </STMTTRNRS>\n"+
|
||||||
" </BANKMSGSRSV1>\n"+
|
" </BANKMSGSRSV1>\n"+
|
||||||
"</OFX>"), 0, nil, nil, nil, nil, nil)
|
"</OFX>"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrMissingAccountData.Message)
|
assert.EqualError(t, err, errs.ErrMissingAccountData.Message)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestOFXTransactionDataFileParseImportedData_MissingCurrencyNode(t *testing.T) {
|
func TestOFXTransactionDataFileParseImportedData_MissingCurrencyNode(t *testing.T) {
|
||||||
converter := OFXTransactionDataImporter
|
importer := OFXTransactionDataImporter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
@@ -642,7 +644,7 @@ func TestOFXTransactionDataFileParseImportedData_MissingCurrencyNode(t *testing.
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Missing Default Currency Node
|
// Missing Default Currency Node
|
||||||
_, _, _, _, _, _, err := converter.ParseImportedData(context, user, []byte(
|
_, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(
|
||||||
"<OFX>\n"+
|
"<OFX>\n"+
|
||||||
" <BANKMSGSRSV1>\n"+
|
" <BANKMSGSRSV1>\n"+
|
||||||
" <STMTTRNRS>\n"+
|
" <STMTTRNRS>\n"+
|
||||||
@@ -660,12 +662,12 @@ func TestOFXTransactionDataFileParseImportedData_MissingCurrencyNode(t *testing.
|
|||||||
" </STMTRS>\n"+
|
" </STMTRS>\n"+
|
||||||
" </STMTTRNRS>\n"+
|
" </STMTTRNRS>\n"+
|
||||||
" </BANKMSGSRSV1>\n"+
|
" </BANKMSGSRSV1>\n"+
|
||||||
"</OFX>"), 0, nil, nil, nil, nil, nil)
|
"</OFX>"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrAccountCurrencyInvalid.Message)
|
assert.EqualError(t, err, errs.ErrAccountCurrencyInvalid.Message)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestOFXTransactionDataFileParseImportedData_MissingTransactionRequiredNode(t *testing.T) {
|
func TestOFXTransactionDataFileParseImportedData_MissingTransactionRequiredNode(t *testing.T) {
|
||||||
converter := OFXTransactionDataImporter
|
importer := OFXTransactionDataImporter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
@@ -674,7 +676,7 @@ func TestOFXTransactionDataFileParseImportedData_MissingTransactionRequiredNode(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Missing Posted Date Node
|
// Missing Posted Date Node
|
||||||
_, _, _, _, _, _, err := converter.ParseImportedData(context, user, []byte(
|
_, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(
|
||||||
"<OFX>\n"+
|
"<OFX>\n"+
|
||||||
" <BANKMSGSRSV1>\n"+
|
" <BANKMSGSRSV1>\n"+
|
||||||
" <STMTTRNRS>\n"+
|
" <STMTTRNRS>\n"+
|
||||||
@@ -692,11 +694,11 @@ func TestOFXTransactionDataFileParseImportedData_MissingTransactionRequiredNode(
|
|||||||
" </STMTRS>\n"+
|
" </STMTRS>\n"+
|
||||||
" </STMTTRNRS>\n"+
|
" </STMTTRNRS>\n"+
|
||||||
" </BANKMSGSRSV1>\n"+
|
" </BANKMSGSRSV1>\n"+
|
||||||
"</OFX>"), 0, nil, nil, nil, nil, nil)
|
"</OFX>"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrMissingTransactionTime.Message)
|
assert.EqualError(t, err, errs.ErrMissingTransactionTime.Message)
|
||||||
|
|
||||||
// Missing Transaction Type Node
|
// Missing Transaction Type Node
|
||||||
_, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte(
|
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(
|
||||||
"<OFX>\n"+
|
"<OFX>\n"+
|
||||||
" <BANKMSGSRSV1>\n"+
|
" <BANKMSGSRSV1>\n"+
|
||||||
" <STMTTRNRS>\n"+
|
" <STMTTRNRS>\n"+
|
||||||
@@ -714,11 +716,11 @@ func TestOFXTransactionDataFileParseImportedData_MissingTransactionRequiredNode(
|
|||||||
" </STMTRS>\n"+
|
" </STMTRS>\n"+
|
||||||
" </STMTTRNRS>\n"+
|
" </STMTTRNRS>\n"+
|
||||||
" </BANKMSGSRSV1>\n"+
|
" </BANKMSGSRSV1>\n"+
|
||||||
"</OFX>"), 0, nil, nil, nil, nil, nil)
|
"</OFX>"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrTransactionTypeInvalid.Message)
|
assert.EqualError(t, err, errs.ErrTransactionTypeInvalid.Message)
|
||||||
|
|
||||||
// Missing Amount Node
|
// Missing Amount Node
|
||||||
_, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte(
|
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(
|
||||||
"<OFX>\n"+
|
"<OFX>\n"+
|
||||||
" <BANKMSGSRSV1>\n"+
|
" <BANKMSGSRSV1>\n"+
|
||||||
" <STMTTRNRS>\n"+
|
" <STMTTRNRS>\n"+
|
||||||
@@ -736,6 +738,6 @@ func TestOFXTransactionDataFileParseImportedData_MissingTransactionRequiredNode(
|
|||||||
" </STMTRS>\n"+
|
" </STMTRS>\n"+
|
||||||
" </STMTTRNRS>\n"+
|
" </STMTTRNRS>\n"+
|
||||||
" </BANKMSGSRSV1>\n"+
|
" </BANKMSGSRSV1>\n"+
|
||||||
"</OFX>"), 0, nil, nil, nil, nil, nil)
|
"</OFX>"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrAmountInvalid.Message)
|
assert.EqualError(t, err, errs.ErrAmountInvalid.Message)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
package qif
|
package qif
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/converters/converter"
|
"github.com/mayswind/ezbookkeeping/pkg/converters/converter"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/core"
|
"github.com/mayswind/ezbookkeeping/pkg/core"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/models"
|
"github.com/mayswind/ezbookkeeping/pkg/models"
|
||||||
@@ -35,7 +37,7 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// ParseImportedData returns the imported data by parsing the quicken interchange format (qif) transaction data
|
// ParseImportedData returns the imported data by parsing the quicken interchange format (qif) transaction data
|
||||||
func (c *qifTransactionDataImporter) ParseImportedData(ctx core.Context, user *models.User, data []byte, defaultTimezoneOffset int16, accountMap map[string]*models.Account, expenseCategoryMap map[string]map[string]*models.TransactionCategory, incomeCategoryMap map[string]map[string]*models.TransactionCategory, transferCategoryMap map[string]map[string]*models.TransactionCategory, tagMap map[string]*models.TransactionTag) (models.ImportedTransactionSlice, []*models.Account, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionTag, error) {
|
func (c *qifTransactionDataImporter) ParseImportedData(ctx core.Context, user *models.User, data []byte, defaultTimezone *time.Location, additionalOptions converter.TransactionDataImporterOptions, accountMap map[string]*models.Account, expenseCategoryMap map[string]map[string]*models.TransactionCategory, incomeCategoryMap map[string]map[string]*models.TransactionCategory, transferCategoryMap map[string]map[string]*models.TransactionCategory, tagMap map[string]*models.TransactionTag) (models.ImportedTransactionSlice, []*models.Account, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionTag, error) {
|
||||||
qifDataReader := createNewQifDataReader(data)
|
qifDataReader := createNewQifDataReader(data)
|
||||||
qifData, err := qifDataReader.read(ctx)
|
qifData, err := qifDataReader.read(ctx)
|
||||||
|
|
||||||
@@ -51,5 +53,5 @@ func (c *qifTransactionDataImporter) ParseImportedData(ctx core.Context, user *m
|
|||||||
|
|
||||||
dataTableImporter := converter.CreateNewSimpleImporterWithTypeNameMapping(qifTransactionTypeNameMapping)
|
dataTableImporter := converter.CreateNewSimpleImporterWithTypeNameMapping(qifTransactionTypeNameMapping)
|
||||||
|
|
||||||
return dataTableImporter.ParseImportedData(ctx, user, transactionDataTable, defaultTimezoneOffset, accountMap, expenseCategoryMap, incomeCategoryMap, transferCategoryMap, tagMap)
|
return dataTableImporter.ParseImportedData(ctx, user, transactionDataTable, defaultTimezone, additionalOptions, accountMap, expenseCategoryMap, incomeCategoryMap, transferCategoryMap, tagMap)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,9 +2,11 @@ package qif
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
|
"github.com/mayswind/ezbookkeeping/pkg/converters/converter"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/core"
|
"github.com/mayswind/ezbookkeeping/pkg/core"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/errs"
|
"github.com/mayswind/ezbookkeeping/pkg/errs"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/models"
|
"github.com/mayswind/ezbookkeeping/pkg/models"
|
||||||
@@ -12,7 +14,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func TestQIFTransactionDataFileParseImportedData_MinimumValidData(t *testing.T) {
|
func TestQIFTransactionDataFileParseImportedData_MinimumValidData(t *testing.T) {
|
||||||
converter := QifYearMonthDayTransactionDataImporter
|
importer := QifYearMonthDayTransactionDataImporter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
@@ -20,7 +22,7 @@ func TestQIFTransactionDataFileParseImportedData_MinimumValidData(t *testing.T)
|
|||||||
DefaultCurrency: "CNY",
|
DefaultCurrency: "CNY",
|
||||||
}
|
}
|
||||||
|
|
||||||
allNewTransactions, allNewAccounts, allNewSubExpenseCategories, allNewSubIncomeCategories, allNewSubTransferCategories, allNewTags, err := converter.ParseImportedData(context, user, []byte(
|
allNewTransactions, allNewAccounts, allNewSubExpenseCategories, allNewSubIncomeCategories, allNewSubTransferCategories, allNewTags, err := importer.ParseImportedData(context, user, []byte(
|
||||||
"!Type:Bank\n"+
|
"!Type:Bank\n"+
|
||||||
"D2024-09-01\n"+
|
"D2024-09-01\n"+
|
||||||
"T123.45\n"+
|
"T123.45\n"+
|
||||||
@@ -42,7 +44,7 @@ func TestQIFTransactionDataFileParseImportedData_MinimumValidData(t *testing.T)
|
|||||||
"D2024-09-05\n"+
|
"D2024-09-05\n"+
|
||||||
"T0.06\n"+
|
"T0.06\n"+
|
||||||
"L[Test Account2]\n"+
|
"L[Test Account2]\n"+
|
||||||
"^\n"), 0, nil, nil, nil, nil, nil)
|
"^\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
|
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
@@ -113,7 +115,7 @@ func TestQIFTransactionDataFileParseImportedData_MinimumValidData(t *testing.T)
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestQIFTransactionDataFileParseImportedData_ParseYearMonthDayDateFormatTime(t *testing.T) {
|
func TestQIFTransactionDataFileParseImportedData_ParseYearMonthDayDateFormatTime(t *testing.T) {
|
||||||
converter := QifYearMonthDayTransactionDataImporter
|
importer := QifYearMonthDayTransactionDataImporter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
@@ -121,7 +123,7 @@ func TestQIFTransactionDataFileParseImportedData_ParseYearMonthDayDateFormatTime
|
|||||||
DefaultCurrency: "CNY",
|
DefaultCurrency: "CNY",
|
||||||
}
|
}
|
||||||
|
|
||||||
allNewTransactions, _, _, _, _, _, err := converter.ParseImportedData(context, user, []byte(
|
allNewTransactions, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(
|
||||||
"!Type:Bank\n"+
|
"!Type:Bank\n"+
|
||||||
"D2024-09-01\n"+
|
"D2024-09-01\n"+
|
||||||
"T-123.45\n"+
|
"T-123.45\n"+
|
||||||
@@ -137,7 +139,7 @@ func TestQIFTransactionDataFileParseImportedData_ParseYearMonthDayDateFormatTime
|
|||||||
"^\n"+
|
"^\n"+
|
||||||
"D2024'9.5\n"+
|
"D2024'9.5\n"+
|
||||||
"T-123.45\n"+
|
"T-123.45\n"+
|
||||||
"^\n"), 0, nil, nil, nil, nil, nil)
|
"^\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
|
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
@@ -151,7 +153,7 @@ func TestQIFTransactionDataFileParseImportedData_ParseYearMonthDayDateFormatTime
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestQIFTransactionDataFileParseImportedData_ParseMonthDayYearDateFormatTime(t *testing.T) {
|
func TestQIFTransactionDataFileParseImportedData_ParseMonthDayYearDateFormatTime(t *testing.T) {
|
||||||
converter := QifMonthDayYearTransactionDataImporter
|
importer := QifMonthDayYearTransactionDataImporter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
@@ -159,7 +161,7 @@ func TestQIFTransactionDataFileParseImportedData_ParseMonthDayYearDateFormatTime
|
|||||||
DefaultCurrency: "CNY",
|
DefaultCurrency: "CNY",
|
||||||
}
|
}
|
||||||
|
|
||||||
allNewTransactions, _, _, _, _, _, err := converter.ParseImportedData(context, user, []byte(
|
allNewTransactions, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(
|
||||||
"!Type:Bank\n"+
|
"!Type:Bank\n"+
|
||||||
"D09-01-2024\n"+
|
"D09-01-2024\n"+
|
||||||
"T-123.45\n"+
|
"T-123.45\n"+
|
||||||
@@ -175,7 +177,7 @@ func TestQIFTransactionDataFileParseImportedData_ParseMonthDayYearDateFormatTime
|
|||||||
"^\n"+
|
"^\n"+
|
||||||
"D9.5'2024\n"+
|
"D9.5'2024\n"+
|
||||||
"T-123.45\n"+
|
"T-123.45\n"+
|
||||||
"^\n"), 0, nil, nil, nil, nil, nil)
|
"^\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
|
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
@@ -189,7 +191,7 @@ func TestQIFTransactionDataFileParseImportedData_ParseMonthDayYearDateFormatTime
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestQIFTransactionDataFileParseImportedData_ParseDayYearMonthDateFormatTime(t *testing.T) {
|
func TestQIFTransactionDataFileParseImportedData_ParseDayYearMonthDateFormatTime(t *testing.T) {
|
||||||
converter := QifDayMonthYearTransactionDataImporter
|
importer := QifDayMonthYearTransactionDataImporter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
@@ -197,7 +199,7 @@ func TestQIFTransactionDataFileParseImportedData_ParseDayYearMonthDateFormatTime
|
|||||||
DefaultCurrency: "CNY",
|
DefaultCurrency: "CNY",
|
||||||
}
|
}
|
||||||
|
|
||||||
allNewTransactions, _, _, _, _, _, err := converter.ParseImportedData(context, user, []byte(
|
allNewTransactions, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(
|
||||||
"!Type:Bank\n"+
|
"!Type:Bank\n"+
|
||||||
"D01-09-2024\n"+
|
"D01-09-2024\n"+
|
||||||
"T-123.45\n"+
|
"T-123.45\n"+
|
||||||
@@ -213,7 +215,7 @@ func TestQIFTransactionDataFileParseImportedData_ParseDayYearMonthDateFormatTime
|
|||||||
"^\n"+
|
"^\n"+
|
||||||
"D5'9.2024\n"+
|
"D5'9.2024\n"+
|
||||||
"T-123.45\n"+
|
"T-123.45\n"+
|
||||||
"^\n"), 0, nil, nil, nil, nil, nil)
|
"^\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
|
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
@@ -227,7 +229,7 @@ func TestQIFTransactionDataFileParseImportedData_ParseDayYearMonthDateFormatTime
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestQIFTransactionDataFileParseImportedData_ParseShortYearMonthDayDateFormatTime(t *testing.T) {
|
func TestQIFTransactionDataFileParseImportedData_ParseShortYearMonthDayDateFormatTime(t *testing.T) {
|
||||||
converter := QifYearMonthDayTransactionDataImporter
|
importer := QifYearMonthDayTransactionDataImporter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
@@ -235,7 +237,7 @@ func TestQIFTransactionDataFileParseImportedData_ParseShortYearMonthDayDateForma
|
|||||||
DefaultCurrency: "CNY",
|
DefaultCurrency: "CNY",
|
||||||
}
|
}
|
||||||
|
|
||||||
allNewTransactions, _, _, _, _, _, err := converter.ParseImportedData(context, user, []byte(
|
allNewTransactions, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(
|
||||||
"!Type:Bank\n"+
|
"!Type:Bank\n"+
|
||||||
"D24-09-01\n"+
|
"D24-09-01\n"+
|
||||||
"T-123.45\n"+
|
"T-123.45\n"+
|
||||||
@@ -251,7 +253,7 @@ func TestQIFTransactionDataFileParseImportedData_ParseShortYearMonthDayDateForma
|
|||||||
"^\n"+
|
"^\n"+
|
||||||
"D24'9.5\n"+
|
"D24'9.5\n"+
|
||||||
"T-123.45\n"+
|
"T-123.45\n"+
|
||||||
"^\n"), 0, nil, nil, nil, nil, nil)
|
"^\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
|
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
@@ -265,7 +267,7 @@ func TestQIFTransactionDataFileParseImportedData_ParseShortYearMonthDayDateForma
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestQIFTransactionDataFileParseImportedData_ParseInvalidTime(t *testing.T) {
|
func TestQIFTransactionDataFileParseImportedData_ParseInvalidTime(t *testing.T) {
|
||||||
converter := QifYearMonthDayTransactionDataImporter
|
importer := QifYearMonthDayTransactionDataImporter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
@@ -273,16 +275,16 @@ func TestQIFTransactionDataFileParseImportedData_ParseInvalidTime(t *testing.T)
|
|||||||
DefaultCurrency: "CNY",
|
DefaultCurrency: "CNY",
|
||||||
}
|
}
|
||||||
|
|
||||||
_, _, _, _, _, _, err := converter.ParseImportedData(context, user, []byte(
|
_, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(
|
||||||
"!Type:Bank\n"+
|
"!Type:Bank\n"+
|
||||||
"D2024 09 01\n"+
|
"D2024 09 01\n"+
|
||||||
"T-123.45\n"+
|
"T-123.45\n"+
|
||||||
"^\n"), 0, nil, nil, nil, nil, nil)
|
"^\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrTransactionTimeInvalid.Message)
|
assert.EqualError(t, err, errs.ErrTransactionTimeInvalid.Message)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestQIFTransactionDataFileParseImportedData_ParseAmountWithThousandsSeparator(t *testing.T) {
|
func TestQIFTransactionDataFileParseImportedData_ParseAmountWithThousandsSeparator(t *testing.T) {
|
||||||
converter := QifYearMonthDayTransactionDataImporter
|
importer := QifYearMonthDayTransactionDataImporter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
@@ -290,11 +292,11 @@ func TestQIFTransactionDataFileParseImportedData_ParseAmountWithThousandsSeparat
|
|||||||
DefaultCurrency: "CNY",
|
DefaultCurrency: "CNY",
|
||||||
}
|
}
|
||||||
|
|
||||||
allNewTransactions, _, _, _, _, _, err := converter.ParseImportedData(context, user, []byte(
|
allNewTransactions, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(
|
||||||
"!Type:Bank\n"+
|
"!Type:Bank\n"+
|
||||||
"D2024-09-01\n"+
|
"D2024-09-01\n"+
|
||||||
"T-123,456.78\n"+
|
"T-123,456.78\n"+
|
||||||
"^\n"), 0, nil, nil, nil, nil, nil)
|
"^\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
|
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
@@ -304,7 +306,7 @@ func TestQIFTransactionDataFileParseImportedData_ParseAmountWithThousandsSeparat
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestQIFTransactionDataFileParseImportedData_ParseInvalidAmount(t *testing.T) {
|
func TestQIFTransactionDataFileParseImportedData_ParseInvalidAmount(t *testing.T) {
|
||||||
converter := QifYearMonthDayTransactionDataImporter
|
importer := QifYearMonthDayTransactionDataImporter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
@@ -312,16 +314,16 @@ func TestQIFTransactionDataFileParseImportedData_ParseInvalidAmount(t *testing.T
|
|||||||
DefaultCurrency: "CNY",
|
DefaultCurrency: "CNY",
|
||||||
}
|
}
|
||||||
|
|
||||||
_, _, _, _, _, _, err := converter.ParseImportedData(context, user, []byte(
|
_, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(
|
||||||
"!Type:Bank\n"+
|
"!Type:Bank\n"+
|
||||||
"D2024-09-01\n"+
|
"D2024-09-01\n"+
|
||||||
"T-123 45\n"+
|
"T-123 45\n"+
|
||||||
"^\n"), 0, nil, nil, nil, nil, nil)
|
"^\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrAmountInvalid.Message)
|
assert.EqualError(t, err, errs.ErrAmountInvalid.Message)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestQIFTransactionDataFileParseImportedData_ParseAccountType(t *testing.T) {
|
func TestQIFTransactionDataFileParseImportedData_ParseAccountType(t *testing.T) {
|
||||||
converter := QifYearMonthDayTransactionDataImporter
|
importer := QifYearMonthDayTransactionDataImporter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
@@ -329,11 +331,11 @@ func TestQIFTransactionDataFileParseImportedData_ParseAccountType(t *testing.T)
|
|||||||
DefaultCurrency: "CNY",
|
DefaultCurrency: "CNY",
|
||||||
}
|
}
|
||||||
|
|
||||||
allNewTransactions, _, _, _, _, _, err := converter.ParseImportedData(context, user, []byte(
|
allNewTransactions, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(
|
||||||
"!Type:Cash\n"+
|
"!Type:Cash\n"+
|
||||||
"D2024-09-01\n"+
|
"D2024-09-01\n"+
|
||||||
"T-123.45\n"+
|
"T-123.45\n"+
|
||||||
"^\n"), 0, nil, nil, nil, nil, nil)
|
"^\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
|
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
assert.Equal(t, 1, len(allNewTransactions))
|
assert.Equal(t, 1, len(allNewTransactions))
|
||||||
@@ -342,11 +344,11 @@ func TestQIFTransactionDataFileParseImportedData_ParseAccountType(t *testing.T)
|
|||||||
assert.Equal(t, int64(1725148800), utils.GetUnixTimeFromTransactionTime(allNewTransactions[0].TransactionTime))
|
assert.Equal(t, int64(1725148800), utils.GetUnixTimeFromTransactionTime(allNewTransactions[0].TransactionTime))
|
||||||
assert.Equal(t, int64(12345), allNewTransactions[0].Amount)
|
assert.Equal(t, int64(12345), allNewTransactions[0].Amount)
|
||||||
|
|
||||||
allNewTransactions, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte(
|
allNewTransactions, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(
|
||||||
"!Type:CCard\n"+
|
"!Type:CCard\n"+
|
||||||
"D2024-09-01\n"+
|
"D2024-09-01\n"+
|
||||||
"T-123.45\n"+
|
"T-123.45\n"+
|
||||||
"^\n"), 0, nil, nil, nil, nil, nil)
|
"^\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
|
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
assert.Equal(t, 1, len(allNewTransactions))
|
assert.Equal(t, 1, len(allNewTransactions))
|
||||||
@@ -355,11 +357,11 @@ func TestQIFTransactionDataFileParseImportedData_ParseAccountType(t *testing.T)
|
|||||||
assert.Equal(t, int64(1725148800), utils.GetUnixTimeFromTransactionTime(allNewTransactions[0].TransactionTime))
|
assert.Equal(t, int64(1725148800), utils.GetUnixTimeFromTransactionTime(allNewTransactions[0].TransactionTime))
|
||||||
assert.Equal(t, int64(12345), allNewTransactions[0].Amount)
|
assert.Equal(t, int64(12345), allNewTransactions[0].Amount)
|
||||||
|
|
||||||
allNewTransactions, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte(
|
allNewTransactions, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(
|
||||||
"!Type:Oth A\n"+
|
"!Type:Oth A\n"+
|
||||||
"D2024-09-01\n"+
|
"D2024-09-01\n"+
|
||||||
"T-123.45\n"+
|
"T-123.45\n"+
|
||||||
"^\n"), 0, nil, nil, nil, nil, nil)
|
"^\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
|
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
assert.Equal(t, 1, len(allNewTransactions))
|
assert.Equal(t, 1, len(allNewTransactions))
|
||||||
@@ -368,11 +370,11 @@ func TestQIFTransactionDataFileParseImportedData_ParseAccountType(t *testing.T)
|
|||||||
assert.Equal(t, int64(1725148800), utils.GetUnixTimeFromTransactionTime(allNewTransactions[0].TransactionTime))
|
assert.Equal(t, int64(1725148800), utils.GetUnixTimeFromTransactionTime(allNewTransactions[0].TransactionTime))
|
||||||
assert.Equal(t, int64(12345), allNewTransactions[0].Amount)
|
assert.Equal(t, int64(12345), allNewTransactions[0].Amount)
|
||||||
|
|
||||||
allNewTransactions, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte(
|
allNewTransactions, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(
|
||||||
"!Type:Oth L\n"+
|
"!Type:Oth L\n"+
|
||||||
"D2024-09-01\n"+
|
"D2024-09-01\n"+
|
||||||
"T-123.45\n"+
|
"T-123.45\n"+
|
||||||
"^\n"), 0, nil, nil, nil, nil, nil)
|
"^\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
|
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
assert.Equal(t, 1, len(allNewTransactions))
|
assert.Equal(t, 1, len(allNewTransactions))
|
||||||
@@ -383,7 +385,7 @@ func TestQIFTransactionDataFileParseImportedData_ParseAccountType(t *testing.T)
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestQIFTransactionDataFileParseImportedData_ParseAccount(t *testing.T) {
|
func TestQIFTransactionDataFileParseImportedData_ParseAccount(t *testing.T) {
|
||||||
converter := QifYearMonthDayTransactionDataImporter
|
importer := QifYearMonthDayTransactionDataImporter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
@@ -391,14 +393,14 @@ func TestQIFTransactionDataFileParseImportedData_ParseAccount(t *testing.T) {
|
|||||||
DefaultCurrency: "CNY",
|
DefaultCurrency: "CNY",
|
||||||
}
|
}
|
||||||
|
|
||||||
allNewTransactions, allNewAccounts, _, _, _, _, err := converter.ParseImportedData(context, user, []byte(
|
allNewTransactions, allNewAccounts, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(
|
||||||
"!Account\n"+
|
"!Account\n"+
|
||||||
"NTest Account\n"+
|
"NTest Account\n"+
|
||||||
"^\n"+
|
"^\n"+
|
||||||
"!Type:Bank\n"+
|
"!Type:Bank\n"+
|
||||||
"D2024-09-01\n"+
|
"D2024-09-01\n"+
|
||||||
"T-123.45\n"+
|
"T-123.45\n"+
|
||||||
"^\n"), 0, nil, nil, nil, nil, nil)
|
"^\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
|
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
assert.Equal(t, 1, len(allNewTransactions))
|
assert.Equal(t, 1, len(allNewTransactions))
|
||||||
@@ -412,7 +414,7 @@ func TestQIFTransactionDataFileParseImportedData_ParseAccount(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestQIFTransactionDataFileParseImportedData_ParseAmountWithLeadingPlusSign(t *testing.T) {
|
func TestQIFTransactionDataFileParseImportedData_ParseAmountWithLeadingPlusSign(t *testing.T) {
|
||||||
converter := QifYearMonthDayTransactionDataImporter
|
importer := QifYearMonthDayTransactionDataImporter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
@@ -420,11 +422,11 @@ func TestQIFTransactionDataFileParseImportedData_ParseAmountWithLeadingPlusSign(
|
|||||||
DefaultCurrency: "CNY",
|
DefaultCurrency: "CNY",
|
||||||
}
|
}
|
||||||
|
|
||||||
allNewTransactions, _, _, _, _, _, err := converter.ParseImportedData(context, user, []byte(
|
allNewTransactions, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(
|
||||||
"!Type:Bank\n"+
|
"!Type:Bank\n"+
|
||||||
"D2024-09-01\n"+
|
"D2024-09-01\n"+
|
||||||
"T+123.45\n"+
|
"T+123.45\n"+
|
||||||
"^\n"), 0, nil, nil, nil, nil, nil)
|
"^\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
|
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
assert.Equal(t, 1, len(allNewTransactions))
|
assert.Equal(t, 1, len(allNewTransactions))
|
||||||
@@ -432,7 +434,7 @@ func TestQIFTransactionDataFileParseImportedData_ParseAmountWithLeadingPlusSign(
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestQIFTransactionDataFileParseImportedData_ParseSubCategory(t *testing.T) {
|
func TestQIFTransactionDataFileParseImportedData_ParseSubCategory(t *testing.T) {
|
||||||
converter := QifYearMonthDayTransactionDataImporter
|
importer := QifYearMonthDayTransactionDataImporter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
@@ -440,12 +442,12 @@ func TestQIFTransactionDataFileParseImportedData_ParseSubCategory(t *testing.T)
|
|||||||
DefaultCurrency: "CNY",
|
DefaultCurrency: "CNY",
|
||||||
}
|
}
|
||||||
|
|
||||||
allNewTransactions, _, allNewSubExpenseCategories, _, _, _, err := converter.ParseImportedData(context, user, []byte(
|
allNewTransactions, _, allNewSubExpenseCategories, _, _, _, err := importer.ParseImportedData(context, user, []byte(
|
||||||
"!Type:Bank\n"+
|
"!Type:Bank\n"+
|
||||||
"D2024-09-01\n"+
|
"D2024-09-01\n"+
|
||||||
"T-123.45\n"+
|
"T-123.45\n"+
|
||||||
"LTest Category:Sub Category\n"+
|
"LTest Category:Sub Category\n"+
|
||||||
"^\n"), 0, nil, nil, nil, nil, nil)
|
"^\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
|
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
assert.Equal(t, 1, len(allNewTransactions))
|
assert.Equal(t, 1, len(allNewTransactions))
|
||||||
@@ -458,7 +460,7 @@ func TestQIFTransactionDataFileParseImportedData_ParseSubCategory(t *testing.T)
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestQIFTransactionDataFileParseImportedData_ParseDescription(t *testing.T) {
|
func TestQIFTransactionDataFileParseImportedData_ParseDescription(t *testing.T) {
|
||||||
converter := QifYearMonthDayTransactionDataImporter
|
importer := QifYearMonthDayTransactionDataImporter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
@@ -466,7 +468,7 @@ func TestQIFTransactionDataFileParseImportedData_ParseDescription(t *testing.T)
|
|||||||
DefaultCurrency: "CNY",
|
DefaultCurrency: "CNY",
|
||||||
}
|
}
|
||||||
|
|
||||||
allNewTransactions, _, _, _, _, _, err := converter.ParseImportedData(context, user, []byte(
|
allNewTransactions, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(
|
||||||
"!Type:Bank\n"+
|
"!Type:Bank\n"+
|
||||||
"D2024-09-01\n"+
|
"D2024-09-01\n"+
|
||||||
"T-123.45\n"+
|
"T-123.45\n"+
|
||||||
@@ -476,7 +478,24 @@ func TestQIFTransactionDataFileParseImportedData_ParseDescription(t *testing.T)
|
|||||||
"D2024-09-02\n"+
|
"D2024-09-02\n"+
|
||||||
"T-234.56\n"+
|
"T-234.56\n"+
|
||||||
"PTest2\n"+
|
"PTest2\n"+
|
||||||
"^\n"), 0, nil, nil, nil, nil, nil)
|
"^\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
|
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.Equal(t, 2, len(allNewTransactions))
|
||||||
|
assert.Equal(t, "foo bar\t#test", allNewTransactions[0].Comment)
|
||||||
|
assert.Equal(t, "", allNewTransactions[1].Comment)
|
||||||
|
|
||||||
|
allNewTransactions, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(
|
||||||
|
"!Type:Bank\n"+
|
||||||
|
"D2024-09-01\n"+
|
||||||
|
"T-123.45\n"+
|
||||||
|
"PTest\n"+
|
||||||
|
"Mfoo bar\t#test\n"+
|
||||||
|
"^\n"+
|
||||||
|
"D2024-09-02\n"+
|
||||||
|
"T-234.56\n"+
|
||||||
|
"PTest2\n"+
|
||||||
|
"^\n"), time.UTC, converter.DefaultImporterOptions.WithPayeeAsDescription(), nil, nil, nil, nil, nil)
|
||||||
|
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
assert.Equal(t, 2, len(allNewTransactions))
|
assert.Equal(t, 2, len(allNewTransactions))
|
||||||
@@ -484,8 +503,41 @@ func TestQIFTransactionDataFileParseImportedData_ParseDescription(t *testing.T)
|
|||||||
assert.Equal(t, "Test2", allNewTransactions[1].Comment)
|
assert.Equal(t, "Test2", allNewTransactions[1].Comment)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestQIFTransactionDataFileParseImportedData_WithAdditionalOptions(t *testing.T) {
|
||||||
|
importer := QifYearMonthDayTransactionDataImporter
|
||||||
|
context := core.NewNullContext()
|
||||||
|
|
||||||
|
user := &models.User{
|
||||||
|
Uid: 1234567890,
|
||||||
|
DefaultCurrency: "CNY",
|
||||||
|
}
|
||||||
|
|
||||||
|
allNewTransactions, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(
|
||||||
|
"!Type:Bank\n"+
|
||||||
|
"D2024-09-01\n"+
|
||||||
|
"T-123.45\n"+
|
||||||
|
"PTest2\n"+
|
||||||
|
"^\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
|
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.Equal(t, 1, len(allNewTransactions))
|
||||||
|
assert.Equal(t, 0, len(allNewTransactions[0].OriginalTagNames))
|
||||||
|
|
||||||
|
allNewTransactions, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(
|
||||||
|
"!Type:Bank\n"+
|
||||||
|
"D2024-09-01\n"+
|
||||||
|
"T-123.45\n"+
|
||||||
|
"PTest2\n"+
|
||||||
|
"^\n"), time.UTC, converter.DefaultImporterOptions.WithPayeeAsTag(), nil, nil, nil, nil, nil)
|
||||||
|
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.Equal(t, 1, len(allNewTransactions))
|
||||||
|
assert.Equal(t, 1, len(allNewTransactions[0].OriginalTagNames))
|
||||||
|
assert.Equal(t, "Test2", allNewTransactions[0].OriginalTagNames[0])
|
||||||
|
}
|
||||||
|
|
||||||
func TestQIFTransactionDataFileParseImportedData_MissingRequiredFields(t *testing.T) {
|
func TestQIFTransactionDataFileParseImportedData_MissingRequiredFields(t *testing.T) {
|
||||||
converter := QifYearMonthDayTransactionDataImporter
|
importer := QifYearMonthDayTransactionDataImporter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
@@ -494,16 +546,16 @@ func TestQIFTransactionDataFileParseImportedData_MissingRequiredFields(t *testin
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Missing Time Field
|
// Missing Time Field
|
||||||
_, _, _, _, _, _, err := converter.ParseImportedData(context, user, []byte(
|
_, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(
|
||||||
"!Type:Bank\n"+
|
"!Type:Bank\n"+
|
||||||
"T-123.45\n"+
|
"T-123.45\n"+
|
||||||
"^\n"), 0, nil, nil, nil, nil, nil)
|
"^\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrMissingTransactionTime.Message)
|
assert.EqualError(t, err, errs.ErrMissingTransactionTime.Message)
|
||||||
|
|
||||||
// Missing Amount Field
|
// Missing Amount Field
|
||||||
_, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte(
|
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(
|
||||||
"!Type:Bank\n"+
|
"!Type:Bank\n"+
|
||||||
"D2024-09-01\n"+
|
"D2024-09-01\n"+
|
||||||
"^\n"), 0, nil, nil, nil, nil, nil)
|
"^\n"), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrAmountInvalid.Message)
|
assert.EqualError(t, err, errs.ErrAmountInvalid.Message)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ var qifTransactionSupportedColumns = map[datatable.TransactionDataTableColumn]bo
|
|||||||
datatable.TRANSACTION_DATA_TABLE_AMOUNT: true,
|
datatable.TRANSACTION_DATA_TABLE_AMOUNT: true,
|
||||||
datatable.TRANSACTION_DATA_TABLE_RELATED_ACCOUNT_NAME: true,
|
datatable.TRANSACTION_DATA_TABLE_RELATED_ACCOUNT_NAME: true,
|
||||||
datatable.TRANSACTION_DATA_TABLE_DESCRIPTION: true,
|
datatable.TRANSACTION_DATA_TABLE_DESCRIPTION: true,
|
||||||
|
datatable.TRANSACTION_DATA_TABLE_PAYEE: true,
|
||||||
}
|
}
|
||||||
|
|
||||||
// qifDateFormatType represents the quicken interchange format (qif) date format type
|
// qifDateFormatType represents the quicken interchange format (qif) date format type
|
||||||
@@ -184,8 +185,10 @@ func (t *qifTransactionDataRowIterator) parseTransaction(ctx core.Context, user
|
|||||||
|
|
||||||
if qifTransaction.Memo != "" {
|
if qifTransaction.Memo != "" {
|
||||||
data[datatable.TRANSACTION_DATA_TABLE_DESCRIPTION] = qifTransaction.Memo
|
data[datatable.TRANSACTION_DATA_TABLE_DESCRIPTION] = qifTransaction.Memo
|
||||||
} else if qifTransaction.Payee != "" && qifTransaction.Payee != qifOpeningBalancePayeeText {
|
}
|
||||||
data[datatable.TRANSACTION_DATA_TABLE_DESCRIPTION] = qifTransaction.Payee
|
|
||||||
|
if qifTransaction.Payee != "" && qifTransaction.Payee != qifOpeningBalancePayeeText {
|
||||||
|
data[datatable.TRANSACTION_DATA_TABLE_PAYEE] = qifTransaction.Payee
|
||||||
}
|
}
|
||||||
|
|
||||||
return data, nil
|
return data, nil
|
||||||
|
|||||||
@@ -5,9 +5,9 @@ import (
|
|||||||
"github.com/mayswind/ezbookkeeping/pkg/converters/beancount"
|
"github.com/mayswind/ezbookkeeping/pkg/converters/beancount"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/converters/camt"
|
"github.com/mayswind/ezbookkeeping/pkg/converters/camt"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/converters/converter"
|
"github.com/mayswind/ezbookkeeping/pkg/converters/converter"
|
||||||
|
"github.com/mayswind/ezbookkeeping/pkg/converters/custom"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/converters/datatable"
|
"github.com/mayswind/ezbookkeeping/pkg/converters/datatable"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/converters/default"
|
"github.com/mayswind/ezbookkeeping/pkg/converters/default"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/converters/dsv"
|
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/converters/feidee"
|
"github.com/mayswind/ezbookkeeping/pkg/converters/feidee"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/converters/fireflyIII"
|
"github.com/mayswind/ezbookkeeping/pkg/converters/fireflyIII"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/converters/gnucash"
|
"github.com/mayswind/ezbookkeeping/pkg/converters/gnucash"
|
||||||
@@ -52,6 +52,8 @@ func GetTransactionDataImporter(fileType string) (converter.TransactionDataImpor
|
|||||||
return qif.QifDayMonthYearTransactionDataImporter, nil
|
return qif.QifDayMonthYearTransactionDataImporter, nil
|
||||||
} else if fileType == "iif" {
|
} else if fileType == "iif" {
|
||||||
return iif.IifTransactionDataFileImporter, nil
|
return iif.IifTransactionDataFileImporter, nil
|
||||||
|
} else if fileType == "camt052" {
|
||||||
|
return camt.Camt052TransactionDataImporter, nil
|
||||||
} else if fileType == "camt053" {
|
} else if fileType == "camt053" {
|
||||||
return camt.Camt053TransactionDataImporter, nil
|
return camt.Camt053TransactionDataImporter, nil
|
||||||
} else if fileType == "mt940" {
|
} else if fileType == "mt940" {
|
||||||
@@ -83,17 +85,29 @@ func GetTransactionDataImporter(fileType string) (converter.TransactionDataImpor
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsCustomDelimiterSeparatedValuesFileType returns whether the file type is the delimiter-separated values file type
|
// IsCustomFileFormatFileType returns whether the file type is the custom file format
|
||||||
func IsCustomDelimiterSeparatedValuesFileType(fileType string) bool {
|
func IsCustomFileFormatFileType(fileType string) bool {
|
||||||
return dsv.IsDelimiterSeparatedValuesFileType(fileType)
|
return custom.IsDelimiterSeparatedValuesFileType(fileType) || custom.IsCustomExcelFileType(fileType)
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateNewDelimiterSeparatedValuesDataParser returns a new delimiter-separated values data parser according to the file type and encoding
|
// CreateNewCustomFileFormatTransactionDataParser returns a new custom transaction data parser according to the file type and encoding
|
||||||
func CreateNewDelimiterSeparatedValuesDataParser(fileType string, fileEncoding string) (dsv.CustomTransactionDataDsvFileParser, error) {
|
func CreateNewCustomFileFormatTransactionDataParser(fileType string, fileEncoding string) (custom.CustomTransactionDataParser, error) {
|
||||||
return dsv.CreateNewCustomTransactionDataDsvFileParser(fileType, fileEncoding)
|
if custom.IsDelimiterSeparatedValuesFileType(fileType) {
|
||||||
|
return custom.CreateNewCustomTransactionDataDsvFileParser(fileType, fileEncoding)
|
||||||
|
} else if custom.IsCustomExcelFileType(fileType) {
|
||||||
|
return custom.CreateNewCustomTransactionDataExcelFileParser(fileType)
|
||||||
|
} else {
|
||||||
|
return nil, errs.ErrImportFileTypeNotSupported
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateNewDelimiterSeparatedValuesDataImporter returns a new delimiter-separated values data importer according to the file type and encoding
|
// CreateNewCustomTransactionDataImporter returns a new custom transaction data importer according to the file type and encoding
|
||||||
func CreateNewDelimiterSeparatedValuesDataImporter(fileType string, fileEncoding string, columnIndexMapping map[datatable.TransactionDataTableColumn]int, transactionTypeNameMapping map[string]models.TransactionType, hasHeaderLine bool, timeFormat string, timezoneFormat string, amountDecimalSeparator string, amountDigitGroupingSymbol string, geoLocationSeparator string, geoLocationOrder string, transactionTagSeparator string) (converter.TransactionDataImporter, error) {
|
func CreateNewCustomTransactionDataImporter(fileType string, fileEncoding string, columnIndexMapping map[datatable.TransactionDataTableColumn]int, transactionTypeNameMapping map[string]models.TransactionType, hasHeaderLine bool, timeFormat string, timezoneFormat string, amountDecimalSeparator string, amountDigitGroupingSymbol string, geoLocationSeparator string, geoLocationOrder string, transactionTagSeparator string) (converter.TransactionDataImporter, error) {
|
||||||
return dsv.CreateNewCustomTransactionDataDsvFileImporter(fileType, fileEncoding, columnIndexMapping, transactionTypeNameMapping, hasHeaderLine, timeFormat, timezoneFormat, amountDecimalSeparator, amountDigitGroupingSymbol, geoLocationSeparator, geoLocationOrder, transactionTagSeparator)
|
if custom.IsDelimiterSeparatedValuesFileType(fileType) {
|
||||||
|
return custom.CreateNewCustomTransactionDataDsvFileImporter(fileType, fileEncoding, columnIndexMapping, transactionTypeNameMapping, hasHeaderLine, timeFormat, timezoneFormat, amountDecimalSeparator, amountDigitGroupingSymbol, geoLocationSeparator, geoLocationOrder, transactionTagSeparator)
|
||||||
|
} else if custom.IsCustomExcelFileType(fileType) {
|
||||||
|
return custom.CreateNewCustomTransactionDataExcelFileImporter(fileType, columnIndexMapping, transactionTypeNameMapping, hasHeaderLine, timeFormat, timezoneFormat, amountDecimalSeparator, amountDigitGroupingSymbol, geoLocationSeparator, geoLocationOrder, transactionTagSeparator)
|
||||||
|
} else {
|
||||||
|
return nil, errs.ErrImportFileTypeNotSupported
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package wechat
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"time"
|
||||||
|
|
||||||
"golang.org/x/text/encoding/unicode"
|
"golang.org/x/text/encoding/unicode"
|
||||||
"golang.org/x/text/transform"
|
"golang.org/x/text/transform"
|
||||||
@@ -27,7 +28,7 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// ParseImportedData returns the imported data by parsing the wechat pay transaction csv data
|
// ParseImportedData returns the imported data by parsing the wechat pay transaction csv data
|
||||||
func (c *wechatPayTransactionDataCsvFileImporter) ParseImportedData(ctx core.Context, user *models.User, data []byte, defaultTimezoneOffset int16, accountMap map[string]*models.Account, expenseCategoryMap map[string]map[string]*models.TransactionCategory, incomeCategoryMap map[string]map[string]*models.TransactionCategory, transferCategoryMap map[string]map[string]*models.TransactionCategory, tagMap map[string]*models.TransactionTag) (models.ImportedTransactionSlice, []*models.Account, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionTag, error) {
|
func (c *wechatPayTransactionDataCsvFileImporter) ParseImportedData(ctx core.Context, user *models.User, data []byte, defaultTimezone *time.Location, additionalOptions converter.TransactionDataImporterOptions, accountMap map[string]*models.Account, expenseCategoryMap map[string]map[string]*models.TransactionCategory, incomeCategoryMap map[string]map[string]*models.TransactionCategory, transferCategoryMap map[string]map[string]*models.TransactionCategory, tagMap map[string]*models.TransactionTag) (models.ImportedTransactionSlice, []*models.Account, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionTag, error) {
|
||||||
fallback := unicode.UTF8.NewDecoder()
|
fallback := unicode.UTF8.NewDecoder()
|
||||||
reader := transform.NewReader(bytes.NewReader(data), unicode.BOMOverride(fallback))
|
reader := transform.NewReader(bytes.NewReader(data), unicode.BOMOverride(fallback))
|
||||||
|
|
||||||
@@ -58,5 +59,5 @@ func (c *wechatPayTransactionDataCsvFileImporter) ParseImportedData(ctx core.Con
|
|||||||
transactionDataTable := datatable.CreateNewTransactionDataTableFromCommonDataTable(commonDataTable, wechatPayTransactionSupportedColumns, transactionRowParser)
|
transactionDataTable := datatable.CreateNewTransactionDataTableFromCommonDataTable(commonDataTable, wechatPayTransactionSupportedColumns, transactionRowParser)
|
||||||
dataTableImporter := converter.CreateNewSimpleImporterWithTypeNameMapping(wechatPayTransactionTypeNameMapping)
|
dataTableImporter := converter.CreateNewSimpleImporterWithTypeNameMapping(wechatPayTransactionTypeNameMapping)
|
||||||
|
|
||||||
return dataTableImporter.ParseImportedData(ctx, user, transactionDataTable, defaultTimezoneOffset, accountMap, expenseCategoryMap, incomeCategoryMap, transferCategoryMap, tagMap)
|
return dataTableImporter.ParseImportedData(ctx, user, transactionDataTable, defaultTimezone, additionalOptions, accountMap, expenseCategoryMap, incomeCategoryMap, transferCategoryMap, tagMap)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import (
|
|||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
|
"github.com/mayswind/ezbookkeeping/pkg/converters/converter"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/core"
|
"github.com/mayswind/ezbookkeeping/pkg/core"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/errs"
|
"github.com/mayswind/ezbookkeeping/pkg/errs"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/models"
|
"github.com/mayswind/ezbookkeeping/pkg/models"
|
||||||
@@ -13,7 +14,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func TestWeChatPayCsvFileImporterParseImportedData_MinimumValidData(t *testing.T) {
|
func TestWeChatPayCsvFileImporterParseImportedData_MinimumValidData(t *testing.T) {
|
||||||
converter := WeChatPayTransactionDataCsvFileImporter
|
importer := WeChatPayTransactionDataCsvFileImporter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
@@ -31,7 +32,7 @@ func TestWeChatPayCsvFileImporterParseImportedData_MinimumValidData(t *testing.T
|
|||||||
"2024-09-01 12:34:56,商户消费,支出,¥123.45,支付成功\n" +
|
"2024-09-01 12:34:56,商户消费,支出,¥123.45,支付成功\n" +
|
||||||
"2024-09-01 23:59:59,零钱充值,/,¥0.05,充值完成\n" +
|
"2024-09-01 23:59:59,零钱充值,/,¥0.05,充值完成\n" +
|
||||||
"2024-09-02 23:59:59,零钱提现,/,¥0.03,提现已到账\n"
|
"2024-09-02 23:59:59,零钱提现,/,¥0.03,提现已到账\n"
|
||||||
allNewTransactions, allNewAccounts, allNewSubExpenseCategories, allNewSubIncomeCategories, allNewSubTransferCategories, allNewTags, err := converter.ParseImportedData(context, user, []byte(data), 0, nil, nil, nil, nil, nil)
|
allNewTransactions, allNewAccounts, allNewSubExpenseCategories, allNewSubIncomeCategories, allNewSubTransferCategories, allNewTags, err := importer.ParseImportedData(context, user, []byte(data), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
assert.Equal(t, 4, len(allNewTransactions))
|
assert.Equal(t, 4, len(allNewTransactions))
|
||||||
@@ -93,7 +94,7 @@ func TestWeChatPayCsvFileImporterParseImportedData_MinimumValidData(t *testing.T
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestWeChatPayCsvFileImporterParseImportedData_ParseRefundTransaction(t *testing.T) {
|
func TestWeChatPayCsvFileImporterParseImportedData_ParseRefundTransaction(t *testing.T) {
|
||||||
converter := WeChatPayTransactionDataCsvFileImporter
|
importer := WeChatPayTransactionDataCsvFileImporter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
@@ -108,7 +109,7 @@ func TestWeChatPayCsvFileImporterParseImportedData_ParseRefundTransaction(t *tes
|
|||||||
"----------------------微信支付账单明细列表--------------------,,,,\n" +
|
"----------------------微信支付账单明细列表--------------------,,,,\n" +
|
||||||
"交易时间,交易类型,收/支,金额(元),当前状态\n" +
|
"交易时间,交易类型,收/支,金额(元),当前状态\n" +
|
||||||
"2024-09-01 01:23:45,xxx-退款,收入,¥0.12,已全额退款\n"
|
"2024-09-01 01:23:45,xxx-退款,收入,¥0.12,已全额退款\n"
|
||||||
allNewTransactions, _, _, _, _, _, err := converter.ParseImportedData(context, user, []byte(data1), 0, nil, nil, nil, nil, nil)
|
allNewTransactions, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(data1), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
assert.Equal(t, int64(1234567890), allNewTransactions[0].Uid)
|
assert.Equal(t, int64(1234567890), allNewTransactions[0].Uid)
|
||||||
@@ -120,7 +121,7 @@ func TestWeChatPayCsvFileImporterParseImportedData_ParseRefundTransaction(t *tes
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestWeChatPayCsvFileImporterParseImportedData_ParseInvalidTime(t *testing.T) {
|
func TestWeChatPayCsvFileImporterParseImportedData_ParseInvalidTime(t *testing.T) {
|
||||||
converter := WeChatPayTransactionDataCsvFileImporter
|
importer := WeChatPayTransactionDataCsvFileImporter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
@@ -135,7 +136,7 @@ func TestWeChatPayCsvFileImporterParseImportedData_ParseInvalidTime(t *testing.T
|
|||||||
"----------------------微信支付账单明细列表--------------------,,,,\n" +
|
"----------------------微信支付账单明细列表--------------------,,,,\n" +
|
||||||
"交易时间,交易类型,收/支,金额(元),当前状态\n" +
|
"交易时间,交易类型,收/支,金额(元),当前状态\n" +
|
||||||
"2024-09-01T01:23:45,二维码收款,收入,¥0.12,已收钱\n"
|
"2024-09-01T01:23:45,二维码收款,收入,¥0.12,已收钱\n"
|
||||||
_, _, _, _, _, _, err := converter.ParseImportedData(context, user, []byte(data1), 0, nil, nil, nil, nil, nil)
|
_, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(data1), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrTransactionTimeInvalid.Message)
|
assert.EqualError(t, err, errs.ErrTransactionTimeInvalid.Message)
|
||||||
|
|
||||||
data2 := "微信支付账单明细,,,,\n" +
|
data2 := "微信支付账单明细,,,,\n" +
|
||||||
@@ -145,12 +146,12 @@ func TestWeChatPayCsvFileImporterParseImportedData_ParseInvalidTime(t *testing.T
|
|||||||
"----------------------微信支付账单明细列表--------------------,,,,\n" +
|
"----------------------微信支付账单明细列表--------------------,,,,\n" +
|
||||||
"交易时间,交易类型,收/支,金额(元),当前状态\n" +
|
"交易时间,交易类型,收/支,金额(元),当前状态\n" +
|
||||||
"09/01/2024 12:34:56,二维码收款,收入,¥0.12,已收钱\n"
|
"09/01/2024 12:34:56,二维码收款,收入,¥0.12,已收钱\n"
|
||||||
_, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte(data2), 0, nil, nil, nil, nil, nil)
|
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(data2), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrTransactionTimeInvalid.Message)
|
assert.EqualError(t, err, errs.ErrTransactionTimeInvalid.Message)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestWeChatPayCsvFileImporterParseImportedData_ParseInvalidType(t *testing.T) {
|
func TestWeChatPayCsvFileImporterParseImportedData_ParseInvalidType(t *testing.T) {
|
||||||
converter := WeChatPayTransactionDataCsvFileImporter
|
importer := WeChatPayTransactionDataCsvFileImporter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
@@ -165,12 +166,35 @@ func TestWeChatPayCsvFileImporterParseImportedData_ParseInvalidType(t *testing.T
|
|||||||
"----------------------微信支付账单明细列表--------------------,,,,\n" +
|
"----------------------微信支付账单明细列表--------------------,,,,\n" +
|
||||||
"交易时间,交易类型,收/支,金额(元),当前状态\n" +
|
"交易时间,交易类型,收/支,金额(元),当前状态\n" +
|
||||||
"2024-09-01T01:23:45,xxx,,¥0.12,支付成功\n"
|
"2024-09-01T01:23:45,xxx,,¥0.12,支付成功\n"
|
||||||
_, _, _, _, _, _, err := converter.ParseImportedData(context, user, []byte(data), 0, nil, nil, nil, nil, nil)
|
_, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(data), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrNotFoundTransactionDataInFile.Message)
|
assert.EqualError(t, err, errs.ErrNotFoundTransactionDataInFile.Message)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestWeChatPayCsvFileImporterParseImportedData_ParseAmountWithThousandSeparator(t *testing.T) {
|
||||||
|
importer := WeChatPayTransactionDataCsvFileImporter
|
||||||
|
context := core.NewNullContext()
|
||||||
|
|
||||||
|
user := &models.User{
|
||||||
|
Uid: 1234567890,
|
||||||
|
DefaultCurrency: "CNY",
|
||||||
|
}
|
||||||
|
|
||||||
|
data1 := "微信支付账单明细,,,,\n" +
|
||||||
|
"微信昵称:[xxx],,,,\n" +
|
||||||
|
"起始时间:[2024-01-01 00:00:00] 终止时间:[2024-09-01 23:59:59],,,,\n" +
|
||||||
|
",,,,\n" +
|
||||||
|
"----------------------微信支付账单明细列表--------------------,,,,\n" +
|
||||||
|
"交易时间,交易类型,收/支,金额(元),支付方式,当前状态\n" +
|
||||||
|
"2024-09-01 01:23:45,二维码收款,收入,\"¥1,234.56\",/,已收钱\n"
|
||||||
|
allNewTransactions, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(data1), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
|
||||||
|
assert.Equal(t, 1, len(allNewTransactions))
|
||||||
|
assert.Equal(t, int64(123456), allNewTransactions[0].Amount)
|
||||||
|
}
|
||||||
|
|
||||||
func TestWeChatPayCsvFileImporterParseImportedData_ParseInvalidAmount(t *testing.T) {
|
func TestWeChatPayCsvFileImporterParseImportedData_ParseInvalidAmount(t *testing.T) {
|
||||||
converter := WeChatPayTransactionDataCsvFileImporter
|
importer := WeChatPayTransactionDataCsvFileImporter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
@@ -185,12 +209,12 @@ func TestWeChatPayCsvFileImporterParseImportedData_ParseInvalidAmount(t *testing
|
|||||||
"----------------------微信支付账单明细列表--------------------,,,,\n" +
|
"----------------------微信支付账单明细列表--------------------,,,,\n" +
|
||||||
"交易时间,交易类型,收/支,金额(元),当前状态\n" +
|
"交易时间,交易类型,收/支,金额(元),当前状态\n" +
|
||||||
"2024-09-01 01:23:45,二维码收款,收入,¥,已收钱\n"
|
"2024-09-01 01:23:45,二维码收款,收入,¥,已收钱\n"
|
||||||
_, _, _, _, _, _, err := converter.ParseImportedData(context, user, []byte(data), 0, nil, nil, nil, nil, nil)
|
_, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(data), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrAmountInvalid.Message)
|
assert.EqualError(t, err, errs.ErrAmountInvalid.Message)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestWeChatPayCsvFileImporterParseImportedData_ParseAccountName(t *testing.T) {
|
func TestWeChatPayCsvFileImporterParseImportedData_ParseAccountName(t *testing.T) {
|
||||||
converter := WeChatPayTransactionDataCsvFileImporter
|
importer := WeChatPayTransactionDataCsvFileImporter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
@@ -206,7 +230,7 @@ func TestWeChatPayCsvFileImporterParseImportedData_ParseAccountName(t *testing.T
|
|||||||
"----------------------微信支付账单明细列表--------------------,,,,\n" +
|
"----------------------微信支付账单明细列表--------------------,,,,\n" +
|
||||||
"交易时间,交易类型,收/支,金额(元),支付方式,当前状态\n" +
|
"交易时间,交易类型,收/支,金额(元),支付方式,当前状态\n" +
|
||||||
"2024-09-01 01:23:45,二维码收款,收入,¥0.12,/,已收钱\n"
|
"2024-09-01 01:23:45,二维码收款,收入,¥0.12,/,已收钱\n"
|
||||||
allNewTransactions, _, _, _, _, _, err := converter.ParseImportedData(context, user, []byte(data1), 0, nil, nil, nil, nil, nil)
|
allNewTransactions, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(data1), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
assert.Equal(t, 1, len(allNewTransactions))
|
assert.Equal(t, 1, len(allNewTransactions))
|
||||||
@@ -222,7 +246,7 @@ func TestWeChatPayCsvFileImporterParseImportedData_ParseAccountName(t *testing.T
|
|||||||
"2024-09-01 01:23:45,xxx-退款,收入,¥0.12,test,已全额退款\n"
|
"2024-09-01 01:23:45,xxx-退款,收入,¥0.12,test,已全额退款\n"
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
allNewTransactions, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte(data2), 0, nil, nil, nil, nil, nil)
|
allNewTransactions, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(data2), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
assert.Equal(t, 1, len(allNewTransactions))
|
assert.Equal(t, 1, len(allNewTransactions))
|
||||||
@@ -238,7 +262,7 @@ func TestWeChatPayCsvFileImporterParseImportedData_ParseAccountName(t *testing.T
|
|||||||
"2024-09-01 23:59:59,零钱充值,/,¥0.05,test,充值完成\n"
|
"2024-09-01 23:59:59,零钱充值,/,¥0.05,test,充值完成\n"
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
allNewTransactions, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte(data3), 0, nil, nil, nil, nil, nil)
|
allNewTransactions, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(data3), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
assert.Equal(t, 1, len(allNewTransactions))
|
assert.Equal(t, 1, len(allNewTransactions))
|
||||||
@@ -255,7 +279,7 @@ func TestWeChatPayCsvFileImporterParseImportedData_ParseAccountName(t *testing.T
|
|||||||
"2024-09-02 23:59:59,零钱提现,/,¥0.03,test,提现已到账\n"
|
"2024-09-02 23:59:59,零钱提现,/,¥0.03,test,提现已到账\n"
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
allNewTransactions, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte(data4), 0, nil, nil, nil, nil, nil)
|
allNewTransactions, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(data4), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
assert.Equal(t, 1, len(allNewTransactions))
|
assert.Equal(t, 1, len(allNewTransactions))
|
||||||
@@ -272,7 +296,7 @@ func TestWeChatPayCsvFileImporterParseImportedData_ParseAccountName(t *testing.T
|
|||||||
"2024-09-03 23:59:59,信用卡还款,/,¥0.01,零钱,支付成功\n"
|
"2024-09-03 23:59:59,信用卡还款,/,¥0.01,零钱,支付成功\n"
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
allNewTransactions, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte(data5), 0, nil, nil, nil, nil, nil)
|
allNewTransactions, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(data5), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
assert.Equal(t, 1, len(allNewTransactions))
|
assert.Equal(t, 1, len(allNewTransactions))
|
||||||
@@ -281,7 +305,7 @@ func TestWeChatPayCsvFileImporterParseImportedData_ParseAccountName(t *testing.T
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestWeChatPayCsvFileImporterParseImportedData_ParseDescription(t *testing.T) {
|
func TestWeChatPayCsvFileImporterParseImportedData_ParseDescription(t *testing.T) {
|
||||||
converter := WeChatPayTransactionDataCsvFileImporter
|
importer := WeChatPayTransactionDataCsvFileImporter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
@@ -296,7 +320,7 @@ func TestWeChatPayCsvFileImporterParseImportedData_ParseDescription(t *testing.T
|
|||||||
"----------------------微信支付账单明细列表--------------------,,,,\n" +
|
"----------------------微信支付账单明细列表--------------------,,,,\n" +
|
||||||
"交易时间,交易类型,收/支,金额(元),当前状态,备注\n" +
|
"交易时间,交易类型,收/支,金额(元),当前状态,备注\n" +
|
||||||
"2024-09-01 01:23:45,二维码收款,收入,¥0.12,已收钱,\"/\"\n"
|
"2024-09-01 01:23:45,二维码收款,收入,¥0.12,已收钱,\"/\"\n"
|
||||||
allNewTransactions, _, _, _, _, _, err := converter.ParseImportedData(context, user, []byte(data1), 0, nil, nil, nil, nil, nil)
|
allNewTransactions, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(data1), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
assert.Equal(t, 1, len(allNewTransactions))
|
assert.Equal(t, 1, len(allNewTransactions))
|
||||||
@@ -309,7 +333,7 @@ func TestWeChatPayCsvFileImporterParseImportedData_ParseDescription(t *testing.T
|
|||||||
"----------------------微信支付账单明细列表--------------------,,,,\n" +
|
"----------------------微信支付账单明细列表--------------------,,,,\n" +
|
||||||
"交易时间,交易类型,商品,收/支,金额(元),当前状态,备注\n" +
|
"交易时间,交易类型,商品,收/支,金额(元),当前状态,备注\n" +
|
||||||
"2024-09-01 01:23:45,二维码收款,Test,收入,¥0.12,已收钱,\"foo\"\"bar,\ntest\"\n"
|
"2024-09-01 01:23:45,二维码收款,Test,收入,¥0.12,已收钱,\"foo\"\"bar,\ntest\"\n"
|
||||||
allNewTransactions, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte(data2), 0, nil, nil, nil, nil, nil)
|
allNewTransactions, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(data2), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.Equal(t, 1, len(allNewTransactions))
|
assert.Equal(t, 1, len(allNewTransactions))
|
||||||
assert.Equal(t, "foo\"bar,\ntest", allNewTransactions[0].Comment)
|
assert.Equal(t, "foo\"bar,\ntest", allNewTransactions[0].Comment)
|
||||||
|
|
||||||
@@ -320,13 +344,13 @@ func TestWeChatPayCsvFileImporterParseImportedData_ParseDescription(t *testing.T
|
|||||||
"----------------------微信支付账单明细列表--------------------,,,,\n" +
|
"----------------------微信支付账单明细列表--------------------,,,,\n" +
|
||||||
"交易时间,交易类型,商品,收/支,金额(元),当前状态,备注\n" +
|
"交易时间,交易类型,商品,收/支,金额(元),当前状态,备注\n" +
|
||||||
"2024-09-01 01:23:45,二维码收款,Test,收入,¥0.12,已收钱,\"\"\n"
|
"2024-09-01 01:23:45,二维码收款,Test,收入,¥0.12,已收钱,\"\"\n"
|
||||||
allNewTransactions, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte(data3), 0, nil, nil, nil, nil, nil)
|
allNewTransactions, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(data3), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.Equal(t, 1, len(allNewTransactions))
|
assert.Equal(t, 1, len(allNewTransactions))
|
||||||
assert.Equal(t, "Test", allNewTransactions[0].Comment)
|
assert.Equal(t, "Test", allNewTransactions[0].Comment)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestWeChatPayCsvFileImporterParseImportedData_SkipUnknownTransferTransaction(t *testing.T) {
|
func TestWeChatPayCsvFileImporterParseImportedData_SkipUnknownTransferTransaction(t *testing.T) {
|
||||||
converter := WeChatPayTransactionDataCsvFileImporter
|
importer := WeChatPayTransactionDataCsvFileImporter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
@@ -341,12 +365,12 @@ func TestWeChatPayCsvFileImporterParseImportedData_SkipUnknownTransferTransactio
|
|||||||
"----------------------微信支付账单明细列表--------------------,,,,\n" +
|
"----------------------微信支付账单明细列表--------------------,,,,\n" +
|
||||||
"交易时间,交易类型,收/支,金额(元),当前状态\n" +
|
"交易时间,交易类型,收/支,金额(元),当前状态\n" +
|
||||||
"2024-09-01 23:59:59,/,/,¥0.05,充值完成\n"
|
"2024-09-01 23:59:59,/,/,¥0.05,充值完成\n"
|
||||||
_, _, _, _, _, _, err := converter.ParseImportedData(context, user, []byte(data), 0, nil, nil, nil, nil, nil)
|
_, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(data), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrNotFoundTransactionDataInFile.Message)
|
assert.EqualError(t, err, errs.ErrNotFoundTransactionDataInFile.Message)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestWeChatPayCsvFileImporterParseImportedData_MissingFileHeader(t *testing.T) {
|
func TestWeChatPayCsvFileImporterParseImportedData_MissingFileHeader(t *testing.T) {
|
||||||
converter := WeChatPayTransactionDataCsvFileImporter
|
importer := WeChatPayTransactionDataCsvFileImporter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
@@ -356,15 +380,15 @@ func TestWeChatPayCsvFileImporterParseImportedData_MissingFileHeader(t *testing.
|
|||||||
|
|
||||||
data := "交易时间,交易类型,收/支,金额(元),当前状态\n" +
|
data := "交易时间,交易类型,收/支,金额(元),当前状态\n" +
|
||||||
"2024-09-01 01:23:45,二维码收款,收入,¥0.12,已收钱\n"
|
"2024-09-01 01:23:45,二维码收款,收入,¥0.12,已收钱\n"
|
||||||
_, _, _, _, _, _, err := converter.ParseImportedData(context, user, []byte(data), 0, nil, nil, nil, nil, nil)
|
_, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(data), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrInvalidFileHeader.Message)
|
assert.EqualError(t, err, errs.ErrInvalidFileHeader.Message)
|
||||||
|
|
||||||
_, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte(""), 0, nil, nil, nil, nil, nil)
|
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(""), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrInvalidFileHeader.Message)
|
assert.EqualError(t, err, errs.ErrInvalidFileHeader.Message)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestWeChatPayCsvFileImporterParseImportedData_MissingRequiredColumn(t *testing.T) {
|
func TestWeChatPayCsvFileImporterParseImportedData_MissingRequiredColumn(t *testing.T) {
|
||||||
converter := WeChatPayTransactionDataCsvFileImporter
|
importer := WeChatPayTransactionDataCsvFileImporter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
@@ -380,7 +404,7 @@ func TestWeChatPayCsvFileImporterParseImportedData_MissingRequiredColumn(t *test
|
|||||||
"----------------------微信支付账单明细列表--------------------,,,,\n" +
|
"----------------------微信支付账单明细列表--------------------,,,,\n" +
|
||||||
"交易类型,收/支,金额(元),当前状态\n" +
|
"交易类型,收/支,金额(元),当前状态\n" +
|
||||||
"二维码收款,收入,¥0.12,已收钱\n"
|
"二维码收款,收入,¥0.12,已收钱\n"
|
||||||
_, _, _, _, _, _, err := converter.ParseImportedData(context, user, []byte(data1), 0, nil, nil, nil, nil, nil)
|
_, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(data1), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrMissingRequiredFieldInHeaderRow.Message)
|
assert.EqualError(t, err, errs.ErrMissingRequiredFieldInHeaderRow.Message)
|
||||||
|
|
||||||
// Missing Category Column
|
// Missing Category Column
|
||||||
@@ -391,7 +415,7 @@ func TestWeChatPayCsvFileImporterParseImportedData_MissingRequiredColumn(t *test
|
|||||||
"----------------------微信支付账单明细列表--------------------,,,,\n" +
|
"----------------------微信支付账单明细列表--------------------,,,,\n" +
|
||||||
"交易时间,收/支,金额(元),当前状态\n" +
|
"交易时间,收/支,金额(元),当前状态\n" +
|
||||||
"2024-09-01 01:23:45,收入,¥0.12,已收钱\n"
|
"2024-09-01 01:23:45,收入,¥0.12,已收钱\n"
|
||||||
_, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte(data2), 0, nil, nil, nil, nil, nil)
|
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(data2), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrMissingRequiredFieldInHeaderRow.Message)
|
assert.EqualError(t, err, errs.ErrMissingRequiredFieldInHeaderRow.Message)
|
||||||
|
|
||||||
// Missing Type Column
|
// Missing Type Column
|
||||||
@@ -402,7 +426,7 @@ func TestWeChatPayCsvFileImporterParseImportedData_MissingRequiredColumn(t *test
|
|||||||
"----------------------微信支付账单明细列表--------------------,,,,\n" +
|
"----------------------微信支付账单明细列表--------------------,,,,\n" +
|
||||||
"交易时间,交易类型,金额(元),当前状态\n" +
|
"交易时间,交易类型,金额(元),当前状态\n" +
|
||||||
"2024-09-01 01:23:45,二维码收款,¥0.12,已收钱\n"
|
"2024-09-01 01:23:45,二维码收款,¥0.12,已收钱\n"
|
||||||
_, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte(data3), 0, nil, nil, nil, nil, nil)
|
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(data3), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrMissingRequiredFieldInHeaderRow.Message)
|
assert.EqualError(t, err, errs.ErrMissingRequiredFieldInHeaderRow.Message)
|
||||||
|
|
||||||
// Missing Amount Column
|
// Missing Amount Column
|
||||||
@@ -413,7 +437,7 @@ func TestWeChatPayCsvFileImporterParseImportedData_MissingRequiredColumn(t *test
|
|||||||
"----------------------微信支付账单明细列表--------------------,,,,\n" +
|
"----------------------微信支付账单明细列表--------------------,,,,\n" +
|
||||||
"交易时间,交易类型,收/支,当前状态\n" +
|
"交易时间,交易类型,收/支,当前状态\n" +
|
||||||
"2024-09-01 01:23:45,二维码收款,收入,已收钱\n"
|
"2024-09-01 01:23:45,二维码收款,收入,已收钱\n"
|
||||||
_, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte(data4), 0, nil, nil, nil, nil, nil)
|
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(data4), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrMissingRequiredFieldInHeaderRow.Message)
|
assert.EqualError(t, err, errs.ErrMissingRequiredFieldInHeaderRow.Message)
|
||||||
|
|
||||||
// Missing Status Column
|
// Missing Status Column
|
||||||
@@ -424,12 +448,12 @@ func TestWeChatPayCsvFileImporterParseImportedData_MissingRequiredColumn(t *test
|
|||||||
"----------------------微信支付账单明细列表--------------------,,,,\n" +
|
"----------------------微信支付账单明细列表--------------------,,,,\n" +
|
||||||
"交易时间,交易类型,收/支,金额(元)\n" +
|
"交易时间,交易类型,收/支,金额(元)\n" +
|
||||||
"2024-09-01 01:23:45,二维码收款,收入,¥0.12\n"
|
"2024-09-01 01:23:45,二维码收款,收入,¥0.12\n"
|
||||||
_, _, _, _, _, _, err = converter.ParseImportedData(context, user, []byte(data5), 0, nil, nil, nil, nil, nil)
|
_, _, _, _, _, _, err = importer.ParseImportedData(context, user, []byte(data5), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrMissingRequiredFieldInHeaderRow.Message)
|
assert.EqualError(t, err, errs.ErrMissingRequiredFieldInHeaderRow.Message)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestWeChatPayCsvFileImporterParseImportedData_NoTransactionData(t *testing.T) {
|
func TestWeChatPayCsvFileImporterParseImportedData_NoTransactionData(t *testing.T) {
|
||||||
converter := WeChatPayTransactionDataCsvFileImporter
|
importer := WeChatPayTransactionDataCsvFileImporter
|
||||||
context := core.NewNullContext()
|
context := core.NewNullContext()
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
@@ -443,6 +467,6 @@ func TestWeChatPayCsvFileImporterParseImportedData_NoTransactionData(t *testing.
|
|||||||
",,,,\n" +
|
",,,,\n" +
|
||||||
"----------------------微信支付账单明细列表--------------------,,,,\n" +
|
"----------------------微信支付账单明细列表--------------------,,,,\n" +
|
||||||
"交易时间,交易类型,收/支,金额(元),当前状态\n"
|
"交易时间,交易类型,收/支,金额(元),当前状态\n"
|
||||||
_, _, _, _, _, _, err := converter.ParseImportedData(context, user, []byte(data), 0, nil, nil, nil, nil, nil)
|
_, _, _, _, _, _, err := importer.ParseImportedData(context, user, []byte(data), time.UTC, converter.DefaultImporterOptions, nil, nil, nil, nil, nil)
|
||||||
assert.EqualError(t, err, errs.ErrNotFoundTransactionDataInFile.Message)
|
assert.EqualError(t, err, errs.ErrNotFoundTransactionDataInFile.Message)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ func (p *weChatPayTransactionDataRowParser) Parse(ctx core.Context, user *models
|
|||||||
}
|
}
|
||||||
|
|
||||||
if p.hasOriginalColumn(wechatPayTransactionAmountColumnName) {
|
if p.hasOriginalColumn(wechatPayTransactionAmountColumnName) {
|
||||||
amount, success := utils.ParseFirstConsecutiveNumber(dataRow.GetData(wechatPayTransactionAmountColumnName))
|
amount, success := utils.ParseFirstConsecutiveNumber(strings.ReplaceAll(dataRow.GetData(wechatPayTransactionAmountColumnName), ",", ""))
|
||||||
|
|
||||||
if !success {
|
if !success {
|
||||||
log.Errorf(ctx, "[wechat_pay_transaction_data_row_parser.Parse] cannot parse amount \"%s\" of transaction in row \"%s\"", dataRow.GetData(wechatPayTransactionAmountColumnName), rowId)
|
log.Errorf(ctx, "[wechat_pay_transaction_data_row_parser.Parse] cannot parse amount \"%s\" of transaction in row \"%s\"", dataRow.GetData(wechatPayTransactionAmountColumnName), rowId)
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
package wechat
|
package wechat
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/converters/converter"
|
"github.com/mayswind/ezbookkeeping/pkg/converters/converter"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/converters/datatable"
|
"github.com/mayswind/ezbookkeeping/pkg/converters/datatable"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/converters/excel"
|
"github.com/mayswind/ezbookkeeping/pkg/converters/excel"
|
||||||
@@ -21,7 +23,7 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// ParseImportedData returns the imported data by parsing the wechat pay transaction csv data
|
// ParseImportedData returns the imported data by parsing the wechat pay transaction csv data
|
||||||
func (c *wechatPayTransactionDataXlsxFileImporter) ParseImportedData(ctx core.Context, user *models.User, data []byte, defaultTimezoneOffset int16, accountMap map[string]*models.Account, expenseCategoryMap map[string]map[string]*models.TransactionCategory, incomeCategoryMap map[string]map[string]*models.TransactionCategory, transferCategoryMap map[string]map[string]*models.TransactionCategory, tagMap map[string]*models.TransactionTag) (models.ImportedTransactionSlice, []*models.Account, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionTag, error) {
|
func (c *wechatPayTransactionDataXlsxFileImporter) ParseImportedData(ctx core.Context, user *models.User, data []byte, defaultTimezone *time.Location, additionalOptions converter.TransactionDataImporterOptions, accountMap map[string]*models.Account, expenseCategoryMap map[string]map[string]*models.TransactionCategory, incomeCategoryMap map[string]map[string]*models.TransactionCategory, transferCategoryMap map[string]map[string]*models.TransactionCategory, tagMap map[string]*models.TransactionTag) (models.ImportedTransactionSlice, []*models.Account, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionCategory, []*models.TransactionTag, error) {
|
||||||
xlsxDataTable, err := excel.CreateNewExcelOOXMLFileBasicDataTable(data, false)
|
xlsxDataTable, err := excel.CreateNewExcelOOXMLFileBasicDataTable(data, false)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -49,5 +51,5 @@ func (c *wechatPayTransactionDataXlsxFileImporter) ParseImportedData(ctx core.Co
|
|||||||
transactionDataTable := datatable.CreateNewTransactionDataTableFromCommonDataTable(commonDataTable, wechatPayTransactionSupportedColumns, transactionRowParser)
|
transactionDataTable := datatable.CreateNewTransactionDataTableFromCommonDataTable(commonDataTable, wechatPayTransactionSupportedColumns, transactionRowParser)
|
||||||
dataTableImporter := converter.CreateNewSimpleImporterWithTypeNameMapping(wechatPayTransactionTypeNameMapping)
|
dataTableImporter := converter.CreateNewSimpleImporterWithTypeNameMapping(wechatPayTransactionTypeNameMapping)
|
||||||
|
|
||||||
return dataTableImporter.ParseImportedData(ctx, user, transactionDataTable, defaultTimezoneOffset, accountMap, expenseCategoryMap, incomeCategoryMap, transferCategoryMap, tagMap)
|
return dataTableImporter.ParseImportedData(ctx, user, transactionDataTable, defaultTimezone, additionalOptions, accountMap, expenseCategoryMap, incomeCategoryMap, transferCategoryMap, tagMap)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
package core
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
// ApplicationName represents the application name
|
||||||
|
const ApplicationName = "ezBookkeeping"
|
||||||
|
|
||||||
|
// Version, CommitHash and BuildTime are set at build
|
||||||
|
var (
|
||||||
|
Version string
|
||||||
|
CommitHash string
|
||||||
|
BuildTime string
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetOutgoingUserAgent() string {
|
||||||
|
if Version == "" {
|
||||||
|
return ApplicationName
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Sprintf("%s/%s", ApplicationName, Version)
|
||||||
|
}
|
||||||
+38
-7
@@ -4,6 +4,7 @@ import (
|
|||||||
"net"
|
"net"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
|
||||||
@@ -25,6 +26,9 @@ const RemoteClientPortHeader = "X-Real-Port"
|
|||||||
// ClientTimezoneOffsetHeaderName represents the header name of client timezone offset
|
// ClientTimezoneOffsetHeaderName represents the header name of client timezone offset
|
||||||
const ClientTimezoneOffsetHeaderName = "X-Timezone-Offset"
|
const ClientTimezoneOffsetHeaderName = "X-Timezone-Offset"
|
||||||
|
|
||||||
|
// ClientTimezoneNameHeaderName represents the header name of client timezone name
|
||||||
|
const ClientTimezoneNameHeaderName = "X-Timezone-Name"
|
||||||
|
|
||||||
const tokenHeaderName = "Authorization"
|
const tokenHeaderName = "Authorization"
|
||||||
const tokenHeaderValuePrefix = "bearer "
|
const tokenHeaderValuePrefix = "bearer "
|
||||||
const tokenQueryStringParam = "token"
|
const tokenQueryStringParam = "token"
|
||||||
@@ -183,16 +187,24 @@ func (c *WebContext) GetClientLocale() string {
|
|||||||
return value
|
return value
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetClientTimezoneOffset returns the client timezone offset
|
func (c *WebContext) GetClientTimezone() (*time.Location, error) {
|
||||||
func (c *WebContext) GetClientTimezoneOffset() (int16, error) {
|
timezoneName := c.getClientTimezoneName()
|
||||||
value := c.GetHeader(ClientTimezoneOffsetHeaderName)
|
|
||||||
offset, err := strconv.Atoi(value)
|
|
||||||
|
|
||||||
if err != nil {
|
if timezoneName != "" {
|
||||||
return 0, err
|
location, err := time.LoadLocation(timezoneName)
|
||||||
|
|
||||||
|
if err == nil && location != nil {
|
||||||
|
return location, nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return int16(offset), nil
|
utcOffset, err := c.getClientTimezoneOffset()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return time.FixedZone("Client Fixed Timezone", int(utcOffset)*60), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetResponseError sets the response error
|
// SetResponseError sets the response error
|
||||||
@@ -211,6 +223,25 @@ func (c *WebContext) GetResponseError() *errs.Error {
|
|||||||
return err.(*errs.Error)
|
return err.(*errs.Error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetClientTimezoneOffset returns the client timezone offset
|
||||||
|
func (c *WebContext) getClientTimezoneOffset() (int16, error) {
|
||||||
|
value := c.GetHeader(ClientTimezoneOffsetHeaderName)
|
||||||
|
offset, err := strconv.Atoi(value)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return int16(offset), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetClientTimezoneName returns the client timezone name
|
||||||
|
func (c *WebContext) getClientTimezoneName() string {
|
||||||
|
value := c.GetHeader(ClientTimezoneNameHeaderName)
|
||||||
|
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
// WrapWebContext returns a context wrapped by this file
|
// WrapWebContext returns a context wrapped by this file
|
||||||
func WrapWebContext(ginCtx *gin.Context) *WebContext {
|
func WrapWebContext(ginCtx *gin.Context) *WebContext {
|
||||||
return &WebContext{
|
return &WebContext{
|
||||||
|
|||||||
@@ -6,6 +6,15 @@ import (
|
|||||||
"github.com/golang-jwt/jwt/v5"
|
"github.com/golang-jwt/jwt/v5"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// TokenUserAgentCreatedViaCli is the user agent of token created via cli
|
||||||
|
const TokenUserAgentCreatedViaCli = ApplicationName + " Cli"
|
||||||
|
|
||||||
|
// TokenUserAgentForAPI is the user agent for API token
|
||||||
|
const TokenUserAgentForAPI = ApplicationName + " API"
|
||||||
|
|
||||||
|
// TokenUserAgentForMCP is the user agent for MCP token
|
||||||
|
const TokenUserAgentForMCP = ApplicationName + " MCP"
|
||||||
|
|
||||||
// TokenType represents token type
|
// TokenType represents token type
|
||||||
type TokenType byte
|
type TokenType byte
|
||||||
|
|
||||||
|
|||||||
@@ -43,6 +43,8 @@ const (
|
|||||||
NormalSubcategoryLargeLanguageModel = 15
|
NormalSubcategoryLargeLanguageModel = 15
|
||||||
NormalSubcategoryUserExternalAuth = 16
|
NormalSubcategoryUserExternalAuth = 16
|
||||||
NormalSubcategoryOAuth2 = 17
|
NormalSubcategoryOAuth2 = 17
|
||||||
|
NormalSubcategoryInsightsExplorer = 18
|
||||||
|
NormalSubcategoryTagGroup = 19
|
||||||
)
|
)
|
||||||
|
|
||||||
// Error represents the specific error returned to user
|
// Error represents the specific error returned to user
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
package errs
|
||||||
|
|
||||||
|
import "net/http"
|
||||||
|
|
||||||
|
// Error codes related to insights explorers
|
||||||
|
var (
|
||||||
|
ErrInsightsExplorerIdInvalid = NewNormalError(NormalSubcategoryInsightsExplorer, 0, http.StatusBadRequest, "explorer id is invalid")
|
||||||
|
ErrInsightsExplorerNotFound = NewNormalError(NormalSubcategoryInsightsExplorer, 1, http.StatusBadRequest, "explorer not found")
|
||||||
|
ErrInsightsExplorerDataInvalid = NewNormalError(NormalSubcategoryInsightsExplorer, 2, http.StatusBadRequest, "explorer data is invalid")
|
||||||
|
)
|
||||||
@@ -94,3 +94,8 @@ func GetParameterInvalidHexRGBColorMessage(field string) string {
|
|||||||
func GetParameterInvalidAmountFilterMessage(field string) string {
|
func GetParameterInvalidAmountFilterMessage(field string) string {
|
||||||
return fmt.Sprintf("parameter \"%s\" is invalid amount filter", field)
|
return fmt.Sprintf("parameter \"%s\" is invalid amount filter", field)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetParameterInvalidTagFilterMessage returns specific error message for invalid tag filter parameter error
|
||||||
|
func GetParameterInvalidTagFilterMessage(field string) string {
|
||||||
|
return fmt.Sprintf("parameter \"%s\" is invalid tag filter", field)
|
||||||
|
}
|
||||||
|
|||||||
@@ -17,4 +17,9 @@ var (
|
|||||||
ErrInvalidOAuth2Token = NewNormalError(NormalSubcategoryOAuth2, 8, http.StatusBadRequest, "invalid oauth2 token")
|
ErrInvalidOAuth2Token = NewNormalError(NormalSubcategoryOAuth2, 8, http.StatusBadRequest, "invalid oauth2 token")
|
||||||
ErrCannotRetrieveUserInfo = NewNormalError(NormalSubcategoryOAuth2, 9, http.StatusBadRequest, "cannot retrieve user info from oauth2 provider")
|
ErrCannotRetrieveUserInfo = NewNormalError(NormalSubcategoryOAuth2, 9, http.StatusBadRequest, "cannot retrieve user info from oauth2 provider")
|
||||||
ErrOAuth2UserAlreadyBoundToAnotherUser = NewNormalError(NormalSubcategoryOAuth2, 10, http.StatusBadRequest, "oauth2 user already bound to another user")
|
ErrOAuth2UserAlreadyBoundToAnotherUser = NewNormalError(NormalSubcategoryOAuth2, 10, http.StatusBadRequest, "oauth2 user already bound to another user")
|
||||||
|
ErrOAuth2UserNameAndEmailEmpty = NewNormalError(NormalSubcategoryOAuth2, 11, http.StatusBadRequest, "user name and email from oauth2 provider are both empty")
|
||||||
|
ErrOAuth2UserNameEmpty = NewNormalError(NormalSubcategoryOAuth2, 12, http.StatusBadRequest, "user name from oauth2 provider is empty")
|
||||||
|
ErrOAuth2EmailEmpty = NewNormalError(NormalSubcategoryOAuth2, 13, http.StatusBadRequest, "email from oauth2 provider is empty")
|
||||||
|
ErrOAuth2UserNameEmptyCannotRegister = NewNormalError(NormalSubcategoryOAuth2, 14, http.StatusBadRequest, "user name from oauth2 provider is empty, cannot register new user")
|
||||||
|
ErrOAuth2EmailEmptyCannotRegister = NewNormalError(NormalSubcategoryOAuth2, 15, http.StatusBadRequest, "email from oauth2 provider is empty, cannot register new user")
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
package errs
|
||||||
|
|
||||||
|
import "net/http"
|
||||||
|
|
||||||
|
// Error codes related to transaction tag groups
|
||||||
|
var (
|
||||||
|
ErrTransactionTagGroupIdInvalid = NewNormalError(NormalSubcategoryTagGroup, 0, http.StatusBadRequest, "transaction tag group id is invalid")
|
||||||
|
ErrTransactionTagGroupNotFound = NewNormalError(NormalSubcategoryTagGroup, 1, http.StatusBadRequest, "transaction tag group not found")
|
||||||
|
ErrTransactionTagGroupInUseCannotBeDeleted = NewNormalError(NormalSubcategoryTagGroup, 2, http.StatusBadRequest, "transaction tag group is in use and cannot be deleted")
|
||||||
|
)
|
||||||
@@ -7,10 +7,10 @@ import (
|
|||||||
|
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/core"
|
"github.com/mayswind/ezbookkeeping/pkg/core"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/errs"
|
"github.com/mayswind/ezbookkeeping/pkg/errs"
|
||||||
|
"github.com/mayswind/ezbookkeeping/pkg/httpclient"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/log"
|
"github.com/mayswind/ezbookkeeping/pkg/log"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/models"
|
"github.com/mayswind/ezbookkeeping/pkg/models"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/settings"
|
"github.com/mayswind/ezbookkeeping/pkg/settings"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/utils"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// HttpExchangeRatesDataSource defines the structure of http exchange rates data source
|
// HttpExchangeRatesDataSource defines the structure of http exchange rates data source
|
||||||
@@ -41,6 +41,10 @@ func (e *CommonHttpExchangeRatesDataProvider) GetLatestExchangeRates(c core.Cont
|
|||||||
|
|
||||||
for i := 0; i < len(requests); i++ {
|
for i := 0; i < len(requests); i++ {
|
||||||
req := requests[i]
|
req := requests[i]
|
||||||
|
req = req.WithContext(httpclient.CustomHttpResponseLog(c, func(data []byte) {
|
||||||
|
log.Debugf(c, "[common_http_exchange_rates_data_provider.GetLatestExchangeRates] response#%d is %s", i, data)
|
||||||
|
}))
|
||||||
|
|
||||||
resp, err := e.httpClient.Do(req)
|
resp, err := e.httpClient.Do(req)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -51,8 +55,6 @@ func (e *CommonHttpExchangeRatesDataProvider) GetLatestExchangeRates(c core.Cont
|
|||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
body, err := io.ReadAll(resp.Body)
|
body, err := io.ReadAll(resp.Body)
|
||||||
|
|
||||||
log.Debugf(c, "[common_http_exchange_rates_data_provider.GetLatestExchangeRates] response#%d is %s", i, body)
|
|
||||||
|
|
||||||
if resp.StatusCode != 200 {
|
if resp.StatusCode != 200 {
|
||||||
log.Errorf(c, "[common_http_exchange_rates_data_provider.GetLatestExchangeRates] failed to get latest exchange rate data response for user \"uid:%d\", because response code is %d", uid, resp.StatusCode)
|
log.Errorf(c, "[common_http_exchange_rates_data_provider.GetLatestExchangeRates] failed to get latest exchange rate data response for user \"uid:%d\", because response code is %d", uid, resp.StatusCode)
|
||||||
return nil, errs.ErrFailedToRequestRemoteApi
|
return nil, errs.ErrFailedToRequestRemoteApi
|
||||||
@@ -106,6 +108,6 @@ func (e *CommonHttpExchangeRatesDataProvider) GetLatestExchangeRates(c core.Cont
|
|||||||
func newCommonHttpExchangeRatesDataProvider(config *settings.Config, dataSource HttpExchangeRatesDataSource) *CommonHttpExchangeRatesDataProvider {
|
func newCommonHttpExchangeRatesDataProvider(config *settings.Config, dataSource HttpExchangeRatesDataSource) *CommonHttpExchangeRatesDataProvider {
|
||||||
return &CommonHttpExchangeRatesDataProvider{
|
return &CommonHttpExchangeRatesDataProvider{
|
||||||
dataSource: dataSource,
|
dataSource: dataSource,
|
||||||
httpClient: utils.NewHttpClient(config.ExchangeRatesRequestTimeout, config.ExchangeRatesProxy, config.ExchangeRatesSkipTLSVerify, settings.GetUserAgent()),
|
httpClient: httpclient.NewHttpClient(config.ExchangeRatesRequestTimeout, config.ExchangeRatesProxy, config.ExchangeRatesSkipTLSVerify, core.GetOutgoingUserAgent(), config.EnableDebugLog),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user