diff --git a/client/nexacro-deploy/assets/app.js b/client/nexacro-deploy/assets/app.js
index fe05cfb..c503a14 100644
--- a/client/nexacro-deploy/assets/app.js
+++ b/client/nexacro-deploy/assets/app.js
@@ -434,9 +434,9 @@ function renderUploads() {
@@ -604,14 +604,6 @@ function shellContent(content) {
-
${content}
@@ -836,6 +828,14 @@ function bindEvents() {
});
}
+ document.querySelectorAll("[data-action='download-sample']").forEach((button) => {
+ button.addEventListener("click", () => {
+ const fiscalPeriod = document.getElementById("upload-period")?.value?.trim() || "2026-03";
+ const sampleCode = button.dataset.sampleCode;
+ window.location.href = `/api/uploads/samples/${sampleCode}/download?fiscalPeriod=${encodeURIComponent(fiscalPeriod)}`;
+ });
+ });
+
document.querySelectorAll("[data-action='delete-upload']").forEach((button) => {
button.addEventListener("click", async () => {
if (!isOperator()) {
diff --git a/server/api/src/main/java/com/hanwha/nexacrodemo/upload/WorkbookTemplateService.java b/server/api/src/main/java/com/hanwha/nexacrodemo/upload/WorkbookTemplateService.java
index d3f9aed..3c9a0aa 100644
--- a/server/api/src/main/java/com/hanwha/nexacrodemo/upload/WorkbookTemplateService.java
+++ b/server/api/src/main/java/com/hanwha/nexacrodemo/upload/WorkbookTemplateService.java
@@ -77,6 +77,13 @@ public class WorkbookTemplateService {
for (int index = 0; index < headers.size(); index++) {
header.createCell(index).setCellValue(headers.get(index));
}
+ int rowIndex = 1;
+ for (List rowValues : rows) {
+ Row row = dataSheet.createRow(rowIndex++);
+ for (int cellIndex = 0; cellIndex < rowValues.size(); cellIndex++) {
+ row.createCell(cellIndex).setCellValue(rowValues.get(cellIndex));
+ }
+ }
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
workbook.write(outputStream);
diff --git a/server/api/src/test/java/com/hanwha/nexacrodemo/upload/UploadValidationIntegrationTest.java b/server/api/src/test/java/com/hanwha/nexacrodemo/upload/UploadValidationIntegrationTest.java
index 59be133..1603933 100644
--- a/server/api/src/test/java/com/hanwha/nexacrodemo/upload/UploadValidationIntegrationTest.java
+++ b/server/api/src/test/java/com/hanwha/nexacrodemo/upload/UploadValidationIntegrationTest.java
@@ -7,7 +7,9 @@ import static org.springframework.test.web.servlet.request.MockMvcRequestBuilder
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
+import java.io.ByteArrayInputStream;
import org.junit.jupiter.api.Test;
+import org.apache.poi.ss.usermodel.WorkbookFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
@@ -111,6 +113,23 @@ class UploadValidationIntegrationTest {
.andExpect(status().isForbidden());
}
+ @Test
+ void sampleDownloadUsesRequestedFiscalPeriod() throws Exception {
+ MockHttpSession session = login("viewer", "demo1234");
+
+ MvcResult result = mockMvc.perform(get("/api/uploads/samples/trial-balance-valid/download")
+ .param("fiscalPeriod", "2026-04")
+ .session(session))
+ .andExpect(status().isOk())
+ .andReturn();
+
+ try (var workbook = WorkbookFactory.create(new ByteArrayInputStream(result.getResponse().getContentAsByteArray()))) {
+ var dataSheet = workbook.getSheet("DATA");
+ var firstDataRow = dataSheet.getRow(1);
+ org.junit.jupiter.api.Assertions.assertEquals("2026-04", firstDataRow.getCell(0).getStringCellValue());
+ }
+ }
+
private MockHttpSession login(String username, String password) throws Exception {
MvcResult result = mockMvc.perform(post("/api/auth/login")
.contentType(MediaType.APPLICATION_JSON)
diff --git a/templates/nexacro/preview-app.js.tpl b/templates/nexacro/preview-app.js.tpl
index 5a6f9d2..487c677 100644
--- a/templates/nexacro/preview-app.js.tpl
+++ b/templates/nexacro/preview-app.js.tpl
@@ -760,6 +760,14 @@ function bindEvents() {
});
}
+ document.querySelectorAll("[data-action='download-sample']").forEach((button) => {
+ button.addEventListener("click", () => {
+ const fiscalPeriod = document.getElementById("upload-period")?.value?.trim() || "2026-03";
+ const sampleCode = button.dataset.sampleCode;
+ window.location.href = `/api/uploads/samples/${sampleCode}/download?fiscalPeriod=${encodeURIComponent(fiscalPeriod)}`;
+ });
+ });
+
document.querySelectorAll("[data-action='delete-upload']").forEach((button) => {
button.addEventListener("click", async () => {
if (!isOperator()) {