Skip to content

Commit 0a931f9

Browse files
committed
FIN-275 Add the front-end integration for the import process.
1 parent df78c08 commit 0a931f9

File tree

12 files changed

+270
-27
lines changed

12 files changed

+270
-27
lines changed

README.md

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,18 +8,18 @@
88

99
-----------------------
1010

11-
**[Read the documentation](https://fintrack.jongsoft.com/)**
11+
**[Read the documentation](https://www.pledger.io/)**
1212

1313
[Report Bug](https://jongsoftdev.atlassian.net/issues/?jql=issuetype%20%3D%20Bug%20AND%20project%20%3D%20FIN%20AND%20resolution%20%3D%20Unresolved%20ORDER%20BY%20priority%20DESC) - [Request Feature](https://jongsoftdev.atlassian.net/browse/FIN-23?jql=issuetype%20%3D%20Story%20AND%20project%20%3D%20FIN%20AND%20resolution%20%3D%20Unresolved%20ORDER%20BY%20priority%20DESC)
1414

1515
-----------------------
1616

17-
## About FinTrack
18-
FinTrack is a "self-hosted" application that helps in keeping track of your personal finances. It helps you keep track
19-
of your income and expenses to allow you to spend less many and save more.
17+
## About Pledger.io
18+
Pledger.io is a "self hosted" application that helps in keeping track of your personal finances.
19+
It helps you keep track of your income and expenses to allow you to spend less many and save more.
2020

21-
Using FinTrack you can keep track of your income vs spending as well as your credit card expenses. FinTrack has the
22-
following features:
21+
Using Pledger.io you can keep track of your income vs spending as well as your credit card expenses.
22+
Pledger.io has the following features:
2323

2424
* Categories to group expenses
2525
* Budgets to track income vs. expenses
@@ -28,9 +28,9 @@ following features:
2828

2929
## Get started
3030

31-
**Note:** the front-end application can be found in the [fintrack-ui](https://bitbucket.org/jongsoftdev/fintrack-ui) repository. The [deployment](https://bitbucket.org/jongsoftdev/fintrack-deployment) build will bundle the front-end with this backend system.
31+
**Note:** the front-end application can be found in the [user-interface](https://bitbucket.org/jongsoftdev/user-interface) repository. The [deployment](https://bitbucket.org/jongsoftdev/fintrack-deployment) build will bundle the front-end with this backend system.
3232

33-
In this repository you will find the backend REST application needed to run FinTrack.
33+
In this repository you will find the backend REST application needed to run Pledger.io.
3434

3535
### Building the source
3636

@@ -51,7 +51,7 @@ API documentation use the url:
5151
http://localhost:8080/spec/index.html
5252

5353
## License
54-
Copyright 2023 Jong Soft Development
54+
Copyright 2024 Jong Soft Development
5555

5656
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
5757
associated documentation files (the "Software"), to deal in the Software without restriction, including
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package com.jongsoft.finance;
2+
3+
import com.fasterxml.jackson.annotation.JsonTypeInfo;
4+
5+
import java.io.Serializable;
6+
7+
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, property = "_type")
8+
public interface ProcessVariable extends Serializable {
9+
}

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

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,14 @@
11
package com.jongsoft.finance.bpmn.delegate.importer;
22

3+
import com.jongsoft.finance.ProcessVariable;
34
import io.micronaut.serde.annotation.Serdeable;
45
import lombok.EqualsAndHashCode;
56
import lombok.Getter;
67

7-
import java.io.Serializable;
8-
98
@Getter
109
@Serdeable
1110
@EqualsAndHashCode(of = {"name"})
12-
public class ExtractionMapping implements Serializable {
11+
public class ExtractionMapping implements ProcessVariable {
1312

1413
private final String name;
1514
private final Long accountId;

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,15 @@
44
import org.camunda.bpm.engine.delegate.DelegateExecution;
55
import org.camunda.bpm.engine.delegate.JavaDelegate;
66

7-
import java.util.Set;
7+
import java.util.Collection;
88

99
@Slf4j
1010
public class LocateAccountInMapping implements JavaDelegate {
1111
@Override
1212
public void execute(DelegateExecution delegateExecution) throws Exception {
1313
var accountName = (String) delegateExecution.getVariableLocal("name");
1414
@SuppressWarnings("unchecked")
15-
var mappings = (Set<ExtractionMapping>) delegateExecution.getVariable("accountMappings");
15+
var mappings = (Collection<ExtractionMapping>) delegateExecution.getVariable("accountMappings");
1616

1717
log.debug("{}: Locating account mapping for {}.",
1818
delegateExecution.getCurrentActivityName(),

bpmn-process/src/main/java/com/jongsoft/finance/serialized/ImportConfigJson.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.jongsoft.finance.serialized;
22

33
import com.fasterxml.jackson.annotation.JsonProperty;
4+
import com.jongsoft.finance.ProcessVariable;
45
import io.micronaut.serde.annotation.Serdeable;
56
import lombok.Getter;
67
import lombok.Setter;
@@ -13,7 +14,7 @@
1314
@Setter
1415
@Serdeable
1516
@ToString(of = {"accountId", "headers", "dateFormat", "delimiter"})
16-
public class ImportConfigJson implements Serializable {
17+
public class ImportConfigJson implements ProcessVariable {
1718

1819
public enum MappingRole {
1920
IGNORE("_ignore"),

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

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,67 @@ void runImportAccountNotFound() {
153153
Mockito.verifyNoMoreInteractions(accountProvider);
154154
}
155155

156+
@Test
157+
void runWithAccountMappingAdjustments() {
158+
// Given:
159+
Mockito.when(accountProvider.lookup(TARGET_ACCOUNT_ID))
160+
.thenReturn(Control.Option(createTargetAccount()));
161+
Mockito.when(importProvider.lookup(IMPORT_JOB_SLUG))
162+
.thenReturn(Control.Option(createBatchImport()));
163+
Mockito.when(storageService.read(JSON_FILE_CODE))
164+
.thenReturn(Control.Option(readResource("/import-test/import-config-test.json")));
165+
Mockito.when(storageService.read(CSV_FILE_CODE))
166+
.thenReturn(Control.Option(readResource("/import-test/import-test.csv")));
167+
168+
// Default account lookup shall yield no results
169+
Mockito.when(accountProvider.lookup(Mockito.anyString()))
170+
.thenReturn(Control.Option());
171+
Mockito.when(accountProvider.synonymOf(Mockito.anyString()))
172+
.thenReturn(Control.Option());
173+
setupPostAccount();
174+
setupPieterseAccount();
175+
176+
// When:
177+
var process = processEngine.getRuntimeService().startProcessInstanceByKey("import_job",
178+
Variables.createVariables()
179+
.putValue("importJobSlug", IMPORT_JOB_SLUG)
180+
);
181+
182+
completeImportConfig(process, false);
183+
var configureJsonTask = processEngine.getTaskService()
184+
.createTaskQuery()
185+
.taskDefinitionKey("confirm_mappings")
186+
.processInstanceId(process.getProcessInstanceId())
187+
.singleResult();
188+
Assertions.assertThat(configureJsonTask).isNotNull();
189+
190+
processEngine.getTaskService()
191+
.complete(configureJsonTask.getId(), Variables.createVariables()
192+
.putValue("account_mappings", Set.of(
193+
new ExtractionMapping("P. Post", 2L),
194+
new ExtractionMapping("Janssen PA", 2L),
195+
new ExtractionMapping("MW GA Pieterse", 4L)
196+
)));
197+
198+
// Then:
199+
Mockito.verify(accountProvider).lookup("Janssen PA");
200+
Mockito.verify(accountProvider).lookup("P. Post");
201+
202+
verifyStorageCleaned();
203+
204+
var createdTransactions = Mockito.mockingDetails(transactionCreationHandler).getInvocations().stream()
205+
.filter(invocation -> invocation.getMethod().getName().equals("handleCreatedEvent"))
206+
.map(invocation -> invocation.getArgument(0, CreateTransactionCommand.class))
207+
.map(CreateTransactionCommand::transaction)
208+
.toList();
209+
210+
Assertions.assertThat(createdTransactions)
211+
.hasSize(4)
212+
.anySatisfy(this::verifyPostTransaction)
213+
.noneSatisfy(this::verifyJanssenTransaction)
214+
.anySatisfy(this::verifyPieterseTransaction);
215+
}
216+
156217
@Test
157218
@DisplayName("Run with manual account creation for Pieterse")
158219
void runWithManualAccountCreate() {

fintrack-api/src/main/java/com/jongsoft/finance/rest/account/AccountResource.java

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -95,12 +95,14 @@ List<AccountResponse> allAccounts() {
9595
}
9696
)
9797
List<AccountResponse> autocomplete(@Nullable String token, @Nullable String type) {
98-
var accounts = accountProvider.lookup(
99-
accountFilterFactory.account()
100-
.name(token, false)
101-
.pageSize(settingProvider.getAutocompleteLimit())
102-
.types(Collections.List(type)));
98+
var filter = accountFilterFactory.account()
99+
.name(token, false)
100+
.pageSize(settingProvider.getAutocompleteLimit());
101+
if (type != null) {
102+
filter.types(Collections.List(type));
103+
}
103104

105+
var accounts = accountProvider.lookup(filter);
104106
return accounts.content()
105107
.map(AccountResponse::new)
106108
.toJava();

fintrack-api/src/main/java/com/jongsoft/finance/rest/process/ProcessTaskResource.java

Lines changed: 36 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,18 @@
22

33
import com.jongsoft.finance.rest.model.ProcessTaskResponse;
44
import com.jongsoft.lang.Collections;
5-
import io.micronaut.http.annotation.Controller;
6-
import io.micronaut.http.annotation.Delete;
7-
import io.micronaut.http.annotation.Get;
8-
import io.micronaut.http.annotation.PathVariable;
5+
import io.micronaut.core.annotation.Nullable;
6+
import io.micronaut.http.annotation.*;
97
import io.micronaut.security.annotation.Secured;
108
import io.micronaut.security.rules.SecurityRule;
119
import io.swagger.v3.oas.annotations.Operation;
1210
import io.swagger.v3.oas.annotations.tags.Tag;
1311
import jakarta.inject.Inject;
1412
import lombok.RequiredArgsConstructor;
1513
import org.camunda.bpm.engine.TaskService;
14+
import org.camunda.bpm.engine.variable.Variables;
1615

16+
import java.util.HashMap;
1717
import java.util.List;
1818

1919
@Tag(name = "Process Engine")
@@ -40,10 +40,41 @@ public List<ProcessTaskResponse> tasks(@PathVariable String processDefinitionKey
4040
.toJava();
4141
}
4242

43+
@Get("/{taskId}/variables")
44+
@Operation(
45+
summary = "Get Task",
46+
description = "Get the details of the given task.",
47+
operationId = "getTask"
48+
)
49+
public synchronized VariableMap variables(@PathVariable String taskId, @Nullable @QueryValue String variable) {
50+
var variableMap = new VariableMap();
51+
if (variable != null) {
52+
variableMap.put(variable, taskService.getVariable(taskId, variable));
53+
} else {
54+
taskService.getVariables(taskId)
55+
.forEach(variableMap::put);
56+
}
57+
58+
return variableMap;
59+
}
60+
61+
@Post("/{taskId}/complete")
62+
@Operation(
63+
summary = "Complete Task",
64+
description = "Completes the given task with the provided data.",
65+
operationId = "completeTask"
66+
)
67+
public void complete(@PathVariable String taskId, @Body VariableMap variables) {
68+
var javaMap = new HashMap<String, Object>();
69+
variables.keySet()
70+
.forEach(key -> javaMap.put(key, variables.get(key)));
71+
taskService.complete(taskId, Variables.fromMap(javaMap));
72+
}
73+
4374
@Delete("/{taskId}")
4475
@Operation(
4576
summary = "Complete Task",
46-
description = "Completes the given task",
77+
description = "Completes the given task without any additional data.",
4778
operationId = "deleteTask"
4879
)
4980
public void complete(@PathVariable String taskId) {
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
package com.jongsoft.finance.rest.process;
2+
3+
import com.jongsoft.finance.ProcessVariable;
4+
import io.micronaut.serde.annotation.Serdeable;
5+
import io.swagger.v3.oas.annotations.media.Schema;
6+
import lombok.AccessLevel;
7+
import lombok.Getter;
8+
import lombok.Setter;
9+
10+
import java.util.Collection;
11+
import java.util.HashMap;
12+
import java.util.List;
13+
import java.util.Set;
14+
15+
@Serdeable
16+
@Schema(name = "VariableMap", description = "A map of variables used in tasks.")
17+
public class VariableMap {
18+
@Serdeable
19+
@Schema(name = "VariableList", description = "A list of variables wrapped for the task.")
20+
public record VariableList(List<ProcessVariable> content) implements ProcessVariable {
21+
}
22+
23+
@Serdeable
24+
@Schema(name = "WrappedVariable", description = "A variable wrapped for the task.")
25+
public record WrappedVariable<T>(T value) implements ProcessVariable {
26+
}
27+
28+
@Setter
29+
@Getter(value = AccessLevel.PACKAGE)
30+
@Schema(description = "The actual map of all the variables set for the task.")
31+
private HashMap<String, ProcessVariable> variables = new HashMap<>();
32+
33+
public <T> T get(String key) {
34+
return (T) convertFrom(variables.get(key));
35+
}
36+
37+
public void put(String key, Object value) {
38+
variables.put(key, convertTo(value));
39+
}
40+
41+
public Set<String> keySet() {
42+
return variables.keySet();
43+
}
44+
45+
private ProcessVariable convertTo(Object value) {
46+
if (value instanceof Collection list) {
47+
return new VariableList(list.stream().map(this::convertTo).toList());
48+
} else if (value instanceof ProcessVariable variable) {
49+
return variable;
50+
} else {
51+
return new WrappedVariable<>(value);
52+
}
53+
}
54+
55+
private Object convertFrom(ProcessVariable value) {
56+
if (value instanceof VariableList list) {
57+
return list.content
58+
.stream()
59+
.map(this::convertFrom)
60+
.toList();
61+
} else if (value instanceof WrappedVariable wrapped) {
62+
return wrapped.value;
63+
}
64+
65+
return value;
66+
}
67+
}

fintrack-api/src/main/resources/application.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,8 @@ micronaut:
3333
multipart:
3434
enabled: true
3535
location: "${micronaut.application.storage.location}/temp"
36-
max-file-size: 10MB
36+
max-file-size: 20MB
37+
max-request-size: 20MB
3738
executors:
3839
io:
3940
type: fixed

0 commit comments

Comments
 (0)