Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add rename rule to transaction preview #737

Merged
merged 1 commit into from
Aug 6, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions api/docs.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 7 additions & 2 deletions api/swagger.json
Original file line number Diff line number Diff line change
Expand Up @@ -3634,7 +3634,7 @@
"type": "object",
"properties": {
"destinationAccountName": {
"description": "Name of the destination account if the ID is not known",
"description": "Name of the destination account from the CSV file",
"type": "string",
"example": "Deutsche Bahn"
},
Expand All @@ -3645,8 +3645,13 @@
"type": "string"
}
},
"renameRuleId": {
"description": "ID of the rename rule that was applied to this transaction preview",
"type": "string",
"example": "042d101d-f1de-4403-9295-59dc0ea58677"
},
"sourceAccountName": {
"description": "Name of the source account if the ID is not known",
"description": "Name of the source account from the CSV file",
"type": "string",
"example": "Employer"
},
Expand Down
8 changes: 6 additions & 2 deletions api/swagger.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -284,16 +284,20 @@ definitions:
importer.TransactionPreview:
properties:
destinationAccountName:
description: Name of the destination account if the ID is not known
description: Name of the destination account from the CSV file
example: Deutsche Bahn
type: string
duplicateTransactionIds:
description: IDs of transactions that this transaction duplicates
items:
type: string
type: array
renameRuleId:
description: ID of the rename rule that was applied to this transaction preview
example: 042d101d-f1de-4403-9295-59dc0ea58677
type: string
sourceAccountName:
description: Name of the source account if the ID is not known
description: Name of the source account from the CSV file
example: Employer
type: string
transaction:
Expand Down
14 changes: 8 additions & 6 deletions pkg/controllers/import.go
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,9 @@ func (co Controller) ImportYnabImportPreview(c *gin.Context) {
}

for i, transaction := range transactions {
rename(&transaction, renameRules)
if len(renameRules) > 0 {
rename(&transaction, renameRules)
}

// Only find accounts when they are not yet both set
if transaction.Transaction.SourceAccountID == uuid.Nil || transaction.Transaction.DestinationAccountID == uuid.Nil {
Expand Down Expand Up @@ -346,23 +348,23 @@ func findAccounts(co Controller, transaction *importer.TransactionPreview, budge

// rename applies the renaming rules to a transaction.
func rename(transaction *importer.TransactionPreview, rules []models.RenameRule) {
replace := func(name string) uuid.UUID {
replace := func(name string) (uuid.UUID, uuid.UUID) {
// Iterate over all rules
for _, rule := range rules {
// If the rule matches, return the account ID. Since rules are loaded from
// the database in priority order, we can simply return the first match
if glob.Glob(rule.Match, name) {
return rule.AccountID
return rule.AccountID, rule.ID
}
}
return uuid.Nil
return uuid.Nil, uuid.Nil
}

if transaction.SourceAccountName != "" {
transaction.Transaction.SourceAccountID = replace(transaction.SourceAccountName)
transaction.Transaction.SourceAccountID, transaction.RenameRuleID = replace(transaction.SourceAccountName)
}

if transaction.DestinationAccountName != "" {
transaction.Transaction.DestinationAccountID = replace(transaction.DestinationAccountName)
transaction.Transaction.DestinationAccountID, transaction.RenameRuleID = replace(transaction.DestinationAccountName)
}
}
33 changes: 23 additions & 10 deletions pkg/controllers/import_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -260,43 +260,47 @@ func (suite *TestSuiteStandard) TestRename() {
internalAccount := suite.createTestAccount(models.AccountCreate{BudgetID: budget.Data.ID, Name: "Envelope Zero Account"})

tests := []struct {
name string // Name of the test
sourceAccountIDs []uuid.UUID // The IDs of the source accounts
destinationAccountIDs []uuid.UUID // The IDs of the destination accounts
preTest func(*testing.T) // Function to execute before running tests
name string // Name of the test
sourceAccountIDs []uuid.UUID // The IDs of the source accounts
destinationAccountIDs []uuid.UUID // The IDs of the destination accounts
preTest func(*testing.T) [3]uuid.UUID // Function to execute before running tests
}{
{
"Rule for Edeka",
[]uuid.UUID{internalAccount.Data.ID, internalAccount.Data.ID, uuid.Nil},
[]uuid.UUID{edeka.Data.ID, uuid.Nil, internalAccount.Data.ID},
func(t *testing.T) {
_ = suite.createTestRenameRule(t, models.RenameRuleCreate{
func(t *testing.T) [3]uuid.UUID {
edeka := suite.createTestRenameRule(t, models.RenameRuleCreate{
Match: "EDEKA*",
AccountID: edeka.Data.ID,
})

return [3]uuid.UUID{edeka.Data.ID}
},
},
{
"Rule for Edeka and DB",
[]uuid.UUID{internalAccount.Data.ID, internalAccount.Data.ID, uuid.Nil},
[]uuid.UUID{edeka.Data.ID, bahn.Data.ID, internalAccount.Data.ID},
func(t *testing.T) {
_ = suite.createTestRenameRule(t, models.RenameRuleCreate{
func(t *testing.T) [3]uuid.UUID {
edeka := suite.createTestRenameRule(t, models.RenameRuleCreate{
Match: "EDEKA*",
AccountID: edeka.Data.ID,
})

_ = suite.createTestRenameRule(t, models.RenameRuleCreate{
db := suite.createTestRenameRule(t, models.RenameRuleCreate{
Match: "DB Vertrieb GmbH",
AccountID: bahn.Data.ID,
})

return [3]uuid.UUID{edeka.Data.ID, db.Data.ID}
},
},
}

for _, tt := range tests {
suite.T().Run(tt.name, func(t *testing.T) {
tt.preTest(t)
renameRuleIDs := tt.preTest(t)
preview := parseCSV(suite, internalAccount.Data.ID, "rename-rule-test.csv")

for i, transaction := range preview.Data {
Expand All @@ -308,6 +312,15 @@ func (suite *TestSuiteStandard) TestRename() {
if tt.destinationAccountIDs[i] != uuid.Nil {
assert.Equal(t, tt.destinationAccountIDs[i], transaction.Transaction.DestinationAccountID, "destinationAccountID does not match in line %d", line)
}

assert.Equal(t, renameRuleIDs[i], transaction.RenameRuleID, "Expected rename rule has match '%s', actual rename rule has match '%s'", renameRuleIDs[i])
}

// Delete rename rules
for _, id := range renameRuleIDs {
if id != uuid.Nil {
suite.controller.DB.Delete(&models.RenameRule{}, id)
}
}
})
}
Expand Down
7 changes: 4 additions & 3 deletions pkg/importer/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,8 @@ type Transaction struct {
// TransactionPreview is used to preview transactions that will be imported to allow for editing.
type TransactionPreview struct {
Transaction models.TransactionCreate `json:"transaction"`
SourceAccountName string `json:"sourceAccountName" example:"Employer"` // Name of the source account if the ID is not known
DestinationAccountName string `json:"destinationAccountName" example:"Deutsche Bahn"` // Name of the destination account if the ID is not known
DuplicateTransactionIDs []uuid.UUID `json:"duplicateTransactionIds"` // IDs of transactions that this transaction duplicates
SourceAccountName string `json:"sourceAccountName" example:"Employer"` // Name of the source account from the CSV file
DestinationAccountName string `json:"destinationAccountName" example:"Deutsche Bahn"` // Name of the destination account from the CSV file
DuplicateTransactionIDs []uuid.UUID `json:"duplicateTransactionIds"` // IDs of transactions that this transaction duplicates
RenameRuleID uuid.UUID `json:"renameRuleId" example:"042d101d-f1de-4403-9295-59dc0ea58677"` // ID of the rename rule that was applied to this transaction preview
}