diff --git a/config.go b/config.go index 225aaef..3d8ffae 100644 --- a/config.go +++ b/config.go @@ -18,10 +18,11 @@ type ConfigAccount struct { } type ConfigTransactionTypes struct { - Names []string `json:"names,omitempty"` - MccCodes []int `json:"mcc_codes,omitempty"` - Firefly3 ConfigTransactionTypeFirefly3 `json:"firefly3,omitempty"` - SumMax int `json:"sum_max,omitempty"` + Names []string `json:"names,omitempty"` + NamesRefund []string `json:"names_refund,omitempty"` + MccCodes []int `json:"mcc_codes,omitempty"` + Firefly3 ConfigTransactionTypeFirefly3 `json:"firefly3,omitempty"` + SumMax int `json:"sum_max,omitempty"` } type ConfigTransactionTypeFirefly3 struct { diff --git a/config.json.example b/config.json.example index 97d336f..dfe0292 100644 --- a/config.json.example +++ b/config.json.example @@ -67,6 +67,13 @@ "description": "NovaPoshta: delivery" }, "sum_max": 150 + }, + { + "names": ["Bolt"], + "names_refund": ["Скасування. Bolt"], + "firefly3": { + "description": "Taxi: Bolt" + } } ] } \ No newline at end of file diff --git a/http.go b/http.go index 6892f5f..fb78d34 100644 --- a/http.go +++ b/http.go @@ -18,7 +18,7 @@ func readResponseBody(r *http.Request) (monobank.Transaction, error) { //w.WriteHeader(http.StatusOK) //return - //body = []byte("{\"type\":\"StatementItem\",\"data\":{\"account\":\"4723djMLsLOCzhoeYjxqRw\",\"statementItem\":{\"id\":\"5_NQ0arGAmp2pyNzvA\",\"time\":1711544958,\"description\":\"З чорної картки\",\"mcc\":4829,\"originalMcc\":4829,\"amount\":-572000,\"operationAmount\":-572000,\"currencyCode\":980,\"commissionRate\":22000,\"cashbackAmount\":0,\"balance\":8101246,\"hold\":true,\"receiptId\":\"EMXC-P266-90PC-EB8C\"}}}") + body = []byte("{\"type\":\"StatementItem\",\"data\":{\"account\":\"4723djMLsLOCzhoeYjxqRw\",\"statementItem\":{\"id\":\"cSMr2xlFsfWPFeDLTg\",\"time\":1711806353,\"description\":\"Скасування. Bolt\",\"mcc\":4121,\"originalMcc\":4121,\"amount\":10300,\"operationAmount\":10300,\"currencyCode\":980,\"commissionRate\":0,\"cashbackAmount\":0,\"balance\":7974533,\"hold\":false}}}") LogString(string(body)) // check empty body diff --git a/webhook.go b/webhook.go index 7655676..70194dc 100644 --- a/webhook.go +++ b/webhook.go @@ -1,8 +1,10 @@ package main import ( + "bytes" "context" "encoding/json" + "github.com/antihax/optional" "main/firefly3" monobank "main/monobank/api/webhook/models" "math" @@ -62,54 +64,99 @@ func handleWebhook(w http.ResponseWriter, r *http.Request) { clientConf.AddDefaultHeader("Authorization", "Bearer "+os.Getenv("FIREFLY3_TOKEN")) firefly3Client := firefly3.NewAPIClient(clientConf) - // create firefly3 transaction + // create firefly3 transactions list var firefly3Transactions []firefly3.TransactionSplitStore - firefly3Transaction := firefly3.TransactionSplitStore{ - Date: time.Unix(int64(monobankTransaction.Data.StatementItem.Time), 0).Add(time.Hour * 2), - Notes: string(monobankTransactionJson), - Amount: strconv.Itoa(int(math.Abs(math.Round(float64(monobankTransaction.Data.StatementItem.Amount/100)))) - int(math.Abs(math.Round(float64(monobankTransaction.Data.StatementItem.CommissionRate/100))))), - SourceId: account.Firefly3Id, - } - // match transaction with config for _, row := range config.TransactionTypes { - // check name & mcc - if !(slices.Contains(row.Names, monobankTransaction.Data.StatementItem.Description) || slices.Contains(row.MccCodes, monobankTransaction.Data.StatementItem.Mcc)) { - continue - } - // check max sum - sum, _ := strconv.Atoi(firefly3Transaction.Amount) - if row.SumMax > 0 && sum > row.SumMax { - continue - } + // is refund + if slices.Contains(row.NamesRefund, monobankTransaction.Data.StatementItem.Description) { + opts := firefly3.TransactionsApiListTransactionOpts{ + Limit: optional.NewInt32(999), + Type_: optional.NewInterface("withdrawal"), + Start: optional.NewString(time.Now().AddDate(0, 0, -7).Format("2006-01-02")), // one week before + } + oldTransactions, _, err := firefly3Client.TransactionsApi.ListTransaction(context.Background(), &opts) + if err != nil { + LogString(err.Error()) + w.WriteHeader(http.StatusOK) + return + } - // make transaction - switch row.Firefly3.Type { - case "withdrawal": - firefly3Transaction.Type_ = &firefly3TransactionTypeWithdrawal + // find matching transaction to delete + for _, tRows := range oldTransactions.Data { + for _, tRow := range tRows.Attributes.Transactions { + // validate notes is json + notesBytes := bytes.NewBufferString(tRow.Notes).Bytes() + if !json.Valid(notesBytes) { + continue + } + + // read monobank transaction + var monobankTransaction monobank.Transaction + err = json.Unmarshal(notesBytes, &monobankTransaction) + if err != nil { + continue + } + + // find transaction + sum := int(math.Abs(math.Round(float64(monobankTransaction.Data.StatementItem.Amount/100)))) - int(math.Abs(math.Round(float64(monobankTransaction.Data.StatementItem.CommissionRate/100)))) + sum2, _ := strconv.ParseFloat(tRow.Amount, 64) + if slices.Contains(row.Names, monobankTransaction.Data.StatementItem.Description) && sum == int(sum2) { + // delete transaction + opts := firefly3.TransactionsApiDeleteTransactionOpts{} + firefly3Client.TransactionsApi.DeleteTransaction(context.Background(), tRows.Id, &opts) + } + } + } break - case "deposit": - firefly3Transaction.Type_ = &firefly3TransactionTypeDeposit - break - case "transfer": - firefly3Transaction.Type_ = &firefly3TransactionTypeTransfer - break - default: - firefly3Transaction.Type_ = &firefly3TransactionTypeWithdrawal - } + } else { + // check name & mcc + if !(slices.Contains(row.Names, monobankTransaction.Data.StatementItem.Description) || slices.Contains(row.MccCodes, monobankTransaction.Data.StatementItem.Mcc)) { + continue + } - firefly3Transaction.Description = row.Firefly3.Description - firefly3Transaction.DestinationName = row.Firefly3.Destination - firefly3Transaction.CategoryName = row.Firefly3.Category - firefly3Transactions = append(firefly3Transactions, firefly3Transaction) + // create firefly3 transaction + firefly3Transaction := firefly3.TransactionSplitStore{ + Date: time.Unix(int64(monobankTransaction.Data.StatementItem.Time), 0).Add(time.Hour * 2), + Notes: string(monobankTransactionJson), + Amount: strconv.Itoa(int(math.Abs(math.Round(float64(monobankTransaction.Data.StatementItem.Amount/100)))) - int(math.Abs(math.Round(float64(monobankTransaction.Data.StatementItem.CommissionRate/100))))), + SourceId: account.Firefly3Id, + } - // swap source and destination - if row.Firefly3.IsUseDestinationAsSource { - firefly3Transaction.SourceName, firefly3Transaction.DestinationName = firefly3Transaction.DestinationName, firefly3Transaction.SourceName + // check max sum + sum, _ := strconv.Atoi(firefly3Transaction.Amount) + if row.SumMax > 0 && sum > row.SumMax { + continue + } + // make transaction + switch row.Firefly3.Type { + case "withdrawal": + firefly3Transaction.Type_ = &firefly3TransactionTypeWithdrawal + break + case "deposit": + firefly3Transaction.Type_ = &firefly3TransactionTypeDeposit + break + case "transfer": + firefly3Transaction.Type_ = &firefly3TransactionTypeTransfer + break + default: + firefly3Transaction.Type_ = &firefly3TransactionTypeWithdrawal + } + + firefly3Transaction.Description = row.Firefly3.Description + firefly3Transaction.DestinationName = row.Firefly3.Destination + firefly3Transaction.CategoryName = row.Firefly3.Category + + // swap source and destination + if row.Firefly3.IsUseDestinationAsSource { + firefly3Transaction.SourceName, firefly3Transaction.DestinationName = firefly3Transaction.DestinationName, firefly3Transaction.SourceName + } + + firefly3Transactions = append(firefly3Transactions, firefly3Transaction) + break } - break } // record transfer fee