Skip to content

Commit 50de3d6

Browse files
committed
FIN-349 fix issue in the importing of budget information.
1 parent f1eafe5 commit 50de3d6

File tree

14 files changed

+358
-17
lines changed

14 files changed

+358
-17
lines changed

bpmn-process/src/main/java/com/jongsoft/finance/bpmn/delegate/budget/ProcessBudgetCreateDelegate.java

Lines changed: 45 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,14 @@
11
package com.jongsoft.finance.bpmn.delegate.budget;
22

33
import com.jongsoft.finance.ProcessMapper;
4+
import com.jongsoft.finance.domain.user.Budget;
5+
import com.jongsoft.finance.messaging.EventBus;
6+
import com.jongsoft.finance.messaging.commands.budget.CloseBudgetCommand;
7+
import com.jongsoft.finance.messaging.commands.budget.CreateBudgetCommand;
48
import com.jongsoft.finance.providers.BudgetProvider;
59
import com.jongsoft.finance.security.CurrentUserProvider;
610
import com.jongsoft.finance.serialized.BudgetJson;
11+
import com.jongsoft.lang.Control;
712
import jakarta.inject.Singleton;
813
import lombok.extern.slf4j.Slf4j;
914
import org.camunda.bpm.engine.delegate.DelegateExecution;
@@ -14,7 +19,7 @@
1419
* This delegate will create process a serialize {@link BudgetJson} into a new budget period in the system. It will
1520
* either create the first budget or close the existing and create a new one.
1621
* <p>
17-
* This delegate expects the following variables to be present:
22+
* This delegate expects the following variables to be present:
1823
* </p>
1924
* <ul>
2025
* <li>budget, a JSON serialized {@link BudgetJson} to be created in the system</li>
@@ -39,7 +44,6 @@ public class ProcessBudgetCreateDelegate implements JavaDelegate {
3944

4045
@Override
4146
public void execute(DelegateExecution execution) {
42-
var userAccount = currentUserProvider.currentUser();
4347
var budgetJson = mapper.readSafe(
4448
execution.<StringValue>getVariableLocalTyped("budget").getValue(),
4549
BudgetJson.class);
@@ -48,16 +52,50 @@ public void execute(DelegateExecution execution) {
4852
execution.getCurrentActivityName(),
4953
budgetJson.getStart());
5054

55+
var start = budgetJson.getStart().withDayOfMonth(1);
5156
var year = budgetJson.getStart().getYear();
5257
var month = budgetJson.getStart().getMonthValue();
5358

59+
// create or update the budget period
60+
var oldBudget = budgetProvider.lookup(year, month);
61+
if (oldBudget.isPresent()) {
62+
log.debug("{}: Budget period already exists for period '{}' with start '{}'",
63+
execution.getCurrentActivityName(),
64+
start,
65+
oldBudget.get().getStart());
66+
// close the existing budget
67+
EventBus.getBus()
68+
.send(new CloseBudgetCommand(oldBudget.get().getId(), start));
69+
// create new budget
70+
EventBus.getBus()
71+
.send(new CreateBudgetCommand(Budget.builder()
72+
.start(start)
73+
.expectedIncome(budgetJson.getExpectedIncome())
74+
.expenses(oldBudget.get().getExpenses())
75+
.build()));
76+
} else {
77+
log.debug("{}: Creating new budget period for period '{}'",
78+
execution.getCurrentActivityName(),
79+
start);
80+
EventBus.getBus().send(new CreateBudgetCommand(
81+
Budget.builder()
82+
.start(start)
83+
.expectedIncome(budgetJson.getExpectedIncome())
84+
.build()));
85+
}
86+
87+
log.trace("{}: Budget period updated for period '{}'",
88+
execution.getCurrentActivityName(),
89+
budgetJson.getStart());
90+
5491
var budget = budgetProvider.lookup(year, month)
55-
.map(b -> b.indexBudget(budgetJson.getStart(), budgetJson.getExpectedIncome()))
56-
.getOrSupply(() -> userAccount.createBudget(budgetJson.getStart(), budgetJson.getExpectedIncome()));
92+
.getOrThrow(() -> new IllegalStateException("Budget period not found for period " + start));
5793

58-
budgetJson.getExpenses().stream()
59-
.filter(e -> budget.determineExpense(e.getName()) == null)
60-
.forEach(e -> budget.createExpense(e.getName(), e.getLowerBound(), e.getUpperBound()));
94+
budgetJson.getExpenses()
95+
// update or create the expenses
96+
.forEach(e -> Control.Option(budget.determineExpense(e.getName()))
97+
.ifPresent(currentExpense -> currentExpense.updateExpense(e.getUpperBound()))
98+
.elseRun(() -> budget.createExpense(e.getName(), e.getLowerBound(), e.getUpperBound())));
6199
}
62100

63101
}

bpmn-process/src/main/java/com/jongsoft/finance/bpmn/delegate/user/ParseUserConfigurationDelegate.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import com.jongsoft.finance.ProcessMapper;
44
import com.jongsoft.finance.StorageService;
5+
import com.jongsoft.finance.serialized.BudgetJson;
56
import com.jongsoft.finance.serialized.ExportJson;
67
import com.jongsoft.finance.serialized.RuleConfigJson;
78
import com.jongsoft.lang.Control;
@@ -11,6 +12,7 @@
1112
import org.camunda.bpm.engine.delegate.JavaDelegate;
1213

1314
import java.nio.charset.StandardCharsets;
15+
import java.util.Comparator;
1416
import java.util.List;
1517
import java.util.Objects;
1618
import java.util.stream.Collectors;
@@ -51,8 +53,12 @@ public void execute(DelegateExecution execution) throws Exception {
5153
execution.setVariableLocal("ruleStorageToken", null);
5254
}
5355

56+
var sortedBudgets = profileJson.getBudgetPeriods().stream()
57+
.sorted(Comparator.comparing(BudgetJson::getStart))
58+
.toList();
59+
5460
execution.setVariableLocal("accounts", serialize(profileJson.getAccounts()));
55-
execution.setVariableLocal("budgetPeriods", serialize(profileJson.getBudgetPeriods()));
61+
execution.setVariableLocal("budgetPeriods", serialize(sortedBudgets));
5662
execution.setVariableLocal("contracts", serialize(profileJson.getContracts()));
5763
execution.setVariableLocal("categories", serialize(profileJson.getCategories()));
5864
execution.setVariableLocal("tags", Control.Option(profileJson.getTags()).getOrSupply(List::of));

bpmn-process/src/test/java/com/jongsoft/finance/bpmn/ProfileImportIT.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,4 +132,15 @@ void runWithContracts(RuntimeContext context) {
132132
LocalDate.of(2018, 12, 31));
133133
}
134134

135+
// @Test
136+
// @DisplayName("Import a profile with budgets")
137+
// void runWithBudgets(RuntimeContext context) {
138+
// context
139+
// .withStorage()
140+
// .withStorage("my-sample-token", "/profile-test/budget-only.json");
141+
//
142+
// context.execute("ImportUserProfile", Map.of(
143+
// "storageToken", "my-sample-token"))
144+
// .verifyCompleted();
145+
// }
135146
}
Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
{
2+
"budgetPeriods": [
3+
{
4+
"start": "2011-12-31",
5+
"end": "2012-12-31",
6+
"expectedIncome": 2500.0,
7+
"expenses": [
8+
{
9+
"name": "Sparen",
10+
"lowerBound": 50.0,
11+
"upperBound": 150.0
12+
},
13+
{
14+
"name": "Vervoer",
15+
"lowerBound": 300.0,
16+
"upperBound": 500.0
17+
},
18+
{
19+
"name": "Abonnementen",
20+
"lowerBound": 100.0,
21+
"upperBound": 200.0
22+
},
23+
{
24+
"name": "Huishoudelijke kosten",
25+
"lowerBound": 700.0,
26+
"upperBound": 800.0
27+
},
28+
{
29+
"name": "Boodschappen",
30+
"lowerBound": 250.0,
31+
"upperBound": 350.0
32+
}
33+
]
34+
},
35+
{
36+
"start": "2012-12-31",
37+
"end": "2016-05-31",
38+
"expectedIncome": 3000.0,
39+
"expenses": [
40+
{
41+
"name": "Vervoer",
42+
"lowerBound": 360.0,
43+
"upperBound": 600.0
44+
},
45+
{
46+
"name": "Boodschappen",
47+
"lowerBound": 300.0,
48+
"upperBound": 420.0
49+
},
50+
{
51+
"name": "Abonnementen",
52+
"lowerBound": 120.0,
53+
"upperBound": 240.0
54+
},
55+
{
56+
"name": "Sparen",
57+
"lowerBound": 60.0,
58+
"upperBound": 180.0
59+
},
60+
{
61+
"name": "Huishoudelijke kosten",
62+
"lowerBound": 840.0,
63+
"upperBound": 1440.0
64+
}
65+
]
66+
},
67+
{
68+
"start": "2016-05-31",
69+
"end": "2018-07-31",
70+
"expectedIncome": 3100.0,
71+
"expenses": [
72+
{
73+
"name": "Huishoudelijke kosten",
74+
"lowerBound": 868.0,
75+
"upperBound": 1488.0
76+
},
77+
{
78+
"name": "Vervoer",
79+
"lowerBound": 372.0,
80+
"upperBound": 620.0
81+
},
82+
{
83+
"name": "Abonnementen",
84+
"lowerBound": 124.0,
85+
"upperBound": 248.0
86+
},
87+
{
88+
"name": "Boodschappen",
89+
"lowerBound": 310.0,
90+
"upperBound": 434.0
91+
},
92+
{
93+
"name": "Sparen",
94+
"lowerBound": 62.0,
95+
"upperBound": 186.0
96+
}
97+
]
98+
},
99+
{
100+
"start": "2018-07-31",
101+
"expectedIncome": 3600.0,
102+
"expenses": [
103+
{
104+
"name": "Vervoer",
105+
"lowerBound": 432.0,
106+
"upperBound": 720.0
107+
},
108+
{
109+
"name": "Gezondheid & persoonlijke verzorging",
110+
"lowerBound": 99.99,
111+
"upperBound": 100.0
112+
},
113+
{
114+
"name": "Restaurants & Bars",
115+
"lowerBound": 199.99,
116+
"upperBound": 200.0
117+
},
118+
{
119+
"name": "Sparen",
120+
"lowerBound": 72.0,
121+
"upperBound": 216.0
122+
},
123+
{
124+
"name": "Abonnementen",
125+
"lowerBound": 144.0,
126+
"upperBound": 288.0
127+
},
128+
{
129+
"name": "Winkelen",
130+
"lowerBound": 399.99,
131+
"upperBound": 400.0
132+
},
133+
{
134+
"name": "Vrije tijd",
135+
"lowerBound": 149.99,
136+
"upperBound": 150.0
137+
},
138+
{
139+
"name": "Boodschappen",
140+
"lowerBound": 360.0,
141+
"upperBound": 504.0
142+
},
143+
{
144+
"name": "Huishoudelijke kosten",
145+
"lowerBound": 1008.0,
146+
"upperBound": 1200.0
147+
}
148+
]
149+
}
150+
]
151+
}

domain/src/main/java/com/jongsoft/finance/domain/user/Budget.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import com.jongsoft.finance.messaging.commands.budget.CloseBudgetCommand;
88
import com.jongsoft.finance.messaging.commands.budget.CreateBudgetCommand;
99
import com.jongsoft.finance.messaging.commands.budget.CreateExpenseCommand;
10+
import com.jongsoft.finance.messaging.commands.budget.UpdateExpenseCommand;
1011
import com.jongsoft.lang.Collections;
1112
import com.jongsoft.lang.collection.Sequence;
1213
import lombok.AllArgsConstructor;
@@ -59,6 +60,17 @@ Expense indexExpense(BigDecimal deviation) {
5960
.build();
6061
}
6162

63+
@BusinessMethod
64+
public void updateExpense(double expectedExpense) {
65+
lowerBound = expectedExpense - .01;
66+
upperBound = expectedExpense;
67+
68+
EventBus.getBus()
69+
.send(new UpdateExpenseCommand(
70+
id,
71+
BigDecimal.valueOf(expectedExpense)));
72+
}
73+
6274
public double computeBudget() {
6375
return BigDecimal.valueOf(lowerBound)
6476
.add(BigDecimal.valueOf(upperBound))
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package com.jongsoft.finance.messaging.commands.budget;
2+
3+
import com.jongsoft.finance.core.ApplicationEvent;
4+
5+
import java.math.BigDecimal;
6+
7+
public record UpdateExpenseCommand(long id, BigDecimal amount)
8+
implements ApplicationEvent {
9+
}

jpa-repository/src/main/java/com/jongsoft/finance/jpa/budget/BudgetProviderJpa.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ public Optional<Budget> lookup(int year, int month) {
4747
select b from BudgetJpa b
4848
where b.user.username = :username
4949
and b.from <= :start
50-
and (b.until is null or b.until >= :end)""";
50+
and (b.until is null or b.until > :end)""";
5151

5252
return reactiveEntityManager.<BudgetJpa>blocking()
5353
.hql(hql)

jpa-repository/src/main/java/com/jongsoft/finance/jpa/budget/CreateBudgetHandler.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ public void handle(CreateBudgetCommand command) {
5353
}
5454

5555
private Collection<ExpensePeriodJpa> createExpenses(BudgetJpa budget, Sequence<Budget.Expense> expenses) {
56+
log.debug("Creating {} expenses for budget period {}", expenses.size(), budget.getFrom());
5657
return expenses.map(expense ->
5758
ExpensePeriodJpa.builder()
5859
.budget(budget)

jpa-repository/src/main/java/com/jongsoft/finance/jpa/budget/ExpenseJpa.java

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,11 @@
11
package com.jongsoft.finance.jpa.budget;
22

3+
import com.jongsoft.finance.jpa.core.entity.EntityJpa;
4+
import com.jongsoft.finance.jpa.user.entity.UserAccountJpa;
35
import jakarta.persistence.Entity;
46
import jakarta.persistence.JoinColumn;
57
import jakarta.persistence.ManyToOne;
68
import jakarta.persistence.Table;
7-
8-
import com.jongsoft.finance.jpa.core.entity.EntityJpa;
9-
10-
import com.jongsoft.finance.jpa.user.entity.UserAccountJpa;
119
import lombok.Builder;
1210
import lombok.Getter;
1311

jpa-repository/src/main/java/com/jongsoft/finance/jpa/budget/ExpensePeriodJpa.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,12 @@
1010

1111
import lombok.Builder;
1212
import lombok.Getter;
13+
import lombok.Setter;
1314

1415
import java.math.BigDecimal;
1516

1617
@Getter
18+
@Setter
1719
@Entity
1820
@Table(name = "budget_period")
1921
public class ExpensePeriodJpa extends EntityJpa {

0 commit comments

Comments
 (0)