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

fix: income now recognized in RecentEnvelopes and transaction import #790

Merged
merged 1 commit into from
Oct 8, 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
17 changes: 6 additions & 11 deletions pkg/controllers/import.go
Original file line number Diff line number Diff line change
Expand Up @@ -173,12 +173,7 @@ func (co Controller) ImportYnabImportPreview(c *gin.Context) {
duplicateTransactions(co, &transaction, account.BudgetID)

// Recommend an envelope
if transaction.Transaction.SourceAccountID != account.ID && transaction.Transaction.SourceAccountID != uuid.Nil {
err = recommendEnvelope(co, &transaction, transaction.Transaction.SourceAccountID)
if err != nil {
httperrors.Handler(c, err)
}
} else if transaction.Transaction.DestinationAccountID != account.ID && transaction.Transaction.DestinationAccountID != uuid.Nil {
if transaction.Transaction.DestinationAccountID != uuid.Nil {
err = recommendEnvelope(co, &transaction, transaction.Transaction.DestinationAccountID)
if err != nil {
httperrors.Handler(c, err)
Expand Down Expand Up @@ -383,20 +378,20 @@ func match(transaction *importer.TransactionPreview, rules []models.MatchRule) {
// recommendEnvelope sets the first of the recommended envelopes for the opposing account.
func recommendEnvelope(co Controller, transaction *importer.TransactionPreview, id uuid.UUID) error {
// Load the account
var opposingAccount models.Account
err := co.DB.First(&opposingAccount, models.Account{DefaultModel: models.DefaultModel{ID: id}}).Error
var destinationAccount models.Account
err := co.DB.First(&destinationAccount, models.Account{DefaultModel: models.DefaultModel{ID: id}}).Error
if err != nil {
return err
}

// Preset the most popular recent envelope
err = opposingAccount.SetRecentEnvelopes(co.DB)
err = destinationAccount.SetRecentEnvelopes(co.DB)
if err != nil {
return err
}

if len(opposingAccount.RecentEnvelopes) > 0 {
transaction.Transaction.EnvelopeID = &opposingAccount.RecentEnvelopes[0].ID
if len(destinationAccount.RecentEnvelopes) > 0 && destinationAccount.RecentEnvelopes[0].ID != uuid.Nil {
transaction.Transaction.EnvelopeID = &destinationAccount.RecentEnvelopes[0].ID
}

return nil
Expand Down
6 changes: 4 additions & 2 deletions pkg/models/account.go
Original file line number Diff line number Diff line change
Expand Up @@ -207,10 +207,12 @@ func (a Account) GetBalanceMonth(db *gorm.DB, month types.Month) (balance, avail
// The list is sorted by decending frequency of the envelope being used.
func (a *Account) SetRecentEnvelopes(db *gorm.DB) error {
var envelopes []Envelope

// TODO: For v2, just return the IDs and use `null` for income
err := db.
Table("transactions").
Select("envelopes.*, count(envelopes.id) AS count").
Joins("JOIN envelopes ON envelopes.id = transactions.envelope_id AND envelopes.deleted_at IS NULL").
Select("envelopes.*, count(*) AS count").
Joins("LEFT JOIN envelopes ON envelopes.id = transactions.envelope_id AND envelopes.deleted_at IS NULL").
Order("count DESC, date(transactions.date) DESC").
Where(&Transaction{
TransactionCreate: TransactionCreate{
Expand Down
32 changes: 24 additions & 8 deletions pkg/models/account_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -169,36 +169,52 @@ func (suite *TestSuiteStandard) TestAccountRecentEnvelopes() {
// * 2 for the first envelope
// * 2 for the second envelope
// * 11 for the last envelope
for i := 1; i <= 15; i++ {
for i := 0; i < 15; i++ {
eIndex := i
if i > 5 {
eIndex = 2
}
_ = suite.createTestTransaction(models.TransactionCreate{
BudgetID: budget.ID,
EnvelopeID: envelopeIDs[eIndex%3],
SourceAccountID: account.ID,
DestinationAccountID: externalAccount.ID,
SourceAccountID: externalAccount.ID,
DestinationAccountID: account.ID,
Amount: decimal.NewFromFloat(17.45),
})
}

err := externalAccount.SetRecentEnvelopes(suite.db)
// Create one income transaction
_ = suite.createTestTransaction(models.TransactionCreate{
BudgetID: budget.ID,
EnvelopeID: nil,
SourceAccountID: externalAccount.ID,
DestinationAccountID: account.ID,
Amount: decimal.NewFromFloat(1337.42),
})

err := account.SetRecentEnvelopes(suite.db)
if err != nil {
suite.Assert().FailNow("Could not compute recent envelopes", err)
}

if !suite.Assert().Len(account.RecentEnvelopes, 4, "The number of envelopes in recentEnvelopes is not correct, expected 4, got %d", len(account.RecentEnvelopes)) {
suite.FailNow("Incorrect envelope number")
}

// The last envelope needs to be the first in the sort since it
// has been the most common one in the last 10 transactions
suite.Assert().Equal(*envelopeIDs[2], externalAccount.RecentEnvelopes[0].ID)
// has been the most common one
suite.Assert().Equal(*envelopeIDs[2], account.RecentEnvelopes[0].ID)

// The second envelope is as common as the first, but its newest transaction
// is newer than the first envelope's newest transaction,
// so it needs to come second
suite.Assert().Equal(*envelopeIDs[1], externalAccount.RecentEnvelopes[1].ID)
suite.Assert().Equal(*envelopeIDs[1], account.RecentEnvelopes[1].ID)

// The first envelope is the last one
suite.Assert().Equal(*envelopeIDs[0], externalAccount.RecentEnvelopes[2].ID)
suite.Assert().Equal(*envelopeIDs[0], account.RecentEnvelopes[2].ID)

// Income is the last one
suite.Assert().Equal(uuid.Nil, account.RecentEnvelopes[3].ID)
}

func (suite *TestSuiteStandard) TestAccountGetBalanceMonthDBFail() {
Expand Down