Skip to content

Commit ca55deb

Browse files
authored
feat: add filtering of transactions by availability (#1006)
1 parent aa2dd96 commit ca55deb

File tree

7 files changed

+99
-23
lines changed

7 files changed

+99
-23
lines changed

.golangci.yml

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ issues:
2121

2222
linters:
2323
enable:
24-
- gocyclo
2524
- gofumpt
2625
- goimports
2726
- govet
@@ -32,5 +31,3 @@ linters:
3231
linters-settings:
3332
gofumpt:
3433
extra-rules: true
35-
gocyclo:
36-
min-complexity: 25

api/docs.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2722,6 +2722,24 @@ const docTemplate = `{
27222722
"name": "untilDate",
27232723
"in": "query"
27242724
},
2725+
{
2726+
"type": "string",
2727+
"description": "Availability date of the transaction. Ignores exact time, matches on the day of the RFC3339 timestamp provided.",
2728+
"name": "availableFromDate",
2729+
"in": "query"
2730+
},
2731+
{
2732+
"type": "string",
2733+
"description": "Transactions available at and after this date. Ignores exact time, matches on the day of the RFC3339 timestamp provided.",
2734+
"name": "availableFromFromDate",
2735+
"in": "query"
2736+
},
2737+
{
2738+
"type": "string",
2739+
"description": "Transactions available before and at this date. Ignores exact time, matches on the day of the RFC3339 timestamp provided.",
2740+
"name": "availableFromUntilDate",
2741+
"in": "query"
2742+
},
27252743
{
27262744
"type": "string",
27272745
"description": "Filter by amount",

api/swagger.json

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2711,6 +2711,24 @@
27112711
"name": "untilDate",
27122712
"in": "query"
27132713
},
2714+
{
2715+
"type": "string",
2716+
"description": "Availability date of the transaction. Ignores exact time, matches on the day of the RFC3339 timestamp provided.",
2717+
"name": "availableFromDate",
2718+
"in": "query"
2719+
},
2720+
{
2721+
"type": "string",
2722+
"description": "Transactions available at and after this date. Ignores exact time, matches on the day of the RFC3339 timestamp provided.",
2723+
"name": "availableFromFromDate",
2724+
"in": "query"
2725+
},
2726+
{
2727+
"type": "string",
2728+
"description": "Transactions available before and at this date. Ignores exact time, matches on the day of the RFC3339 timestamp provided.",
2729+
"name": "availableFromUntilDate",
2730+
"in": "query"
2731+
},
27142732
{
27152733
"type": "string",
27162734
"description": "Filter by amount",

api/swagger.yaml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3231,6 +3231,21 @@ paths:
32313231
in: query
32323232
name: untilDate
32333233
type: string
3234+
- description: Availability date of the transaction. Ignores exact time, matches
3235+
on the day of the RFC3339 timestamp provided.
3236+
in: query
3237+
name: availableFromDate
3238+
type: string
3239+
- description: Transactions available at and after this date. Ignores exact
3240+
time, matches on the day of the RFC3339 timestamp provided.
3241+
in: query
3242+
name: availableFromFromDate
3243+
type: string
3244+
- description: Transactions available before and at this date. Ignores exact
3245+
time, matches on the day of the RFC3339 timestamp provided.
3246+
in: query
3247+
name: availableFromUntilDate
3248+
type: string
32343249
- description: Filter by amount
32353250
in: query
32363251
name: amount

pkg/controllers/v4/transaction.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,9 @@ func GetTransaction(c *gin.Context) {
115115
// @Param date query string false "Date of the transaction. Ignores exact time, matches on the day of the RFC3339 timestamp provided."
116116
// @Param fromDate query string false "Transactions at and after this date. Ignores exact time, matches on the day of the RFC3339 timestamp provided."
117117
// @Param untilDate query string false "Transactions before and at this date. Ignores exact time, matches on the day of the RFC3339 timestamp provided."
118+
// @Param availableFromDate query string false "Availability date of the transaction. Ignores exact time, matches on the day of the RFC3339 timestamp provided."
119+
// @Param availableFromFromDate query string false "Transactions available at and after this date. Ignores exact time, matches on the day of the RFC3339 timestamp provided."
120+
// @Param availableFromUntilDate query string false "Transactions available before and at this date. Ignores exact time, matches on the day of the RFC3339 timestamp provided."
118121
// @Param amount query string false "Filter by amount"
119122
// @Param amountLessOrEqual query string false "Amount less than or equal to this"
120123
// @Param amountMoreOrEqual query string false "Amount more than or equal to this"
@@ -169,6 +172,20 @@ func GetTransactions(c *gin.Context) {
169172
q = q.Where("transactions.date < date(?)", time.Date(filter.UntilDate.Year(), filter.UntilDate.Month(), filter.UntilDate.Day()+1, 0, 0, 0, 0, time.UTC))
170173
}
171174

175+
// Filter for the transaction being available at the same date
176+
if !filter.AvailableFromDate.IsZero() {
177+
date := time.Date(filter.AvailableFromDate.Year(), filter.AvailableFromDate.Month(), filter.AvailableFromDate.Day(), 0, 0, 0, 0, time.UTC)
178+
q = q.Where("transactions.available_from >= date(?)", date).Where("transactions.available_from < date(?)", date.AddDate(0, 0, 1))
179+
}
180+
181+
if !filter.AvailableFromFromDate.IsZero() {
182+
q = q.Where("transactions.available_from >= date(?)", time.Date(filter.AvailableFromFromDate.Year(), filter.AvailableFromFromDate.Month(), filter.AvailableFromFromDate.Day(), 0, 0, 0, 0, time.UTC))
183+
}
184+
185+
if !filter.AvailableFromUntilDate.IsZero() {
186+
q = q.Where("transactions.available_from < date(?)", time.Date(filter.AvailableFromUntilDate.Year(), filter.AvailableFromUntilDate.Month(), filter.AvailableFromUntilDate.Day()+1, 0, 0, 0, 0, time.UTC))
187+
}
188+
172189
if filter.BudgetID != "" {
173190
budgetID, err := httputil.UUIDFromString(filter.BudgetID)
174191
if err != nil {

pkg/controllers/v4/transaction_test.go

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,8 @@ func (suite *TestSuiteStandard) TestTransactionsGetFilter() {
192192
ReconciledDestination: false,
193193
})
194194

195+
// Note that this transaction will be available from 2016-05-01 because it's also at
196+
// that date, but by default, "available from" is set to the first of the month
195197
_ = createTestTransaction(suite.T(), v4.TransactionEditable{
196198
Date: time.Date(2016, 5, 1, 14, 13, 25, 584575, time.UTC),
197199
Amount: decimal.NewFromFloat(11235.813),
@@ -230,18 +232,24 @@ func (suite *TestSuiteStandard) TestTransactionsGetFilter() {
230232
{"Amount more or equal to 100", "amountMoreOrEqual=100", 1},
231233
{"Amount more or equal to 11235.813", "amountMoreOrEqual=11235.813", 1},
232234
{"Amount more or equal to 99999", "amountMoreOrEqual=99999", 0},
235+
{"Available after - no transactions", fmt.Sprintf("availableFromFromDate=%s", time.Date(2020, 7, 1, 0, 0, 0, 0, time.UTC).Format(time.RFC3339Nano)), 1}, // Available from is only relevant for income, but set for all transactions
236+
{"Available after - returns transactions", fmt.Sprintf("availableFromFromDate=%s", time.Date(2000, 12, 1, 0, 0, 0, 0, time.UTC).Format(time.RFC3339Nano)), 3}, // Available from is only relevant for income, but set for all transactions
237+
{"Available at date - no transactions", fmt.Sprintf("availableFromDate=%s", time.Date(2016, 5, 2, 11, 17, 0, 0, time.UTC).Format(time.RFC3339Nano)), 0},
238+
{"Available at month - with transaction", fmt.Sprintf("availableFromDate=%s", time.Date(2016, 5, 1, 12, 53, 15, 148041, time.UTC).Format(time.RFC3339Nano)), 1},
239+
{"Available before - no transactions", fmt.Sprintf("availableFromUntilDate=%s", time.Date(2016, 4, 1, 0, 0, 0, 0, time.UTC).Format(time.RFC3339Nano)), 0}, // Needs to be before 2016-05-01T00:00:00Z since that's what the transaction defaults to when created
240+
{"Available before - returns transactions", fmt.Sprintf("availableFromUntilDate=%s", time.Date(2024, 12, 1, 0, 0, 0, 0, time.UTC).Format(time.RFC3339Nano)), 3}, // Available from is only relevant for income, but set for all transactions
233241
{"Before all dates", fmt.Sprintf("untilDate=%s", time.Date(2010, 8, 17, 0, 0, 0, 0, time.UTC).Format(time.RFC3339Nano)), 0},
234242
{"Before date", fmt.Sprintf("untilDate=%s", time.Date(2017, 1, 1, 0, 0, 0, 0, time.UTC).Format(time.RFC3339Nano)), 1},
235243
{"Between two dates", fmt.Sprintf("untilDate=%s&fromDate=%s", time.Date(2019, 8, 17, 0, 0, 0, 0, time.UTC).Format(time.RFC3339Nano), time.Date(2015, 12, 24, 0, 0, 0, 0, time.UTC).Format(time.RFC3339Nano)), 2},
236-
{"Budget Match", fmt.Sprintf("budget=%s", b.Data.ID), 3},
237244
{"Budget and Note", fmt.Sprintf("budget=%s&note=Not", b.Data.ID), 1},
245+
{"Budget Match", fmt.Sprintf("budget=%s", b.Data.ID), 3},
238246
{"Destination Account", fmt.Sprintf("destination=%s", a2.Data.ID), 2},
239-
{"Direction=TRANSFER and Budget ID", fmt.Sprintf("budget=%s&direction=TRANSFER", b.Data.ID), 0},
240247
{"Direction=INCOMING", "direction=INCOMING", 1},
241248
{"Direction=OUTGOING", "direction=OUTGOING", 2},
249+
{"Direction=TRANSFER and Budget ID", fmt.Sprintf("budget=%s&direction=TRANSFER", b.Data.ID), 0},
242250
{"Envelope 2", fmt.Sprintf("envelope=%s", e2.Data.ID), 1},
243251
{"Exact Amount", fmt.Sprintf("amount=%s", decimal.NewFromFloat(2.718).String()), 2},
244-
{"Exact Time", fmt.Sprintf("date=%s", time.Date(2021, 2, 6, 5, 1, 0, 585, time.UTC).Format(time.RFC3339Nano)), 1},
252+
{"Exact Date", fmt.Sprintf("date=%s", time.Date(2021, 2, 6, 5, 1, 0, 585, time.UTC).Format(time.RFC3339Nano)), 1},
245253
{"Existing Account 1", fmt.Sprintf("account=%s", a1.Data.ID), 2},
246254
{"Existing Account 2", fmt.Sprintf("account=%s", a2.Data.ID), 3},
247255
{"Fuzzy note", "note=important", 2},

pkg/controllers/v4/transaction_types.go

Lines changed: 20 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -120,23 +120,26 @@ const (
120120
)
121121

122122
type TransactionQueryFilter struct {
123-
Date time.Time `form:"date" filterField:"false"` // Exact date. Time is ignored.
124-
FromDate time.Time `form:"fromDate" filterField:"false"` // From this date. Time is ignored.
125-
UntilDate time.Time `form:"untilDate" filterField:"false"` // Until this date. Time is ignored.
126-
Amount decimal.Decimal `form:"amount"` // Exact amount
127-
AmountLessOrEqual decimal.Decimal `form:"amountLessOrEqual" filterField:"false"` // Amount less than or equal to this
128-
AmountMoreOrEqual decimal.Decimal `form:"amountMoreOrEqual" filterField:"false"` // Amount more than or equal to this
129-
Note string `form:"note" filterField:"false"` // Note contains this string
130-
BudgetID string `form:"budget" filterField:"false"` // ID of the budget
131-
SourceAccountID string `form:"source"` // ID of the source account
132-
DestinationAccountID string `form:"destination"` // ID of the destination account
133-
Direction TransactionDirection `form:"direction" filterField:"false"` // Direction of the transaction
134-
EnvelopeID string `form:"envelope"` // ID of the envelope
135-
ReconciledSource bool `form:"reconciledSource"` // Is the transaction reconciled in the source account?
136-
ReconciledDestination bool `form:"reconciledDestination"` // Is the transaction reconciled in the destination account?
137-
AccountID string `form:"account" filterField:"false"` // ID of either source or destination account
138-
Offset uint `form:"offset" filterField:"false"` // The offset of the first Transaction returned. Defaults to 0.
139-
Limit int `form:"limit" filterField:"false"` // Maximum number of transactions to return. Defaults to 50.
123+
AvailableFromDate time.Time `form:"availableFromDate" filterField:"false"` // Exact date. Time is ignored.
124+
AvailableFromFromDate time.Time `form:"availableFromFromDate" filterField:"false"` // From this date. Time is ignored.
125+
AvailableFromUntilDate time.Time `form:"availableFromUntilDate" filterField:"false"` // Until this date. Time is ignored.
126+
Date time.Time `form:"date" filterField:"false"` // Exact date. Time is ignored.
127+
FromDate time.Time `form:"fromDate" filterField:"false"` // From this date. Time is ignored.
128+
UntilDate time.Time `form:"untilDate" filterField:"false"` // Until this date. Time is ignored.
129+
Amount decimal.Decimal `form:"amount"` // Exact amount
130+
AmountLessOrEqual decimal.Decimal `form:"amountLessOrEqual" filterField:"false"` // Amount less than or equal to this
131+
AmountMoreOrEqual decimal.Decimal `form:"amountMoreOrEqual" filterField:"false"` // Amount more than or equal to this
132+
Note string `form:"note" filterField:"false"` // Note contains this string
133+
BudgetID string `form:"budget" filterField:"false"` // ID of the budget
134+
SourceAccountID string `form:"source"` // ID of the source account
135+
DestinationAccountID string `form:"destination"` // ID of the destination account
136+
Direction TransactionDirection `form:"direction" filterField:"false"` // Direction of the transaction
137+
EnvelopeID string `form:"envelope"` // ID of the envelope
138+
ReconciledSource bool `form:"reconciledSource"` // Is the transaction reconciled in the source account?
139+
ReconciledDestination bool `form:"reconciledDestination"` // Is the transaction reconciled in the destination account?
140+
AccountID string `form:"account" filterField:"false"` // ID of either source or destination account
141+
Offset uint `form:"offset" filterField:"false"` // The offset of the first Transaction returned. Defaults to 0.
142+
Limit int `form:"limit" filterField:"false"` // Maximum number of transactions to return. Defaults to 50.
140143
}
141144

142145
func (f TransactionQueryFilter) model() (models.Transaction, error) {

0 commit comments

Comments
 (0)