Skip to content

Commit 0bf4246

Browse files
authored
feat: add more fields to month endpoint (#500)
This adds more fields to the /v1/month endpoint: * `data` now has an additional `allocation` field that contains the sum of all allocations for the month * All categories (in `data.categories`) now have the following additional fields: * `balance`: Sum of all allocations for envelopes in that category * `allocation`: Sum of all allocations for that category * `spent`: Sum of spend for all envelopes This deprecates the `data.budgeted` field on the /v1/month endpoint in favor of the `data.allocation` field.
1 parent 20c3dc3 commit 0bf4246

File tree

6 files changed

+134
-32
lines changed

6 files changed

+134
-32
lines changed

api/docs.go

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3471,19 +3471,37 @@ const docTemplate = `{
34713471
"models.CategoryEnvelopes": {
34723472
"type": "object",
34733473
"properties": {
3474+
"allocation": {
3475+
"description": "Sum of allocations for the envelopes",
3476+
"type": "number",
3477+
"example": 90
3478+
},
3479+
"balance": {
3480+
"description": "Sum of the balances of the envelopes",
3481+
"type": "number",
3482+
"example": -10.13
3483+
},
34743484
"envelopes": {
3485+
"description": "Slice of all envelopes",
34753486
"type": "array",
34763487
"items": {
34773488
"$ref": "#/definitions/models.EnvelopeMonth"
34783489
}
34793490
},
34803491
"id": {
3492+
"description": "ID of the category",
34813493
"type": "string",
34823494
"example": "dafd9a74-6aeb-46b9-9f5a-cfca624fea85"
34833495
},
34843496
"name": {
3497+
"description": "Name of the category",
34853498
"type": "string",
34863499
"example": "Rainy Day Funds"
3500+
},
3501+
"spent": {
3502+
"description": "Sum spent for all envelopes",
3503+
"type": "number",
3504+
"example": 100.13
34873505
}
34883506
}
34893507
},
@@ -3557,6 +3575,11 @@ const docTemplate = `{
35573575
"models.Month": {
35583576
"type": "object",
35593577
"properties": {
3578+
"allocation": {
3579+
"description": "The sum of all allocations for this month",
3580+
"type": "number",
3581+
"example": 1200.5
3582+
},
35603583
"available": {
35613584
"description": "The amount available to budget",
35623585
"type": "number",
@@ -3568,7 +3591,7 @@ const docTemplate = `{
35683591
"example": 5231.37
35693592
},
35703593
"budgeted": {
3571-
"description": "The sum of all allocations for the month",
3594+
"description": "The sum of all allocations for the month. **Deprecated, please use the ` + "`" + `allocation` + "`" + ` field**",
35723595
"type": "number",
35733596
"example": 2100
35743597
},

api/swagger.json

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3459,19 +3459,37 @@
34593459
"models.CategoryEnvelopes": {
34603460
"type": "object",
34613461
"properties": {
3462+
"allocation": {
3463+
"description": "Sum of allocations for the envelopes",
3464+
"type": "number",
3465+
"example": 90
3466+
},
3467+
"balance": {
3468+
"description": "Sum of the balances of the envelopes",
3469+
"type": "number",
3470+
"example": -10.13
3471+
},
34623472
"envelopes": {
3473+
"description": "Slice of all envelopes",
34633474
"type": "array",
34643475
"items": {
34653476
"$ref": "#/definitions/models.EnvelopeMonth"
34663477
}
34673478
},
34683479
"id": {
3480+
"description": "ID of the category",
34693481
"type": "string",
34703482
"example": "dafd9a74-6aeb-46b9-9f5a-cfca624fea85"
34713483
},
34723484
"name": {
3485+
"description": "Name of the category",
34733486
"type": "string",
34743487
"example": "Rainy Day Funds"
3488+
},
3489+
"spent": {
3490+
"description": "Sum spent for all envelopes",
3491+
"type": "number",
3492+
"example": 100.13
34753493
}
34763494
}
34773495
},
@@ -3545,6 +3563,11 @@
35453563
"models.Month": {
35463564
"type": "object",
35473565
"properties": {
3566+
"allocation": {
3567+
"description": "The sum of all allocations for this month",
3568+
"type": "number",
3569+
"example": 1200.5
3570+
},
35483571
"available": {
35493572
"description": "The amount available to budget",
35503573
"type": "number",
@@ -3556,7 +3579,7 @@
35563579
"example": 5231.37
35573580
},
35583581
"budgeted": {
3559-
"description": "The sum of all allocations for the month",
3582+
"description": "The sum of all allocations for the month. **Deprecated, please use the `allocation` field**",
35603583
"type": "number",
35613584
"example": 2100
35623585
},

api/swagger.yaml

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -564,16 +564,31 @@ definitions:
564564
type: object
565565
models.CategoryEnvelopes:
566566
properties:
567+
allocation:
568+
description: Sum of allocations for the envelopes
569+
example: 90
570+
type: number
571+
balance:
572+
description: Sum of the balances of the envelopes
573+
example: -10.13
574+
type: number
567575
envelopes:
576+
description: Slice of all envelopes
568577
items:
569578
$ref: '#/definitions/models.EnvelopeMonth'
570579
type: array
571580
id:
581+
description: ID of the category
572582
example: dafd9a74-6aeb-46b9-9f5a-cfca624fea85
573583
type: string
574584
name:
585+
description: Name of the category
575586
example: Rainy Day Funds
576587
type: string
588+
spent:
589+
description: Sum spent for all envelopes
590+
example: 100.13
591+
type: number
577592
type: object
578593
models.EnvelopeCreate:
579594
properties:
@@ -627,6 +642,10 @@ definitions:
627642
type: object
628643
models.Month:
629644
properties:
645+
allocation:
646+
description: The sum of all allocations for this month
647+
example: 1200.5
648+
type: number
630649
available:
631650
description: The amount available to budget
632651
example: 217.34
@@ -636,7 +655,8 @@ definitions:
636655
example: 5231.37
637656
type: number
638657
budgeted:
639-
description: The sum of all allocations for the month
658+
description: The sum of all allocations for the month. **Deprecated, please
659+
use the `allocation` field**
640660
example: 2100
641661
type: number
642662
categories:

pkg/controllers/month.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ func (co Controller) GetMonth(c *gin.Context) {
109109
return
110110
}
111111
month.Budgeted = budgeted
112+
month.Allocation = budgeted
112113

113114
// Add income to response
114115
income, err := budget.Income(co.DB, month.Month)
@@ -161,10 +162,15 @@ func (co Controller) GetMonth(c *gin.Context) {
161162
return
162163
}
163164

164-
// Update the month's balance
165+
// Update the month's summarized data
165166
month.Balance = month.Balance.Add(envelopeMonth.Balance)
166167
month.Spent = month.Spent.Add(envelopeMonth.Spent)
167168

169+
// Update the category's summarized data
170+
categoryEnvelopes.Balance = categoryEnvelopes.Balance.Add(envelopeMonth.Balance)
171+
categoryEnvelopes.Spent = categoryEnvelopes.Spent.Add(envelopeMonth.Spent)
172+
categoryEnvelopes.Allocation = categoryEnvelopes.Allocation.Add(envelopeMonth.Allocation)
173+
168174
// Set the allocation link. If there is no allocation, we send the collection endpoint.
169175
// With this, any client will be able to see that the "Budgeted" amount is 0 and therefore
170176
// send a HTTP POST for creation instead of a patch.

pkg/controllers/month_test.go

Lines changed: 50 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -95,14 +95,18 @@ func (suite *TestSuiteStandard) TestMonth() {
9595
strings.Replace(budget.Data.Links.GroupedMonth, "YYYY-MM", "2022-01", -1),
9696
controllers.MonthResponse{
9797
Data: models.Month{
98-
Month: time.Date(2022, 1, 1, 0, 0, 0, 0, time.UTC),
99-
Income: decimal.NewFromFloat(0),
100-
Balance: decimal.NewFromFloat(10.99),
101-
Spent: decimal.NewFromFloat(10),
98+
Month: time.Date(2022, 1, 1, 0, 0, 0, 0, time.UTC),
99+
Income: decimal.NewFromFloat(0),
100+
Balance: decimal.NewFromFloat(10.99),
101+
Spent: decimal.NewFromFloat(10),
102+
Allocation: decimal.NewFromFloat(20.99),
102103
Categories: []models.CategoryEnvelopes{
103104
{
104-
Name: category.Data.Name,
105-
ID: category.Data.ID,
105+
Name: category.Data.Name,
106+
ID: category.Data.ID,
107+
Balance: decimal.NewFromFloat(10.99),
108+
Spent: decimal.NewFromFloat(10),
109+
Allocation: decimal.NewFromFloat(20.99),
106110
Envelopes: []models.EnvelopeMonth{
107111
{
108112
Name: "Utilities",
@@ -124,14 +128,18 @@ func (suite *TestSuiteStandard) TestMonth() {
124128
strings.Replace(budget.Data.Links.GroupedMonth, "YYYY-MM", "2022-02", -1),
125129
controllers.MonthResponse{
126130
Data: models.Month{
127-
Month: time.Date(2022, 2, 1, 0, 0, 0, 0, time.UTC),
128-
Income: decimal.NewFromFloat(0),
129-
Balance: decimal.NewFromFloat(53.11),
130-
Spent: decimal.NewFromFloat(5),
131+
Month: time.Date(2022, 2, 1, 0, 0, 0, 0, time.UTC),
132+
Income: decimal.NewFromFloat(0),
133+
Balance: decimal.NewFromFloat(53.11),
134+
Spent: decimal.NewFromFloat(5),
135+
Allocation: decimal.NewFromFloat(47.12),
131136
Categories: []models.CategoryEnvelopes{
132137
{
133-
Name: category.Data.Name,
134-
ID: category.Data.ID,
138+
Name: category.Data.Name,
139+
ID: category.Data.ID,
140+
Balance: decimal.NewFromFloat(53.11),
141+
Spent: decimal.NewFromFloat(5),
142+
Allocation: decimal.NewFromFloat(47.12),
135143
Envelopes: []models.EnvelopeMonth{
136144
{
137145
Name: "Utilities",
@@ -153,14 +161,18 @@ func (suite *TestSuiteStandard) TestMonth() {
153161
strings.Replace(budget.Data.Links.GroupedMonth, "YYYY-MM", "2022-03", -1),
154162
controllers.MonthResponse{
155163
Data: models.Month{
156-
Month: time.Date(2022, 3, 1, 0, 0, 0, 0, time.UTC),
157-
Income: decimal.NewFromFloat(1500),
158-
Balance: decimal.NewFromFloat(69.28),
159-
Spent: decimal.NewFromFloat(15),
164+
Month: time.Date(2022, 3, 1, 0, 0, 0, 0, time.UTC),
165+
Income: decimal.NewFromFloat(1500),
166+
Balance: decimal.NewFromFloat(69.28),
167+
Spent: decimal.NewFromFloat(15),
168+
Allocation: decimal.NewFromFloat(31.17),
160169
Categories: []models.CategoryEnvelopes{
161170
{
162-
Name: category.Data.Name,
163-
ID: category.Data.ID,
171+
Name: category.Data.Name,
172+
ID: category.Data.ID,
173+
Balance: decimal.NewFromFloat(69.28),
174+
Spent: decimal.NewFromFloat(15),
175+
Allocation: decimal.NewFromFloat(31.17),
164176
Envelopes: []models.EnvelopeMonth{
165177
{
166178
Name: "Utilities",
@@ -192,6 +204,12 @@ func (suite *TestSuiteStandard) TestMonth() {
192204
// Verify month balance calculation
193205
suite.Assert().True(month.Data.Balance.Equal(tt.response.Data.Balance), "Month balance calculation for %v is wrong: should be %v, but is %v: %#v", month.Data.Month, tt.response.Data.Balance, month.Data.Balance, month.Data)
194206

207+
// Verify allocation calculation
208+
suite.Assert().True(month.Data.Allocation.Equal(tt.response.Data.Allocation), "Month allocation sum for %v is wrong: should be %v, but is %v: %#v", month.Data.Month, tt.response.Data.Allocation, month.Data.Allocation, month.Data)
209+
210+
// Verify month spent calculation
211+
suite.Assert().True(month.Data.Spent.Equal(tt.response.Data.Spent), "Month spent is wrong. Should be %v, but is %v: %#v", tt.response.Data.Spent, month.Data.Spent, month.Data)
212+
195213
if !suite.Assert().Len(month.Data.Categories, 1) {
196214
suite.Assert().FailNow("Response category length does not match!", "Category list does not have exactly 1 item, it has %d, Request ID: %s", len(month.Data.Categories), r.Header().Get("x-request-id"))
197215
}
@@ -200,15 +218,23 @@ func (suite *TestSuiteStandard) TestMonth() {
200218
suite.Assert().FailNow("Response envelope length does not match!", "Envelope list does not have exactly 1 item, it has %d, Request ID: %s", len(month.Data.Categories[0].Envelopes), r.Header().Get("x-request-id"))
201219
}
202220

203-
suite.Assert().True(month.Data.Spent.Equal(tt.response.Data.Spent), "Month spent is wrong. Should be %v, but is %v: %#v", tt.response.Data.Spent, month.Data.Spent, month.Data)
221+
// Category calculations
222+
expectedCategory := tt.response.Data.Categories[0]
223+
category := month.Data.Categories[0]
224+
225+
suite.Assert().True(category.Spent.Equal(expectedCategory.Spent), "Monthly category spent calculation for %v is wrong: should be %v, but is %v: %#v", month.Data.Month, expectedCategory.Spent, category.Spent, month.Data)
226+
suite.Assert().True(category.Balance.Equal(expectedCategory.Balance), "Monthly category balance calculation for %v is wrong: should be %v, but is %v: %#v", month.Data.Month, expectedCategory.Balance, category.Balance, month.Data)
227+
suite.Assert().True(category.Allocation.Equal(expectedCategory.Allocation), "Monthly category allocation fetch for %v is wrong: should be %v, but is %v: %#v", month.Data.Month, expectedCategory.Allocation, category.Allocation, month.Data)
204228

205-
expected := tt.response.Data.Categories[0].Envelopes[0]
229+
// Envelope calculation
230+
expectedEnvelope := tt.response.Data.Categories[0].Envelopes[0]
206231
envelope := month.Data.Categories[0].Envelopes[0]
207-
suite.Assert().True(envelope.Spent.Equal(expected.Spent), "Monthly spent calculation for %v is wrong: should be %v, but is %v: %#v", month.Data.Month, expected.Spent, envelope.Spent, month.Data)
208-
suite.Assert().True(envelope.Balance.Equal(expected.Balance), "Monthly balance calculation for %v is wrong: should be %v, but is %v: %#v", month.Data.Month, expected.Balance, envelope.Balance, month.Data)
209-
suite.Assert().True(envelope.Allocation.Equal(expected.Allocation), "Monthly allocation fetch for %v is wrong: should be %v, but is %v: %#v", month.Data.Month, expected.Allocation, envelope.Allocation, month.Data)
210232

211-
suite.Assert().Equal(expected.Links.Allocation, envelope.Links.Allocation)
233+
suite.Assert().True(envelope.Spent.Equal(expectedEnvelope.Spent), "Monthly envelope spent calculation for %v is wrong: should be %v, but is %v: %#v", month.Data.Month, expectedEnvelope.Spent, envelope.Spent, month.Data)
234+
suite.Assert().True(envelope.Balance.Equal(expectedEnvelope.Balance), "Monthly envelope balance calculation for %v is wrong: should be %v, but is %v: %#v", month.Data.Month, expectedEnvelope.Balance, envelope.Balance, month.Data)
235+
suite.Assert().True(envelope.Allocation.Equal(expectedEnvelope.Allocation), "Monthly envelope allocation fetch for %v is wrong: should be %v, but is %v: %#v", month.Data.Month, expectedEnvelope.Allocation, envelope.Allocation, month.Data)
236+
237+
suite.Assert().Equal(expectedEnvelope.Links.Allocation, envelope.Links.Allocation)
212238
}
213239
}
214240

pkg/models/month.go

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,19 +8,23 @@ import (
88
)
99

1010
type CategoryEnvelopes struct {
11-
ID uuid.UUID `json:"id" example:"dafd9a74-6aeb-46b9-9f5a-cfca624fea85"`
12-
Name string `json:"name" example:"Rainy Day Funds" default:""`
13-
Envelopes []EnvelopeMonth `json:"envelopes"`
11+
ID uuid.UUID `json:"id" example:"dafd9a74-6aeb-46b9-9f5a-cfca624fea85"` // ID of the category
12+
Name string `json:"name" example:"Rainy Day Funds" default:""` // Name of the category
13+
Envelopes []EnvelopeMonth `json:"envelopes"` // Slice of all envelopes
14+
Balance decimal.Decimal `json:"balance" example:"-10.13"` // Sum of the balances of the envelopes
15+
Allocation decimal.Decimal `json:"allocation" example:"90"` // Sum of allocations for the envelopes
16+
Spent decimal.Decimal `json:"spent" example:"100.13"` // Sum spent for all envelopes
1417
}
1518

1619
type Month struct {
1720
ID uuid.UUID `json:"id" example:"1e777d24-3f5b-4c43-8000-04f65f895578"` // The ID of the Budget
1821
Name string `json:"name" example:"Zero budget"` // The name of the Budget
1922
Month time.Time `json:"month" example:"2006-05-01T00:00:00.000000Z"` // This is always set to 00:00 UTC on the first of the month.
20-
Budgeted decimal.Decimal `json:"budgeted" example:"2100"` // The sum of all allocations for the month
23+
Budgeted decimal.Decimal `json:"budgeted" example:"2100"` // The sum of all allocations for the month. **Deprecated, please use the `allocation` field**
2124
Income decimal.Decimal `json:"income" example:"2317.34"` // The total income for the month (sum of all incoming transactions without an Envelope)
2225
Available decimal.Decimal `json:"available" example:"217.34"` // The amount available to budget
2326
Balance decimal.Decimal `json:"balance" example:"5231.37"` // The sum of all envelope balances
2427
Spent decimal.Decimal `json:"spent" example:"133.70"` // The amount of money spent in this month
28+
Allocation decimal.Decimal `json:"allocation" example:"1200.50"` // The sum of all allocations for this month
2529
Categories []CategoryEnvelopes `json:"categories"` // A list of envelope month calculations grouped by category
2630
}

0 commit comments

Comments
 (0)