add account and transaction api impl
This commit is contained in:
parent
27accd1fb9
commit
571c3303c8
|
|
@ -3,5 +3,6 @@
|
|||
"dataPath": "E:\\beancount",
|
||||
"operatingCurrency": "CNY",
|
||||
"startDate": "1970-01-01",
|
||||
"isBak": true
|
||||
"isBak": true,
|
||||
"openingBalances": "Equity:OpeningBalances"
|
||||
}
|
||||
|
|
@ -23,15 +23,16 @@ type Config struct {
|
|||
OperatingCurrency string `json:"operatingCurrency"`
|
||||
StartDate string `json:"startDate"`
|
||||
IsBak bool `json:"isBak"`
|
||||
OpeningBalances string `json:"openingBalances"`
|
||||
}
|
||||
|
||||
type Account struct {
|
||||
Acc string `json:"account"`
|
||||
StartDate string `json:"startDate"`
|
||||
Commodity string `json:"commodity,omitempty"`
|
||||
PriceAmount string `json:"priceAmount,omitempty"`
|
||||
PriceCommodity string `json:"priceCommodity,omitempty"`
|
||||
PriceCommoditySymbol string `json:"priceCommoditySymbol,omitempty"`
|
||||
Currency string `json:"currency,omitempty"`
|
||||
MarketNumber string `json:"marketNumber,omitempty"`
|
||||
MarketCurrency string `json:"marketCurrency,omitempty"`
|
||||
MarketCurrencySymbol string `json:"marketCurrencySymbol,omitempty"`
|
||||
EndDate string `json:"endDate,omitempty"`
|
||||
Type *AccountType `json:"type,omitempty"`
|
||||
}
|
||||
|
|
@ -77,10 +78,28 @@ func GetLedgerAccounts(ledgerId string) []Account {
|
|||
return ledgerAccountsMap[ledgerId]
|
||||
}
|
||||
|
||||
func GetLedgerAccount(ledgerId string, account string) Account {
|
||||
accounts := ledgerAccountsMap[ledgerId]
|
||||
for _, acc := range accounts {
|
||||
if acc.Acc == account {
|
||||
return acc
|
||||
}
|
||||
}
|
||||
panic("Invalid account")
|
||||
}
|
||||
|
||||
func UpdateLedgerAccounts(ledgerId string, accounts []Account) {
|
||||
ledgerAccountsMap[ledgerId] = accounts
|
||||
}
|
||||
|
||||
func GetLedgerAccountTypes(ledgerId string) map[string]string {
|
||||
return ledgerAccountTypesMap[ledgerId]
|
||||
}
|
||||
|
||||
func UpdateLedgerAccountTypes(ledgerId string, accountTypesMap map[string]string) {
|
||||
ledgerAccountTypesMap[ledgerId] = accountTypesMap
|
||||
}
|
||||
|
||||
func GetAccountType(ledgerId string, acc string) AccountType {
|
||||
accountTypes := ledgerAccountTypesMap[ledgerId]
|
||||
accNodes := strings.Split(acc, ":")
|
||||
|
|
@ -122,6 +141,10 @@ func LoadServerConfig() error {
|
|||
LogSystemError("Failed unmarshall config file (/config/config.json)")
|
||||
return err
|
||||
}
|
||||
// 兼容旧版本数据,设置默认平衡账户
|
||||
if serverConfig.OpeningBalances == "" {
|
||||
serverConfig.OpeningBalances = "Equity:OpeningBalances"
|
||||
}
|
||||
LogSystemInfo("Success load config file (/config/config.json)")
|
||||
// load white list
|
||||
fileContent, err = ReadFile("./config/white_list.json")
|
||||
|
|
@ -148,6 +171,15 @@ func LoadLedgerConfigMap() error {
|
|||
LogSystemError("Failed unmarshal config file (" + path + ")")
|
||||
return err
|
||||
}
|
||||
// 兼容旧数据,初始化 平衡账户
|
||||
temp := make(map[string]Config)
|
||||
for key, val := range ledgerConfigMap {
|
||||
if val.OpeningBalances == "" {
|
||||
val.OpeningBalances = serverConfig.OpeningBalances
|
||||
}
|
||||
temp[key] = val
|
||||
}
|
||||
ledgerConfigMap = temp
|
||||
LogSystemInfo("Success load ledger_config file (" + path + ")")
|
||||
return nil
|
||||
}
|
||||
|
|
@ -185,7 +217,7 @@ func LoadLedgerAccountsMap() error {
|
|||
account := Account{Acc: key, Type: nil}
|
||||
// 货币单位
|
||||
if len(words) >= 4 {
|
||||
account.Commodity = words[3]
|
||||
account.Currency = words[3]
|
||||
}
|
||||
if words[1] == "open" {
|
||||
account.StartDate = words[0]
|
||||
|
|
@ -257,3 +289,8 @@ func GetCommoditySymbol(commodity string) string {
|
|||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func GetAccountPrefix(account string) string {
|
||||
nodes := strings.Split(account, ":")
|
||||
return nodes[0]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,15 +27,35 @@ func ReadFile(filePath string) ([]byte, error) {
|
|||
}
|
||||
|
||||
func WriteFile(filePath string, content string) error {
|
||||
err := ioutil.WriteFile(filePath, []byte(content), os.ModePerm)
|
||||
if err != nil {
|
||||
LogSystemError("Failed to write file (" + filePath + ")")
|
||||
return err
|
||||
content = "\r\n" + content
|
||||
file, err := os.OpenFile(filePath, os.O_CREATE, 0644)
|
||||
if err == nil {
|
||||
_, err = file.WriteString(content)
|
||||
if err != nil {
|
||||
LogSystemError("Failed to write file (" + filePath + ")")
|
||||
return err
|
||||
}
|
||||
}
|
||||
defer file.Close()
|
||||
LogSystemInfo("Success write file (" + filePath + ")")
|
||||
return nil
|
||||
}
|
||||
|
||||
func AppendFileInNewLine(filePath string, content string) error {
|
||||
content = "\r\n" + content
|
||||
file, err := os.OpenFile(filePath, os.O_APPEND, 0644)
|
||||
if err == nil {
|
||||
_, err = file.WriteString(content)
|
||||
if err != nil {
|
||||
LogSystemError("Failed to append file (" + filePath + ")")
|
||||
return err
|
||||
}
|
||||
}
|
||||
defer file.Close()
|
||||
LogSystemInfo("Success append file (" + filePath + ")")
|
||||
return nil
|
||||
}
|
||||
|
||||
func CreateFile(filePath string) error {
|
||||
f, err := os.Create(filePath)
|
||||
if nil != err {
|
||||
|
|
|
|||
|
|
@ -21,3 +21,11 @@ func GetLedgerTransactionsTemplateFilePath(dataPath string) string {
|
|||
func GetLedgerAccountTypeFilePath(dataPath string) string {
|
||||
return dataPath + "/.beancount-ns/account_type.json"
|
||||
}
|
||||
|
||||
func GetLedgerPriceFilePath(dataPath string) string {
|
||||
return dataPath + "/price/prices.bean"
|
||||
}
|
||||
|
||||
func GetLedgerMonthsFilePath(dataPath string) string {
|
||||
return dataPath + "/month/months.bean"
|
||||
}
|
||||
|
|
|
|||
12
server.go
12
server.go
|
|
@ -52,6 +52,12 @@ func RegisterRouter(router *gin.Engine) {
|
|||
authorized.GET("/account/valid", service.QueryValidAccount)
|
||||
authorized.GET("/account/all", service.QueryAllAccount)
|
||||
authorized.GET("/account/type", service.QueryAccountType)
|
||||
authorized.POST("/account", service.AddAccount)
|
||||
authorized.POST("/account/type", service.AddAccountType)
|
||||
authorized.POST("/account/close", service.CloseAccount)
|
||||
authorized.POST("/account/icon", service.ChangeAccountIcon)
|
||||
authorized.POST("/account/balance", service.BalanceAccount)
|
||||
authorized.POST("/commodity/price", service.SyncCommodityPrice)
|
||||
authorized.GET("/stats/months", service.MonthsList)
|
||||
authorized.GET("/stats/total", service.StatsTotal)
|
||||
authorized.GET("/stats/payee", service.StatsPayee)
|
||||
|
|
@ -59,17 +65,13 @@ func RegisterRouter(router *gin.Engine) {
|
|||
authorized.GET("/stats/account/trend", service.StatsAccountTrend)
|
||||
authorized.GET("/stats/month/total", service.StatsMonthTotal)
|
||||
authorized.GET("/transaction", service.QueryTransactions)
|
||||
authorized.POST("/transaction", service.AddTransactions)
|
||||
authorized.GET("/transaction/payee", service.QueryTransactionPayees)
|
||||
authorized.GET("/transaction/template", service.QueryTransactionTemplates)
|
||||
authorized.GET("/tags", service.QueryTags)
|
||||
authorized.GET("/file/dir", service.QueryLedgerSourceFileDir)
|
||||
authorized.GET("/file/content", service.QueryLedgerSourceFileContent)
|
||||
authorized.POST("/file", service.UpdateLedgerSourceFileContent)
|
||||
|
||||
// 兼容旧版本
|
||||
authorized.GET("/entry", service.QueryTransactions)
|
||||
authorized.GET("/payee", service.QueryTransactionPayees)
|
||||
authorized.GET("/stats/month/incomeExpenses", service.StatsMonthTotal)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,16 +1,25 @@
|
|||
package service
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/beancount-gs/script"
|
||||
"github.com/gin-gonic/gin"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
func QueryValidAccount(c *gin.Context) {
|
||||
ledgerConfig := script.GetLedgerConfigFromContext(c)
|
||||
OK(c, script.GetLedgerAccounts(ledgerConfig.Id))
|
||||
allAccounts := script.GetLedgerAccounts(ledgerConfig.Id)
|
||||
result := make([]script.Account, 0)
|
||||
for _, account := range allAccounts {
|
||||
if account.EndDate == "" {
|
||||
result = append(result, account)
|
||||
}
|
||||
}
|
||||
OK(c, result)
|
||||
}
|
||||
|
||||
type accountPosition struct {
|
||||
|
|
@ -37,21 +46,22 @@ func QueryAllAccount(c *gin.Context) {
|
|||
accounts := script.GetLedgerAccounts(ledgerConfig.Id)
|
||||
result := make([]script.Account, 0, len(accounts))
|
||||
for i := 0; i < len(accounts); i++ {
|
||||
account := accounts[i]
|
||||
// 过滤已结束的账户
|
||||
if accounts[i].EndDate != "" {
|
||||
if account.EndDate != "" {
|
||||
continue
|
||||
}
|
||||
key := accounts[i].Acc
|
||||
key := account.Acc
|
||||
typ := script.GetAccountType(ledgerConfig.Id, key)
|
||||
accounts[i].Type = &typ
|
||||
account.Type = &typ
|
||||
position := strings.Trim(accountPositionMap[key].Position, " ")
|
||||
if position != "" {
|
||||
fields := strings.Fields(position)
|
||||
accounts[i].PriceAmount = fields[0]
|
||||
accounts[i].PriceCommodity = fields[1]
|
||||
accounts[i].PriceCommoditySymbol = script.GetCommoditySymbol(fields[1])
|
||||
account.MarketNumber = fields[0]
|
||||
account.MarketCurrency = fields[1]
|
||||
account.MarketCurrencySymbol = script.GetCommoditySymbol(fields[1])
|
||||
}
|
||||
result = append(result, accounts[i])
|
||||
result = append(result, account)
|
||||
}
|
||||
OK(c, result)
|
||||
}
|
||||
|
|
@ -67,3 +77,165 @@ func QueryAccountType(c *gin.Context) {
|
|||
sort.Sort(script.AccountTypeSort(result))
|
||||
OK(c, result)
|
||||
}
|
||||
|
||||
type AddAccountForm struct {
|
||||
Date string `form:"date" binding:"required"`
|
||||
Account string `form:"account" binding:"required"`
|
||||
// 账户计量单位可以为空
|
||||
Currency string `form:"currency"`
|
||||
}
|
||||
|
||||
func AddAccount(c *gin.Context) {
|
||||
var accountForm AddAccountForm
|
||||
if err := c.ShouldBindJSON(&accountForm); err != nil {
|
||||
BadRequest(c, err.Error())
|
||||
return
|
||||
}
|
||||
ledgerConfig := script.GetLedgerConfigFromContext(c)
|
||||
// 判断账户是否已存在
|
||||
accounts := script.GetLedgerAccounts(ledgerConfig.Id)
|
||||
for _, acc := range accounts {
|
||||
if acc.Acc == accountForm.Account {
|
||||
DuplicateAccount(c)
|
||||
return
|
||||
}
|
||||
}
|
||||
line := fmt.Sprintf("%s open %s %s", accountForm.Date, accountForm.Account, accountForm.Currency)
|
||||
if accountForm.Currency != "" && accountForm.Currency != ledgerConfig.OperatingCurrency {
|
||||
line += " \"FIFO\""
|
||||
}
|
||||
// 写入文件
|
||||
filePath := ledgerConfig.DataPath + "/account/" + script.GetAccountPrefix(accountForm.Account) + ".bean"
|
||||
err := script.AppendFileInNewLine(filePath, line)
|
||||
if err != nil {
|
||||
InternalError(c, err.Error())
|
||||
return
|
||||
}
|
||||
// 更新缓存
|
||||
typ := script.GetAccountType(ledgerConfig.Id, accountForm.Account)
|
||||
account := script.Account{Acc: accountForm.Account, StartDate: accountForm.Date, Currency: accountForm.Currency, Type: &typ}
|
||||
accounts = append(accounts, account)
|
||||
script.UpdateLedgerAccounts(ledgerConfig.Id, accounts)
|
||||
OK(c, account)
|
||||
}
|
||||
|
||||
type AddAccountTypeForm struct {
|
||||
Type string `form:"type" binding:"required"`
|
||||
Name string `form:"name" binding:"required"`
|
||||
}
|
||||
|
||||
func AddAccountType(c *gin.Context) {
|
||||
var addAccountTypeForm AddAccountTypeForm
|
||||
if err := c.ShouldBindJSON(&addAccountTypeForm); err != nil {
|
||||
BadRequest(c, err.Error())
|
||||
return
|
||||
}
|
||||
ledgerConfig := script.GetLedgerConfigFromContext(c)
|
||||
accountTypesMap := script.GetLedgerAccountTypes(ledgerConfig.Id)
|
||||
typ := addAccountTypeForm.Type
|
||||
accountTypesMap[typ] = addAccountTypeForm.Name
|
||||
// 更新文件
|
||||
pathFile := script.GetLedgerAccountTypeFilePath(ledgerConfig.DataPath)
|
||||
bytes, err := json.Marshal(accountTypesMap)
|
||||
if err != nil {
|
||||
InternalError(c, err.Error())
|
||||
return
|
||||
}
|
||||
err = script.WriteFile(pathFile, string(bytes))
|
||||
if err != nil {
|
||||
InternalError(c, err.Error())
|
||||
return
|
||||
}
|
||||
// 更新缓存
|
||||
script.UpdateLedgerAccountTypes(ledgerConfig.Id, accountTypesMap)
|
||||
OK(c, script.AccountType{
|
||||
Key: addAccountTypeForm.Type,
|
||||
Name: addAccountTypeForm.Name,
|
||||
})
|
||||
}
|
||||
|
||||
type CloseAccountForm struct {
|
||||
Date string `form:"date" binding:"required"`
|
||||
Account string `form:"account" binding:"required"`
|
||||
}
|
||||
|
||||
func CloseAccount(c *gin.Context) {
|
||||
var accountForm CloseAccountForm
|
||||
if err := c.ShouldBindJSON(&accountForm); err != nil {
|
||||
BadRequest(c, err.Error())
|
||||
return
|
||||
}
|
||||
ledgerConfig := script.GetLedgerConfigFromContext(c)
|
||||
line := fmt.Sprintf("%s close %s", accountForm.Date, accountForm.Account)
|
||||
// 写入文件
|
||||
filePath := ledgerConfig.DataPath + "/account/" + script.GetAccountPrefix(accountForm.Account) + ".bean"
|
||||
err := script.AppendFileInNewLine(filePath, line)
|
||||
if err != nil {
|
||||
InternalError(c, err.Error())
|
||||
return
|
||||
}
|
||||
// 更新缓存
|
||||
accounts := script.GetLedgerAccounts(ledgerConfig.Id)
|
||||
for i := 0; i < len(accounts); i++ {
|
||||
if accounts[i].Acc == accountForm.Account {
|
||||
accounts[i].EndDate = accountForm.Date
|
||||
}
|
||||
}
|
||||
script.UpdateLedgerAccounts(ledgerConfig.Id, accounts)
|
||||
OK(c, script.Account{
|
||||
Acc: accountForm.Account, EndDate: accountForm.Date,
|
||||
})
|
||||
}
|
||||
|
||||
func ChangeAccountIcon(c *gin.Context) {
|
||||
|
||||
}
|
||||
|
||||
type BalanceAccountForm struct {
|
||||
Date string `form:"date" binding:"required" json:"date"`
|
||||
Account string `form:"account" binding:"required" json:"account"`
|
||||
Number string `form:"number" binding:"required" json:"number"`
|
||||
}
|
||||
|
||||
func BalanceAccount(c *gin.Context) {
|
||||
var accountForm BalanceAccountForm
|
||||
if err := c.ShouldBindJSON(&accountForm); err != nil {
|
||||
BadRequest(c, err.Error())
|
||||
return
|
||||
}
|
||||
ledgerConfig := script.GetLedgerConfigFromContext(c)
|
||||
|
||||
// 获取当前账户信息
|
||||
var acc script.Account
|
||||
accounts := script.GetLedgerAccounts(ledgerConfig.Id)
|
||||
for _, account := range accounts {
|
||||
if account.Acc == accountForm.Account {
|
||||
acc = account
|
||||
}
|
||||
}
|
||||
|
||||
today, err := time.Parse("2006-01-02", accountForm.Date)
|
||||
if err != nil {
|
||||
InternalError(c, err.Error())
|
||||
return
|
||||
}
|
||||
todayStr := today.Format("2006-01-02")
|
||||
yesterdayStr := today.AddDate(0, 0, -1).Format("2006-01-02")
|
||||
month := today.Format("2006-01")
|
||||
line := fmt.Sprintf("\r\n%s pad %s Equity:OpeningBalances", yesterdayStr, accountForm.Account)
|
||||
line += fmt.Sprintf("\r\n%s balance %s %s %s", todayStr, accountForm.Account, accountForm.Number, acc.Currency)
|
||||
|
||||
filePath := fmt.Sprintf("%s/month/%s.bean", ledgerConfig.DataPath, month)
|
||||
err = script.AppendFileInNewLine(filePath, line)
|
||||
if err != nil {
|
||||
InternalError(c, err.Error())
|
||||
return
|
||||
}
|
||||
result := make(map[string]string)
|
||||
result["account"] = accountForm.Account
|
||||
result["date"] = accountForm.Date
|
||||
result["marketNumber"] = accountForm.Number
|
||||
result["marketCurrency"] = ledgerConfig.OperatingCurrency
|
||||
result["marketCurrencySymbol"] = script.GetCommoditySymbol(ledgerConfig.OperatingCurrency)
|
||||
OK(c, result)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,32 @@
|
|||
package service
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/beancount-gs/script"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
type SyncCommodityPriceForm struct {
|
||||
Commodity string `form:"commodity" binding:"required" json:"commodity"`
|
||||
Date string `form:"date" binding:"required" json:"date"`
|
||||
Price string `form:"price" binding:"required" json:"price"`
|
||||
}
|
||||
|
||||
func SyncCommodityPrice(c *gin.Context) {
|
||||
var syncCommodityPriceForm SyncCommodityPriceForm
|
||||
if err := c.ShouldBindJSON(&syncCommodityPriceForm); err != nil {
|
||||
BadRequest(c, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
ledgerConfig := script.GetLedgerConfigFromContext(c)
|
||||
filePath := script.GetLedgerPriceFilePath(ledgerConfig.DataPath)
|
||||
line := fmt.Sprintf("%s price %s %s %s", syncCommodityPriceForm.Date, syncCommodityPriceForm.Commodity, syncCommodityPriceForm.Price, ledgerConfig.OperatingCurrency)
|
||||
// 写入文件
|
||||
err := script.AppendFileInNewLine(filePath, line)
|
||||
if err != nil {
|
||||
InternalError(c, err.Error())
|
||||
return
|
||||
}
|
||||
OK(c, syncCommodityPriceForm)
|
||||
}
|
||||
|
|
@ -21,10 +21,18 @@ func InternalError(c *gin.Context, message string) {
|
|||
c.JSON(http.StatusOK, gin.H{"code": 500, "message": message})
|
||||
}
|
||||
|
||||
func TransactionNotBalance(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, gin.H{"code": 1001})
|
||||
}
|
||||
|
||||
func LedgerIsNotExist(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, gin.H{"code": 1006, "message": "ledger is not exist"})
|
||||
c.JSON(http.StatusOK, gin.H{"code": 1006})
|
||||
}
|
||||
|
||||
func LedgerIsNotAllowAccess(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, gin.H{"code": 1006, "message": "ledger is not allow access"})
|
||||
c.JSON(http.StatusOK, gin.H{"code": 1006})
|
||||
}
|
||||
|
||||
func DuplicateAccount(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, gin.H{"code": 1007})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,9 +2,12 @@ package service
|
|||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/beancount-gs/script"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/shopspring/decimal"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Transaction struct {
|
||||
|
|
@ -47,6 +50,124 @@ func QueryTransactions(c *gin.Context) {
|
|||
OK(c, transactions)
|
||||
}
|
||||
|
||||
type AddTransactionForm struct {
|
||||
Date string `form:"date" binding:"required"`
|
||||
Payee string `form:"payee"`
|
||||
Desc string `form:"desc" binding:"required"`
|
||||
Tags []string `form:"tags"`
|
||||
Entries []AddTransactionEntryForm `form:"entries"`
|
||||
}
|
||||
|
||||
type AddTransactionEntryForm struct {
|
||||
Account string `form:"account" binding:"required"`
|
||||
Number decimal.Decimal `form:"number"`
|
||||
//Currency string `form:"currency"`
|
||||
Price decimal.Decimal `form:"price"`
|
||||
//PriceCurrency string `form:"priceCurrency"`
|
||||
}
|
||||
|
||||
func sum(entries []AddTransactionEntryForm, openingBalances string) decimal.Decimal {
|
||||
sumVal := decimal.NewFromInt(0)
|
||||
for _, entry := range entries {
|
||||
if entry.Account == openingBalances {
|
||||
return sumVal
|
||||
}
|
||||
if entry.Price.IntPart() == 0 {
|
||||
sumVal = entry.Number.Add(sumVal)
|
||||
} else {
|
||||
sumVal = entry.Number.Mul(entry.Price).Add(sumVal)
|
||||
}
|
||||
}
|
||||
return sumVal
|
||||
}
|
||||
|
||||
func AddTransactions(c *gin.Context) {
|
||||
var addTransactionForm AddTransactionForm
|
||||
if err := c.ShouldBindJSON(&addTransactionForm); err != nil {
|
||||
BadRequest(c, err.Error())
|
||||
return
|
||||
}
|
||||
ledgerConfig := script.GetLedgerConfigFromContext(c)
|
||||
// 账户是否平衡
|
||||
sumVal := sum(addTransactionForm.Entries, ledgerConfig.OpeningBalances)
|
||||
val, _ := decimal.NewFromString("0.01")
|
||||
if sumVal.Abs().GreaterThan(val) {
|
||||
TransactionNotBalance(c)
|
||||
return
|
||||
}
|
||||
|
||||
// 2021-09-29 * "支付宝" "黄金补仓X元" #Invest
|
||||
line := fmt.Sprintf("\r\n%s * \"%s\" \"%s\"", addTransactionForm.Date, addTransactionForm.Payee, addTransactionForm.Desc)
|
||||
if len(addTransactionForm.Tags) > 0 {
|
||||
for _, tag := range addTransactionForm.Tags {
|
||||
line += "#" + tag + " "
|
||||
}
|
||||
}
|
||||
|
||||
var autoBalance bool
|
||||
for _, entry := range addTransactionForm.Entries {
|
||||
account := script.GetLedgerAccount(ledgerConfig.Id, entry.Account)
|
||||
if entry.Account == ledgerConfig.OpeningBalances {
|
||||
line += fmt.Sprintf("\r\n %s", entry.Account)
|
||||
} else {
|
||||
line += fmt.Sprintf("\r\n %s %s %s", entry.Account, entry.Number.Round(2).String(), account.Currency)
|
||||
}
|
||||
// 判断是否设计多币种的转换
|
||||
if account.Currency != ledgerConfig.OperatingCurrency && entry.Account != ledgerConfig.OpeningBalances {
|
||||
autoBalance = true
|
||||
// 根据 number 的正负来判断是买入还是卖出
|
||||
if entry.Number.GreaterThan(decimal.NewFromInt(0)) {
|
||||
// {351.729 CNY, 2021-09-29}
|
||||
line += fmt.Sprintf(" {%s %s, %s}", entry.Price, ledgerConfig.OperatingCurrency, addTransactionForm.Date)
|
||||
} else {
|
||||
// {} @ 359.019 CNY
|
||||
line += fmt.Sprintf(" {} @ %s %s", entry.Price, ledgerConfig.OperatingCurrency)
|
||||
}
|
||||
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 autoBalance {
|
||||
line += "\r\n " + ledgerConfig.OpeningBalances
|
||||
}
|
||||
// 记账的日期
|
||||
month, err := time.Parse("2006-01-02", addTransactionForm.Date)
|
||||
if err != nil {
|
||||
InternalError(c, err.Error())
|
||||
return
|
||||
}
|
||||
monthStr := month.Format("2006-01")
|
||||
filePath := fmt.Sprintf("%s/month/%s.bean", ledgerConfig.DataPath, monthStr)
|
||||
|
||||
// 文件不存在,则创建
|
||||
if !script.FileIfExist(filePath) {
|
||||
err = script.CreateFile(filePath)
|
||||
if err != nil {
|
||||
InternalError(c, err.Error())
|
||||
return
|
||||
}
|
||||
// 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
|
||||
}
|
||||
}
|
||||
|
||||
err = script.AppendFileInNewLine(filePath, line)
|
||||
if err != nil {
|
||||
InternalError(c, err.Error())
|
||||
return
|
||||
}
|
||||
OK(c, nil)
|
||||
}
|
||||
|
||||
type transactionPayee struct {
|
||||
Value string `bql:"distinct payee" json:"value"`
|
||||
}
|
||||
|
|
@ -70,11 +191,11 @@ func QueryTransactionPayees(c *gin.Context) {
|
|||
}
|
||||
|
||||
type transactionTemplate struct {
|
||||
Id string `json:"id"`
|
||||
Date string `json:"date"`
|
||||
TemplateName string `json:"templateName"`
|
||||
Payee string `json:"payee"`
|
||||
Desc string `json:"desc"`
|
||||
Id string `json:"id"`
|
||||
Date string `json:"date"`
|
||||
TemplateName string `json:"templateName"`
|
||||
Payee string `json:"payee"`
|
||||
Desc string `json:"desc"`
|
||||
Entries []transactionTemplateEntity `json:"entries"`
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue