Skip to content

Commit 84eeba7

Browse files
authored
Spending pattern analysis (#124)
1 parent 4176b9d commit 84eeba7

File tree

297 files changed

+12180
-2386
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

297 files changed

+12180
-2386
lines changed

bpmn-process/src/main/java/com/jongsoft/finance/ProcessMapper.java

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -18,21 +18,19 @@ public ProcessMapper(ObjectMapper objectMapper) {
1818

1919
public <T> String writeSafe(T entity) {
2020
return Control.Try(() -> objectMapper.writeValueAsString(entity))
21-
.recover(
22-
x -> {
23-
log.warn("Could not serialize entity {}", entity, x);
24-
return null;
25-
})
21+
.recover(x -> {
22+
log.warn("Could not serialize entity {}", entity, x);
23+
return null;
24+
})
2625
.get();
2726
}
2827

2928
public <T> T readSafe(String json, Class<T> clazz) {
3029
return Control.Try(() -> objectMapper.readValue(json, clazz))
31-
.recover(
32-
x -> {
33-
log.warn("Could not deserialize json {}", json, x);
34-
return null;
35-
})
30+
.recover(x -> {
31+
log.warn("Could not deserialize json {}", json, x);
32+
return null;
33+
})
3634
.get();
3735
}
3836

bpmn-process/src/main/java/com/jongsoft/finance/bpmn/ProcessEngineConfiguration.java

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -60,12 +60,11 @@ public ProcessEngine processEngine() throws IOException {
6060
configuration.setHistoryCleanupBatchWindowEndTime("03:00");
6161
configuration.setHistoryTimeToLive("P1D");
6262
configuration.setResolverFactories(List.of(new MicronautBeanResolver(applicationContext)));
63-
configuration.setCustomPreVariableSerializers(
64-
List.of(
65-
new JsonRecordSerializer<>(
66-
applicationContext.getBean(ObjectMapper.class), ProcessVariable.class),
67-
new JsonRecordSerializer<>(
68-
applicationContext.getBean(ObjectMapper.class), TransactionDTO.class)));
63+
configuration.setCustomPreVariableSerializers(List.of(
64+
new JsonRecordSerializer<>(
65+
applicationContext.getBean(ObjectMapper.class), ProcessVariable.class),
66+
new JsonRecordSerializer<>(
67+
applicationContext.getBean(ObjectMapper.class), TransactionDTO.class)));
6968

7069
var processEngine = configuration.buildProcessEngine();
7170
log.info("Created camunda process engine");

bpmn-process/src/main/java/com/jongsoft/finance/bpmn/camunda/MicronautBeanResolver.java

Lines changed: 15 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -56,30 +56,26 @@ public Resolver createResolver(VariableScope variableScope) {
5656

5757
protected synchronized Set<String> getKeySet() {
5858
if (keySet == null) {
59-
keySet =
60-
applicationContext.getAllBeanDefinitions().stream()
61-
.filter(
62-
beanDefinition ->
63-
!beanDefinition.getClass().getName().startsWith("io.micronaut."))
64-
.map(this::getBeanName)
65-
.collect(Collectors.toSet());
59+
keySet = applicationContext.getAllBeanDefinitions().stream()
60+
.filter(
61+
beanDefinition -> !beanDefinition.getClass().getName().startsWith("io.micronaut."))
62+
.map(this::getBeanName)
63+
.collect(Collectors.toSet());
6664
}
6765
return keySet;
6866
}
6967

7068
protected String getBeanName(BeanDefinition<?> beanDefinition) {
71-
var beanQualifier =
72-
beanDefinition
73-
.getAnnotationMetadata()
74-
.findDeclaredAnnotation(AnnotationUtil.NAMED)
75-
.flatMap(AnnotationValue::stringValue);
76-
return beanQualifier.orElseGet(
77-
() -> {
78-
if (beanDefinition instanceof NameResolver resolver) {
79-
return resolver.resolveName().orElse(getBeanNameFromType(beanDefinition));
80-
}
81-
return getBeanNameFromType(beanDefinition);
82-
});
69+
var beanQualifier = beanDefinition
70+
.getAnnotationMetadata()
71+
.findDeclaredAnnotation(AnnotationUtil.NAMED)
72+
.flatMap(AnnotationValue::stringValue);
73+
return beanQualifier.orElseGet(() -> {
74+
if (beanDefinition instanceof NameResolver resolver) {
75+
return resolver.resolveName().orElse(getBeanNameFromType(beanDefinition));
76+
}
77+
return getBeanNameFromType(beanDefinition);
78+
});
8379
}
8480

8581
protected String getBeanNameFromType(BeanDefinition<?> beanDefinition) {

bpmn-process/src/main/java/com/jongsoft/finance/bpmn/camunda/MicronautElResolver.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -51,10 +51,10 @@ public boolean isReadOnly(ELContext context, Object base, Object property) {
5151
public void setValue(ELContext context, Object base, Object property, Object value) {
5252
if (base == null
5353
&& !applicationContext.containsBean(TYPE, Qualifiers.byName(property.toString()))) {
54-
throw new ProcessEngineException(
55-
"Cannot set value of '"
56-
+ property
57-
+ "', it resolves to a bean defined in the Micronaut application-context.");
54+
throw new ProcessEngineException("Cannot set value of '"
55+
+ property
56+
+ "', it resolves to a bean defined in the Micronaut"
57+
+ " application-context.");
5858
}
5959
}
6060

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,8 @@ public void execute(DelegateExecution execution) throws Exception {
4545
}
4646

4747
if (execution.hasVariableLocal("onlyIncome")) {
48-
boolean onlyIncome = execution.<BooleanValue>getVariableLocalTyped("onlyIncome").getValue();
48+
boolean onlyIncome =
49+
execution.<BooleanValue>getVariableLocalTyped("onlyIncome").getValue();
4950
requestBuilder.onlyIncome(onlyIncome);
5051
}
5152

bpmn-process/src/main/java/com/jongsoft/finance/bpmn/delegate/account/ProcessAccountCreationDelegate.java

Lines changed: 24 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -46,48 +46,39 @@ public class ProcessAccountCreationDelegate implements JavaDelegate, JavaBean {
4646

4747
@Override
4848
public void execute(DelegateExecution execution) {
49-
var accountJson =
50-
mapper.readSafe(
51-
execution.<StringValue>getVariableLocalTyped("account").getValue(), AccountJson.class);
49+
var accountJson = mapper.readSafe(
50+
execution.<StringValue>getVariableLocalTyped("account").getValue(), AccountJson.class);
5251

5352
log.debug(
5453
"{}: Processing account creation from json '{}'",
5554
execution.getCurrentActivityName(),
5655
accountJson.getName());
5756

58-
accountProvider
59-
.lookup(accountJson.getName())
60-
.ifNotPresent(
61-
() -> {
62-
userProvider
63-
.currentUser()
64-
.createAccount(
65-
accountJson.getName(), accountJson.getCurrency(), accountJson.getType());
57+
accountProvider.lookup(accountJson.getName()).ifNotPresent(() -> {
58+
userProvider
59+
.currentUser()
60+
.createAccount(accountJson.getName(), accountJson.getCurrency(), accountJson.getType());
6661

67-
accountProvider
68-
.lookup(accountJson.getName())
69-
.ifPresent(
70-
account -> {
71-
account.changeAccount(
72-
handleEmptyAsNull(accountJson.getIban()),
73-
handleEmptyAsNull(accountJson.getBic()),
74-
handleEmptyAsNull(accountJson.getNumber()));
75-
account.rename(
76-
accountJson.getName(),
77-
accountJson.getDescription(),
78-
accountJson.getCurrency(),
79-
accountJson.getType());
62+
accountProvider.lookup(accountJson.getName()).ifPresent(account -> {
63+
account.changeAccount(
64+
handleEmptyAsNull(accountJson.getIban()),
65+
handleEmptyAsNull(accountJson.getBic()),
66+
handleEmptyAsNull(accountJson.getNumber()));
67+
account.rename(
68+
accountJson.getName(),
69+
accountJson.getDescription(),
70+
accountJson.getCurrency(),
71+
accountJson.getType());
8072

81-
if (accountJson.getPeriodicity() != null) {
82-
account.interest(accountJson.getInterest(), accountJson.getPeriodicity());
83-
}
73+
if (accountJson.getPeriodicity() != null) {
74+
account.interest(accountJson.getInterest(), accountJson.getPeriodicity());
75+
}
8476

85-
if (accountJson.getIcon() != null) {
86-
account.registerIcon(
87-
storageService.store(Hex.decode(accountJson.getIcon())));
88-
}
89-
});
90-
});
77+
if (accountJson.getIcon() != null) {
78+
account.registerIcon(storageService.store(Hex.decode(accountJson.getIcon())));
79+
}
80+
});
81+
});
9182
}
9283

9384
private String handleEmptyAsNull(String value) {

bpmn-process/src/main/java/com/jongsoft/finance/bpmn/delegate/account/ProcessAccountLookupDelegate.java

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -57,11 +57,10 @@ public void execute(DelegateExecution execution) {
5757
if (!matchedAccount.isPresent() && execution.hasVariableLocal("iban")) {
5858
final String iban = (String) execution.getVariableLocal("iban");
5959
if (iban != null && !iban.trim().isEmpty()) {
60-
matchedAccount =
61-
accountProvider
62-
.lookup(accountFilterFactory.account().iban(iban, true))
63-
.content()
64-
.first(x -> true);
60+
matchedAccount = accountProvider
61+
.lookup(accountFilterFactory.account().iban(iban, true))
62+
.content()
63+
.first(ignored -> true);
6564
}
6665
}
6766

bpmn-process/src/main/java/com/jongsoft/finance/bpmn/delegate/account/ReconcileAccountDelegate.java

Lines changed: 8 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -58,22 +58,17 @@ public void execute(DelegateExecution execution) throws Exception {
5858
amount);
5959

6060
Account toReconcile = accountProvider.lookup(accountId).get();
61-
Account reconcileAccount =
62-
accountProvider
63-
.lookup(SystemAccountTypes.RECONCILE)
64-
.getOrThrow(() -> StatusException.badRequest("Reconcile account not found"));
61+
Account reconcileAccount = accountProvider
62+
.lookup(SystemAccountTypes.RECONCILE)
63+
.getOrThrow(() -> StatusException.badRequest("Reconcile account not found"));
6564

6665
Transaction.Type type =
6766
amount.compareTo(BigDecimal.ZERO) >= 0 ? Transaction.Type.CREDIT : Transaction.Type.DEBIT;
68-
Transaction transaction =
69-
toReconcile.createTransaction(
70-
reconcileAccount,
71-
amount.abs().doubleValue(),
72-
type,
73-
t ->
74-
t.description("Reconcile transaction")
75-
.currency(toReconcile.getCurrency())
76-
.date(transactionDate));
67+
Transaction transaction = toReconcile.createTransaction(
68+
reconcileAccount, amount.abs().doubleValue(), type, t -> t.description(
69+
"Reconcile transaction")
70+
.currency(toReconcile.getCurrency())
71+
.date(transactionDate));
7772

7873
creationHandler.handleCreatedEvent(new CreateTransactionCommand(transaction));
7974
}

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

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -64,23 +64,20 @@ public void execute(DelegateExecution execution) {
6464
for (int i = budgetAnalysisMonths; i > 0; i--) {
6565
var transactions = transactionProvider.lookup(searchCommand.range(dateRange));
6666

67-
var spentInMonth =
68-
transactions
69-
.content()
70-
.map(transaction -> transaction.computeAmount(transaction.computeTo()))
71-
.sum()
72-
.get();
67+
var spentInMonth = transactions
68+
.content()
69+
.map(transaction -> transaction.computeAmount(transaction.computeTo()))
70+
.sum()
71+
.get();
7372

7473
deviation += forExpense.computeBudget() - spentInMonth;
7574
dateRange = dateRange.previous();
7675
}
7776

78-
var averageDeviation =
79-
BigDecimal.valueOf(deviation)
80-
.divide(
81-
BigDecimal.valueOf(budgetAnalysisMonths), new MathContext(6, RoundingMode.HALF_UP))
82-
.setScale(2, RoundingMode.HALF_UP)
83-
.doubleValue();
77+
var averageDeviation = BigDecimal.valueOf(deviation)
78+
.divide(BigDecimal.valueOf(budgetAnalysisMonths), new MathContext(6, RoundingMode.HALF_UP))
79+
.setScale(2, RoundingMode.HALF_UP)
80+
.doubleValue();
8481
if (Math.abs(averageDeviation) / forExpense.computeBudget()
8582
> settingProvider.getMaximumBudgetDeviation()) {
8683
execution.setVariableLocal("deviation", averageDeviation);

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

Lines changed: 18 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,8 @@ public class ProcessBudgetCreateDelegate implements JavaDelegate, JavaBean {
3939

4040
@Override
4141
public void execute(DelegateExecution execution) {
42-
var budgetJson =
43-
mapper.readSafe(
44-
execution.<StringValue>getVariableLocalTyped("budget").getValue(), BudgetJson.class);
42+
var budgetJson = mapper.readSafe(
43+
execution.<StringValue>getVariableLocalTyped("budget").getValue(), BudgetJson.class);
4544

4645
log.debug(
4746
"{}: Processing budget creation from json for period '{}'",
@@ -64,48 +63,38 @@ public void execute(DelegateExecution execution) {
6463
EventBus.getBus().send(new CloseBudgetCommand(oldBudget.get().getId(), start));
6564
// create new budget
6665
EventBus.getBus()
67-
.send(
68-
new CreateBudgetCommand(
69-
Budget.builder()
70-
.start(start)
71-
.expectedIncome(budgetJson.getExpectedIncome())
72-
.expenses(oldBudget.get().getExpenses())
73-
.build()));
66+
.send(new CreateBudgetCommand(Budget.builder()
67+
.start(start)
68+
.expectedIncome(budgetJson.getExpectedIncome())
69+
.expenses(oldBudget.get().getExpenses())
70+
.build()));
7471
} else {
7572
log.debug(
7673
"{}: Creating new budget period for period '{}'",
7774
execution.getCurrentActivityName(),
7875
start);
7976
EventBus.getBus()
80-
.send(
81-
new CreateBudgetCommand(
82-
Budget.builder()
83-
.start(start)
84-
.expectedIncome(budgetJson.getExpectedIncome())
85-
.build()));
77+
.send(new CreateBudgetCommand(Budget.builder()
78+
.start(start)
79+
.expectedIncome(budgetJson.getExpectedIncome())
80+
.build()));
8681
}
8782

8883
log.trace(
8984
"{}: Budget period updated for period '{}'",
9085
execution.getCurrentActivityName(),
9186
budgetJson.getStart());
9287

93-
var budget =
94-
budgetProvider
95-
.lookup(year, month)
96-
.getOrThrow(
97-
() -> new IllegalStateException("Budget period not found for period " + start));
88+
var budget = budgetProvider
89+
.lookup(year, month)
90+
.getOrThrow(() -> new IllegalStateException("Budget period not found for period " + start));
9891

9992
budgetJson
10093
.getExpenses()
10194
// update or create the expenses
102-
.forEach(
103-
e ->
104-
Control.Option(budget.determineExpense(e.getName()))
105-
.ifPresent(currentExpense -> currentExpense.updateExpense(e.getUpperBound()))
106-
.elseRun(
107-
() ->
108-
budget.createExpense(
109-
e.getName(), e.getLowerBound(), e.getUpperBound())));
95+
.forEach(e -> Control.Option(budget.determineExpense(e.getName()))
96+
.ifPresent(currentExpense -> currentExpense.updateExpense(e.getUpperBound()))
97+
.elseRun(
98+
() -> budget.createExpense(e.getName(), e.getLowerBound(), e.getUpperBound())));
11099
}
111100
}

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

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,9 @@ public void execute(DelegateExecution execution) throws Exception {
2828

2929
budgetProvider
3030
.lookup(year, month)
31-
.ifPresent(budget -> execution.setVariable("expenses", budget.getExpenses().toJava()))
32-
.elseThrow(
33-
() ->
34-
new IllegalStateException(
35-
"Budget cannot be found for year " + year + " and month " + month));
31+
.ifPresent(
32+
budget -> execution.setVariable("expenses", budget.getExpenses().toJava()))
33+
.elseThrow(() -> new IllegalStateException(
34+
"Budget cannot be found for year " + year + " and month " + month));
3635
}
3736
}

bpmn-process/src/main/java/com/jongsoft/finance/bpmn/delegate/category/ProcessCategoryLookupDelegate.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,8 @@ public void execute(DelegateExecution execution) throws Exception {
4747

4848
category = categoryProvider.lookup(label).get();
4949
} else {
50-
category = categoryProvider.lookup((Long) execution.getVariableLocal("id")).get();
50+
category =
51+
categoryProvider.lookup((Long) execution.getVariableLocal("id")).get();
5152
}
5253

5354
execution.setVariable("category", category);

0 commit comments

Comments
 (0)