From 076b6b73e516dcccc2cf2d2d1af771331d7e73fc Mon Sep 17 00:00:00 2001 From: Robert Goldmann <deadlocker@gmx.de> Date: Wed, 30 Mar 2022 21:29:51 +0200 Subject: [PATCH] #691 - importer for transactions --- .../database/DatabaseService.java | 2 +- .../database/importer/TagImporter.java | 37 ++ .../importer/TransactionImporter.java | 44 +++ .../budgetmaster/services/ImportService.java | 56 +--- .../de/deadlocker8/budgetmaster/tags/Tag.java | 3 +- .../transactions/Transaction.java | 3 +- .../unit/database/ImportServiceTest.java | 61 ---- .../importer/TransactionImporterTest.java | 317 ++++++++++++++++++ 8 files changed, 407 insertions(+), 116 deletions(-) create mode 100644 src/main/java/de/deadlocker8/budgetmaster/database/importer/TagImporter.java create mode 100644 src/main/java/de/deadlocker8/budgetmaster/database/importer/TransactionImporter.java create mode 100644 src/test/java/de/deadlocker8/budgetmaster/unit/database/importer/TransactionImporterTest.java diff --git a/src/main/java/de/deadlocker8/budgetmaster/database/DatabaseService.java b/src/main/java/de/deadlocker8/budgetmaster/database/DatabaseService.java index a694dc11c..a77b411db 100644 --- a/src/main/java/de/deadlocker8/budgetmaster/database/DatabaseService.java +++ b/src/main/java/de/deadlocker8/budgetmaster/database/DatabaseService.java @@ -81,7 +81,7 @@ public class DatabaseService public void reset() { - final List<Resettable> services = List.of(this.transactionService, templateService, templateGroupService, categoryService, accountService, tagService, chartService, iconService, imageService); + final List<Resettable> services = List.of(transactionService, templateService, templateGroupService, categoryService, accountService, tagService, chartService, iconService, imageService); for(Resettable service : services) { diff --git a/src/main/java/de/deadlocker8/budgetmaster/database/importer/TagImporter.java b/src/main/java/de/deadlocker8/budgetmaster/database/importer/TagImporter.java new file mode 100644 index 000000000..2a01e8a52 --- /dev/null +++ b/src/main/java/de/deadlocker8/budgetmaster/database/importer/TagImporter.java @@ -0,0 +1,37 @@ +package de.deadlocker8.budgetmaster.database.importer; + +import de.deadlocker8.budgetmaster.services.EntityType; +import de.deadlocker8.budgetmaster.tags.Tag; +import de.deadlocker8.budgetmaster.tags.TagRepository; + +public class TagImporter extends ItemImporter<Tag> +{ + public TagImporter(TagRepository tagRepository) + { + super(tagRepository, EntityType.TAGS, false); + } + + @Override + protected int importSingleItem(Tag tag) + { + if(!(repository instanceof TagRepository repository)) + { + throw new IllegalArgumentException("Invalid repository type"); + } + + final Tag existingTag = repository.findByName(tag.getName()); + if(existingTag == null) + { + final Tag newTag = repository.save(new Tag(tag.getName())); + return newTag.getID(); + } + + return tag.getID(); + } + + @Override + protected String getNameForItem(Tag item) + { + return String.valueOf(item.getName()); + } +} diff --git a/src/main/java/de/deadlocker8/budgetmaster/database/importer/TransactionImporter.java b/src/main/java/de/deadlocker8/budgetmaster/database/importer/TransactionImporter.java new file mode 100644 index 000000000..354b865a3 --- /dev/null +++ b/src/main/java/de/deadlocker8/budgetmaster/database/importer/TransactionImporter.java @@ -0,0 +1,44 @@ +package de.deadlocker8.budgetmaster.database.importer; + +import de.deadlocker8.budgetmaster.services.EntityType; +import de.deadlocker8.budgetmaster.transactions.Transaction; +import de.deadlocker8.budgetmaster.transactions.TransactionRepository; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.text.MessageFormat; + +public class TransactionImporter extends ItemImporter<Transaction> +{ + private static final Logger LOGGER = LoggerFactory.getLogger(TransactionImporter.class); + + private final TagImporter tagImporter; + + public TransactionImporter(TransactionRepository transactionRepository, TagImporter tagImporter) + { + super(transactionRepository, EntityType.TRANSACTION, true); + this.tagImporter = tagImporter; + } + + @Override + protected int importSingleItem(Transaction transaction) + { + if(!(repository instanceof TransactionRepository repository)) + { + throw new IllegalArgumentException("Invalid repository type"); + } + + LOGGER.debug(MessageFormat.format("Importing transaction with name: {0}, date: {1}", transaction.getName(), transaction.getDate())); + tagImporter.importItems(transaction.getTags()); + transaction.setID(null); + final Transaction newTransaction = repository.save(transaction); + + return newTransaction.getID(); + } + + @Override + protected String getNameForItem(Transaction item) + { + return item.getName(); + } +} diff --git a/src/main/java/de/deadlocker8/budgetmaster/services/ImportService.java b/src/main/java/de/deadlocker8/budgetmaster/services/ImportService.java index 1dad5b5ae..190c101f4 100644 --- a/src/main/java/de/deadlocker8/budgetmaster/services/ImportService.java +++ b/src/main/java/de/deadlocker8/budgetmaster/services/ImportService.java @@ -86,7 +86,9 @@ public class ImportService new IconImporter(iconRepository).importItems(database.getIcons()); importResultItems.add(new CategoryImporter(categoryRepository).importItems(database.getCategories())); importResultItems.add(new AccountImporter(accountRepository).importItems(database.getAccounts(), accountMatchList)); - importResultItems.add(importTransactions()); + + final TagImporter tagImporter = new TagImporter(tagRepository); + importResultItems.add(new TransactionImporter(transactionRepository, tagImporter).importItems(database.getTransactions())); if(importTemplateGroups) { @@ -137,56 +139,6 @@ public class ImportService return MessageFormat.format("{0}: {1} ({2})", errorMessage, e.getClass().getName(), e.getMessage()); } - private ImportResultItem importTransactions() - { - List<Transaction> transactions = database.getTransactions(); - LOGGER.debug(MessageFormat.format("Importing {0} transactions...", transactions.size())); - - int numberOfImportedTransactions = 0; - - for(int i = 0; i < transactions.size(); i++) - { - Transaction transaction = transactions.get(i); - try - { - LOGGER.debug(MessageFormat.format("Importing transaction {0}/{1} (name: {2}, date: {3})", i + 1, transactions.size(), transaction.getName(), transaction.getDate())); - updateTagsForItem(transaction); - transaction.setID(null); - transactionRepository.save(transaction); - - numberOfImportedTransactions++; - } - catch(Exception e) - { - final String errorMessage = MessageFormat.format("Error while importing transaction with name \"{0}\" from {1}", transaction.getName(), transaction.getDate().format(DateTimeFormatter.ofPattern(DateFormatStyle.NORMAL.getKey()))); - LOGGER.error(errorMessage, e); - collectedErrorMessages.add(formatErrorMessage(errorMessage, e)); - } - } - - LOGGER.debug(MessageFormat.format("Importing transactions DONE ({0}/{1})", numberOfImportedTransactions, transactions.size())); - return new ImportResultItem(EntityType.TRANSACTION, numberOfImportedTransactions, transactions.size(), collectedErrorMessages); - } - - public void updateTagsForItem(TransactionBase item) - { - List<Tag> tags = item.getTags(); - for(int i = 0; i < tags.size(); i++) - { - Tag currentTag = tags.get(i); - Tag existingTag = tagRepository.findByName(currentTag.getName()); - if(existingTag == null) - { - final Tag newTag = tagRepository.save(new Tag(currentTag.getName())); - tags.set(i, newTag); - } - else - { - tags.set(i, existingTag); - } - } - } - private ImportResultItem importTemplateGroups() { List<TemplateGroup> templateGroups = database.getTemplateGroups(); @@ -290,7 +242,7 @@ public class ImportService try { LOGGER.debug(MessageFormat.format("Importing template {0}/{1} (templateName: {2})", i + 1, templates.size(), template.getTemplateName())); - updateTagsForItem(template); +// updateTagsForItem(template); template.setID(null); if(!importTemplateGroups || template.getTemplateGroup() == null) diff --git a/src/main/java/de/deadlocker8/budgetmaster/tags/Tag.java b/src/main/java/de/deadlocker8/budgetmaster/tags/Tag.java index d5281062a..e9e8aa26a 100644 --- a/src/main/java/de/deadlocker8/budgetmaster/tags/Tag.java +++ b/src/main/java/de/deadlocker8/budgetmaster/tags/Tag.java @@ -3,6 +3,7 @@ package de.deadlocker8.budgetmaster.tags; import com.google.gson.annotations.Expose; import de.deadlocker8.budgetmaster.templates.Template; import de.deadlocker8.budgetmaster.transactions.Transaction; +import de.deadlocker8.budgetmaster.utils.ProvidesID; import javax.persistence.*; import javax.validation.constraints.NotNull; @@ -11,7 +12,7 @@ import java.util.List; import java.util.Objects; @Entity -public class Tag +public class Tag implements ProvidesID { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) diff --git a/src/main/java/de/deadlocker8/budgetmaster/transactions/Transaction.java b/src/main/java/de/deadlocker8/budgetmaster/transactions/Transaction.java index 8f2a3a650..c6bfc4133 100644 --- a/src/main/java/de/deadlocker8/budgetmaster/transactions/Transaction.java +++ b/src/main/java/de/deadlocker8/budgetmaster/transactions/Transaction.java @@ -8,6 +8,7 @@ import de.deadlocker8.budgetmaster.categories.CategoryType; import de.deadlocker8.budgetmaster.repeating.RepeatingOption; import de.deadlocker8.budgetmaster.tags.Tag; import de.deadlocker8.budgetmaster.utils.DateHelper; +import de.deadlocker8.budgetmaster.utils.ProvidesID; import org.springframework.format.annotation.DateTimeFormat; import javax.persistence.*; @@ -17,7 +18,7 @@ import java.util.List; import java.util.Objects; @Entity -public class Transaction implements TransactionBase +public class Transaction implements TransactionBase, ProvidesID { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) 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 f74b0da6d..6996d9e22 100644 --- a/src/test/java/de/deadlocker8/budgetmaster/unit/database/ImportServiceTest.java +++ b/src/test/java/de/deadlocker8/budgetmaster/unit/database/ImportServiceTest.java @@ -16,9 +16,6 @@ import de.deadlocker8.budgetmaster.database.accountmatches.AccountMatchList; import de.deadlocker8.budgetmaster.icon.Icon; import de.deadlocker8.budgetmaster.icon.IconRepository; import de.deadlocker8.budgetmaster.icon.IconService; -import de.deadlocker8.budgetmaster.images.Image; -import de.deadlocker8.budgetmaster.images.ImageFileExtension; -import de.deadlocker8.budgetmaster.images.ImageRepository; import de.deadlocker8.budgetmaster.images.ImageService; import de.deadlocker8.budgetmaster.repeating.RepeatingTransactionUpdater; import de.deadlocker8.budgetmaster.services.ImportService; @@ -81,64 +78,6 @@ class ImportServiceTest @InjectMocks private ImportService importService; - - @Test - void test_updateTagsForItem_ExistingTag() - { - Account account1 = new Account("Account_1", AccountType.CUSTOM); - account1.setID(2); - - Tag existingTag = new Tag("ExistingTag"); - existingTag.setID(2); - - Transaction transaction1 = new Transaction(); - transaction1.setAccount(account1); - transaction1.setName("ShouldGoInAccount_1"); - transaction1.setAmount(200); - transaction1.setDate(LocalDate.of(2018, 10, 3)); - List<Tag> tags = new ArrayList<>(); - tags.add(existingTag); - transaction1.setTags(tags); - - Mockito.when(tagRepository.findByName(existingTag.getName())).thenReturn(existingTag); - - importService.updateTagsForItem(transaction1); - assertThat(transaction1.getTags()).hasSize(1); - assertThat(transaction1.getTags().get(0)) - .hasFieldOrPropertyWithValue("ID", 2) - .hasFieldOrPropertyWithValue("name", existingTag.getName()); - } - - @Test - void test_updateTagsForItem_NewTag() - { - Account account1 = new Account("Account_1", AccountType.CUSTOM); - account1.setID(2); - - Tag newTag = new Tag("NewTag"); - newTag.setID(5); - - Transaction transaction1 = new Transaction(); - transaction1.setAccount(account1); - transaction1.setName("ShouldGoInAccount_1"); - transaction1.setAmount(200); - transaction1.setDate(LocalDate.of(2018, 10, 3)); - List<Tag> tags = new ArrayList<>(); - tags.add(newTag); - transaction1.setTags(tags); - - Tag savedTag = new Tag("NewTag"); - savedTag.setID(1); - Mockito.when(tagRepository.save(Mockito.any(Tag.class))).thenReturn(savedTag); - Mockito.when(tagRepository.findByName(newTag.getName())).thenReturn(null); - - importService.updateTagsForItem(transaction1); - assertThat(transaction1.getTags()).hasSize(1); - assertThat(transaction1.getTags().get(0)) - .hasFieldOrPropertyWithValue("ID", 1) - .hasFieldOrPropertyWithValue("name", newTag.getName()); - } - @Test void test_importFullDatabase() { diff --git a/src/test/java/de/deadlocker8/budgetmaster/unit/database/importer/TransactionImporterTest.java b/src/test/java/de/deadlocker8/budgetmaster/unit/database/importer/TransactionImporterTest.java new file mode 100644 index 000000000..575a5540e --- /dev/null +++ b/src/test/java/de/deadlocker8/budgetmaster/unit/database/importer/TransactionImporterTest.java @@ -0,0 +1,317 @@ +package de.deadlocker8.budgetmaster.unit.database.importer; + +import de.deadlocker8.budgetmaster.accounts.Account; +import de.deadlocker8.budgetmaster.accounts.AccountRepository; +import de.deadlocker8.budgetmaster.accounts.AccountType; +import de.deadlocker8.budgetmaster.categories.Category; +import de.deadlocker8.budgetmaster.categories.CategoryRepository; +import de.deadlocker8.budgetmaster.categories.CategoryType; +import de.deadlocker8.budgetmaster.database.importer.IconImporter; +import de.deadlocker8.budgetmaster.database.importer.TagImporter; +import de.deadlocker8.budgetmaster.database.importer.TransactionImporter; +import de.deadlocker8.budgetmaster.icon.Icon; +import de.deadlocker8.budgetmaster.icon.IconRepository; +import de.deadlocker8.budgetmaster.images.Image; +import de.deadlocker8.budgetmaster.images.ImageFileExtension; +import de.deadlocker8.budgetmaster.images.ImageRepository; +import de.deadlocker8.budgetmaster.repeating.RepeatingOption; +import de.deadlocker8.budgetmaster.repeating.endoption.RepeatingEndAfterXTimes; +import de.deadlocker8.budgetmaster.repeating.modifier.RepeatingModifierDays; +import de.deadlocker8.budgetmaster.services.EntityType; +import de.deadlocker8.budgetmaster.services.ImportResultItem; +import de.deadlocker8.budgetmaster.tags.Tag; +import de.deadlocker8.budgetmaster.tags.TagRepository; +import de.deadlocker8.budgetmaster.transactions.Transaction; +import de.deadlocker8.budgetmaster.transactions.TransactionRepository; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.junit.jupiter.SpringExtension; + +import java.time.LocalDate; +import java.util.List; + +import static org.assertj.core.api.Assertions.as; +import static org.assertj.core.api.Assertions.assertThat; + +@ExtendWith(SpringExtension.class) +@DataJpaTest +@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD) +class TransactionImporterTest +{ + @Autowired + private AccountRepository accountRepository; + + @Autowired + private CategoryRepository categoryRepository; + + @Autowired + private TransactionRepository transactionRepository; + + @Autowired + private TagRepository tagRepository; + + @Test + void test_importNormalTransaction() + { + Category category = new Category("Awesome Category", "#ff0000", CategoryType.CUSTOM); + category = categoryRepository.save(category); + + Account account = new Account("Awesome Account", AccountType.CUSTOM); + account = accountRepository.save(account); + + final Transaction transaction = new Transaction(); + transaction.setID(15); + transaction.setName("My transaction"); + transaction.setAmount(-100); + transaction.setIsExpenditure(true); + transaction.setCategory(category); + transaction.setAccount(account); + transaction.setTags(List.of()); + transaction.setDate(LocalDate.of(2022, 3, 30)); + transaction.setDescription("Lorem Ipsum"); + + final TagImporter tagImporter = new TagImporter(tagRepository); + final TransactionImporter importer = new TransactionImporter(transactionRepository, tagImporter); + final ImportResultItem resultItem = importer.importItems(List.of(transaction)); + + final ImportResultItem expected = new ImportResultItem(EntityType.TRANSACTION, 1, 1, List.of()); + assertThat(resultItem).isEqualTo(expected); + + final List<Transaction> transactions = transactionRepository.findAll(); + assertThat(transactions).hasSize(1); + final Transaction actualTransaction = transactions.get(0); + assertThat(actualTransaction) + .hasFieldOrPropertyWithValue("ID", 1) + .hasFieldOrPropertyWithValue("name", "My transaction") + .hasFieldOrPropertyWithValue("amount", -100) + .hasFieldOrPropertyWithValue("isExpenditure", true) + .hasFieldOrPropertyWithValue("category", category) + .hasFieldOrPropertyWithValue("account", account) + .hasFieldOrPropertyWithValue("date", LocalDate.of(2022, 3, 30)) + .hasFieldOrPropertyWithValue("description","Lorem Ipsum") + .hasFieldOrPropertyWithValue("repeatingOption", null) + .hasFieldOrPropertyWithValue("transferAccount", null); + assertThat(actualTransaction.getTags()).isEmpty(); + } + + @Test + void test_importTransferTransaction() + { + Category category = new Category("Awesome Category", "#ff0000", CategoryType.CUSTOM); + category = categoryRepository.save(category); + + Account account = new Account("Awesome Account", AccountType.CUSTOM); + account = accountRepository.save(account); + + Account transferAccount = new Account("Transfer Account", AccountType.CUSTOM); + transferAccount = accountRepository.save(transferAccount); + + final Transaction transaction = new Transaction(); + transaction.setID(15); + transaction.setName("My transaction"); + transaction.setAmount(-100); + transaction.setIsExpenditure(true); + transaction.setCategory(category); + transaction.setAccount(account); + transaction.setTransferAccount(transferAccount); + transaction.setTags(List.of()); + transaction.setDate(LocalDate.of(2022, 3, 30)); + transaction.setDescription("Lorem Ipsum"); + + final TagImporter tagImporter = new TagImporter(tagRepository); + final TransactionImporter importer = new TransactionImporter(transactionRepository, tagImporter); + final ImportResultItem resultItem = importer.importItems(List.of(transaction)); + + final ImportResultItem expected = new ImportResultItem(EntityType.TRANSACTION, 1, 1, List.of()); + assertThat(resultItem).isEqualTo(expected); + + final List<Transaction> transactions = transactionRepository.findAll(); + assertThat(transactions).hasSize(1); + final Transaction actualTransaction = transactions.get(0); + assertThat(actualTransaction) + .hasFieldOrPropertyWithValue("ID", 1) + .hasFieldOrPropertyWithValue("name", "My transaction") + .hasFieldOrPropertyWithValue("amount", -100) + .hasFieldOrPropertyWithValue("isExpenditure", true) + .hasFieldOrPropertyWithValue("category", category) + .hasFieldOrPropertyWithValue("account", account) + .hasFieldOrPropertyWithValue("date", LocalDate.of(2022, 3, 30)) + .hasFieldOrPropertyWithValue("description","Lorem Ipsum") + .hasFieldOrPropertyWithValue("repeatingOption", null) + .hasFieldOrPropertyWithValue("transferAccount", transferAccount); + assertThat(actualTransaction.getTags()).isEmpty(); + } + + @Test + void test_importRepeatingTransaction() + { + Category category = new Category("Awesome Category", "#ff0000", CategoryType.CUSTOM); + category = categoryRepository.save(category); + + Account account = new Account("Awesome Account", AccountType.CUSTOM); + account = accountRepository.save(account); + + final Transaction transaction = new Transaction(); + transaction.setID(15); + transaction.setName("My transaction"); + transaction.setAmount(-100); + transaction.setIsExpenditure(true); + transaction.setCategory(category); + transaction.setAccount(account); + transaction.setTags(List.of()); + final LocalDate date = LocalDate.of(2022, 3, 30); + transaction.setDate(date); + transaction.setDescription("Lorem Ipsum"); + + final RepeatingOption repeatingOption = new RepeatingOption(date, new RepeatingModifierDays(2), new RepeatingEndAfterXTimes(3)); + transaction.setRepeatingOption(repeatingOption); + + final TagImporter tagImporter = new TagImporter(tagRepository); + final TransactionImporter importer = new TransactionImporter(transactionRepository, tagImporter); + final ImportResultItem resultItem = importer.importItems(List.of(transaction)); + + final ImportResultItem expected = new ImportResultItem(EntityType.TRANSACTION, 1, 1, List.of()); + assertThat(resultItem).isEqualTo(expected); + + final List<Transaction> transactions = transactionRepository.findAll(); + assertThat(transactions).hasSize(1); + final Transaction actualTransaction = transactions.get(0); + assertThat(actualTransaction) + .hasFieldOrPropertyWithValue("ID", 1) + .hasFieldOrPropertyWithValue("name", "My transaction") + .hasFieldOrPropertyWithValue("amount", -100) + .hasFieldOrPropertyWithValue("isExpenditure", true) + .hasFieldOrPropertyWithValue("category", category) + .hasFieldOrPropertyWithValue("account", account) + .hasFieldOrPropertyWithValue("date", date) + .hasFieldOrPropertyWithValue("description","Lorem Ipsum") + .hasFieldOrPropertyWithValue("repeatingOption", repeatingOption) + .hasFieldOrPropertyWithValue("transferAccount", null); + assertThat(actualTransaction.getTags()).isEmpty(); + } + + @Test + void test_importTransactionWithTags() + { + Category category = new Category("Awesome Category", "#ff0000", CategoryType.CUSTOM); + category = categoryRepository.save(category); + + Account account = new Account("Awesome Account", AccountType.CUSTOM); + account = accountRepository.save(account); + + final Transaction transaction = new Transaction(); + transaction.setID(15); + transaction.setName("My transaction"); + transaction.setAmount(-100); + transaction.setIsExpenditure(true); + transaction.setCategory(category); + transaction.setAccount(account); + transaction.setDate(LocalDate.of(2022, 3, 30)); + transaction.setDescription("Lorem Ipsum"); + + final Tag tag1 = new Tag("0815"); + tag1.setID(1); + final Tag tag2 = new Tag("Apple Pie"); + tag2.setID(2); + + transaction.setTags(List.of(tag1, tag2)); + + final TagImporter tagImporter = new TagImporter(tagRepository); + final TransactionImporter importer = new TransactionImporter(transactionRepository, tagImporter); + final ImportResultItem resultItem = importer.importItems(List.of(transaction)); + + final ImportResultItem expected = new ImportResultItem(EntityType.TRANSACTION, 1, 1, List.of()); + assertThat(resultItem).isEqualTo(expected); + + final List<Transaction> transactions = transactionRepository.findAll(); + assertThat(transactions).hasSize(1); + final Transaction actualTransaction = transactions.get(0); + assertThat(actualTransaction) + .hasFieldOrPropertyWithValue("ID", 1) + .hasFieldOrPropertyWithValue("name", "My transaction") + .hasFieldOrPropertyWithValue("amount", -100) + .hasFieldOrPropertyWithValue("isExpenditure", true) + .hasFieldOrPropertyWithValue("category", category) + .hasFieldOrPropertyWithValue("account", account) + .hasFieldOrPropertyWithValue("date", LocalDate.of(2022, 3, 30)) + .hasFieldOrPropertyWithValue("description","Lorem Ipsum") + .hasFieldOrPropertyWithValue("repeatingOption", null) + .hasFieldOrPropertyWithValue("transferAccount", null); + + final Tag expectedTag1 = new Tag("0815"); + expectedTag1.setID(1); + final Tag expectedTag2 = new Tag("Apple Pie"); + expectedTag2.setID(2); + assertThat(actualTransaction.getTags()) + .containsExactly(expectedTag1, expectedTag2); + } + + @Test + void test_importMultipleTransactionWithSomeSimilarTags() + { + Category category = new Category("Awesome Category", "#ff0000", CategoryType.CUSTOM); + category = categoryRepository.save(category); + + Account account = new Account("Awesome Account", AccountType.CUSTOM); + account = accountRepository.save(account); + + final Tag tag1 = new Tag("0815"); + tag1.setID(1); + final Tag tag2 = new Tag("Apple Pie"); + tag2.setID(2); + + final Transaction transaction = new Transaction(); + transaction.setID(15); + transaction.setName("My transaction"); + transaction.setAmount(-100); + transaction.setIsExpenditure(true); + transaction.setCategory(category); + transaction.setAccount(account); + transaction.setDate(LocalDate.of(2022, 3, 30)); + transaction.setTags(List.of(tag1, tag2)); + + final Transaction transaction2 = new Transaction(); + transaction2.setID(16); + transaction2.setName("My transaction 2"); + transaction2.setAmount(-250); + transaction2.setIsExpenditure(true); + transaction2.setCategory(category); + transaction2.setAccount(account); + transaction2.setDate(LocalDate.of(2022, 3, 30)); + transaction2.setTags(List.of(tag1)); + + final TagImporter tagImporter = new TagImporter(tagRepository); + final TransactionImporter importer = new TransactionImporter(transactionRepository, tagImporter); + final ImportResultItem resultItem = importer.importItems(List.of(transaction, transaction2)); + + final ImportResultItem expected = new ImportResultItem(EntityType.TRANSACTION, 2, 2, List.of()); + assertThat(resultItem).isEqualTo(expected); + + final List<Transaction> transactions = transactionRepository.findAll(); + assertThat(transactions).hasSize(2); + final Transaction actualTransaction = transactions.get(0); + assertThat(actualTransaction) + .hasFieldOrPropertyWithValue("ID", 1) + .hasFieldOrPropertyWithValue("name", "My transaction"); + + final Tag expectedTag1 = new Tag("0815"); + expectedTag1.setID(1); + final Tag expectedTag2 = new Tag("Apple Pie"); + expectedTag2.setID(2); + assertThat(actualTransaction.getTags()) + .containsExactly(expectedTag1, expectedTag2); + + final Transaction actualTransaction2 = transactions.get(1); + assertThat(actualTransaction2) + .hasFieldOrPropertyWithValue("ID", 2) + .hasFieldOrPropertyWithValue("name", "My transaction 2"); + + assertThat(actualTransaction2.getTags()) + .containsExactly(expectedTag1); + + assertThat(tagRepository.findAll()).hasSize(2); + } +} \ No newline at end of file -- GitLab