diff --git a/hartmann-foto-documentation-app/pom.xml b/hartmann-foto-documentation-app/pom.xml index fccdd6b..48a540f 100644 --- a/hartmann-foto-documentation-app/pom.xml +++ b/hartmann-foto-documentation-app/pom.xml @@ -226,7 +226,7 @@ commons-io commons-io - ${version.commons-io} + 2.21.0 diff --git a/hartmann-foto-documentation-app/src/main/java/marketing/heyday/hartmann/fotodocumentation/core/model/QuestionnaireCustomer.java b/hartmann-foto-documentation-app/src/main/java/marketing/heyday/hartmann/fotodocumentation/core/model/QuestionnaireCustomer.java index 82ce87b..92fc5bc 100644 --- a/hartmann-foto-documentation-app/src/main/java/marketing/heyday/hartmann/fotodocumentation/core/model/QuestionnaireCustomer.java +++ b/hartmann-foto-documentation-app/src/main/java/marketing/heyday/hartmann/fotodocumentation/core/model/QuestionnaireCustomer.java @@ -20,14 +20,16 @@ import jakarta.persistence.*; @Entity @Table(name = "questionnaire_customer") +@NamedQuery(name = QuestionnaireCustomer.FIND_ALL, query = "select c from QuestionnaireCustomer c order by c.name") @NamedQuery(name = QuestionnaireCustomer.FIND_BY_NUMBER, query = "select c from QuestionnaireCustomer c where c.customerNumber = :customerNumber") public class QuestionnaireCustomer extends AbstractDateEntity { private static final long serialVersionUID = 1L; public static final String SEQUENCE = "questionnaire_customer_seq"; + public static final String FIND_ALL = "QuestionnaireCustomer.findAll"; public static final String FIND_BY_NUMBER = "QuestionnaireCustomer.findByNumber"; public static final String PARAM_NUMBER = "customerNumber"; - + @Id @Column(name = "customer_id", length = 22) @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = SEQUENCE) diff --git a/hartmann-foto-documentation-app/src/main/java/marketing/heyday/hartmann/fotodocumentation/core/query/QueryService.java b/hartmann-foto-documentation-app/src/main/java/marketing/heyday/hartmann/fotodocumentation/core/query/QueryService.java index 2ba80c7..20852c9 100644 --- a/hartmann-foto-documentation-app/src/main/java/marketing/heyday/hartmann/fotodocumentation/core/query/QueryService.java +++ b/hartmann-foto-documentation-app/src/main/java/marketing/heyday/hartmann/fotodocumentation/core/query/QueryService.java @@ -33,6 +33,14 @@ public class QueryService { @PersistenceContext private EntityManager eManager; + public T callNamedQueryList(String namedQuery, Param... objects) { + Query query = eManager.createNamedQuery(namedQuery); + for (Param param : objects) { + query.setParameter(param.name(), param.value()); + } + return (T) query.getResultList(); + } + public Optional callNamedQuerySingleResult(String namedQuery, Param... params) { return singleResult(eManager.createNamedQuery(namedQuery), Arrays.asList(params)); } diff --git a/hartmann-foto-documentation-app/src/main/java/marketing/heyday/hartmann/fotodocumentation/core/service/QuestionnaireCustomerService.java b/hartmann-foto-documentation-app/src/main/java/marketing/heyday/hartmann/fotodocumentation/core/service/QuestionnaireCustomerService.java index 214b6dd..e60ace0 100644 --- a/hartmann-foto-documentation-app/src/main/java/marketing/heyday/hartmann/fotodocumentation/core/service/QuestionnaireCustomerService.java +++ b/hartmann-foto-documentation-app/src/main/java/marketing/heyday/hartmann/fotodocumentation/core/service/QuestionnaireCustomerService.java @@ -5,6 +5,7 @@ import java.util.*; import org.apache.commons.lang3.StringUtils; import jakarta.annotation.security.PermitAll; +import jakarta.ejb.EJB; import jakarta.ejb.LocalBean; import jakarta.ejb.Stateless; import jakarta.inject.Inject; @@ -12,8 +13,10 @@ import jakarta.persistence.TypedQuery; import jakarta.persistence.criteria.*; import marketing.heyday.hartmann.fotodocumentation.core.model.Questionnaire; import marketing.heyday.hartmann.fotodocumentation.core.model.QuestionnaireCustomer; +import marketing.heyday.hartmann.fotodocumentation.core.query.QueryService; import marketing.heyday.hartmann.fotodocumentation.core.utils.CalendarUtil; import marketing.heyday.hartmann.fotodocumentation.core.utils.ExcelUtils; +import marketing.heyday.hartmann.fotodocumentation.core.utils.ZipExportUtils; import marketing.heyday.hartmann.fotodocumentation.rest.vo.QuestionnaireCustomerListValue; import marketing.heyday.hartmann.fotodocumentation.rest.vo.QuestionnaireCustomerValue; @@ -32,6 +35,12 @@ import marketing.heyday.hartmann.fotodocumentation.rest.vo.QuestionnaireCustomer @PermitAll public class QuestionnaireCustomerService extends AbstractService { + @EJB + private QueryService queryService; + + @Inject + private ZipExportUtils zipExportUtils; + @Inject private ExcelUtils excelUtils; @@ -100,11 +109,16 @@ public class QuestionnaireCustomerService extends AbstractService { return QuestionnaireCustomerValue.builder(customer); } + public Optional getExport() { + List customers = queryService.callNamedQueryList(QuestionnaireCustomer.FIND_ALL); + return zipExportUtils.getExport(customers); + } + public Optional getExport(Long id, Long questionnaireId) { QuestionnaireCustomer customer = entityManager.find(QuestionnaireCustomer.class, id); if (customer == null) { return Optional.empty(); - } + } List questionnaires = customer.getQuestionnaires().stream().sorted((x, y) -> x.getQuestionnaireDate().compareTo(y.getQuestionnaireDate())).toList(); diff --git a/hartmann-foto-documentation-app/src/main/java/marketing/heyday/hartmann/fotodocumentation/core/utils/ExcelUtils.java b/hartmann-foto-documentation-app/src/main/java/marketing/heyday/hartmann/fotodocumentation/core/utils/ExcelUtils.java index 41436e6..0187100 100644 --- a/hartmann-foto-documentation-app/src/main/java/marketing/heyday/hartmann/fotodocumentation/core/utils/ExcelUtils.java +++ b/hartmann-foto-documentation-app/src/main/java/marketing/heyday/hartmann/fotodocumentation/core/utils/ExcelUtils.java @@ -38,7 +38,6 @@ public class ExcelUtils { try (ByteArrayOutputStream bos = new ByteArrayOutputStream(); XSSFWorkbook workbook = new XSSFWorkbook()) { for (var questionnaire : questionnaires) { - //TODO: set sheet name writeSheet(workbook, customer, questionnaire); } diff --git a/hartmann-foto-documentation-app/src/main/java/marketing/heyday/hartmann/fotodocumentation/core/utils/ZipExportUtils.java b/hartmann-foto-documentation-app/src/main/java/marketing/heyday/hartmann/fotodocumentation/core/utils/ZipExportUtils.java new file mode 100644 index 0000000..31cc8b2 --- /dev/null +++ b/hartmann-foto-documentation-app/src/main/java/marketing/heyday/hartmann/fotodocumentation/core/utils/ZipExportUtils.java @@ -0,0 +1,55 @@ +package marketing.heyday.hartmann.fotodocumentation.core.utils; + +import java.io.IOException; +import java.util.List; +import java.util.Optional; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import jakarta.inject.Inject; +import marketing.heyday.hartmann.fotodocumentation.core.model.Questionnaire; +import marketing.heyday.hartmann.fotodocumentation.core.model.QuestionnaireCustomer; + +/** + * + *

Copyright: Copyright (c) 2024

+ *

Company: heyday Marketing GmbH

+ * @author Patrick Verboom + * @version 1.0 + * + * created: 23 Feb 2026 + */ + +public class ZipExportUtils { + private static final Log LOG = LogFactory.getLog(ZipExportUtils.class); + + @Inject + private ExcelUtils excelUtils; + + public Optional getExport(List customers) { + if (customers.isEmpty()) { + return Optional.empty(); + } + + try (ZipUtils zipUtils = new ZipUtils()) { + boolean hasContent = false; + for (var customer : customers) { + List questionnaires = customer.getQuestionnaires().stream().sorted((x, y) -> x.getQuestionnaireDate().compareTo(y.getQuestionnaireDate())).toList(); + var file = excelUtils.create(customer, questionnaires); + if (file.isPresent()) { + zipUtils.addFile(customer.getName() + ".xlsx", file.get()); + hasContent = true; + } + } + if (!hasContent) { + return Optional.empty(); + } + + return Optional.of(zipUtils.create()); + } catch (IOException ioe) { + LOG.error("Failed to create zip file " + ioe.getMessage(), ioe); + return Optional.empty(); + } + } +} diff --git a/hartmann-foto-documentation-app/src/main/java/marketing/heyday/hartmann/fotodocumentation/core/utils/ZipUtils.java b/hartmann-foto-documentation-app/src/main/java/marketing/heyday/hartmann/fotodocumentation/core/utils/ZipUtils.java new file mode 100644 index 0000000..d424927 --- /dev/null +++ b/hartmann-foto-documentation-app/src/main/java/marketing/heyday/hartmann/fotodocumentation/core/utils/ZipUtils.java @@ -0,0 +1,41 @@ +package marketing.heyday.hartmann.fotodocumentation.core.utils; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; + +/** + * + *

Copyright: Copyright (c) 2024

+ *

Company: heyday Marketing GmbH

+ * @author Patrick Verboom + * @version 1.0 + * + * created: 23 Feb 2026 + */ + +public class ZipUtils implements AutoCloseable { + private ByteArrayOutputStream baos = new ByteArrayOutputStream(); + private ZipOutputStream zos = new ZipOutputStream(baos); + + public void addFile(String name, byte[] file) throws IOException { + ZipEntry entry = new ZipEntry(name); + + zos.putNextEntry(entry); + zos.write(file); + zos.closeEntry(); + } + + public byte[] create() throws IOException { + zos.flush(); + zos.close(); + return baos.toByteArray(); + } + + @Override + public void close() throws IOException { + zos.close(); + baos.close(); + } +} diff --git a/hartmann-foto-documentation-app/src/main/java/marketing/heyday/hartmann/fotodocumentation/rest/QuestionnaireCustomerResource.java b/hartmann-foto-documentation-app/src/main/java/marketing/heyday/hartmann/fotodocumentation/rest/QuestionnaireCustomerResource.java index 32d409d..bb74d41 100644 --- a/hartmann-foto-documentation-app/src/main/java/marketing/heyday/hartmann/fotodocumentation/rest/QuestionnaireCustomerResource.java +++ b/hartmann-foto-documentation-app/src/main/java/marketing/heyday/hartmann/fotodocumentation/rest/QuestionnaireCustomerResource.java @@ -65,6 +65,28 @@ public class QuestionnaireCustomerResource { var retVal = questionnaireCustomerService.get(id); return Response.ok().entity(retVal).build(); } + + + @GZIP + @GET + @Path("exportall") + @Produces("application/zip") + @Operation(summary = "Get Export") + @ApiResponse(responseCode = "200", description = "Successfully retrieved export") + public Response doExport() { + LOG.debug("Create export for customer "); + Optional pdfOpt = questionnaireCustomerService.getExport(); + + if (pdfOpt.isEmpty()) { + return Response.status(Status.NOT_FOUND).build(); + } + + StreamingOutput streamingOutput = (OutputStream output) -> { + LOG.debug("Start writing content to OutputStream available bytes"); + output.write(pdfOpt.get()); + }; + return Response.status(Status.OK).entity(streamingOutput).build(); + } @GZIP @GET diff --git a/hartmann-foto-documentation-app/src/test/java/marketing/heyday/hartmann/fotodocumentation/core/utils/ExcelUtilsTest.java b/hartmann-foto-documentation-app/src/test/java/marketing/heyday/hartmann/fotodocumentation/core/utils/ExcelUtilsTest.java index ae9d296..bb7ad0a 100644 --- a/hartmann-foto-documentation-app/src/test/java/marketing/heyday/hartmann/fotodocumentation/core/utils/ExcelUtilsTest.java +++ b/hartmann-foto-documentation-app/src/test/java/marketing/heyday/hartmann/fotodocumentation/core/utils/ExcelUtilsTest.java @@ -3,17 +3,12 @@ package marketing.heyday.hartmann.fotodocumentation.core.utils; import static org.junit.jupiter.api.Assertions.*; import java.io.ByteArrayInputStream; -import java.io.File; -import java.io.FileOutputStream; import java.io.IOException; import java.util.Collections; import java.util.Date; import java.util.List; import java.util.Optional; -import org.apache.commons.io.IOUtils; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; import org.apache.poi.xssf.usermodel.XSSFRow; import org.apache.poi.xssf.usermodel.XSSFSheet; import org.apache.poi.xssf.usermodel.XSSFWorkbook; @@ -32,8 +27,7 @@ import marketing.heyday.hartmann.fotodocumentation.core.model.QuestionnaireCusto * * created: 19 Feb 2026 */ -class ExcelUtilsTest { - private static final Log LOG = LogFactory.getLog(ExcelUtilsTest.class); +class ExcelUtilsTest implements TestAble { private ExcelUtils excelUtils; @@ -103,7 +97,7 @@ class ExcelUtilsTest { try (XSSFWorkbook workbook = new XSSFWorkbook(new ByteArrayInputStream(bytes))) { assertEquals(3, workbook.getNumberOfSheets()); } - + writeToFile(bytes, "create_multipleQuestionnaires_createsSheetPerQuestionnaire.xlsx"); } @@ -190,7 +184,6 @@ class ExcelUtilsTest { assertNotNull(row.getCell(0)); } } - // --- create: null handling --- @@ -256,15 +249,4 @@ class ExcelUtilsTest { .questions(QuestionnaireJsonParserTest.testJson1) .build(); } - - public void writeToFile(final byte[] content, final String fileName) { - File file = new File("target/test/output/"); - file.mkdirs(); - try (FileOutputStream out = new FileOutputStream(new File(file, fileName))) { - - IOUtils.write(content, out); - } catch (Exception e) { - LOG.error("Error saveing pdf file", e); - } - } } diff --git a/hartmann-foto-documentation-app/src/test/java/marketing/heyday/hartmann/fotodocumentation/core/utils/QuestionnaireJsonParserTest.java b/hartmann-foto-documentation-app/src/test/java/marketing/heyday/hartmann/fotodocumentation/core/utils/QuestionnaireJsonParserTest.java index 8a7f5b6..41f9143 100644 --- a/hartmann-foto-documentation-app/src/test/java/marketing/heyday/hartmann/fotodocumentation/core/utils/QuestionnaireJsonParserTest.java +++ b/hartmann-foto-documentation-app/src/test/java/marketing/heyday/hartmann/fotodocumentation/core/utils/QuestionnaireJsonParserTest.java @@ -139,7 +139,7 @@ public class QuestionnaireJsonParserTest { [ { "id": "answer1", - "answer": 47, + "answer": "47", "selected": true } ] @@ -172,7 +172,7 @@ public class QuestionnaireJsonParserTest { { "id": "answer4", "answer": "Ontex", - "selected": true + "selected": false }, { "id": "answer5", @@ -205,7 +205,7 @@ public class QuestionnaireJsonParserTest { { "id": "answer1", "answer": "HARTMANN", - "selected": true + "selected": false }, { "id": "answer2", @@ -261,7 +261,7 @@ public class QuestionnaireJsonParserTest { { "id": "answer3", "answer": "Ontex", - "selected": true + "selected": false } ] } diff --git a/hartmann-foto-documentation-app/src/test/java/marketing/heyday/hartmann/fotodocumentation/core/utils/TestAble.java b/hartmann-foto-documentation-app/src/test/java/marketing/heyday/hartmann/fotodocumentation/core/utils/TestAble.java new file mode 100644 index 0000000..b273137 --- /dev/null +++ b/hartmann-foto-documentation-app/src/test/java/marketing/heyday/hartmann/fotodocumentation/core/utils/TestAble.java @@ -0,0 +1,33 @@ +package marketing.heyday.hartmann.fotodocumentation.core.utils; + +import java.io.File; +import java.io.FileOutputStream; + +import org.apache.commons.io.IOUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * + *

Copyright: Copyright (c) 2024

+ *

Company: heyday Marketing GmbH

+ * @author Patrick Verboom + * @version 1.0 + * + * created: 23 Feb 2026 + */ + +public interface TestAble { + public static final Log LOG = LogFactory.getLog(ExcelUtilsTest.class); + + default void writeToFile(final byte[] content, final String fileName) { + File file = new File("target/test/output/"); + file.mkdirs(); + try (FileOutputStream out = new FileOutputStream(new File(file, fileName))) { + + IOUtils.write(content, out); + } catch (Exception e) { + LOG.error("Error saveing pdf file", e); + } + } +} diff --git a/hartmann-foto-documentation-app/src/test/java/marketing/heyday/hartmann/fotodocumentation/core/utils/ZipExportUtilsTest.java b/hartmann-foto-documentation-app/src/test/java/marketing/heyday/hartmann/fotodocumentation/core/utils/ZipExportUtilsTest.java new file mode 100644 index 0000000..4df8f8d --- /dev/null +++ b/hartmann-foto-documentation-app/src/test/java/marketing/heyday/hartmann/fotodocumentation/core/utils/ZipExportUtilsTest.java @@ -0,0 +1,224 @@ +package marketing.heyday.hartmann.fotodocumentation.core.utils; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.lang.reflect.Field; +import java.util.*; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import marketing.heyday.hartmann.fotodocumentation.core.model.Questionnaire; +import marketing.heyday.hartmann.fotodocumentation.core.model.QuestionnaireCustomer; + +/** + * + *

Copyright: Copyright (c) 2024

+ *

Company: heyday Marketing GmbH

+ * @author Patrick Verboom + * @version 1.0 + * + * created: 23 Feb 2026 + */ +class ZipExportUtilsTest implements TestAble { + + private ZipExportUtils zipExportUtils; + private ExcelUtils excelUtils; + + @BeforeEach + void setUp() throws Exception { + zipExportUtils = new ZipExportUtils(); + excelUtils = mock(ExcelUtils.class); + + Field field = ZipExportUtils.class.getDeclaredField("excelUtils"); + field.setAccessible(true); + field.set(zipExportUtils, excelUtils); + } + + // --- getExport: empty input --- + + @Test + void getExport_emptyList_returnsEmpty() { + Optional result = zipExportUtils.getExport(Collections.emptyList()); + + assertTrue(result.isEmpty()); + } + + // --- getExport: single customer --- + + @Test + void getExport_singleCustomer_returnsPresent() { + QuestionnaireCustomer customer = createCustomerWithQuestionnaires("Müller Apotheke", 1); + when(excelUtils.create(any(), anyList())).thenReturn(Optional.of(new byte[] { 1, 2, 3 })); + + Optional result = zipExportUtils.getExport(List.of(customer)); + + assertTrue(result.isPresent()); + } + + @Test + void getExport_singleCustomer_returnsValidZip() throws IOException { + QuestionnaireCustomer customer = createCustomerWithQuestionnaires("Müller Apotheke", 1); + when(excelUtils.create(any(), anyList())).thenReturn(Optional.of(new byte[] { 1, 2, 3 })); + + byte[] bytes = zipExportUtils.getExport(List.of(customer)).orElseThrow(); + + List entries = getZipEntryNames(bytes); + assertEquals(1, entries.size()); + } + + @Test + void getExport_singleCustomer_zipEntryNamedAfterCustomer() throws IOException { + QuestionnaireCustomer customer = createCustomerWithQuestionnaires("Müller Apotheke", 1); + when(excelUtils.create(any(), anyList())).thenReturn(Optional.of(new byte[] { 1, 2, 3 })); + + byte[] bytes = zipExportUtils.getExport(List.of(customer)).orElseThrow(); + + List entries = getZipEntryNames(bytes); + assertEquals("Müller Apotheke.xlsx", entries.get(0)); + } + + @Test + void getExport_singleCustomer_zipEntryContainsExcelContent() throws IOException { + byte[] excelContent = new byte[] { 10, 20, 30, 40, 50 }; + QuestionnaireCustomer customer = createCustomerWithQuestionnaires("Test", 1); + when(excelUtils.create(any(), anyList())).thenReturn(Optional.of(excelContent)); + + byte[] bytes = zipExportUtils.getExport(List.of(customer)).orElseThrow(); + + byte[] entryContent = getZipEntryContent(bytes, "Test.xlsx"); + assertArrayEquals(excelContent, entryContent); + } + + // --- getExport: multiple customers --- + + @Test + void getExport_multipleCustomers_createsEntryPerCustomer() throws IOException { + QuestionnaireCustomer c1 = createCustomerWithQuestionnaires("Apotheke A", 1); + QuestionnaireCustomer c2 = createCustomerWithQuestionnaires("Apotheke B", 1); + QuestionnaireCustomer c3 = createCustomerWithQuestionnaires("Apotheke C", 1); + when(excelUtils.create(any(), anyList())).thenReturn(Optional.of(new byte[] { 1 })); + + byte[] bytes = zipExportUtils.getExport(List.of(c1, c2, c3)).orElseThrow(); + + List entries = getZipEntryNames(bytes); + assertEquals(3, entries.size()); + assertTrue(entries.contains("Apotheke A.xlsx")); + assertTrue(entries.contains("Apotheke B.xlsx")); + assertTrue(entries.contains("Apotheke C.xlsx")); + + writeToFile(bytes, "getExport_multipleCustomers_createsEntryPerCustomer.zip"); + } + + // --- getExport: excel creation fails --- + + @Test + void getExport_excelReturnsEmpty_returnsEmpty() { + QuestionnaireCustomer customer = createCustomerWithQuestionnaires("Test", 1); + when(excelUtils.create(any(), anyList())).thenReturn(Optional.empty()); + + Optional result = zipExportUtils.getExport(List.of(customer)); + + assertTrue(result.isEmpty()); + } + + @Test + void getExport_someExcelsFail_onlyIncludesSuccessful() throws IOException { + QuestionnaireCustomer c1 = createCustomerWithQuestionnaires("Success", 1); + QuestionnaireCustomer c2 = createCustomerWithQuestionnaires("Fail", 1); + when(excelUtils.create(eq(c1), anyList())).thenReturn(Optional.of(new byte[] { 1 })); + when(excelUtils.create(eq(c2), anyList())).thenReturn(Optional.empty()); + + byte[] bytes = zipExportUtils.getExport(List.of(c1, c2)).orElseThrow(); + + List entries = getZipEntryNames(bytes); + assertEquals(1, entries.size()); + assertEquals("Success.xlsx", entries.get(0)); + } + + // --- getExport: questionnaire sorting --- + + @Test + void getExport_questionnairesPassedSortedByDate() { + QuestionnaireCustomer customer = new QuestionnaireCustomer.Builder() + .name("Test").customerNumber("C-001").build(); + + Questionnaire q1 = new Questionnaire.Builder().questionnaireDate(new Date(2000)).build(); + Questionnaire q2 = new Questionnaire.Builder().questionnaireDate(new Date(1000)).build(); + Questionnaire q3 = new Questionnaire.Builder().questionnaireDate(new Date(3000)).build(); + customer.getQuestionnaires().addAll(Set.of(q1, q2, q3)); + + when(excelUtils.create(any(), anyList())).thenAnswer(invocation -> { + List questionnaires = invocation.getArgument(1); + for (int i = 0; i < questionnaires.size() - 1; i++) { + assertTrue(questionnaires.get(i).getQuestionnaireDate() + .compareTo(questionnaires.get(i + 1).getQuestionnaireDate()) <= 0, + "Questionnaires should be sorted by date"); + } + return Optional.of(new byte[] { 1 }); + }); + + zipExportUtils.getExport(List.of(customer)); + + verify(excelUtils).create(eq(customer), anyList()); + } + + // --- getExport: calls excelUtils correctly --- + + @Test + void getExport_callsExcelUtilsForEachCustomer() { + QuestionnaireCustomer c1 = createCustomerWithQuestionnaires("A", 1); + QuestionnaireCustomer c2 = createCustomerWithQuestionnaires("B", 1); + when(excelUtils.create(any(), anyList())).thenReturn(Optional.of(new byte[] { 1 })); + + zipExportUtils.getExport(List.of(c1, c2)); + + verify(excelUtils).create(eq(c1), anyList()); + verify(excelUtils).create(eq(c2), anyList()); + } + + // --- helpers --- + + private QuestionnaireCustomer createCustomerWithQuestionnaires(String name, int questionnaireCount) { + QuestionnaireCustomer customer = new QuestionnaireCustomer.Builder() + .name(name).customerNumber("C-" + name.hashCode()).build(); + + for (int i = 0; i < questionnaireCount; i++) { + Questionnaire q = new Questionnaire.Builder() + .questionnaireDate(new Date(1000L * (i + 1))) + .questions("[]") + .build(); + customer.getQuestionnaires().add(q); + } + return customer; + } + + private List getZipEntryNames(byte[] zipBytes) throws IOException { + List names = new ArrayList<>(); + try (ZipInputStream zis = new ZipInputStream(new ByteArrayInputStream(zipBytes))) { + ZipEntry entry; + while ((entry = zis.getNextEntry()) != null) { + names.add(entry.getName()); + } + } + return names; + } + + private byte[] getZipEntryContent(byte[] zipBytes, String entryName) throws IOException { + try (ZipInputStream zis = new ZipInputStream(new ByteArrayInputStream(zipBytes))) { + ZipEntry entry; + while ((entry = zis.getNextEntry()) != null) { + if (entry.getName().equals(entryName)) { + return zis.readAllBytes(); + } + } + } + throw new IOException("Entry not found: " + entryName); + } +} diff --git a/hartmann-foto-documentation-docker/src/test/java/marketing/heyday/hartmann/fotodocumentation/rest/AbstractTest.java b/hartmann-foto-documentation-docker/src/test/java/marketing/heyday/hartmann/fotodocumentation/rest/AbstractTest.java index 67db0e6..267371d 100644 --- a/hartmann-foto-documentation-docker/src/test/java/marketing/heyday/hartmann/fotodocumentation/rest/AbstractTest.java +++ b/hartmann-foto-documentation-docker/src/test/java/marketing/heyday/hartmann/fotodocumentation/rest/AbstractTest.java @@ -85,6 +85,8 @@ public abstract class AbstractTest { repDataSet.addReplacementObject("NULL_DATE", null); repDataSet.addReplacementObject("NULL_NUMBER", null); repDataSet.addReplacementObject("NULL_STRING", null); + repDataSet.addReplacementObject("QUESTIONNAIRE_JSON", QUESTIONNAIRE_JSON); + try { DatabaseOperation.CLEAN_INSERT.execute(conn, repDataSet); } finally { @@ -179,4 +181,323 @@ public abstract class AbstractTest { throw new RuntimeException(e); } } + + private static final String QUESTIONNAIRE_JSON = """ + [ + { + "id": "question1", + "title": "", + "order": 1, + "type": "singleChoice", + "data": + { + "answers": + [ + { + "id": "answer1", + "answer": "Nicht-kaufender Kunde", + "selected": true + }, + { + "id": "answer2", + "answer": "Bestandskunde", + "selected": false + } + ] + } + }, + { + "id": "question2", + "title": "Haben Sie Rezeptpatienten(GKV) für Inko?", + "order": 2, + "type": "singleChoice", + "data": + { + "answers": + [ + { + "id": "answer1", + "answer": "Ja", + "selected": false + }, + { + "id": "answer1", + "answer": "Nein", + "selected": true + } + ] + } + }, + { + "id": "question3", + "title": "Warum nicht?", + "order": 3, + "type": "multiplChoice", + "data": + { + "answers": + [ + { + "id": "answer1", + "answer": "Lagergründe", + "selected": false + }, + { + "id": "answer2", + "answer": "Wirtschaftlichkeitsgründe", + "selected": true + }, + { + "id": "answer3", + "answer": "Administrativer Aufwand", + "selected": true + }, + { + "id": "answer4", + "answer": "Personeller Aufwand", + "selected": false + } + ] + } + }, + { + "id": "question4", + "title": "Haben Sie Privatrezeptpatienten für inko?", + "order": 4, + "type": "singleChoice", + "data": + { + "answers": + [ + { + "id": "answer1", + "answer": "Ja", + "selected": false + }, + { + "id": "answer2", + "answer": "Nein", + "selected": true + } + ] + } + }, + { + "id": "question5", + "title": "Wie viele Patienten versorgen Sie regelmäßig? (Privat) un GKV", + "order": 5, + "type": "number", + "data": + { + "answers": + [ + { + "id": "answer1", + "answer": 47, + "selected": true + } + ] + } + }, + { + "id": "question6", + "title": "Mit welchem Herstellern arbeiten Sie zusammen?", + "order": 6, + "type": "multiplChoice", + "data": + { + "answers": + [ + { + "id": "answer1", + "answer": "HARTMANN", + "selected": false + }, + { + "id": "answer2", + "answer": "TZMO", + "selected": true + }, + { + "id": "answer3", + "answer": "Essity", + "selected": true + }, + { + "id": "answer4", + "answer": "Ontex", + "selected": false + }, + { + "id": "answer5", + "answer": "Param", + "selected": false + }, + { + "id": "answer6", + "answer": "Andere", + "selected": false + } + ] + } + }, + { + "id": "question7", + "title": "Was sind Ihre Gründe für die Zusammenarbeit?", + "order": 7, + "type": "matrix", + "data": + { + "questions": + [ + { + "id": "subq1", + "title": "Preis", + "order": 1, + "answers": + [ + { + "id": "answer1", + "answer": "HARTMANN", + "selected": false + }, + { + "id": "answer2", + "answer": "Essity", + "selected": true + }, + { + "id": "answer3", + "answer": "Ontex", + "selected": false + } + ] + }, + { + "id": "subq2", + "title": "Einkaufskondition", + "order": 2, + "answers": + [ + { + "id": "answer1", + "answer": "HARTMANN", + "selected": false + }, + { + "id": "answer2", + "answer": "Essity", + "selected": true + }, + { + "id": "answer3", + "answer": "Ontex", + "selected": false + } + ] + }, + { + "id": "subq3", + "title": "Qualität", + "order": 3, + "answers": + [ + { + "id": "answer1", + "answer": "HARTMANN", + "selected": false + }, + { + "id": "answer2", + "answer": "Essity", + "selected": true + }, + { + "id": "answer3", + "answer": "Ontex", + "selected": false + } + ] + } + ] + } + }, + { + "id": "question8", + "title": "Beziehen Sie Produkte direkt oder über den Großhandel?", + "order": 8, + "type": "singleChoice", + "data": + { + "answers": + [ + { + "id": "answer1", + "answer": "primär direkt", + "selected": false + }, + { + "id": "answer2", + "answer": "primär Großhandel", + "selected": true + }, + { + "id": "answer3", + "answer": "ptimär teils. Großhandel", + "selected": true + }, + { + "id": "answer4", + "answer": "unterschiedlich", + "selected": false + } + ] + } + }, + { + "id": "question9", + "title": "Gründe für Bezug?", + "order": 10, + "type": "singleChoice", + "data": + { + "answers": + [ + { + "id": "answer1", + "answer": "Umsatzziel mit Händler", + "selected": false + }, + { + "id": "answer2", + "answer": "Warenverfügbarkeit/ Liefergeschwindigkeit und Frequent", + "selected": true + }, + { + "id": "answer3", + "answer": "Einkaufskondition", + "selected": true + } + ] + } + }, + { + "id": "question10", + "title": "Weiter/Kommentare Hinweise?", + "order": 11, + "type": "freeText", + "data": + { + "answers": + [ + { + "id": "answer1", + "answer": "Kommentar eintragen", + "selected": true + } + ] + } + } + ] + """; } diff --git a/hartmann-foto-documentation-docker/src/test/java/marketing/heyday/hartmann/fotodocumentation/rest/QuestionnaireCustomerResourceTest.java b/hartmann-foto-documentation-docker/src/test/java/marketing/heyday/hartmann/fotodocumentation/rest/QuestionnaireCustomerResourceTest.java index febc070..a73696f 100644 --- a/hartmann-foto-documentation-docker/src/test/java/marketing/heyday/hartmann/fotodocumentation/rest/QuestionnaireCustomerResourceTest.java +++ b/hartmann-foto-documentation-docker/src/test/java/marketing/heyday/hartmann/fotodocumentation/rest/QuestionnaireCustomerResourceTest.java @@ -194,11 +194,29 @@ public class QuestionnaireCustomerResourceTest extends AbstractRestTest { String expected = fileToString(BASE_DOWNLOAD + "doGetCustomer.json"); jsonAssert(expected, text); } + + @Test + @Order(1) + public void doDownloadZip() throws IOException { + LOG.info("doDownload"); + + String authorization = getAuthorization(); + LOG.info("authorization: " + authorization); + String path = deploymentURL + PATH + "/exportall"; + Request request = Request.Get(path).addHeader("Accept", "application/zip") + .addHeader("Authorization", authorization); + + HttpResponse httpResponse = executeRequest(request); + int code = httpResponse.getStatusLine().getStatusCode(); + assertEquals(200, code); + + byte[] text = getResponse(httpResponse); + writeFile(text, "doDownload.zip"); + } @Test @Order(1) - @Disabled // FIXME: enable when we have implemented excel download - public void doDownload() throws IOException { + public void doDownloadExcel() throws IOException { LOG.info("doDownload"); String authorization = getAuthorization(); @@ -212,12 +230,12 @@ public class QuestionnaireCustomerResourceTest extends AbstractRestTest { assertEquals(200, code); byte[] text = getResponse(httpResponse); - writeFile(text, "doDownload.pdf"); + writeFile(text, "doDownload.xlsx"); } @Test @Order(1) - public void doDownloadNotExist() throws IOException { + public void doDownloadExcelNotExist() throws IOException { LOG.info("doDownloadNotExist"); String authorization = getAuthorization(); diff --git a/hartmann-foto-documentation-docker/src/test/resources/datasets/dataset.xml b/hartmann-foto-documentation-docker/src/test/resources/datasets/dataset.xml index d5329b5..b75bd0d 100644 --- a/hartmann-foto-documentation-docker/src/test/resources/datasets/dataset.xml +++ b/hartmann-foto-documentation-docker/src/test/resources/datasets/dataset.xml @@ -36,14 +36,14 @@ + questions="QUESTIONNAIRE_JSON"/> + questions="QUESTIONNAIRE_JSON"/> + questions="QUESTIONNAIRE_JSON"/> + questions="QUESTIONNAIRE_JSON"/> + questions="QUESTIONNAIRE_JSON"/> diff --git a/hartmann-foto-documentation-web/pom.xml b/hartmann-foto-documentation-web/pom.xml index f123110..d25d08d 100644 --- a/hartmann-foto-documentation-web/pom.xml +++ b/hartmann-foto-documentation-web/pom.xml @@ -20,43 +20,6 @@ true - diff --git a/hartmann-foto-documentation/pom.xml b/hartmann-foto-documentation/pom.xml index c8af823..7d43d9f 100644 --- a/hartmann-foto-documentation/pom.xml +++ b/hartmann-foto-documentation/pom.xml @@ -231,7 +231,7 @@ 6.2.5.Final 1.2 - 2.7 + 2.21.0 2.6 3.5 1.2