From 54ef138d90df76f7343f66fdfc5eae660f927802 Mon Sep 17 00:00:00 2001 From: BaoXuebin Date: Sun, 12 Dec 2021 22:42:07 +0800 Subject: [PATCH] add:import alipay payroll --- server.go | 2 ++ service/import.go | 60 +++++++++++++++++++++++++++++++++++++ service/transactions.go | 66 +++++++++++++++++++++++++++++++++-------- 3 files changed, 115 insertions(+), 13 deletions(-) create mode 100644 service/import.go diff --git a/server.go b/server.go index 9e0b4e3..1ee11b1 100644 --- a/server.go +++ b/server.go @@ -76,6 +76,7 @@ func RegisterRouter(router *gin.Engine) { authorized.GET("/stats/month/total", service.StatsMonthTotal) authorized.GET("/transaction", service.QueryTransactions) authorized.POST("/transaction", service.AddTransactions) + authorized.POST("/transaction/batch", service.AddBatchTransactions) authorized.GET("/transaction/payee", service.QueryTransactionPayees) authorized.GET("/transaction/template", service.QueryTransactionTemplates) authorized.POST("/transaction/template", service.AddTransactionTemplate) @@ -84,6 +85,7 @@ func RegisterRouter(router *gin.Engine) { authorized.GET("/file/dir", service.QueryLedgerSourceFileDir) authorized.GET("/file/content", service.QueryLedgerSourceFileContent) authorized.POST("/file", service.UpdateLedgerSourceFileContent) + authorized.POST("/import/alipay", service.ImportAliPayCSV) } } diff --git a/service/import.go b/service/import.go new file mode 100644 index 0000000..5f61714 --- /dev/null +++ b/service/import.go @@ -0,0 +1,60 @@ +package service + +import ( + "bufio" + "encoding/csv" + "github.com/beancount-gs/script" + "github.com/gin-gonic/gin" + "golang.org/x/text/encoding/simplifiedchinese" + "io" + "strings" +) + +func ImportAliPayCSV(c *gin.Context) { + ledgerConfig := script.GetLedgerConfigFromContext(c) + + file, _ := c.FormFile("file") + f, _ := file.Open() + reader := csv.NewReader(simplifiedchinese.GBK.NewDecoder().Reader(bufio.NewReader(f))) + + result := make([]Transaction, 0) + + currency := "CNY" + currencySymbol := script.GetCommoditySymbol(currency) + + for { + lines, err := reader.Read() + if err == io.EOF { + break + } else if err != nil { + script.LogError(ledgerConfig.Mail, err.Error()) + } + if len(lines) > 11 { + fields := strings.Fields(lines[2]) + status := strings.Trim(lines[15], " ") + account := "" + if status == "已收入" { + account = "Income:" + } else if status == "已支出" { + account = "Expenses:" + } else { + continue + } + + if len(fields) >= 2 { + result = append(result, Transaction{ + Id: strings.Trim(lines[0], " "), + Date: strings.Trim(fields[0], " "), + Payee: strings.Trim(lines[7], " "), + Narration: strings.Trim(lines[8], " "), + Number: strings.Trim(lines[9], " "), + Account: account, + Currency: currency, + CurrencySymbol: currencySymbol, + }) + } + } + } + + OK(c, result) +} diff --git a/service/transactions.go b/service/transactions.go index b8e5cd5..ea43304 100644 --- a/service/transactions.go +++ b/service/transactions.go @@ -4,6 +4,7 @@ import ( "crypto/sha1" "encoding/hex" "encoding/json" + "errors" "fmt" "github.com/beancount-gs/script" "github.com/gin-gonic/gin" @@ -84,6 +85,25 @@ func sum(entries []AddTransactionEntryForm, openingBalances string) decimal.Deci return sumVal } +func AddBatchTransactions(c *gin.Context) { + var addTransactionForms []AddTransactionForm + if err := c.ShouldBindJSON(&addTransactionForms); err != nil { + BadRequest(c, err.Error()) + return + } + result := make([]string, 0) + ledgerConfig := script.GetLedgerConfigFromContext(c) + for _, form := range addTransactionForms { + err := saveTransaction(nil, form, ledgerConfig) + if err == nil { + result = append(result, form.Date+form.Payee+form.Desc) + } else { + script.LogError(ledgerConfig.Mail, err.Error()) + } + } + OK(c, result) +} + func AddTransactions(c *gin.Context) { var addTransactionForm AddTransactionForm if err := c.ShouldBindJSON(&addTransactionForm); err != nil { @@ -91,12 +111,22 @@ func AddTransactions(c *gin.Context) { return } ledgerConfig := script.GetLedgerConfigFromContext(c) + err := saveTransaction(c, addTransactionForm, ledgerConfig) + if err != nil { + return + } + OK(c, nil) +} + +func saveTransaction(c *gin.Context, addTransactionForm AddTransactionForm, ledgerConfig *script.Config) error { // 账户是否平衡 sumVal := sum(addTransactionForm.Entries, ledgerConfig.OpeningBalances) val, _ := decimal.NewFromString("0.01") if sumVal.Abs().GreaterThan(val) { - TransactionNotBalance(c) - return + if c != nil { + TransactionNotBalance(c) + } + return errors.New("transaction not balance") } // 2021-09-29 * "支付宝" "黄金补仓X元" #Invest @@ -129,8 +159,10 @@ func AddTransactions(c *gin.Context) { priceLine := fmt.Sprintf("%s price %s %s %s", addTransactionForm.Date, account.Currency, entry.Price, ledgerConfig.OperatingCurrency) err := script.AppendFileInNewLine(script.GetLedgerPriceFilePath(ledgerConfig.DataPath), priceLine) if err != nil { - InternalError(c, err.Error()) - return + if c != nil { + InternalError(c, err.Error()) + } + return errors.New("internal error") } } } @@ -142,8 +174,10 @@ func AddTransactions(c *gin.Context) { // 记账的日期 month, err := time.Parse("2006-01-02", addTransactionForm.Date) if err != nil { - InternalError(c, err.Error()) - return + if c != nil { + InternalError(c, err.Error()) + } + return errors.New("internal error") } monthStr := month.Format("2006-01") filePath := fmt.Sprintf("%s/month/%s.bean", ledgerConfig.DataPath, monthStr) @@ -152,23 +186,29 @@ func AddTransactions(c *gin.Context) { if !script.FileIfExist(filePath) { err = script.CreateFile(filePath) if err != nil { - InternalError(c, err.Error()) - return + if c != nil { + InternalError(c, err.Error()) + } + return errors.New("internal error") } // include ./2021-11.bean err = script.AppendFileInNewLine(script.GetLedgerMonthsFilePath(ledgerConfig.DataPath), fmt.Sprintf("include \"./%s.bean\"", monthStr)) if err != nil { - InternalError(c, err.Error()) - return + if c != nil { + InternalError(c, err.Error()) + } + return errors.New("internal error") } } err = script.AppendFileInNewLine(filePath, line) if err != nil { - InternalError(c, err.Error()) - return + if c != nil { + InternalError(c, err.Error()) + } + return errors.New("internal error") } - OK(c, nil) + return nil } type transactionPayee struct {