diff --git a/Makefile b/Makefile index 1a8226c..bbdc672 100644 --- a/Makefile +++ b/Makefile @@ -90,7 +90,7 @@ clean: ## Clean all the temporary files @rm -rf ./double-entry-generator @rm -rf ./wasm-dist -test: test-go test-alipay-beancount test-alipay-ledger test-wechat-beancount test-wechat-ledger test-huobi-beancount test-huobi-ledger test-htsec-beancount test-htsec-ledger test-icbc-beancount test-icbc-ledger test-td-beancount test-td-ledger ## Run all tests +test: test-go test-alipay-beancount test-alipay-ledger test-wechat-beancount test-wechat-ledger test-huobi-beancount test-huobi-ledger test-htsec-beancount test-htsec-ledger test-icbc-beancount test-icbc-ledger test-td-beancount test-td-ledger test-bmo-beancount test-bmo-ledger ## Run all tests test-go: ## Run Golang tests @go test ./... @@ -127,6 +127,11 @@ test-td-beancount: ## Run tests for TD provider against beancount compiler test-td-ledger: ## Run tests for TD provider against ledger compiler @$(SHELL) ./test/td-test-ledger.sh +test-bmo-beancount: ## Run tests for BMO provider against beancount compiler + @$(SHELL) ./test/bmo-test-beancount.sh +test-bmo-ledger: ## Run tests for BMO provider against ledger compiler + @$(SHELL) ./test/bmo-test-ledger.sh + format: ## Format code @gofmt -s -w pkg @goimports -w pkg diff --git a/example/bmo/credit/config.yaml b/example/bmo/credit/config.yaml new file mode 100644 index 0000000..e03167e --- /dev/null +++ b/example/bmo/credit/config.yaml @@ -0,0 +1,24 @@ +defaultMinusAccount: Assets:FIXME +defaultPlusAccount: Expenses:FIXME +defaultCashAccount: Liabilities:CreditCard:BMO +defaultCurrency: CAD +title: 测试 +bmo: + rules: + - item: "T T" + targetAccount: Expenses:Grocery + tag: tt_tag + - item: "COSTCO" + targetAccount: Expenses:Grocery + - item: "T&T SUPERMARKET" + targetAccount: Expenses:Grocery + - item: "JOLLIBEE" + targetAccount: Expenses:Food:Lunch + - item: "DOLLARAMA" + targetAccount: Expenses:Grocery + tag: grocery_tag1,cheap_tag2 + - item: "DEVELOPM MSP" + targetAccount: Income:Salary + - type: 收入 + item: "SEND E-TFR" + targetAccount: Income:FIXME diff --git a/example/bmo/credit/example-bmo-output.beancount b/example/bmo/credit/example-bmo-output.beancount new file mode 100644 index 0000000..001dfcc --- /dev/null +++ b/example/bmo/credit/example-bmo-output.beancount @@ -0,0 +1,22 @@ +option "title" "测试" +option "operating_currency" "CAD" + +1970-01-01 open Assets:FIXME +1970-01-01 open Expenses:FIXME +1970-01-01 open Expenses:Food:Lunch +1970-01-01 open Expenses:Grocery +1970-01-01 open Income:FIXME +1970-01-01 open Income:Salary + +2023-05-11 * "BMO" "COSTCO WHOLESALE W54 SURREY BC" + Expenses:Grocery 3.27 CAD + Liabilities:CreditCard:BMO -3.27 CAD + +2023-05-11 * "BMO" "COSTCO WHOLESALE W54 SURREY BC" + Expenses:Grocery 85.06 CAD + Liabilities:CreditCard:BMO -85.06 CAD + +2023-05-12 * "BMO" "T&T SUPERMARKET #026 SURREY BC" + Expenses:Grocery 6.00 CAD + Liabilities:CreditCard:BMO -6.00 CAD + diff --git a/example/bmo/credit/example-bmo-output.ledger b/example/bmo/credit/example-bmo-output.ledger new file mode 100644 index 0000000..7a90adb --- /dev/null +++ b/example/bmo/credit/example-bmo-output.ledger @@ -0,0 +1,20 @@ +1970/01/01 * Open Balance + Assets:FIXME 0 CAD + Expenses:FIXME 0 CAD + Expenses:Food:Lunch 0 CAD + Expenses:Grocery 0 CAD + Income:FIXME 0 CAD + Income:Salary 0 CAD + Equity:Opening Balances +2023/05/11 * BMO - COSTCO WHOLESALE W54 SURREY BC + Expenses:Grocery 3.27 CAD + Liabilities:CreditCard:BMO - 3.27 CAD + +2023/05/11 * BMO - COSTCO WHOLESALE W54 SURREY BC + Expenses:Grocery 85.06 CAD + Liabilities:CreditCard:BMO - 85.06 CAD + +2023/05/12 * BMO - T&T SUPERMARKET #026 SURREY BC + Expenses:Grocery 6.00 CAD + Liabilities:CreditCard:BMO - 6.00 CAD + diff --git a/example/bmo/credit/example-bmo-records.csv b/example/bmo/credit/example-bmo-records.csv new file mode 100644 index 0000000..56d0e51 --- /dev/null +++ b/example/bmo/credit/example-bmo-records.csv @@ -0,0 +1,6 @@ +Following data is valid as of 202304230942432: + +Item #,Card #,Transaction Date,Posting Date,Transaction Amount,Description +1,'5191230567984561',20230511,20230512,3.27,COSTCO WHOLESALE W54 SURREY BC +2,'5191230567984561',20230511,20230512,85.06,COSTCO WHOLESALE W54 SURREY BC +3,'5191230567984561',20230512,20230515,6.0,T&T SUPERMARKET #026 SURREY BC \ No newline at end of file diff --git a/example/bmo/debit/config.yaml b/example/bmo/debit/config.yaml new file mode 100644 index 0000000..86bdf2d --- /dev/null +++ b/example/bmo/debit/config.yaml @@ -0,0 +1,18 @@ +defaultMinusAccount: Assets:FIXME +defaultPlusAccount: Expenses:FIXME +defaultCashAccount: Assets:DebitCard:BMOChequing +defaultCurrency: CAD +title: 测试 +bmo: + rules: + - item: "T T" + targetAccount: Expenses:Grocery + tag: tt_tag + - item: "DOLLARAMA" + targetAccount: Expenses:Grocery + tag: grocery_tag1,cheap_tag2 + - item: "DEVELOPM MSP" + targetAccount: Income:Salary + - type: 收入 + item: "SEND E-TFR" + targetAccount: Income:FIXME diff --git a/example/bmo/debit/example-bmo-output.beancount b/example/bmo/debit/example-bmo-output.beancount new file mode 100644 index 0000000..45b92dd --- /dev/null +++ b/example/bmo/debit/example-bmo-output.beancount @@ -0,0 +1,25 @@ +option "title" "测试" +option "operating_currency" "CAD" + +1970-01-01 open Assets:FIXME +1970-01-01 open Expenses:FIXME +1970-01-01 open Expenses:Grocery +1970-01-01 open Income:FIXME +1970-01-01 open Income:Salary + +2023-05-01 * "BMO" "[DN]ACFS MSP/DIV" + Assets:DebitCard:BMOChequing 0.01 CAD + Assets:FIXME -0.01 CAD + +2023-05-18 * "BMO" "[CW]TELUS MOB" + Expenses:FIXME 68.32 CAD + Assets:DebitCard:BMOChequing -68.32 CAD + +2023-05-23 * "BMO" "[CW]BC HYDRO" + Expenses:FIXME 50.00 CAD + Assets:DebitCard:BMOChequing -50.00 CAD + +2023-06-12 * "BMO" "[IB] 6088 #3 ROAD" + Expenses:FIXME 40.00 CAD + Assets:DebitCard:BMOChequing -40.00 CAD + diff --git a/example/bmo/debit/example-bmo-output.ledger b/example/bmo/debit/example-bmo-output.ledger new file mode 100644 index 0000000..be955bb --- /dev/null +++ b/example/bmo/debit/example-bmo-output.ledger @@ -0,0 +1,23 @@ +1970/01/01 * Open Balance + Assets:FIXME 0 CAD + Expenses:FIXME 0 CAD + Expenses:Grocery 0 CAD + Income:FIXME 0 CAD + Income:Salary 0 CAD + Equity:Opening Balances +2023/05/01 * BMO - [DN]ACFS MSP/DIV + Assets:DebitCard:BMOChequing 0.01 CAD + Assets:FIXME - 0.01 CAD + +2023/05/18 * BMO - [CW]TELUS MOB + Expenses:FIXME 68.32 CAD + Assets:DebitCard:BMOChequing - 68.32 CAD + +2023/05/23 * BMO - [CW]BC HYDRO + Expenses:FIXME 50.00 CAD + Assets:DebitCard:BMOChequing - 50.00 CAD + +2023/06/12 * BMO - [IB] 6088 #3 ROAD + Expenses:FIXME 40.00 CAD + Assets:DebitCard:BMOChequing - 40.00 CAD + diff --git a/example/bmo/debit/example-bmo-records.csv b/example/bmo/debit/example-bmo-records.csv new file mode 100644 index 0000000..cbe66eb --- /dev/null +++ b/example/bmo/debit/example-bmo-records.csv @@ -0,0 +1,10 @@ +Following data is valid as of 20239810344234 (Year/Month/Day/Hour/Minute/Second) + + +First Bank Card,Transaction Type,Date Posted, Transaction Amount,Description + + +'5678653123124124',CREDIT,20230501,0.01,[DN]ACFS MSP/DIV +'5678653123124124',DEBIT,20230518,-68.32,[CW]TELUS MOB +'5678653123124124',DEBIT,20230523,-50.0,[CW]BC HYDRO +'5678653123124124',DEBIT,20230612,-40.0,[IB] 6088 #3 ROAD diff --git a/pkg/analyser/bmo/bmo.go b/pkg/analyser/bmo/bmo.go new file mode 100644 index 0000000..0cdf4c3 --- /dev/null +++ b/pkg/analyser/bmo/bmo.go @@ -0,0 +1,98 @@ +package bmo + +import ( + "strings" + + "github.com/deb-sig/double-entry-generator/pkg/config" + "github.com/deb-sig/double-entry-generator/pkg/ir" + "github.com/deb-sig/double-entry-generator/pkg/util" +) + +type Bmo struct { +} + +// GetAllCandidateAccounts returns all accounts defined in config. +func (bmo Bmo) GetAllCandidateAccounts(cfg *config.Config) map[string]bool { + // uniqMap will be used to create the concepts. + uniqMap := make(map[string]bool) + + if cfg.Bmo == nil || len(cfg.Bmo.Rules) == 0 { + return uniqMap + } + + for _, rule := range cfg.Bmo.Rules { + if rule.MethodAccount != nil { + uniqMap[*rule.MethodAccount] = true + } + if rule.TargetAccount != nil { + uniqMap[*rule.TargetAccount] = true + } + } + uniqMap[cfg.DefaultPlusAccount] = true + uniqMap[cfg.DefaultMinusAccount] = true + return uniqMap +} + +// GetAccountsAndTags GetAccounts returns minus and plus account. +func (bmo Bmo) GetAccountsAndTags(order *ir.Order, cfg *config.Config, target, provider string) (bool, string, string, map[ir.Account]string, []string) { + ignore := false + + if cfg.Bmo == nil || len(cfg.Bmo.Rules) == 0 { + return ignore, cfg.DefaultMinusAccount, cfg.DefaultPlusAccount, nil, nil + } + + var tags = make([]string, 0) + resMinus := cfg.DefaultMinusAccount + resPlus := cfg.DefaultPlusAccount + cashAccount := cfg.DefaultCashAccount + + // method account (bank card account) + if order.Type == ir.TypeRecv { + resPlus = cashAccount + } else { + resMinus = cashAccount + } + + for _, rule := range cfg.Bmo.Rules { + match := true + // get separator + sep := "," + if rule.Separator != nil { + sep = *rule.Separator + } + + matchFunc := util.SplitFindContains + if rule.FullMatch { + matchFunc = util.SplitFindEquals + } + + if rule.Item != nil { + match = matchFunc(*rule.Item, order.Item, sep, match) + } + if rule.Type != nil { + match = matchFunc(*rule.Type, order.TypeOriginal, sep, match) + } + + if match { + if rule.Ignore { + ignore = true + break + } + // Support multiple matches, like one rule matches the minus account, the other rule matches the plus account. + if rule.TargetAccount != nil { + if order.Type == ir.TypeRecv { + resMinus = *rule.TargetAccount + } else { + resPlus = *rule.TargetAccount + } + } + + if rule.Tag != nil { + tags = strings.Split(*rule.Tag, sep) + } + + } + + } + return ignore, resMinus, resPlus, nil, tags +} diff --git a/pkg/analyser/interface.go b/pkg/analyser/interface.go index 403c9c5..8ea90ba 100644 --- a/pkg/analyser/interface.go +++ b/pkg/analyser/interface.go @@ -3,6 +3,7 @@ package analyser import ( "fmt" + "github.com/deb-sig/double-entry-generator/pkg/analyser/bmo" "github.com/deb-sig/double-entry-generator/pkg/analyser/icbc" "github.com/deb-sig/double-entry-generator/pkg/analyser/td" @@ -36,6 +37,8 @@ func New(providerName string) (Interface, error) { return icbc.Icbc{}, nil case consts.ProviderTd: return td.Td{}, nil + case consts.ProviderBmo: + return bmo.Bmo{}, nil default: return nil, fmt.Errorf("Fail to create the analyser for the given name %s", providerName) } diff --git a/pkg/config/config.go b/pkg/config/config.go index 692f458..91f5f45 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -2,6 +2,7 @@ package config import ( "github.com/deb-sig/double-entry-generator/pkg/provider/alipay" + "github.com/deb-sig/double-entry-generator/pkg/provider/bmo" "github.com/deb-sig/double-entry-generator/pkg/provider/htsec" "github.com/deb-sig/double-entry-generator/pkg/provider/huobi" "github.com/deb-sig/double-entry-generator/pkg/provider/icbc" @@ -25,4 +26,5 @@ type Config struct { Htsec *htsec.Config `yaml:"htsec,omitempty"` Icbc *icbc.Config `yaml:"icbc,omitempty"` Td *td.Config `yaml:"td,omitempty"` + Bmo *bmo.Config `yaml:"bmo,omitempty"` } diff --git a/pkg/consts/consts.go b/pkg/consts/consts.go index 6f02902..cf65756 100644 --- a/pkg/consts/consts.go +++ b/pkg/consts/consts.go @@ -34,4 +34,6 @@ const ( ProviderIcbc = "icbc" //ProviderTd is the name for TD provider. ProviderTd = "td" + //ProviderBmo is the name for BMO provider. + ProviderBmo = "bmo" ) diff --git a/pkg/provider/bmo/bmo.go b/pkg/provider/bmo/bmo.go new file mode 100644 index 0000000..787c794 --- /dev/null +++ b/pkg/provider/bmo/bmo.go @@ -0,0 +1,84 @@ +package bmo + +import ( + "encoding/csv" + "fmt" + "io" + "log" + + "github.com/deb-sig/double-entry-generator/pkg/io/reader" + "github.com/deb-sig/double-entry-generator/pkg/ir" +) + +type Bmo struct { + Statistics Statistics `json:"statistics,omitempty"` + LineNum int `json:"line_num,omitempty"` + Orders []Order `json:"orders,omitempty"` + CardName string `json:"card_name,omitempty"` + Mode CardMode `json:"mode,omitempty"` +} + +func New() *Bmo { + return &Bmo{ + Statistics: Statistics{}, + LineNum: 0, + Orders: make([]Order, 0), + CardName: "", + Mode: DebitMode, + } +} + +// Translate the bmo bill records to IR. +func (bmo *Bmo) Translate(filename string) (*ir.IR, error) { + log.SetPrefix("[Provider-BMO] ") + + billReader, err := reader.GetReader(filename) + if err != nil { + return nil, fmt.Errorf("can't get bill reader, err: %v", err) + } + + csvReader := csv.NewReader(billReader) + csvReader.LazyQuotes = true + // If FieldsPerRecord is negative, no check is made and records + // may have a variable number of fields. + csvReader.FieldsPerRecord = -1 + + for { + line, err := csvReader.Read() + + if err == io.EOF { + break + } else if err != nil { + return nil, err + } + + bmo.LineNum++ + + if bmo.LineNum == 2 { + // 借记卡(default) or 信用卡 + if line[0] == "Item #" { + bmo.Mode = CreditMode + continue + } + } + + if bmo.Mode == DebitMode && bmo.LineNum <= 2 { + // bypass the useless + continue + } + + if bmo.Mode == DebitMode { + err = bmo.translateDebitToOrders(line) + if err != nil { + return nil, fmt.Errorf("Failed to translate debit bill: line %d: %v", bmo.LineNum, err) + } + } else { + err = bmo.translateCreditToOrders(line) + if err != nil { + return nil, fmt.Errorf("Failed to translate credit bill: line %d: %v", bmo.LineNum, err) + } + } + } + log.Printf("Finished to parse the file %s", filename) + return bmo.convertToIR(), nil +} diff --git a/pkg/provider/bmo/config.go b/pkg/provider/bmo/config.go new file mode 100644 index 0000000..7180179 --- /dev/null +++ b/pkg/provider/bmo/config.go @@ -0,0 +1,19 @@ +package bmo + +// Config is the configuration for Bmo. +type Config struct { + Rules []Rule `mapstructure:"rules,omitempty"` +} + +// Rule is the type for match rules. +type Rule struct { + Peer *string `mapstructure:"peer,omitempty"` // 交易对手 + Item *string `mapstructure:"item,omitempty"` // 商品描述 + Type *string `mapstructure:"type,omitempty"` // 类型 + Separator *string `mapstructure:"sep,omitempty"` // default: , + MethodAccount *string `mapstructure:"methodAccount,omitempty"` + TargetAccount *string `mapstructure:"targetAccount,omitempty"` + FullMatch bool `mapstructure:"fullMatch,omitempty"` // default: false + Tag *string `mapstructure:"tag,omitempty"` + Ignore bool `mapstructure:"ignore,omitempty"` // default: false +} diff --git a/pkg/provider/bmo/converter.go b/pkg/provider/bmo/converter.go new file mode 100644 index 0000000..bf791b5 --- /dev/null +++ b/pkg/provider/bmo/converter.go @@ -0,0 +1,31 @@ +package bmo + +import "github.com/deb-sig/double-entry-generator/pkg/ir" + +func (bmo *Bmo) convertToIR() *ir.IR { + itermediateRepresentation := ir.New() + for _, order := range bmo.Orders { + + irO := ir.Order{ + Peer: "BMO", + Item: order.TransactionDescription, + PayTime: order.PayTime, + Type: convertType(order.Type), + TypeOriginal: string(order.Type), + Money: order.Money, + } + itermediateRepresentation.Orders = append(itermediateRepresentation.Orders, irO) + } + return itermediateRepresentation +} + +func convertType(t OrderType) ir.Type { + switch t { + case OrderTypeSend: + return ir.TypeSend + case OrderTypeRecv: + return ir.TypeRecv + default: + return ir.TypeUnknown + } +} diff --git a/pkg/provider/bmo/parser.go b/pkg/provider/bmo/parser.go new file mode 100644 index 0000000..a8e8c41 --- /dev/null +++ b/pkg/provider/bmo/parser.go @@ -0,0 +1,63 @@ +package bmo + +import ( + "fmt" + "strconv" + "strings" + "time" +) + +// translate Debit Card csv file to []Order. +func (bmo *Bmo) translateDebitToOrders(columns []string) error { + for idx, record := range columns { + record = strings.Trim(record, " ") + record = strings.Trim(record, "\t") + columns[idx] = record + } + + var bill Order + var err error + bill.PayTime, err = time.Parse(LocalTimeFmt, columns[2]+" -0700") + if err != nil { + return fmt.Errorf("parse Pay time %s error: %v", columns[2], err) + } + + bill.TransactionDescription = columns[4] + var transactionType = columns[1] + bill.Type = getOrderType(transactionType) + var amount = columns[3] + bill.Money, err = strconv.ParseFloat(strings.TrimLeft(amount, "-"), 64) + if err != nil { + return fmt.Errorf("parse money %s error: %v", columns[3], err) + } + + bmo.Orders = append(bmo.Orders, bill) + return nil +} + +// translate Credit Card csv file to []Order. +func (bmo *Bmo) translateCreditToOrders(columns []string) error { + for idx, record := range columns { + record = strings.Trim(record, " ") + record = strings.Trim(record, "\t") + columns[idx] = record + } + + var bill Order + var err error + bill.PayTime, err = time.Parse(LocalTimeFmt, columns[2]+" -0700") + if err != nil { + return fmt.Errorf("parse Pay time %s error: %v", columns[2], err) + } + + bill.TransactionDescription = columns[5] + var amount = columns[4] + bill.Type = getOrderTypeByTransactionAmount(amount) + bill.Money, err = strconv.ParseFloat(strings.TrimLeft(amount, "-"), 64) + if err != nil { + return fmt.Errorf("parse money %s error: %v", amount, err) + } + + bmo.Orders = append(bmo.Orders, bill) + return nil +} diff --git a/pkg/provider/bmo/types.go b/pkg/provider/bmo/types.go new file mode 100644 index 0000000..658cff9 --- /dev/null +++ b/pkg/provider/bmo/types.go @@ -0,0 +1,50 @@ +package bmo + +import "time" + +type Statistics struct { + UserID string `json:"user_id,omitempty"` + Username string `json:"username,omitempty"` + ParsedItems int `json:"parsed_items,omitempty"` + Start time.Time `json:"start,omitempty"` + End time.Time `json:"end,omitempty"` + TotalInRecords int `json:"total_in_records,omitempty"` + TotalInMoney float64 `json:"total_in_money,omitempty"` + TotalOutRecords int `json:"total_out_records,omitempty"` + TotalOutMoney float64 `json:"total_out_money,omitempty"` +} + +// BMO的账单相当简单,只有4个字段 +// Transaction Type,Date Posted, Transaction Amount,Description +type Order struct { + PayTime time.Time // 记账日期 + TransactionDescription string // 交易描述(包括交易对手及摘要) + Money float64 // 记账金额(收入/支出) + Type OrderType // 收/支 (数据中无该列,推测而来) +} + +type OrderType string + +const ( + OrderTypeSend OrderType = "支出" + OrderTypeRecv OrderType = "收入" + OrderTypeUnknown OrderType = "Unknown" +) + +type TransactionType string + +const ( + TransactionTypeCredit TransactionType = "CREDIT" + TransactionTypeDebit TransactionType = "DEBIT" + TransactionTypeUnknown TransactionType = "UNKNOWN" +) + +type CardMode string + +const ( + DebitMode CardMode = "Debit" + CreditMode CardMode = "Credit" +) + +// LocalTimeFmt set time format to utc-7 +const LocalTimeFmt = "20060102 -0700" diff --git a/pkg/provider/bmo/util.go b/pkg/provider/bmo/util.go new file mode 100644 index 0000000..076f7a1 --- /dev/null +++ b/pkg/provider/bmo/util.go @@ -0,0 +1,21 @@ +package bmo + +import "strings" + +func getOrderType(transactionType string) OrderType { + if transactionType == string(TransactionTypeDebit) { + return OrderTypeSend + } else if transactionType == string(TransactionTypeCredit) { + return OrderTypeRecv + } else { + return OrderTypeUnknown + } +} + +func getOrderTypeByTransactionAmount(amount string) OrderType { + if strings.HasPrefix(amount, "-") { + return OrderTypeRecv + } else { + return OrderTypeSend + } +} diff --git a/pkg/provider/interface.go b/pkg/provider/interface.go index 2dd8c18..b85254a 100644 --- a/pkg/provider/interface.go +++ b/pkg/provider/interface.go @@ -19,6 +19,7 @@ package provider import ( "fmt" + "github.com/deb-sig/double-entry-generator/pkg/provider/bmo" "github.com/deb-sig/double-entry-generator/pkg/provider/icbc" "github.com/deb-sig/double-entry-generator/pkg/provider/td" @@ -50,6 +51,8 @@ func New(name string) (Interface, error) { return icbc.New(), nil case consts.ProviderTd: return td.New(), nil + case consts.ProviderBmo: + return bmo.New(), nil default: return nil, fmt.Errorf("Fail to create the provider for the given name %s", name) } diff --git a/test/bmo-test-beancount.sh b/test/bmo-test-beancount.sh new file mode 100644 index 0000000..bc27238 --- /dev/null +++ b/test/bmo-test-beancount.sh @@ -0,0 +1,46 @@ +#!/usr/bin/env bash +# +# E2E test for bmo provider. + +# set -x # debug +set -eo errexit + +TEST_DIR=`dirname "$(realpath $0)"` +ROOT_DIR="$TEST_DIR/.." +CREDIT_OUTPUT="$ROOT_DIR/test/output/test-bmo-credit-output.beancount" +DEBIT_OUTPUT="$ROOT_DIR/test/output/test-bmo-debit-output.beancount" + +make -f "$ROOT_DIR/Makefile" build +mkdir -p "$ROOT_DIR/test/output" + +# generate bmo bills output in beancount format +"$ROOT_DIR/bin/double-entry-generator" translate \ + --provider bmo \ + --config "$ROOT_DIR/example/bmo/credit/config.yaml" \ + --output "$CREDIT_OUTPUT" \ + "$ROOT_DIR/example/bmo/credit/example-bmo-records.csv" + +diff -u --color \ + "$ROOT_DIR/example/bmo/credit/example-bmo-output.beancount" \ + "$CREDIT_OUTPUT" + +if [ $? -ne 0 ]; then + echo "[FAIL] bmo provider(credit mode) output is different from expected output." + exit 1 +fi + +"$ROOT_DIR/bin/double-entry-generator" translate \ + --provider bmo \ + --config "$ROOT_DIR/example/bmo/debit/config.yaml" \ + --output "$CREDIT_OUTPUT" \ + "$ROOT_DIR/example/bmo/debit/example-bmo-records.csv" + +diff -u --color \ + "$ROOT_DIR/example/bmo/debit/example-bmo-output.beancount" \ + "$CREDIT_OUTPUT" + +if [ $? -ne 0 ]; then + echo "[FAIL] bmo provider(credit mode) output is different from expected output." + exit 1 +fi +echo "[PASS] All bmo provider tests!" diff --git a/test/bmo-test-ledger.sh b/test/bmo-test-ledger.sh new file mode 100644 index 0000000..605dc27 --- /dev/null +++ b/test/bmo-test-ledger.sh @@ -0,0 +1,48 @@ +#!/usr/bin/env bash +# +# E2E test for bmo provider. + +# set -x # debug +set -eo errexit + +TEST_DIR=`dirname "$(realpath $0)"` +ROOT_DIR="$TEST_DIR/.." +CREDIT_OUTPUT="$ROOT_DIR/test/output/test-bmo-credit-output.ledger" +DEBIT_OUTPUT="$ROOT_DIR/test/output/test-bmo-debit-output.ledger" + +make -f "$ROOT_DIR/Makefile" build +mkdir -p "$ROOT_DIR/test/output" + +# generate bmo bills output in ledger format +"$ROOT_DIR/bin/double-entry-generator" translate \ + --provider bmo \ + --target ledger \ + --config "$ROOT_DIR/example/bmo/credit/config.yaml" \ + --output "$CREDIT_OUTPUT" \ + "$ROOT_DIR/example/bmo/credit/example-bmo-records.csv" + +diff -u --color \ + "$ROOT_DIR/example/bmo/credit/example-bmo-output.ledger" \ + "$CREDIT_OUTPUT" + +if [ $? -ne 0 ]; then + echo "[FAIL] bmo provider(credit mode) output is different from expected output." + exit 1 +fi + +"$ROOT_DIR/bin/double-entry-generator" translate \ + --provider bmo \ + --target ledger \ + --config "$ROOT_DIR/example/bmo/debit/config.yaml" \ + --output "$CREDIT_OUTPUT" \ + "$ROOT_DIR/example/bmo/debit/example-bmo-records.csv" + +diff -u --color \ + "$ROOT_DIR/example/bmo/debit/example-bmo-output.ledger" \ + "$CREDIT_OUTPUT" + +if [ $? -ne 0 ]; then + echo "[FAIL] bmo provider(credit mode) output is different from expected output." + exit 1 +fi +echo "[PASS] All bmo provider tests!"