Skip to content

Commit 435cac7

Browse files
committed
fix: reconciled flag per account
Transactions now have a reconciled flag for both the source and the destination account named `reconciledSource` and `reconciledDestination`. This is needed since transactions are reconciled per account, not globally. It is recommended for all clients to switch to the new field names. The existing field `reconciled` is now deprecated and will be removed in API v2.
1 parent 3274a60 commit 435cac7

File tree

7 files changed

+180
-62
lines changed

7 files changed

+180
-62
lines changed

api/docs.go

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2438,9 +2438,21 @@ const docTemplate = `{
24382438
},
24392439
{
24402440
"type": "boolean",
2441-
"description": "Filter by reconcilication state",
2441+
"description": "DEPRECATED. Filter by reconcilication state",
24422442
"name": "reconciled",
24432443
"in": "query"
2444+
},
2445+
{
2446+
"type": "boolean",
2447+
"description": "Reconcilication state in source account",
2448+
"name": "reconciledSource",
2449+
"in": "query"
2450+
},
2451+
{
2452+
"type": "boolean",
2453+
"description": "Reconcilication state in destination account",
2454+
"name": "reconciledDestination",
2455+
"in": "query"
24442456
}
24452457
],
24462458
"responses": {
@@ -3316,6 +3328,17 @@ const docTemplate = `{
33163328
"example": "Lunch"
33173329
},
33183330
"reconciled": {
3331+
"description": "DEPRECATED. Do not use, this field does not work as intended. See https://github.com/envelope-zero/backend/issues/528. Use reconciledSource and reconciledDestination instead.",
3332+
"type": "boolean",
3333+
"default": false,
3334+
"example": true
3335+
},
3336+
"reconciledDestination": {
3337+
"type": "boolean",
3338+
"default": false,
3339+
"example": true
3340+
},
3341+
"reconciledSource": {
33193342
"type": "boolean",
33203343
"default": false,
33213344
"example": true
@@ -3768,6 +3791,17 @@ const docTemplate = `{
37683791
"example": "Lunch"
37693792
},
37703793
"reconciled": {
3794+
"description": "DEPRECATED. Do not use, this field does not work as intended. See https://github.com/envelope-zero/backend/issues/528. Use reconciledSource and reconciledDestination instead.",
3795+
"type": "boolean",
3796+
"default": false,
3797+
"example": true
3798+
},
3799+
"reconciledDestination": {
3800+
"type": "boolean",
3801+
"default": false,
3802+
"example": true
3803+
},
3804+
"reconciledSource": {
37713805
"type": "boolean",
37723806
"default": false,
37733807
"example": true

api/swagger.json

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2426,9 +2426,21 @@
24262426
},
24272427
{
24282428
"type": "boolean",
2429-
"description": "Filter by reconcilication state",
2429+
"description": "DEPRECATED. Filter by reconcilication state",
24302430
"name": "reconciled",
24312431
"in": "query"
2432+
},
2433+
{
2434+
"type": "boolean",
2435+
"description": "Reconcilication state in source account",
2436+
"name": "reconciledSource",
2437+
"in": "query"
2438+
},
2439+
{
2440+
"type": "boolean",
2441+
"description": "Reconcilication state in destination account",
2442+
"name": "reconciledDestination",
2443+
"in": "query"
24322444
}
24332445
],
24342446
"responses": {
@@ -3304,6 +3316,17 @@
33043316
"example": "Lunch"
33053317
},
33063318
"reconciled": {
3319+
"description": "DEPRECATED. Do not use, this field does not work as intended. See https://github.com/envelope-zero/backend/issues/528. Use reconciledSource and reconciledDestination instead.",
3320+
"type": "boolean",
3321+
"default": false,
3322+
"example": true
3323+
},
3324+
"reconciledDestination": {
3325+
"type": "boolean",
3326+
"default": false,
3327+
"example": true
3328+
},
3329+
"reconciledSource": {
33073330
"type": "boolean",
33083331
"default": false,
33093332
"example": true
@@ -3756,6 +3779,17 @@
37563779
"example": "Lunch"
37573780
},
37583781
"reconciled": {
3782+
"description": "DEPRECATED. Do not use, this field does not work as intended. See https://github.com/envelope-zero/backend/issues/528. Use reconciledSource and reconciledDestination instead.",
3783+
"type": "boolean",
3784+
"default": false,
3785+
"example": true
3786+
},
3787+
"reconciledDestination": {
3788+
"type": "boolean",
3789+
"default": false,
3790+
"example": true
3791+
},
3792+
"reconciledSource": {
37593793
"type": "boolean",
37603794
"default": false,
37613795
"example": true

api/swagger.yaml

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -436,6 +436,17 @@ definitions:
436436
example: Lunch
437437
type: string
438438
reconciled:
439+
default: false
440+
description: DEPRECATED. Do not use, this field does not work as intended.
441+
See https://github.com/envelope-zero/backend/issues/528. Use reconciledSource
442+
and reconciledDestination instead.
443+
example: true
444+
type: boolean
445+
reconciledDestination:
446+
default: false
447+
example: true
448+
type: boolean
449+
reconciledSource:
439450
default: false
440451
example: true
441452
type: boolean
@@ -777,6 +788,17 @@ definitions:
777788
example: Lunch
778789
type: string
779790
reconciled:
791+
default: false
792+
description: DEPRECATED. Do not use, this field does not work as intended.
793+
See https://github.com/envelope-zero/backend/issues/528. Use reconciledSource
794+
and reconciledDestination instead.
795+
example: true
796+
type: boolean
797+
reconciledDestination:
798+
default: false
799+
example: true
800+
type: boolean
801+
reconciledSource:
780802
default: false
781803
example: true
782804
type: boolean
@@ -2488,10 +2510,18 @@ paths:
24882510
in: query
24892511
name: envelope
24902512
type: string
2491-
- description: Filter by reconcilication state
2513+
- description: DEPRECATED. Filter by reconcilication state
24922514
in: query
24932515
name: reconciled
24942516
type: boolean
2517+
- description: Reconcilication state in source account
2518+
in: query
2519+
name: reconciledSource
2520+
type: boolean
2521+
- description: Reconcilication state in destination account
2522+
in: query
2523+
name: reconciledDestination
2524+
type: boolean
24952525
produces:
24962526
- application/json
24972527
responses:

pkg/controllers/transaction.go

Lines changed: 31 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -33,15 +33,17 @@ type TransactionLinks struct {
3333
}
3434

3535
type TransactionQueryFilter struct {
36-
Date time.Time `form:"date"`
37-
Amount decimal.Decimal `form:"amount"`
38-
Note string `form:"note" filterField:"false"`
39-
BudgetID string `form:"budget"`
40-
SourceAccountID string `form:"source"`
41-
DestinationAccountID string `form:"destination"`
42-
EnvelopeID string `form:"envelope"`
43-
Reconciled bool `form:"reconciled"`
44-
AccountID string `form:"account" filterField:"false"`
36+
Date time.Time `form:"date"`
37+
Amount decimal.Decimal `form:"amount"`
38+
Note string `form:"note" filterField:"false"`
39+
BudgetID string `form:"budget"`
40+
SourceAccountID string `form:"source"`
41+
DestinationAccountID string `form:"destination"`
42+
EnvelopeID string `form:"envelope"`
43+
Reconciled bool `form:"reconciled"` // DEPRECATED. Do not use, this field does not work as intended. See https://github.com/envelope-zero/backend/issues/528. Use reconciledSource and reconciledDestination instead.
44+
ReconciledSource bool `form:"reconciledSource"` // Is the transaction reconciled in the source account?
45+
ReconciledDestination bool `form:"reconciledDestination"` // Is the transaction reconciled in the destination account?
46+
AccountID string `form:"account" filterField:"false"`
4547
}
4648

4749
func (f TransactionQueryFilter) ToCreate(c *gin.Context) (models.TransactionCreate, bool) {
@@ -72,13 +74,15 @@ func (f TransactionQueryFilter) ToCreate(c *gin.Context) (models.TransactionCrea
7274
}
7375

7476
return models.TransactionCreate{
75-
Date: f.Date,
76-
Amount: f.Amount,
77-
BudgetID: budgetID,
78-
SourceAccountID: sourceAccountID,
79-
DestinationAccountID: destinationAccountID,
80-
EnvelopeID: eID,
81-
Reconciled: f.Reconciled,
77+
Date: f.Date,
78+
Amount: f.Amount,
79+
BudgetID: budgetID,
80+
SourceAccountID: sourceAccountID,
81+
DestinationAccountID: destinationAccountID,
82+
EnvelopeID: eID,
83+
Reconciled: f.Reconciled,
84+
ReconciledSource: f.ReconciledSource,
85+
ReconciledDestination: f.ReconciledDestination,
8286
}, true
8387
}
8488

@@ -195,15 +199,17 @@ func (co Controller) CreateTransaction(c *gin.Context) {
195199
// @Failure 404
196200
// @Failure 500 {object} httperrors.HTTPError
197201
// @Router /v1/transactions [get]
198-
// @Param date query time.Time false "Filter by date"
199-
// @Param amount query decimal.Decimal false "Filter by amount"
200-
// @Param note query string false "Filter by note"
201-
// @Param budget query string false "Filter by budget ID"
202-
// @Param account query string false "Filter by ID of associated account, regardeless of source or destination"
203-
// @Param source query string false "Filter by source account ID"
204-
// @Param destination query string false "Filter by destination account ID"
205-
// @Param envelope query string false "Filter by envelope ID"
206-
// @Param reconciled query bool false "Filter by reconcilication state"
202+
// @Param date query time.Time false "Filter by date"
203+
// @Param amount query decimal.Decimal false "Filter by amount"
204+
// @Param note query string false "Filter by note"
205+
// @Param budget query string false "Filter by budget ID"
206+
// @Param account query string false "Filter by ID of associated account, regardeless of source or destination"
207+
// @Param source query string false "Filter by source account ID"
208+
// @Param destination query string false "Filter by destination account ID"
209+
// @Param envelope query string false "Filter by envelope ID"
210+
// @Param reconciled query bool false "DEPRECATED. Filter by reconcilication state"
211+
// @Param reconciledSource query bool false "Reconcilication state in source account"
212+
// @Param reconciledDestination query bool false "Reconcilication state in destination account"
207213
func (co Controller) GetTransactions(c *gin.Context) {
208214
var filter TransactionQueryFilter
209215
if err := c.Bind(&filter); err != nil {

pkg/controllers/transaction_test.go

Lines changed: 34 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -115,36 +115,42 @@ func (suite *TestSuiteStandard) TestGetTransactionsFilter() {
115115
e2ID := &e2.Data.ID
116116

117117
_ = suite.createTestTransaction(models.TransactionCreate{
118-
Date: time.Date(2018, 9, 5, 17, 13, 29, 45256, time.UTC),
119-
Amount: decimal.NewFromFloat(2.718),
120-
Note: "This was an important expense",
121-
BudgetID: b.Data.ID,
122-
EnvelopeID: e1ID,
123-
SourceAccountID: a1.Data.ID,
124-
DestinationAccountID: a2.Data.ID,
125-
Reconciled: false,
118+
Date: time.Date(2018, 9, 5, 17, 13, 29, 45256, time.UTC),
119+
Amount: decimal.NewFromFloat(2.718),
120+
Note: "This was an important expense",
121+
BudgetID: b.Data.ID,
122+
EnvelopeID: e1ID,
123+
SourceAccountID: a1.Data.ID,
124+
DestinationAccountID: a2.Data.ID,
125+
Reconciled: false,
126+
ReconciledSource: true,
127+
ReconciledDestination: false,
126128
})
127129

128130
_ = suite.createTestTransaction(models.TransactionCreate{
129-
Date: time.Date(2016, 5, 1, 14, 13, 25, 584575, time.UTC),
130-
Amount: decimal.NewFromFloat(11235.813),
131-
Note: "Not important",
132-
BudgetID: b.Data.ID,
133-
EnvelopeID: e2ID,
134-
SourceAccountID: a2.Data.ID,
135-
DestinationAccountID: a1.Data.ID,
136-
Reconciled: false,
131+
Date: time.Date(2016, 5, 1, 14, 13, 25, 584575, time.UTC),
132+
Amount: decimal.NewFromFloat(11235.813),
133+
Note: "Not important",
134+
BudgetID: b.Data.ID,
135+
EnvelopeID: e2ID,
136+
SourceAccountID: a2.Data.ID,
137+
DestinationAccountID: a1.Data.ID,
138+
Reconciled: false,
139+
ReconciledSource: true,
140+
ReconciledDestination: true,
137141
})
138142

139143
_ = suite.createTestTransaction(models.TransactionCreate{
140-
Date: time.Date(2021, 2, 6, 5, 1, 0, 585, time.UTC),
141-
Amount: decimal.NewFromFloat(2.718),
142-
Note: "",
143-
BudgetID: b.Data.ID,
144-
EnvelopeID: e1ID,
145-
SourceAccountID: a3.Data.ID,
146-
DestinationAccountID: a2.Data.ID,
147-
Reconciled: true,
144+
Date: time.Date(2021, 2, 6, 5, 1, 0, 585, time.UTC),
145+
Amount: decimal.NewFromFloat(2.718),
146+
Note: "",
147+
BudgetID: b.Data.ID,
148+
EnvelopeID: e1ID,
149+
SourceAccountID: a3.Data.ID,
150+
DestinationAccountID: a2.Data.ID,
151+
Reconciled: true,
152+
ReconciledSource: false,
153+
ReconciledDestination: true,
148154
})
149155

150156
tests := []struct {
@@ -161,7 +167,10 @@ func (suite *TestSuiteStandard) TestGetTransactionsFilter() {
161167
{"Envelope 2", fmt.Sprintf("envelope=%s", e2.Data.ID), 1},
162168
{"Non-existing Source Account", "source=3340a084-acf8-4cb4-8f86-9e7f88a86190", 0},
163169
{"Destination Account", fmt.Sprintf("destination=%s", a2.Data.ID), 2},
164-
{"Reconciled", "reconciled=false", 2},
170+
{"Not reconciled in source account", "reconciledSource=false", 1},
171+
{"Not reconciled in destination account", "reconciledDestination=false", 1},
172+
{"Reconciled in source account", "reconciledSource=true", 2},
173+
{"Reconciled in destination account", "reconciledDestination=true", 2},
165174
{"Non-existing Account", "account=534a3562-c5e8-46d1-a2e2-e96c00e7efec", 0},
166175
{"Existing Account 2", fmt.Sprintf("account=%s", a2.Data.ID), 3},
167176
{"Existing Account 1", fmt.Sprintf("account=%s", a1.Data.ID), 2},

pkg/models/database.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ func Migrate(db *gorm.DB) error {
2929

3030
// Transaction
3131
db.Unscoped().Model(&Transaction{}).Select("Reconciled").Where("transactions.reconciled IS NULL").Update("Reconciled", false)
32+
db.Unscoped().Model(&Transaction{}).Select("ReconciledSource").Where("transactions.reconciled_source IS NULL").Update("ReconciledSource", false)
33+
db.Unscoped().Model(&Transaction{}).Select("ReconciledDestination").Where("transactions.reconciled_destination IS NULL").Update("ReconciledDestination", false)
3234

3335
return nil
3436
}

pkg/models/transaction.go

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -20,15 +20,18 @@ type Transaction struct {
2020
}
2121

2222
type TransactionCreate struct {
23-
Date time.Time `json:"date" example:"1815-12-10T18:43:00.271152Z"`
24-
Amount decimal.Decimal `json:"amount" gorm:"type:DECIMAL(20,8)" example:"14.03" minimum:"0.00000001" maximum:"999999999999.99999999" multipleOf:"0.00000001"` // The maximum value is "999999999999.99999999", swagger unfortunately rounds this.
25-
Note string `json:"note" example:"Lunch" default:""`
26-
BudgetID uuid.UUID `json:"budgetId" example:"55eecbd8-7c46-4b06-ada9-f287802fb05e"`
27-
SourceAccountID uuid.UUID `json:"sourceAccountId" gorm:"check:source_destination_different,source_account_id != destination_account_id" example:"fd81dc45-a3a2-468e-a6fa-b2618f30aa45"`
28-
DestinationAccountID uuid.UUID `json:"destinationAccountId" example:"8e16b456-a719-48ce-9fec-e115cfa7cbcc"`
29-
EnvelopeID *uuid.UUID `json:"envelopeId" example:"2649c965-7999-4873-ae16-89d5d5fa972e"`
30-
Reconciled bool `json:"reconciled" example:"true" default:"false"`
31-
AvailableFrom types.Month `json:"availableFrom" example:"2021-11-17:00:00:00Z"` // The date from which on the transaction amount is available for budgeting. Only used for income transactions. Defaults to the transaction date.
23+
Date time.Time `json:"date" example:"1815-12-10T18:43:00.271152Z"`
24+
Amount decimal.Decimal `json:"amount" gorm:"type:DECIMAL(20,8)" example:"14.03" minimum:"0.00000001" maximum:"999999999999.99999999" multipleOf:"0.00000001"` // The maximum value is "999999999999.99999999", swagger unfortunately rounds this.
25+
Note string `json:"note" example:"Lunch" default:""`
26+
BudgetID uuid.UUID `json:"budgetId" example:"55eecbd8-7c46-4b06-ada9-f287802fb05e"`
27+
SourceAccountID uuid.UUID `json:"sourceAccountId" gorm:"check:source_destination_different,source_account_id != destination_account_id" example:"fd81dc45-a3a2-468e-a6fa-b2618f30aa45"`
28+
DestinationAccountID uuid.UUID `json:"destinationAccountId" example:"8e16b456-a719-48ce-9fec-e115cfa7cbcc"`
29+
EnvelopeID *uuid.UUID `json:"envelopeId" example:"2649c965-7999-4873-ae16-89d5d5fa972e"`
30+
Reconciled bool `json:"reconciled" example:"true" default:"false"` // DEPRECATED. Do not use, this field does not work as intended. See https://github.com/envelope-zero/backend/issues/528. Use reconciledSource and reconciledDestination instead.
31+
ReconciledSource bool `json:"reconciledSource" example:"true" default:"false"`
32+
ReconciledDestination bool `json:"reconciledDestination" example:"true" default:"false"`
33+
34+
AvailableFrom types.Month `json:"availableFrom" example:"2021-11-17:00:00:00Z"` // The date from which on the transaction amount is available for budgeting. Only used for income transactions. Defaults to the transaction date.
3235
}
3336

3437
// AfterFind updates the timestamps to use UTC as

0 commit comments

Comments
 (0)