2021-11-19 09:54:02 +00:00
|
|
|
package service
|
|
|
|
|
|
|
|
|
|
import (
|
2021-11-22 14:50:10 +00:00
|
|
|
"fmt"
|
2021-11-19 09:54:02 +00:00
|
|
|
"github.com/beancount-gs/script"
|
|
|
|
|
"github.com/gin-gonic/gin"
|
2021-11-24 15:45:45 +00:00
|
|
|
"strconv"
|
2021-11-22 14:50:10 +00:00
|
|
|
"strings"
|
2021-11-19 09:54:02 +00:00
|
|
|
)
|
|
|
|
|
|
2021-11-22 08:47:49 +00:00
|
|
|
type YearMonth struct {
|
|
|
|
|
Year string `bql:"distinct year(date)" json:"year"`
|
|
|
|
|
Month string `bql:"month(date)" json:"month"`
|
|
|
|
|
}
|
2021-11-19 09:54:02 +00:00
|
|
|
|
2021-11-22 08:47:49 +00:00
|
|
|
func MonthsList(c *gin.Context) {
|
2021-11-21 14:37:13 +00:00
|
|
|
ledgerConfig := script.GetLedgerConfigFromContext(c)
|
2021-11-22 08:47:49 +00:00
|
|
|
yearMonthList := make([]YearMonth, 0)
|
|
|
|
|
err := script.BQLQueryList(ledgerConfig, nil, &yearMonthList)
|
2021-11-19 09:54:02 +00:00
|
|
|
if err != nil {
|
2021-11-22 08:47:49 +00:00
|
|
|
InternalError(c, err.Error())
|
2021-11-19 09:54:02 +00:00
|
|
|
return
|
|
|
|
|
}
|
2021-11-22 08:47:49 +00:00
|
|
|
months := make([]string, 0)
|
|
|
|
|
for _, yearMonth := range yearMonthList {
|
|
|
|
|
months = append(months, yearMonth.Year+"-"+yearMonth.Month)
|
2021-11-21 14:37:13 +00:00
|
|
|
}
|
|
|
|
|
OK(c, months)
|
2021-11-19 09:54:02 +00:00
|
|
|
}
|
2021-11-22 14:50:10 +00:00
|
|
|
|
2021-11-24 15:45:45 +00:00
|
|
|
type StatsResult struct {
|
|
|
|
|
Key string
|
|
|
|
|
Value string
|
2021-11-22 14:50:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func StatsTotal(c *gin.Context) {
|
|
|
|
|
ledgerConfig := script.GetLedgerConfigFromContext(c)
|
|
|
|
|
queryParams := script.GetQueryParams(c)
|
|
|
|
|
selectBql := fmt.Sprintf("SELECT '\\', root(account, 1), '\\', sum(convert(value(position), '%s')), '\\'", ledgerConfig.OperatingCurrency)
|
2021-11-24 15:45:45 +00:00
|
|
|
accountTypeTotalList := make([]StatsResult, 0)
|
2021-11-22 14:50:10 +00:00
|
|
|
err := script.BQLQueryListByCustomSelect(ledgerConfig, selectBql, &queryParams, &accountTypeTotalList)
|
|
|
|
|
if err != nil {
|
|
|
|
|
InternalError(c, err.Error())
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
result := make(map[string]string, 0)
|
|
|
|
|
for _, total := range accountTypeTotalList {
|
2021-11-24 15:45:45 +00:00
|
|
|
fields := strings.Fields(total.Value)
|
2021-11-22 14:50:10 +00:00
|
|
|
if len(fields) > 1 {
|
2021-11-24 15:45:45 +00:00
|
|
|
result[total.Key] = fields[0]
|
2021-11-22 14:50:10 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
OK(c, result)
|
|
|
|
|
}
|
2021-11-24 15:45:45 +00:00
|
|
|
|
|
|
|
|
type StatsQuery struct {
|
|
|
|
|
Prefix string `form:"prefix" binding:"required"`
|
|
|
|
|
Year int `form:"year"`
|
|
|
|
|
Month int `form:"month"`
|
|
|
|
|
Level int `form:"level"`
|
|
|
|
|
Type string `form:"type"`
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type AccountPercentResult struct {
|
|
|
|
|
Account string `json:"account"`
|
|
|
|
|
Amount string `json:"amount"`
|
|
|
|
|
OperatingCurrency string `json:"operatingCurrency"`
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func StatsAccountPercent(c *gin.Context) {
|
|
|
|
|
ledgerConfig := script.GetLedgerConfigFromContext(c)
|
|
|
|
|
var statsQuery StatsQuery
|
|
|
|
|
if err := c.ShouldBindQuery(&statsQuery); err != nil {
|
|
|
|
|
BadRequest(c, err.Error())
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
queryParams := script.QueryParams{
|
|
|
|
|
AccountLike: statsQuery.Prefix,
|
|
|
|
|
Year: statsQuery.Year,
|
|
|
|
|
Month: statsQuery.Month,
|
|
|
|
|
Where: true,
|
|
|
|
|
}
|
|
|
|
|
var bql string
|
|
|
|
|
if statsQuery.Level != 0 {
|
|
|
|
|
prefixNodeLen := len(strings.Split(strings.Trim(statsQuery.Prefix, ":"), ":"))
|
|
|
|
|
bql = fmt.Sprintf("SELECT '\\', root(account, %d) as subAccount, '\\', sum(convert(value(position), '%s')), '\\'", statsQuery.Level+prefixNodeLen, ledgerConfig.OperatingCurrency)
|
|
|
|
|
} else {
|
|
|
|
|
bql = fmt.Sprintf("SELECT '\\', account, '\\', sum(convert(value(position), '%s')), '\\'", ledgerConfig.OperatingCurrency)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
statsResultList := make([]AccountPercentResult, 0)
|
|
|
|
|
err := script.BQLQueryListByCustomSelect(ledgerConfig, bql, &queryParams, &statsResultList)
|
|
|
|
|
if err != nil {
|
|
|
|
|
InternalError(c, err.Error())
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for idx, result := range statsResultList {
|
|
|
|
|
fields := strings.Fields(result.Amount)
|
|
|
|
|
statsResultList[idx].Amount = fields[0]
|
|
|
|
|
statsResultList[idx].OperatingCurrency = fields[1]
|
|
|
|
|
}
|
|
|
|
|
OK(c, statsResultList)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type AccountTrendResult struct {
|
|
|
|
|
Date string `json:"date"`
|
|
|
|
|
Amount float64 `json:"amount"`
|
|
|
|
|
OperatingCurrency string `json:"operatingCurrency"`
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func StatsAccountTrend(c *gin.Context) {
|
|
|
|
|
ledgerConfig := script.GetLedgerConfigFromContext(c)
|
|
|
|
|
var statsQuery StatsQuery
|
|
|
|
|
if err := c.ShouldBindQuery(&statsQuery); err != nil {
|
|
|
|
|
BadRequest(c, err.Error())
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
queryParams := script.QueryParams{
|
|
|
|
|
AccountLike: statsQuery.Prefix,
|
|
|
|
|
Year: statsQuery.Year,
|
|
|
|
|
Month: statsQuery.Month,
|
|
|
|
|
Where: true,
|
|
|
|
|
}
|
|
|
|
|
var bql string
|
|
|
|
|
if statsQuery.Type == "avg" {
|
|
|
|
|
bql = fmt.Sprintf("SELECT '\\', date, '\\', sum(convert(value(position), '%s')), '\\'", ledgerConfig.OperatingCurrency)
|
|
|
|
|
} else if statsQuery.Type == "sum" {
|
|
|
|
|
bql = fmt.Sprintf("SELECT '\\', date, '\\', convert(balance, '%s'), '\\'", ledgerConfig.OperatingCurrency)
|
|
|
|
|
} else {
|
|
|
|
|
OK(c, new([]string))
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
statsResultList := make([]StatsResult, 0)
|
|
|
|
|
err := script.BQLQueryListByCustomSelect(ledgerConfig, bql, &queryParams, &statsResultList)
|
|
|
|
|
if err != nil {
|
|
|
|
|
InternalError(c, err.Error())
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
result := make([]AccountTrendResult, 0)
|
|
|
|
|
for _, stats := range statsResultList {
|
|
|
|
|
fields := strings.Fields(stats.Value)
|
|
|
|
|
amount, _ := strconv.ParseFloat(fields[0], 32)
|
|
|
|
|
result = append(result, AccountTrendResult{Date: stats.Key, Amount: amount, OperatingCurrency: fields[1]})
|
|
|
|
|
}
|
|
|
|
|
OK(c, result)
|
|
|
|
|
}
|
2021-11-25 07:41:28 +00:00
|
|
|
|
|
|
|
|
type MonthTotalBQLResult struct {
|
|
|
|
|
Year int
|
|
|
|
|
Month int
|
|
|
|
|
Value string
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type MonthTotal struct {
|
|
|
|
|
Type string `json:"type"`
|
|
|
|
|
Month string `json:"month"`
|
|
|
|
|
Amount float64 `json:"amount"`
|
|
|
|
|
OperatingCurrency string `json:"operatingCurrency"`
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func StatsMonthTotal(c *gin.Context) {
|
|
|
|
|
ledgerConfig := script.GetLedgerConfigFromContext(c)
|
|
|
|
|
|
|
|
|
|
monthSet := make(map[string]bool)
|
|
|
|
|
queryParams := script.QueryParams{
|
|
|
|
|
AccountLike: "Income",
|
|
|
|
|
Where: true,
|
|
|
|
|
OrderBy: "year, month",
|
|
|
|
|
}
|
|
|
|
|
// 按月查询收入
|
|
|
|
|
queryIncomeBql := fmt.Sprintf("select '\\', year, '\\', month, '\\', neg(sum(convert(value(position), '%s'))), '\\'", ledgerConfig.OperatingCurrency)
|
|
|
|
|
monthIncomeTotalResultList := make([]MonthTotalBQLResult, 0)
|
|
|
|
|
err := script.BQLQueryListByCustomSelect(ledgerConfig, queryIncomeBql, &queryParams, &monthIncomeTotalResultList)
|
|
|
|
|
if err != nil {
|
|
|
|
|
InternalError(c, err.Error())
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
monthIncomeMap := make(map[string]MonthTotalBQLResult)
|
|
|
|
|
for _, income := range monthIncomeTotalResultList {
|
|
|
|
|
month := fmt.Sprintf("%d-%d", income.Year, income.Month)
|
|
|
|
|
monthSet[month] = true
|
|
|
|
|
monthIncomeMap[month] = income
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 按月查询支出
|
|
|
|
|
queryParams.AccountLike = "Expenses"
|
|
|
|
|
queryExpensesBql := fmt.Sprintf("select '\\', year, '\\', month, '\\', sum(convert(value(position), '%s')), '\\'", ledgerConfig.OperatingCurrency)
|
|
|
|
|
monthExpensesTotalResultList := make([]MonthTotalBQLResult, 0)
|
|
|
|
|
err = script.BQLQueryListByCustomSelect(ledgerConfig, queryExpensesBql, &queryParams, &monthExpensesTotalResultList)
|
|
|
|
|
if err != nil {
|
|
|
|
|
InternalError(c, err.Error())
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
monthExpensesMap := make(map[string]MonthTotalBQLResult)
|
|
|
|
|
for _, expenses := range monthExpensesTotalResultList {
|
|
|
|
|
month := fmt.Sprintf("%d-%d", expenses.Year, expenses.Month)
|
|
|
|
|
monthSet[month] = true
|
|
|
|
|
monthExpensesMap[month] = expenses
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
monthTotalResult := make([]MonthTotal, 0)
|
|
|
|
|
// 合并结果
|
|
|
|
|
var monthIncome, monthExpenses MonthTotal
|
|
|
|
|
for month := range monthSet {
|
|
|
|
|
if monthIncomeMap[month].Value != "" {
|
|
|
|
|
fields := strings.Fields(monthIncomeMap[month].Value)
|
|
|
|
|
amount, _ := strconv.ParseFloat(fields[0], 64)
|
|
|
|
|
monthIncome = MonthTotal{Type: "收入", Month: month, Amount: amount, OperatingCurrency: fields[1]}
|
|
|
|
|
} else {
|
|
|
|
|
monthIncome = MonthTotal{Type: "收入", Month: month, Amount: 0, OperatingCurrency: ledgerConfig.OperatingCurrency}
|
|
|
|
|
}
|
|
|
|
|
monthTotalResult = append(monthTotalResult, monthIncome)
|
|
|
|
|
|
|
|
|
|
if monthExpensesMap[month].Value != "" {
|
|
|
|
|
fields := strings.Fields(monthExpensesMap[month].Value)
|
|
|
|
|
amount, _ := strconv.ParseFloat(fields[0], 64)
|
|
|
|
|
monthExpenses = MonthTotal{Type: "支出", Month: month, Amount: amount, OperatingCurrency: fields[1]}
|
|
|
|
|
} else {
|
|
|
|
|
monthExpenses = MonthTotal{Type: "支出", Month: month, Amount: 0, OperatingCurrency: ledgerConfig.OperatingCurrency}
|
|
|
|
|
}
|
|
|
|
|
monthTotalResult = append(monthTotalResult, monthExpenses)
|
|
|
|
|
monthTotalResult = append(monthTotalResult, MonthTotal{Type: "结余", Month: month, Amount: monthIncome.Amount - monthExpenses.Amount, OperatingCurrency: ledgerConfig.OperatingCurrency})
|
|
|
|
|
}
|
|
|
|
|
OK(c, monthTotalResult)
|
|
|
|
|
}
|