From 55428c62a963392129c9f2687973af178ccf908b Mon Sep 17 00:00:00 2001 From: Robert Goldmann <deadlocker@gmx.de> Date: Wed, 6 Apr 2022 23:18:32 +0200 Subject: [PATCH] #691 - fixed account importer (update source account with data from destination account) --- .../budgetmaster/accounts/Account.java | 11 + .../database/importer/AccountImporter.java | 2 +- .../unit/database/ImportServiceTest.java | 233 ++++++++++++++---- src/test/resources/ImportServiceTest.json | 13 +- 4 files changed, 203 insertions(+), 56 deletions(-) diff --git a/src/main/java/de/deadlocker8/budgetmaster/accounts/Account.java b/src/main/java/de/deadlocker8/budgetmaster/accounts/Account.java index bbe07a598..2b5e19b53 100644 --- a/src/main/java/de/deadlocker8/budgetmaster/accounts/Account.java +++ b/src/main/java/de/deadlocker8/budgetmaster/accounts/Account.java @@ -63,6 +63,17 @@ public class Account implements ProvidesID, Iconizable { } + public void updateFromOtherAccount(Account otherAccount) + { + this.setID(otherAccount.ID); + this.setName(otherAccount.name); + this.setType(otherAccount.type); + this.setSelected(otherAccount.isSelected); + this.setDefault(otherAccount.isDefault); + this.setAccountState(otherAccount.accountState); + this.setIconReference(otherAccount.iconReference); + } + public Integer getID() { return ID; diff --git a/src/main/java/de/deadlocker8/budgetmaster/database/importer/AccountImporter.java b/src/main/java/de/deadlocker8/budgetmaster/database/importer/AccountImporter.java index fcd5a837c..db03780d6 100644 --- a/src/main/java/de/deadlocker8/budgetmaster/database/importer/AccountImporter.java +++ b/src/main/java/de/deadlocker8/budgetmaster/database/importer/AccountImporter.java @@ -50,7 +50,7 @@ public class AccountImporter extends ItemImporter<Account> repository.save(destinationAccount); } - sourceAccount.setID(destinationAccount.getID()); + sourceAccount.updateFromOtherAccount(destinationAccount); numberOfImportedItems++; } catch(Exception e) diff --git a/src/test/java/de/deadlocker8/budgetmaster/unit/database/ImportServiceTest.java b/src/test/java/de/deadlocker8/budgetmaster/unit/database/ImportServiceTest.java index 321d6d8d0..f9c9c1e70 100644 --- a/src/test/java/de/deadlocker8/budgetmaster/unit/database/ImportServiceTest.java +++ b/src/test/java/de/deadlocker8/budgetmaster/unit/database/ImportServiceTest.java @@ -5,11 +5,10 @@ import de.deadlocker8.budgetmaster.accounts.Account; import de.deadlocker8.budgetmaster.accounts.AccountRepository; import de.deadlocker8.budgetmaster.accounts.AccountState; import de.deadlocker8.budgetmaster.accounts.AccountType; +import de.deadlocker8.budgetmaster.categories.Category; import de.deadlocker8.budgetmaster.categories.CategoryRepository; -import de.deadlocker8.budgetmaster.charts.Chart; -import de.deadlocker8.budgetmaster.charts.ChartRepository; -import de.deadlocker8.budgetmaster.charts.ChartService; -import de.deadlocker8.budgetmaster.charts.ChartType; +import de.deadlocker8.budgetmaster.categories.CategoryType; +import de.deadlocker8.budgetmaster.charts.*; import de.deadlocker8.budgetmaster.database.DatabaseParser; import de.deadlocker8.budgetmaster.database.InternalDatabase; import de.deadlocker8.budgetmaster.database.accountmatches.AccountMatch; @@ -21,6 +20,7 @@ import de.deadlocker8.budgetmaster.images.ImageFileExtension; import de.deadlocker8.budgetmaster.images.ImageService; import de.deadlocker8.budgetmaster.repeating.RepeatingTransactionUpdater; import de.deadlocker8.budgetmaster.services.ImportService; +import de.deadlocker8.budgetmaster.tags.Tag; import de.deadlocker8.budgetmaster.tags.TagRepository; import de.deadlocker8.budgetmaster.templategroup.TemplateGroup; import de.deadlocker8.budgetmaster.templategroup.TemplateGroupRepository; @@ -29,13 +29,11 @@ import de.deadlocker8.budgetmaster.templates.Template; import de.deadlocker8.budgetmaster.templates.TemplateRepository; import de.deadlocker8.budgetmaster.transactions.TransactionRepository; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mockito; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.test.annotation.DirtiesContext; -import org.springframework.test.context.junit.jupiter.SpringExtension; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.transaction.annotation.Transactional; import java.io.IOException; import java.net.URISyntaxException; @@ -49,6 +47,8 @@ import java.util.List; import static org.assertj.core.api.Assertions.assertThat; @SpringBootTest(classes = Main.class) +@ActiveProfiles("test") +@Transactional class ImportServiceTest { @Autowired @@ -93,18 +93,21 @@ class ImportServiceTest final InternalDatabase importedDatabase = parser.parseDatabaseFromJSON(); // source accounts - final Account sourceAccount1 = new Account("Source_Account_1", AccountType.CUSTOM); + final Account sourceAccount1 = new Account("Default Account", AccountType.CUSTOM); sourceAccount1.setID(2); - final Account sourceAccount2 = new Account("Source_Account_2", AccountType.CUSTOM); - sourceAccount2.setID(3); + final Account sourceAccount2 = new Account("Read-only account", AccountType.CUSTOM); + sourceAccount2.setID(6); + final Account sourceAccount3 = new Account("Second Account", AccountType.CUSTOM); + sourceAccount3.setID(8); // destination accounts - Account destAccount1 = new Account("Destination_Account_1", AccountType.CUSTOM); - destAccount1.setAccountState(AccountState.FULL_ACCESS); - destAccount1 = accountRepository.save(destAccount1); - Account destAccount2 = new Account("Destination_Account_2", AccountType.CUSTOM); + final Account destAccount1 = accountRepository.findByIsDefault(true); + Account destAccount2 = new Account("Destination_Account_1", AccountType.CUSTOM); destAccount2.setAccountState(AccountState.FULL_ACCESS); destAccount2 = accountRepository.save(destAccount2); + Account destAccount3 = new Account("Destination_Account_2", AccountType.CUSTOM); + destAccount3.setAccountState(AccountState.FULL_ACCESS); + destAccount3 = accountRepository.save(destAccount3); // account matches final AccountMatch match1 = new AccountMatch(sourceAccount1); @@ -113,56 +116,171 @@ class ImportServiceTest final AccountMatch match2 = new AccountMatch(sourceAccount2); match2.setAccountDestination(destAccount2); - final List<AccountMatch> matches = List.of(match1, match2); + final AccountMatch match3 = new AccountMatch(sourceAccount3); + match3.setAccountDestination(destAccount3); + + final List<AccountMatch> matches = List.of(match1, match2, match3); final AccountMatchList accountMatchList = new AccountMatchList(matches); // act importService.importDatabase(importedDatabase, accountMatchList, true, true, true); final InternalDatabase databaseResult = importService.getDatabase(); - final Image image = new Image(new Byte[0], "awesomeIcon.png", ImageFileExtension.PNG); + // assert images + final Image image = new Image(new Byte[0], "BudgetMaster.svg", ImageFileExtension.SVG); image.setID(1); - assertThat(imageService.getRepository().findAll()) .hasSize(1) .containsExactly(image); - + // assert icons + final Icon iconAllAccounts = createIcon(1, "fas fa-landmark", null, null); + final Icon iconCategoryNone = createIcon(3, null, null, null); + final Icon iconCategoryRest = createIcon(4, null, null, null); + final Icon iconAccountReadOnly = createIcon(8, "fas fa-ban", "#2eb952ff", null); + final Icon iconAccountDefault = createIcon(9, null, null, image); + final Icon iconAccountSecond = createIcon(10, null, "#2e79b9ff", null); + final Icon iconCategoryCar = createIcon(11, "fas fa-ambulance", null, null); + final Icon iconCategoryRent = createIcon(12, null, null, image); + final Icon iconTemplateFull = createIcon(13, "fas fa-battery-three-quarters", "#e34f4fff", null); + final Icon iconTemplateUngrouped = createIcon(14, null, "#212121ff", null); + final Icon iconTemplateRandom = createIcon(15, "fas fa-award", "#212121ff", null); + final Icon iconTemplateWithTags = createIcon(16, null, null, image); assertThat(iconService.getRepository().findAll()) - .hasSize(12) - .containsExactlyInAnyOrder(createIcon(1, "fas fa-landmark", null, null), - createIcon(3, null, null, null), - createIcon(4, null, null, null), - createIcon(6, "fas fa-ban", "#2eb952ff", null), - createIcon(7, null, null, image), // - createIcon(8, null, "#2e79b9ff", null), - createIcon(11, "fas fa-ambulance", null, null), - createIcon(12, null, null, image), // - createIcon(13, "fas fa-battery-three-quarters", "#e34f4fff", null), - createIcon(15, null, "#212121ff", null), - createIcon(17, "fas fa-award", "#212121ff", null), - createIcon(18, null, null, image)); - + .hasSize(16) + .containsExactlyInAnyOrder( + iconAllAccounts, + createIcon(2, null, null, null), + iconCategoryNone, + iconCategoryRest, + createIcon(5, "fas fa-landmark", null, null), + createIcon(6, null, null, null), + createIcon(7, null, null, null), + iconAccountReadOnly, + iconAccountDefault, + iconAccountSecond, + iconCategoryCar, + iconCategoryRent, + iconTemplateFull, + iconTemplateUngrouped, + iconTemplateRandom, + iconTemplateWithTags); + + // assert categories + final Category categoryNone = createCategory(1, "No Category", "#FFFFFF", CategoryType.NONE, iconCategoryNone); + final Category categoryRest = createCategory(2, "Rest", "#FFFF00", CategoryType.REST, iconCategoryRest); + final Category categoryCar = createCategory(3, "Car", "#007afa", CategoryType.CUSTOM, iconCategoryCar); + final Category categoryRent = createCategory(4, "Rent", "#eeeeee", CategoryType.CUSTOM, iconCategoryRent); + assertThat(categoryRepository.findAll()) + .hasSize(4) + .containsExactlyInAnyOrder( + categoryNone, + categoryRest, + categoryCar, + categoryRent); + + // assert accounts + final Account accountPlaceholder = createAccount(1, "Placeholder", AccountType.ALL, AccountState.FULL_ACCESS, iconAllAccounts, false, false); + final Account accountDefault = createAccount(2, "Default Account", AccountType.CUSTOM, AccountState.FULL_ACCESS, iconAccountDefault, true, true); + final Account accountReadOnly = createAccount(3, "Destination_Account_1", AccountType.CUSTOM, AccountState.FULL_ACCESS, iconAccountReadOnly, false, false); + final Account accountSecond = createAccount(4, "Destination_Account_2", AccountType.CUSTOM, AccountState.FULL_ACCESS, iconAccountSecond, false, false); + assertThat(accountRepository.findAll()) + .hasSize(4) + .contains(accountPlaceholder, + accountReadOnly, + accountSecond); + assertThat(accountRepository.getById(accountDefault.getID())) + .hasFieldOrPropertyWithValue("name", accountDefault.getName()) + .hasFieldOrPropertyWithValue("type", accountDefault.getType()) + .hasFieldOrPropertyWithValue("accountState", accountDefault.getAccountState()) + .hasFieldOrPropertyWithValue("isSelected", accountDefault.isSelected()) + .hasFieldOrPropertyWithValue("isDefault", accountDefault.isDefault()) + .hasFieldOrPropertyWithValue("iconReference", accountDefault.getIconReference()); + + // assert template groups + final TemplateGroup templateGroupDefault = new TemplateGroup(1, "Not grouped", TemplateGroupType.DEFAULT); + final TemplateGroup templateGroup1 = new TemplateGroup(2, "Enter the group!", TemplateGroupType.CUSTOM); + final TemplateGroup templateGroup2 = new TemplateGroup(3, "Group 2.0", TemplateGroupType.CUSTOM); + assertThat(templateGroupRepository.findAll()) + .hasSize(3) + .containsExactlyInAnyOrder(templateGroupDefault, templateGroup1, templateGroup2); + + + // assert charts + final Chart chart = new Chart("Custom chart", "/* This list will be dynamically filled with all the transactions between\r\n* the start and and date you select on the \"Show Chart\" page\r\n* and filtered according to your specified filter.\r\n* An example entry for this list and tutorial about how to create custom charts ca be found in the BudgetMaster wiki:\r\n* https://github.com/deadlocker8/BudgetMaster/wiki/How-to-create-custom-charts\r\n*/\r\nvar transactionData \u003d [];\r\n\r\n// Prepare your chart settings here (mandatory)\r\nvar plotlyData \u003d [{\r\n x: [],\r\n y: [],\r\n type: \u0027bar\u0027\r\n}];\r\n\r\n// Add your Plotly layout settings here (optional)\r\nvar plotlyLayout \u003d {};\r\n\r\n// Add your Plotly configuration settings here (optional)\r\nvar plotlyConfig \u003d {\r\n showSendToCloud: false,\r\n displaylogo: false,\r\n showLink: false,\r\n responsive: true\r\n};\r\n\r\nconsole.log(\"dummy\");\r\n\r\n// Don\u0027t touch this line\r\nPlotly.newPlot(\"containerID\", plotlyData, plotlyLayout, plotlyConfig);", ChartType.CUSTOM, 0, ChartDisplayType.CUSTOM, ChartGroupType.NONE, null); + chart.setID(13); + assertThat(chartService.getRepository().findAll()) + .hasSize(13) + .contains(chart); + + // assert templates + final Template templateFull = new Template(); + templateFull.setID(1); + templateFull.setTemplateName("Full template"); + templateFull.setAmount(-1200); + templateFull.setIsExpenditure(true); + templateFull.setAccount(accountDefault); + templateFull.setCategory(categoryCar); + templateFull.setName("My awesome transaction"); + templateFull.setDescription("Lorem Ipsum"); + templateFull.setIconReference(iconTemplateFull); + final Tag tag0815 = new Tag("0815"); + tag0815.setID(1); + final Tag tag12 = new Tag("12"); + tag12.setID(2); + final Tag tag13 = new Tag("13"); + tag13.setID(3); + templateFull.setTags(List.of(tag0815, tag12)); + templateFull.setTransferAccount(accountSecond); + templateFull.setTemplateGroup(templateGroup2); + + final Template templateWithTags = new Template(); + templateWithTags.setID(2); + templateWithTags.setIsExpenditure(true); + templateWithTags.setTemplateName("Template with tags"); + templateWithTags.setCategory(categoryNone); + templateWithTags.setName(""); + templateWithTags.setDescription(""); + templateWithTags.setIconReference(iconTemplateWithTags); + templateWithTags.setTags(List.of(tag12, tag13)); + templateWithTags.setTemplateGroup(templateGroup2); + + final Template templateUngrouped = new Template(); + templateUngrouped.setID(3); + templateUngrouped.setIsExpenditure(true); + templateUngrouped.setTemplateName("Ungrouped template"); + templateUngrouped.setCategory(categoryNone); + templateUngrouped.setName(""); + templateUngrouped.setDescription(""); + templateUngrouped.setIconReference(iconTemplateUngrouped); + templateUngrouped.setTags(List.of()); + templateUngrouped.setTemplateGroup(templateGroupDefault); + + final Template templateRandom = new Template(); + templateRandom.setID(4); + templateRandom.setAmount(2000); + templateRandom.setIsExpenditure(false); + templateRandom.setTemplateName("Random template"); + templateRandom.setCategory(categoryNone); + templateRandom.setName(""); + templateRandom.setDescription(""); + templateRandom.setIconReference(iconTemplateRandom); + templateRandom.setTags(List.of()); + templateRandom.setTemplateGroup(templateGroup1); + + assertThat(templateRepository.findAll().get(0)).isEqualTo(templateFull); + assertThat(templateRepository.findAll().get(1)).isEqualTo(templateWithTags); + assertThat(templateRepository.findAll().get(2)).isEqualTo(templateUngrouped); + assertThat(templateRepository.findAll().get(3)).isEqualTo(templateRandom); + + assertThat(templateRepository.findAll()) + .hasSize(4) + .containsExactlyInAnyOrder(templateFull, templateWithTags, templateUngrouped, templateRandom); -// new Category("No Category", "#FFFFFF", CategoryType.NONE, ) - - - // assert -// assertThat(categoryRepository.findAll()) -// .hasSize(4) -// .contains(expectedCategory1, expectedCategory2); // assertThat(databaseResult.getTransactions()) // .hasSize(3) // .contains(expectedTransaction1, expectedTransaction2, expectedTransaction3); -// assertThat(databaseResult.getTemplateGroups()) -// .hasSize(1) -// .contains(expectedTemplateGroup); -// assertThat(databaseResult.getTemplates()) -// .hasSize(3) -// .contains(expectedTemplate1, expectedTemplate2, expectedTemplate3); -// assertThat(databaseResult.getCharts()) -// .hasSize(1) -// .contains(expectedC); + assertThat(importService.getCollectedErrorMessages()).isEmpty(); } @@ -182,6 +300,23 @@ class ImportServiceTest return icon; } + private Category createCategory(int ID, String name, String color, CategoryType type, Icon icon) + { + final Category category = new Category(name, color, type, icon); + category.setID(ID); + return category; + } + + private Account createAccount(int ID, String name, AccountType type, AccountState state, Icon icon, boolean isSelected, boolean isDefault) + { + final Account account = new Account(name, type, icon); + account.setID(ID); + account.setAccountState(state); + account.setSelected(isSelected); + account.setDefault(isDefault); + return account; + } + @Test void test_skipTemplates() { diff --git a/src/test/resources/ImportServiceTest.json b/src/test/resources/ImportServiceTest.json index 09246e138..8e54e61e0 100644 --- a/src/test/resources/ImportServiceTest.json +++ b/src/test/resources/ImportServiceTest.json @@ -47,14 +47,14 @@ "iconReferenceID": 7 }, { - "ID": 3, + "ID": 6, "name": "Read-only account", "accountState": "READ_ONLY", "type": "CUSTOM", "iconReferenceID": 6 }, { - "ID": 4, + "ID": 8, "name": "Second Account", "accountState": "FULL_ACCESS", "type": "CUSTOM", @@ -110,7 +110,7 @@ "name": "12" } ], - "transferAccountID": 4 + "transferAccountID": 8 }, { "amount": -200, @@ -132,7 +132,7 @@ "times": 2 } }, - "transferAccountID": 4 + "transferAccountID": 8 }, { "amount": -2036, @@ -187,7 +187,7 @@ "name": "12" } ], - "transferAccountID": 4, + "transferAccountID": 8, "templateGroupID": 3 }, { @@ -219,7 +219,8 @@ }, { "templateName": "Random template", - "isExpenditure": true, + "amount": 2000, + "isExpenditure": false, "categoryID": 1, "name": "", "description": "", -- GitLab