diff --git a/src/main/java/com/ffii/lioner/modules/lioner/pdf/entity/Pdf.java b/src/main/java/com/ffii/lioner/modules/lioner/pdf/entity/Pdf.java new file mode 100644 index 0000000..65fc61b --- /dev/null +++ b/src/main/java/com/ffii/lioner/modules/lioner/pdf/entity/Pdf.java @@ -0,0 +1,69 @@ +package com.ffii.lioner.modules.lioner.pdf.entity; + +import java.time.LocalDate; +import java.time.LocalDateTime; + +import com.ffii.core.entity.BaseEntity; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Inheritance; +import jakarta.persistence.InheritanceType; +import jakarta.persistence.Table; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; + +@Entity +@Table(name = "filled_form") +@Inheritance(strategy=InheritanceType.TABLE_PER_CLASS) +public class Pdf extends BaseEntity{ + + @NotNull + @Column + private Long templateId; + + @Column + private Long clientId; + + @NotNull + @Column + private Long fileId; + + @Column + private String remarks; + + + public Long getTemplateId() { + return templateId; + } + + public void setTemplateId(Long templateId) { + this.templateId = templateId; + } + + public Long getClientId() { + return clientId; + } + + public void setClientId(Long clientId) { + this.clientId = clientId; + } + + public Long getFileId() { + return fileId; + } + + public void setFileId(Long fileId) { + this.fileId = fileId; + } + + public String getRemarks() { + return remarks; + } + + public void setRemarks(String remarks) { + this.remarks = remarks; + } + +} + diff --git a/src/main/java/com/ffii/lioner/modules/lioner/pdf/entity/PdfRepository.java b/src/main/java/com/ffii/lioner/modules/lioner/pdf/entity/PdfRepository.java new file mode 100644 index 0000000..2bd4e15 --- /dev/null +++ b/src/main/java/com/ffii/lioner/modules/lioner/pdf/entity/PdfRepository.java @@ -0,0 +1,10 @@ +package com.ffii.lioner.modules.lioner.pdf.entity; + +import java.util.List; +// import java.util.Map; + +import com.ffii.core.support.AbstractRepository; + +public interface PdfRepository extends AbstractRepository { + List findAllByClientId(Long clientId); +} diff --git a/src/main/java/com/ffii/lioner/modules/lioner/pdf/req/UpdatePdfReq.java b/src/main/java/com/ffii/lioner/modules/lioner/pdf/req/UpdatePdfReq.java new file mode 100644 index 0000000..f4fbca7 --- /dev/null +++ b/src/main/java/com/ffii/lioner/modules/lioner/pdf/req/UpdatePdfReq.java @@ -0,0 +1,47 @@ +package com.ffii.lioner.modules.lioner.pdf.req; + +public class UpdatePdfReq { + + private Long id; + + private Long templateId; + + private Long clientId; + + private String remarks; + + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public Long getTemplateId() { + return templateId; + } + + public void setTemplateId(Long templateId) { + this.templateId = templateId; + } + + public Long getClientId() { + return clientId; + } + + public void setClientId(Long clientId) { + this.clientId = clientId; + } + + public String getRemarks() { + return remarks; + } + + public void setRemarks(String remarks) { + this.remarks = remarks; + } + + +} diff --git a/src/main/java/com/ffii/lioner/modules/lioner/pdf/service/PdfService.java b/src/main/java/com/ffii/lioner/modules/lioner/pdf/service/PdfService.java new file mode 100644 index 0000000..5cc7624 --- /dev/null +++ b/src/main/java/com/ffii/lioner/modules/lioner/pdf/service/PdfService.java @@ -0,0 +1,237 @@ +package com.ffii.lioner.modules.lioner.pdf.service; + +import java.io.IOException; +import java.io.InputStream; + +import java.time.LocalDate; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; +import org.springframework.core.io.ByteArrayResource; +import org.springframework.core.io.ClassPathResource; + +import com.ffii.lioner.modules.lioner.pdf.entity.Pdf; +import com.ffii.lioner.modules.lioner.pdf.entity.PdfRepository; +import com.ffii.lioner.modules.lioner.entity.ImpEvent; +import com.ffii.lioner.modules.lioner.pdf.req.UpdatePdfReq; +import com.ffii.lioner.modules.lioner.service.FileService; +import com.ffii.lioner.modules.common.SecurityUtils; +import com.ffii.lioner.modules.common.service.AuditLogService; +import com.ffii.lioner.modules.master.entity.SubDivision; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.ffii.core.exception.NotFoundException; +import com.ffii.core.exception.UnprocessableEntityException; +import com.ffii.core.support.AbstractBaseEntityService; +import com.ffii.core.support.JdbcDao; +import com.ffii.core.utils.BeanUtils; +import com.ffii.core.utils.JsonUtils; +import com.ffii.core.utils.Params; + +import jakarta.persistence.Table; + +@Service +public class PdfService extends AbstractBaseEntityService { + + private String pdf_path = "templates/pdf/"; + + private AuditLogService auditLogService; + private FileService fileService; + + public PdfService(JdbcDao jdbcDao, PdfRepository repository, AuditLogService auditLogService, FileService fileService) { + super(jdbcDao, repository); + this.auditLogService = auditLogService; + this.fileService = fileService; + } + + // public Map getAuditLogObject(Map req){ + // StringBuilder sql = new StringBuilder("SELECT" + // + " e.id, " + // + " e.created, " + // + " e.createdBy AS createdById, " + // + " u1.name AS createdBy, " + // + " e.version, " + // + " e.modified, " + // + " e.modifiedBy AS modifiedById, " + // + " e.deleted, " + // + " u2.name AS modifiedBy, " + // + " e.name, " + // + " e.nameCht, " + // + " e.description, " + // + " e.region, " + // + " e.organization, " + // + " e.eventType, " + // + " e.frequency, " + // + " e.series, " + // + " e.startDate, " + // + " e.eventFrom, " + // + " e.eventTo, " + // + " e.applicationDeadline, " + // + " e.nextApplicationDate, " + // + " e.announcementDate, " + // + " e.awardDate, " + // + " e.reminderFlag, " + // + " e.reminderThreshold, " + // + " e.reminderInterval, " + // + " e.reminderLimit " + // + " FROM event e " + // + " LEFT JOIN `user` u1 ON u1.id = e.createdBy " + // + " LEFT JOIN `user` u2 ON u2.id = e.modifiedBy " + // + " WHERE e.id = :id " + // ); + // return jdbcDao.queryForMap(sql.toString(), req).get(); + // } + + public Map saveOrUpdate(String recordJson, MultipartFile file) { + ObjectMapper objectMapper = new ObjectMapper(); + UpdatePdfReq req; + try { + req = objectMapper.readValue(recordJson, UpdatePdfReq.class); + } catch (Exception e) { + throw new UnprocessableEntityException("Invalid JSON format"); + } + + Pdf instance; + // List onUsingIdList = new ArrayList(); + if(req.getId()>0){ + instance = find(req.getId()).get(); + fileService.deleteFile(instance.getFileId()); // Delete previous record - WIP + // onUsingIdList = this.getSelectedSubDivisionList(req.getId()); + } + else{ + instance = new Pdf(); + } + System.out.println(req.getTemplateId()); + BeanUtils.copyProperties(req, instance); + + Map pdf = new HashMap<>(); + Map response = new HashMap<>(); + + if (file.isEmpty()) { + throw new UnprocessableEntityException("No file uploaded."); + // return ResponseEntity.badRequest().body(Map.of("message", "No file uploaded.")); + } + try { + // Convert the MultipartFile to a byte array + byte[] pdfData = file.getBytes(); + + MultipartFile[] files = new MultipartFile[1]; + files[0] = file; + + try { + pdf = fileService.saveOrUpdate(files, (long) 0, "pdf"); + } catch (Exception e) { + System.out.println(e); + } + + // Save the PDF data to the database + // File pdfForm = new PdfForm(); + // pdfForm.setPdfData(pdfData); + // pdfFormRepository.save(pdfForm); // Assuming you have autowired this repository + + response.put("message", "PDF saved successfully."); + // response.put("id", pdfForm.getId().toString()); // Return the ID of the saved PDF + + } catch (IOException e) { + throw new UnprocessableEntityException( + "Failed to save PDF: " + + e + ); + // e.printStackTrace(); + // return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) + // .body(Map.of("message", "Failed to save PDF: " + e.getMessage())); + } + + Object fileIdList = pdf.get("fileIdList"); + List list = (List) fileIdList; + Long fileId = (long) list.get(0); + + instance.setFileId(fileId); + instance = save(instance); + + response = Map.of( + "id", instance.getId() + ); + + return response; + } + + public byte[] getTemplateForm() { + // Ensure the template PDF is in src/main/resources/template/pdf + ClassPathResource pdfFile = new ClassPathResource(pdf_path + "HSBC B24102883_fillable_Financial Needs Analysis for Individual Nov2024.pdf"); + if (!pdfFile.exists()) { + throw new UnprocessableEntityException("PDF template not found."); + } + try (InputStream is = pdfFile.getInputStream()) { + return is.readAllBytes(); + } catch (Exception e) { + throw new UnprocessableEntityException("Fail to load PDF: " + e); + } + + // return fileService.getfileBlob((long) 2); + } + + public List> list(Map args){ + StringBuilder sql = new StringBuilder("SELECT" + + " id, " + + " templateId, " + + " fileId, " + + " created, " + + " modified " + + " FROM filled_form " + + " WHERE deleted = FALSE " + ); + + if (args != null) { + if (args.containsKey("clientId")) + sql.append(" AND clientId = :clientId "); + if (args.containsKey("templateId")) + sql.append(" AND templateId = :templateId "); + if (args.containsKey("remarks")) + sql.append(" AND remarks LIKE :remarks "); + if (args.containsKey("createDateFrom")) + sql.append(" AND created >= :createDateFrom "); + if (args.containsKey("createDateTo")) + sql.append(" AND created <= :createDateTo "); + } + // sql.append(" ORDER BY id desc "); + + return jdbcDao.queryForList(sql.toString(), args); + } + + public Map loadPDF(Long id) { + // Pdf pdfInstance = find(id).orElseThrow(NotFoundException::new); + // byte[] blobBytes = fileService.getfileBlob(pdfInstance.getFileId()); + + StringBuilder sql = new StringBuilder("SELECT" + + " ff.*, " + + " fb.bytes AS blobValue" + + " FROM filled_form ff " + + " LEFT JOIN file_blob fb ON ff.fileId = fb.fileId " + + " WHERE ff.deleted = FALSE" + + " AND ff.id = :id " + ); + // Map result = jdbcDao.queryForMap(sql.toString(), Map.of("id", id)).orElseThrow(NotFoundException::new); + // result.put("blobValue", new ByteArrayResource(blobBytes)); + // return result; + return jdbcDao.queryForMap(sql.toString(), Map.of("id", id)).orElseThrow(NotFoundException::new); + } + + public Integer getIdByName(String name) { + StringBuilder sql = new StringBuilder("SELECT" + + " e.id " + + " FROM event e " + + " WHERE e.deleted = FALSE" + + " AND e.name = :name " + ); + + return jdbcDao.queryForInt(sql.toString(), Map.of("name", name)); + } + +} \ No newline at end of file diff --git a/src/main/java/com/ffii/lioner/modules/lioner/pdf/web/PdfController.java b/src/main/java/com/ffii/lioner/modules/lioner/pdf/web/PdfController.java new file mode 100644 index 0000000..23c82ff --- /dev/null +++ b/src/main/java/com/ffii/lioner/modules/lioner/pdf/web/PdfController.java @@ -0,0 +1,112 @@ +package com.ffii.lioner.modules.lioner.pdf.web; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.ServletRequestBindingException; +import org.springframework.web.bind.annotation.CrossOrigin; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.transaction.annotation.Isolation; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.multipart.MultipartFile; + +import com.ffii.core.response.DataRes; +import com.ffii.core.response.RecordsRes; +import com.ffii.core.utils.CriteriaArgsBuilder; + +import jakarta.servlet.http.HttpServletRequest; +import jakarta.validation.Valid; +import com.ffii.core.exception.NotFoundException; +import com.ffii.core.utils.Params; + +import com.ffii.lioner.modules.lioner.pdf.req.UpdatePdfReq; +import com.ffii.lioner.modules.lioner.pdf.service.PdfService; + + +@RestController +@RequestMapping("/pdf") +@CrossOrigin(origins = "", allowedHeaders = "") + +public class PdfController { + + private PdfService pdfService; + + public PdfController(PdfService pdfService) { + this.pdfService = pdfService; + } + + // @GetMapping("/{id}") + // public List> get(@PathVariable Long id) { + // return Map.of( + // Params.DATA, pdfService.findByClientId(id).orElseThrow(NotFoundException::new) + // ); + // } + + @GetMapping + public RecordsRes> list(HttpServletRequest request) throws ServletRequestBindingException { + return new RecordsRes<>(pdfService.list( + CriteriaArgsBuilder.withRequest(request) + .addInteger("clientId") + .addInteger("templateId") + .addDate("createDateFrom") + .addDate("createDateTo") + .addStringLike("remarks") + .build())); + } + + @GetMapping("/{id}") + public Map get(@PathVariable Long id) { + return pdfService.loadPDF(id); + // return Map.of( + // Params.DATA, pdfService.loadPDF(id) + // ); + } + + // Endpoint to serve the initial template PDF + @GetMapping(value = "/template", produces = MediaType.APPLICATION_PDF_VALUE) + public ResponseEntity getPdfTemplate() throws IOException { + + byte[] pdfBytes = pdfService.getTemplateForm(); + + HttpHeaders headers = new HttpHeaders(); + headers.setContentDispositionFormData("inline", "template_form.pdf"); + headers.setContentType(MediaType.APPLICATION_PDF); + return new ResponseEntity<>(pdfBytes, headers, HttpStatus.OK); + } + + // Endpoint to receive the filled PDF from the frontend + @Transactional(isolation = Isolation.SERIALIZABLE, rollbackFor = Exception.class, readOnly = false) + @PostMapping(value = "/save", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) + // public Map saveFilledPdf(@RequestParam("pdf") @Valid UpdatePdfReq req, @RequestParam("file") MultipartFile file) { + // public Map saveFilledPdf(@RequestParam("record") UpdatePdfReq req, @RequestParam("file") MultipartFile file) { + public Map saveFilledPdf(@RequestParam("record") String recordJson, @RequestParam("file") MultipartFile file) { + return Map.of( + Params.DATA,pdfService.saveOrUpdate(recordJson, file) + ); + } + + // Optional: Endpoint to receive just the form data (e.g., JSON) + @PostMapping("/saveFormData") + public ResponseEntity> saveFormData(@RequestBody Map formData) { + System.out.println("Received form data: " + formData); + Map response = new HashMap<>(); + response.put("message", "Form data received and processed successfully."); + return ResponseEntity.ok(response); + } +} diff --git a/src/main/java/com/ffii/lioner/modules/lioner/web/PdfController.java b/src/main/java/com/ffii/lioner/modules/lioner/web/PdfController.java deleted file mode 100644 index c6b8d4c..0000000 --- a/src/main/java/com/ffii/lioner/modules/lioner/web/PdfController.java +++ /dev/null @@ -1,86 +0,0 @@ -package com.ffii.lioner.modules.lioner.web; - -import java.io.IOException; -import java.io.InputStream; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.HashMap; -import java.util.Map; -import java.util.UUID; - -import org.springframework.core.io.ClassPathResource; -import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpStatus; -import org.springframework.http.MediaType; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.CrossOrigin; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.RestController; -import org.springframework.web.multipart.MultipartFile; - -@RestController -@RequestMapping("/pdf") -@CrossOrigin(origins = "", allowedHeaders = "") - -public class PdfController { - - private String pdf_path = "templates/pdf/"; - - // Endpoint to serve the initial template PDF - @GetMapping(value = "/template", produces = MediaType.APPLICATION_PDF_VALUE) - public ResponseEntity getPdfTemplate() throws IOException { - // Ensure your template PDF is in src/main/resources/static/ - ClassPathResource pdfFile = new ClassPathResource(pdf_path + "HSBC B24102883_fillable_Financial Needs Analysis for Individual Nov2024.pdf"); - if (!pdfFile.exists()) { - return ResponseEntity.status(HttpStatus.NOT_FOUND).body("PDF template not found.".getBytes()); - } - byte[] pdfBytes; - try (InputStream is = pdfFile.getInputStream()) { - pdfBytes = is.readAllBytes(); - } - - HttpHeaders headers = new HttpHeaders(); - headers.setContentDispositionFormData("inline", "template_form.pdf"); - headers.setContentType(MediaType.APPLICATION_PDF); - return new ResponseEntity<>(pdfBytes, headers, HttpStatus.OK); - } - - // Endpoint to receive the filled PDF from the frontend - @PostMapping(value = "/save", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) - public ResponseEntity> saveFilledPdf(@RequestParam("file") MultipartFile file) { - if (file.isEmpty()) { - return ResponseEntity.badRequest().body(Map.of("message", "No file uploaded.")); - } - try { - // Generate a unique filename and save to an 'uploads' directory - String fileName = "filled_form_" + UUID.randomUUID().toString() + ".pdf"; - Path filePath = Paths.get("uploads", fileName); - Files.createDirectories(filePath.getParent()); // Ensure directory exists - Files.copy(file.getInputStream(), filePath); - - Map response = new HashMap<>(); - response.put("message", "PDF saved successfully: " + fileName); - response.put("filePath", filePath.toString()); - return ResponseEntity.ok(response); - - } catch (IOException e) { - e.printStackTrace(); - return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) - .body(Map.of("message", "Failed to save PDF: " + e.getMessage())); - } - } - - // Optional: Endpoint to receive just the form data (e.g., JSON) - @PostMapping("/saveFormData") - public ResponseEntity> saveFormData(@RequestBody Map formData) { - System.out.println("Received form data: " + formData); - Map response = new HashMap<>(); - response.put("message", "Form data received and processed successfully."); - return ResponseEntity.ok(response); - } -} diff --git a/src/main/resources/db/changelog/changes/05_filled_form/01_add_filled_form_table.sql b/src/main/resources/db/changelog/changes/05_filled_form/01_add_filled_form_table.sql new file mode 100644 index 0000000..9a68c3d --- /dev/null +++ b/src/main/resources/db/changelog/changes/05_filled_form/01_add_filled_form_table.sql @@ -0,0 +1,20 @@ +--liquibase formatted sql + +--changeset kelvin:add filled form table + +CREATE TABLE `filled_form` ( + `id` int NOT NULL AUTO_INCREMENT, + `created` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + `createdBy` int DEFAULT NULL, + `version` int NOT NULL DEFAULT '0', + `modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + `modifiedBy` int DEFAULT NULL, + `deleted` tinyint(1) NOT NULL DEFAULT '0', + + `templateId` int(11) NOT NULL, + `clientId` int(11), + `fileId` int(11) NOT NULL, + `remarks` VARCHAR(600), + + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; \ No newline at end of file diff --git a/src/main/resources/db/changelog/changes/05_filled_form/02_update_client_table.sql b/src/main/resources/db/changelog/changes/05_filled_form/02_update_client_table.sql new file mode 100644 index 0000000..5c6efe4 --- /dev/null +++ b/src/main/resources/db/changelog/changes/05_filled_form/02_update_client_table.sql @@ -0,0 +1,7 @@ +--liquibase formatted sql + +--changeset kelvin:update client table + +ALTER TABLE `lionerdb`.`client` +CHANGE COLUMN `createdBy` `createdBy` INT NULL DEFAULT NULL , +CHANGE COLUMN `modifiedBy` `modifiedBy` INT NULL DEFAULT NULL ; \ No newline at end of file