Skip to content

Commit

Permalink
feat(icbc): automatically change debit/credit bill mode (#90)
Browse files Browse the repository at this point in the history
* fix(icbc): ignore the single space line at the bottom of the csv file

Signed-off-by: TripleZ <me@triplez.cn>

* fix(icbc): set default methodAccount by cashAccount and tx type

Signed-off-by: TripleZ <me@triplez.cn>

* feat(icbc): support ICBC debit card bills

Signed-off-by: TripleZ <me@triplez.cn>

* fix(alipay): metadata `type`

Signed-off-by: TripleZ <me@triplez.cn>

---------

Signed-off-by: TripleZ <me@triplez.cn>
  • Loading branch information
Triple-Z authored May 13, 2023
1 parent 1d0542b commit cd53df8
Show file tree
Hide file tree
Showing 16 changed files with 279 additions and 72 deletions.
14 changes: 10 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -104,8 +104,8 @@ double-entry-generator translate \
double-entry-generator translate \
--config ./example/icbc/config.yaml \
--provider icbc \
--output ./example/icbc/example-icbc-output.beancount \
./example/icbc/example-icbc-records.csv
--output ./example/icbc/example-icbc-credit-output.beancount \
./example/icbc/example-icbc-credit-records.csv
```

## 账单下载与格式问题
Expand Down Expand Up @@ -174,9 +174,15 @@ double-entry-generator translate \

#### 格式示例

[example-icbc-records.csv](./example/icbc/example-icbc-records.csv)
> `double-entry-generator` 能够自动识别出中国工商银行的账单类型(借记卡/信用卡)。
转换后的结果示例:[exmaple-icbc-output.beancount](./example/icbc/example-icbc-output.beancount).
借记卡账单示例: [example-icbc-debit-records.csv](example/icbc/debit/example-icbc-debit-records.csv)

借记卡账单转换后的结果示例:[example-icbc-debit-output.beancount](example/icbc/debit/example-icbc-debit-output.beancount).

信用卡账单示例: [example-icbc-credit-records.csv](example/icbc/credit/example-icbc-credit-records.csv)

信用卡账单转换后的结果示例:[example-icbc-credit-output.beancount](example/icbc/credit/example-icbc-credit-output.beancount).

## 配置

Expand Down
10 changes: 5 additions & 5 deletions example/alipay/example-alipay-output.beancount
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ option "operating_currency" "CNY"
payTime: "2023-01-18 10:17:29 +0800 CST"
source: "支付宝"
status: "交易成功"
txType: "收入"
type: "收入"
Assets:Alipay 222228.50 CNY
Income:FIXME -222228.50 CNY

Expand All @@ -35,7 +35,7 @@ option "operating_currency" "CNY"
payTime: "2023-02-02 15:24:35 +0800 CST"
source: "支付宝"
status: "交易成功"
txType: "不计收支"
type: "不计收支"
Expenses:FIXME 99.34 CNY
Assets:Alipay -99.34 CNY

Expand All @@ -47,7 +47,7 @@ option "operating_currency" "CNY"
payTime: "2023-02-04 18:21:04 +0800 CST"
source: "支付宝"
status: "退款成功"
txType: "不计收支"
type: "不计收支"
Liabilities:CC:COMM:7449 16.03 CNY
Expenses:FIXME -16.03 CNY

Expand All @@ -59,7 +59,7 @@ option "operating_currency" "CNY"
payTime: "2023-02-08 14:16:52 +0800 CST"
source: "支付宝"
status: "等待确认收货"
txType: "支出"
type: "支出"
Expenses:Groceries 20.00 CNY
Assets:Alipay -20.00 CNY

Expand All @@ -71,7 +71,7 @@ option "operating_currency" "CNY"
payTime: "2023-02-12 21:32:14 +0800 CST"
source: "支付宝"
status: "交易成功"
txType: "支出"
type: "支出"
Expenses:FIXME 49.74 CNY
Liabilities:CC:COMM:7449 -49.74 CNY

2 changes: 0 additions & 2 deletions example/icbc/config.yaml → example/icbc/credit/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,3 @@ icbc:
targetAccount: Expenses:Transport:Highway
- txType: 人民币自动转帐还款
targetAccount: Assets:Bank:CN:ICBC:Savings
- peer: XX旗舰店
targetAccount: Expenses:Joy
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ option "operating_currency" "CNY"
1970-01-01 open Assets:Bank:CN:ICBC:Savings
1970-01-01 open Assets:FIXME
1970-01-01 open Expenses:FIXME
1970-01-01 open Expenses:Joy
1970-01-01 open Expenses:Transport:Highway

2023-03-20 * "广东联合电子收费股份"
Expand All @@ -22,7 +21,7 @@ option "operating_currency" "CNY"
source: "中国工商银行"
txType: "银联在线支付"
type: "支出"
Expenses:Joy 0.01 CNY
Expenses:FIXME 0.01 CNY
Liabilities:Bank:CN:ICBC -0.01 CNY

2023-04-25 * "XX分行银行卡中心"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,5 @@
2023-04-22 ,2023-04-22 ,"银联在线支付 ",XX旗舰店 ,"CHN "," ","0.01 ",人民币 ," ","0.01 ",人民币 ,"-6,086.11 "," ",
2023-03-20 ,2023-03-20 ,"消费 ",财付通-餐馆 ,"CHN "," ","22.00 ",人民币 ," ","22.00 ",人民币 ,"-16,064.73 "," ",
2023-03-20 ,2023-03-20 ,"******************** ","广东联合电子收费股份 ","CHN "," ","29.45 ",人民币 ," ","29.45 ",人民币 ,"-16,042.73 "," ",

合计金额,,,,,"1234.56 ","6543.21 ", ,"1234.56 ","6543.21 ",
合计金额,,,,,"1234.56 ","6543.21 ", ,"1234.56 ","6543.21 ",
22 changes: 22 additions & 0 deletions example/icbc/debit/config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
defaultMinusAccount: Assets:FIXME
defaultPlusAccount: Expenses:FIXME
defaultCashAccount: Assets:Bank:CN:ICBC
defaultCurrency: CNY
title: 测试
icbc:
rules:
- peer: 财付通-,支付宝-
ignore: true
- peer: 支付宝
txType: 蚂蚁基金赎回到银行
ignore: true
- peer: 总行信用卡合伙人
targetAccount: Income:Bank:ICBC:CreditCard
- peer: 掌上生活还款
targetAccount: Liabilities:Bank:CMB:CreditCard
- txType: 自动还款
peer: 广东XX分行银行卡中心
ignore: true
- peer: 张三,李四,王五
txTpe: 汇款,网转,汇入
targetAccount: Assets:Borrow
69 changes: 69 additions & 0 deletions example/icbc/debit/example-icbc-debit-output.beancount
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
option "title" "测试"
option "operating_currency" "CNY"

1970-01-01 open Assets:Borrow
1970-01-01 open Assets:FIXME
1970-01-01 open Expenses:FIXME
1970-01-01 open Income:Bank:ICBC:CreditCard
1970-01-01 open Liabilities:Bank:CMB:CreditCard

2023-02-10 * "总行信用卡合伙人"
cardName: "这是卡别名"
currency: "人民币"
peerAccount: "总行信用卡合伙人"
source: "中国工商银行"
txType: "合伙人返现"
type: "收入"
Assets:Bank:CN:ICBC 30.00 CNY
Income:Bank:ICBC:CreditCard -30.00 CNY

2023-02-20 * "手机银行 张三"
cardName: "这是卡别名"
currency: "人民币"
peerAccount: "张三"
source: "中国工商银行"
txType: "跨行汇款"
type: "支出"
Assets:Borrow 1234.56 CNY
Assets:Bank:CN:ICBC -1234.56 CNY

2023-04-14 * "ABC公司"
cardName: "这是卡别名"
currency: "人民币"
peerAccount: "ABC公司"
source: "中国工商银行"
txType: "工资"
type: "收入"
Assets:Bank:CN:ICBC 1234.56 CNY
Assets:FIXME -1234.56 CNY

2023-04-14 * "手机银行 王五"
cardName: "这是卡别名"
currency: "人民币"
peerAccount: "王五"
source: "中国工商银行"
txType: "网转"
type: "支出"
Assets:Borrow 500.00 CNY
Assets:Bank:CN:ICBC -500.00 CNY

2023-04-20 * "银联无卡支付业务((特约)掌上生活还款)"
cardName: "这是卡别名"
currency: "人民币"
peerAccount: "银联无卡支付业务((特约)掌上生活还款)"
source: "中国工商银行"
txType: "银联消费"
type: "支出"
Liabilities:Bank:CMB:CreditCard 1234.56 CNY
Assets:Bank:CN:ICBC -1234.56 CNY

2023-04-22 * "李四"
cardName: "这是卡别名"
currency: "人民币"
peerAccount: "李四"
source: "中国工商银行"
txType: "他行汇入"
type: "收入"
Assets:Bank:CN:ICBC 1234.56 CNY
Assets:Borrow -1234.56 CNY

20 changes: 20 additions & 0 deletions example/icbc/debit/example-icbc-debit-records.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
明细查询文件下载

卡号: 1234****9876,"卡别名: 这是卡别名"

子账户序号: 00000,子账户类别: 活期,"子账户别名: "

交易日期,摘要,交易场所,交易国家或地区简称,钞/汇,交易金额(收入),交易金额(支出),交易币种,记账金额(收入),记账金额(支出),记账币种,余额,对方户名
2023-05-02 ,"消费 ","财付通-广东三元麦当劳食品有 ","CHN ",钞 ,"- ","- ",- ," ","13.40 ",人民币 ,"xx,yyyy.zz ","深圳市财付通支付科技有限公司 ",
2023-04-25 ,"自动还款 ","广东XX分行银行卡中心 ","CHN ",钞 ,"- ","- ",- ," ","1,234.56 ",人民币 ,"xx,yyyy.zz ","张三 ",
2023-04-22 ,"他行汇入 "," ","CHN ",钞 ,"- ","- ",- ,"1,234.56 "," ",人民币 ,"xx,yyyy.zz ","李四 ",
2023-04-20 ,"银联消费 "," ","CHN ",钞 ,"- ","- ",- ," ","1,234.56 ",人民币 ,"xx,yyyy.zz ","银联无卡支付业务((特约)掌上生活还款) ",
2023-04-14 ,"工资 "," ","CHN ",钞 ,"- ","- ",- ,"1,234.56 "," ",人民币 ,"xx,yyyy.zz ","ABC公司 ",
2023-04-14 ,"网转 ","手机银行 ","CHN ",钞 ,"- ","- ",- ," ","500.00 ",人民币 ,"xx,yyyy.zz ","王五 ",
2023-02-28 ,"蚂蚁基金赎回到银行 ","支付宝 ","CHN ",钞 ,"- ","- ",- ,"341.58 "," ",人民币 ,"xx,yyyy.zz ","支付宝(中国)网络技术有限公司 ",
2023-02-20 ,"跨行汇款 ","手机银行 ","CHN ",钞 ,"- ","- ",- ," ","1,234.56 ",人民币 ,"xx,yyyy.zz ","张三 ",
2023-02-10 ,"合伙人返现 "," ","CHN ",钞 ,"- ","- ",- ,"30.00 "," ",人民币 ,"xx,yyyy.zz ","总行信用卡合伙人 ",
2023-02-09 ,"消费 ","财付通-群收款 ","CHN ",钞 ,"- ","- ",- ," ","24.00 ",人民币 ,"xx,yyyy.zz ","深圳市财付通支付科技有限公司 ",
2023-02-08 ,"退款 ","财付通-微信红包 ","CHN ",钞 ,"- ","- ",- ,"1.04 "," ",人民币 ,"xx,yyyy.zz ","深圳市财付通支付科技有限公司 ",

人民币合计,,,,,,,,"1,234.56 ","9,876.54 ",
16 changes: 8 additions & 8 deletions pkg/analyser/icbc/icbc.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,13 @@ func (i Icbc) GetAccountsAndTags(o *ir.Order, cfg *config.Config, target, provid
resPlus := cfg.DefaultPlusAccount
cashAccount := cfg.DefaultCashAccount

// method account (bank card account)
if o.Type == ir.TypeRecv {
resPlus = cashAccount
} else {
resMinus = cashAccount
}

//var err error
for _, r := range cfg.Icbc.Rules {
match := true
Expand All @@ -67,7 +74,7 @@ func (i Icbc) GetAccountsAndTags(o *ir.Order, cfg *config.Config, target, provid
match = matchFunc(*r.Peer, o.Peer, sep, match)
}
if r.Type != nil {
match = matchFunc(*r.Type, string(o.Type), sep, match)
match = matchFunc(*r.Type, o.TypeOriginal, sep, match)
}
if r.TxType != nil {
match = matchFunc(*r.TxType, o.TxTypeOriginal, sep, match)
Expand All @@ -87,13 +94,6 @@ func (i Icbc) GetAccountsAndTags(o *ir.Order, cfg *config.Config, target, provid
}
}

// method account (bank card account)
if o.Type == ir.TypeRecv {
resPlus = cashAccount
} else {
resMinus = cashAccount
}

if r.Tag != nil {
tags = strings.Split(*r.Tag, sep)
}
Expand Down
6 changes: 3 additions & 3 deletions pkg/provider/alipay/convert.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ func convertType(t Type) ir.Type {
func getMetadata(o Order) map[string]string {
// FIXME(TripleZ): hard-coded, bad pattern
source := "支付宝"
var status, method, category, txType, orderId, merchantId, paytime string
var status, method, category, typeOriginal, orderId, merchantId, paytime string

paytime = o.PayTime.Format(localTimeFmt)

Expand All @@ -62,7 +62,7 @@ func getMetadata(o Order) map[string]string {
}

if o.TypeOriginal != "" {
txType = o.TypeOriginal
typeOriginal = o.TypeOriginal
}

if o.Method != "" {
Expand All @@ -78,7 +78,7 @@ func getMetadata(o Order) map[string]string {
"payTime": paytime,
"orderId": orderId,
"merchantId": merchantId,
"txType": txType,
"type": typeOriginal,
"category": category,
"method": method,
"status": status,
Expand Down
10 changes: 5 additions & 5 deletions pkg/provider/alipay/parse.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,21 +30,21 @@ func (a *Alipay) translateToOrders(array []string) error {
bill.PeerAccount = array[3]
bill.ItemName = array[4]
bill.Method = array[7]
bill.Category = array[1]
bill.DealNo = array[9]
bill.MerchantId = array[10]
bill.Money, err = strconv.ParseFloat(array[6], 32)
if err != nil {
log.Println("parse money error:", array[6], err)
return err
}
bill.Status = array[8]
if bill.Status == "交易关闭" {
log.Printf("Line %d: There is a mole, The tx is canceled.", a.LineNum)
log.Printf("[orderId %s ] There is a mole, The tx is canceled.", bill.DealNo)
}
if bill.Status == "退款成功" {
log.Printf("Lind %d: There has a refund transaction.", a.LineNum)
log.Printf("[orderId %s ] There has a refund transaction.", bill.DealNo)
}
bill.Category = array[1]
bill.DealNo = array[9]
bill.MerchantId = array[10]
bill.PayTime, err = time.Parse(localTimeFmt, array[0]+" +0800 CST")
if err != nil {
log.Println("parse create time error:", array[0], err)
Expand Down
13 changes: 10 additions & 3 deletions pkg/provider/icbc/convert.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,10 @@ func (icbc *Icbc) convertToIR() *ir.IR {
Money: o.Money,
PayTime: o.PayTime,
Type: convertType(o.Type),
TypeOriginal: string(o.Type),
TxTypeOriginal: o.TxTypeOriginal,
}
irO.Metadata = getMetadata(o)
irO.Metadata = icbc.getMetadata(o)
i.Orders = append(i.Orders, irO)
}
return i
Expand All @@ -36,7 +37,7 @@ func convertType(t OrderType) ir.Type {

// getMetadata get the metadata (e.g. status, method, category and so on.)
// from order.
func getMetadata(o Order) map[string]string {
func (icbc *Icbc) getMetadata(o Order) map[string]string {
// FIXME(TripleZ): hard-coded, bad pattern
source := "中国工商银行"
var txTypeOriginal, guessedType, currency, balances, peerAccount string
Expand All @@ -61,12 +62,18 @@ func getMetadata(o Order) map[string]string {
peerAccount = o.PeerAccountName
}

return map[string]string{
metadata := map[string]string{
"source": source,
"txType": txTypeOriginal,
"type": guessedType,
"currency": currency,
"balances": balances,
"peerAccount": peerAccount,
}

if icbc.CardName != "" {
metadata["cardName"] = icbc.CardName
}

return metadata
}
Loading

0 comments on commit cd53df8

Please sign in to comment.