23 changed files with 1092 additions and 22 deletions
-
17.env
-
41src/main/java/com/adjustment_file_management_system/springboot/controller/OCFileDataController.java
-
47src/main/java/com/adjustment_file_management_system/springboot/mapper/OCFileDataMapper.java
-
7src/main/java/com/adjustment_file_management_system/springboot/model/FileData.java
-
118src/main/java/com/adjustment_file_management_system/springboot/model/OCFileData.java
-
4src/main/java/com/adjustment_file_management_system/springboot/repository/FileDataRepository.java
-
11src/main/java/com/adjustment_file_management_system/springboot/repository/OCFileDataRepository.java
-
25src/main/java/com/adjustment_file_management_system/springboot/request_dto/AddOCFileDataRequestDTO.java
-
21src/main/java/com/adjustment_file_management_system/springboot/request_dto/OCFileDataRequestDTO.java
-
27src/main/java/com/adjustment_file_management_system/springboot/response_dto/OCFileDataResponseDTO.java
-
48src/main/java/com/adjustment_file_management_system/springboot/rest_controller/OCFileDataRestController.java
-
16src/main/java/com/adjustment_file_management_system/springboot/service/FileDataService.java
-
195src/main/java/com/adjustment_file_management_system/springboot/service/OCFileDataService.java
-
6src/main/resources/application.properties
-
51src/main/resources/static/js/add-oc-file-script.js
-
16src/main/resources/static/js/view-file-script.js
-
211src/main/resources/static/js/view-oc-file-script.js
-
14src/main/resources/templates/file/reports.html
-
14src/main/resources/templates/file/view-file.html
-
4src/main/resources/templates/fragments/navbar.html
-
4src/main/resources/templates/home/home.html
-
78src/main/resources/templates/oc-file/add-oc-file.html
-
139src/main/resources/templates/oc-file/view-oc-file.html
@ -1,9 +1,10 @@ |
|||
# DB_URL=jdbc:oracle:thin:@10.1.0.14:1521:chncorp |
|||
# DB_USERNAME=fileadjust |
|||
# DB_PASSWORD=fileadjust |
|||
# FORCE_HTTPS=false |
|||
DB_URL=jdbc:oracle:thin:@10.1.0.14:1521:chncorp |
|||
DB_USERNAME=fileadjust |
|||
DB_PASSWORD=fileadjust |
|||
FORCE_HTTPS=false |
|||
|
|||
DB_URL=jdbc:oracle:thin:@//localhost:1522/XEPDB1 |
|||
DB_USERNAME=system |
|||
DB_PASSWORD=root |
|||
FORCE_HTTPS=false |
|||
##### testing db connection ##### |
|||
# DB_URL=jdbc:oracle:thin:@//localhost:1522/XEPDB1 |
|||
# DB_USERNAME=fileadjust |
|||
# DB_PASSWORD=root |
|||
# FORCE_HTTPS=false |
|||
@ -0,0 +1,41 @@ |
|||
package com.adjustment_file_management_system.springboot.controller; |
|||
|
|||
import java.util.List; |
|||
|
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.stereotype.Controller; |
|||
import org.springframework.ui.Model; |
|||
import org.springframework.web.bind.annotation.GetMapping; |
|||
import org.springframework.web.bind.annotation.RequestMapping; |
|||
|
|||
import com.adjustment_file_management_system.springboot.response_dto.FileDataResponseDTO; |
|||
import com.adjustment_file_management_system.springboot.response_dto.OCFileDataResponseDTO; |
|||
import com.adjustment_file_management_system.springboot.service.FileDataService; |
|||
import com.adjustment_file_management_system.springboot.service.OCFileDataService; |
|||
|
|||
@Controller |
|||
@RequestMapping("/oc-file") |
|||
public class OCFileDataController { |
|||
|
|||
@Autowired |
|||
FileDataService fileDataSer; |
|||
|
|||
@Autowired |
|||
OCFileDataService ocFileDataSer; |
|||
|
|||
@GetMapping("/view-oc-file") |
|||
public String ocFile(Model model) { |
|||
List<OCFileDataResponseDTO> ocFileDataList = ocFileDataSer.getAllOcFileData(); |
|||
model.addAttribute("ocFileDataList", ocFileDataList); |
|||
model.addAttribute("curPage", "view-oc-file"); |
|||
return "oc-file/view-oc-file"; |
|||
} |
|||
|
|||
@GetMapping("/add-oc-file") |
|||
public String addOcFile(Model model) { |
|||
List<FileDataResponseDTO> fileDataResDTOs = fileDataSer.getAllFileData(); |
|||
model.addAttribute("fileDatum", fileDataResDTOs); |
|||
return "oc-file/add-oc-file"; |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,47 @@ |
|||
package com.adjustment_file_management_system.springboot.mapper; |
|||
|
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.stereotype.Component; |
|||
|
|||
import com.adjustment_file_management_system.springboot.model.OCFileData; |
|||
import com.adjustment_file_management_system.springboot.repository.FileDataRepository; |
|||
import com.adjustment_file_management_system.springboot.request_dto.OCFileDataRequestDTO; |
|||
import com.adjustment_file_management_system.springboot.response_dto.OCFileDataResponseDTO; |
|||
|
|||
@Component |
|||
public class OCFileDataMapper { |
|||
|
|||
@Autowired |
|||
private FileDataRepository fileDataRepo; |
|||
|
|||
public OCFileData toOcFileDataEnt(OCFileDataRequestDTO ocFileDataRequestDTO, Long fileDataId) { |
|||
OCFileData ocFileData = new OCFileData(); |
|||
ocFileData.setOcFileSub(ocFileDataRequestDTO.getOcFileSub()); |
|||
ocFileData.setOcFileUplDate(ocFileDataRequestDTO.getOcFileUplDate()); |
|||
ocFileData.setOcFilePath(ocFileDataRequestDTO.getOcFilePath()); |
|||
ocFileData.setFileData(fileDataRepo.findById(fileDataId).get()); |
|||
return ocFileData; |
|||
} |
|||
|
|||
public OCFileDataResponseDTO toOcFileDataResDTO(OCFileData ocFileData) { |
|||
OCFileDataResponseDTO dto = new OCFileDataResponseDTO(); |
|||
dto.setOcFileDataId(ocFileData.getOcFileDataId()); |
|||
dto.setOcFileSub(ocFileData.getOcFileSub()); |
|||
dto.setOcFilePath(ocFileData.getOcFilePath()); |
|||
dto.setOcFileUplDate(ocFileData.getOcFileUplDate()); |
|||
if (ocFileData.getFileData() != null) { |
|||
dto.setFileDataId(ocFileData.getFileData().getFileDataId()); |
|||
dto.setFileDataNum(ocFileData.getFileData().getFileDataNum()); |
|||
dto.setFinYear(ocFileData.getFileData().getFinYear()); |
|||
if (ocFileData.getFileData().getAdvType() != null) { |
|||
dto.setAdvType(ocFileData.getFileData().getAdvType().getAdvType()); |
|||
} |
|||
if (ocFileData.getFileData().getFundType() != null) { |
|||
dto.setFundType(ocFileData.getFileData().getFundType().getFundType()); |
|||
} |
|||
dto.setVendName(ocFileData.getFileData().getVendName()); |
|||
} |
|||
return dto; |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,118 @@ |
|||
package com.adjustment_file_management_system.springboot.model; |
|||
|
|||
import java.util.Date; |
|||
|
|||
import org.springframework.data.annotation.CreatedBy; |
|||
import org.springframework.data.annotation.CreatedDate; |
|||
import org.springframework.data.annotation.LastModifiedBy; |
|||
import org.springframework.data.annotation.LastModifiedDate; |
|||
import org.springframework.data.jpa.domain.support.AuditingEntityListener; |
|||
|
|||
import jakarta.persistence.Column; |
|||
import jakarta.persistence.Entity; |
|||
import jakarta.persistence.EntityListeners; |
|||
import jakarta.persistence.FetchType; |
|||
import jakarta.persistence.GeneratedValue; |
|||
import jakarta.persistence.GenerationType; |
|||
import jakarta.persistence.Id; |
|||
import jakarta.persistence.JoinColumn; |
|||
import jakarta.persistence.ManyToOne; |
|||
import jakarta.persistence.OneToOne; |
|||
import jakarta.persistence.PrePersist; |
|||
import jakarta.persistence.PreUpdate; |
|||
import jakarta.persistence.SequenceGenerator; |
|||
import jakarta.persistence.Table; |
|||
import jakarta.persistence.Temporal; |
|||
import jakarta.persistence.TemporalType; |
|||
import jakarta.validation.constraints.NotBlank; |
|||
import jakarta.validation.constraints.NotNull; |
|||
import lombok.AllArgsConstructor; |
|||
import lombok.Getter; |
|||
import lombok.NoArgsConstructor; |
|||
import lombok.Setter; |
|||
import lombok.ToString; |
|||
|
|||
@Entity |
|||
@EntityListeners(AuditingEntityListener.class) |
|||
@Getter |
|||
@Setter |
|||
@ToString(doNotUseGetters = true, includeFieldNames = true, exclude = { "fileData", "createdBy", |
|||
"updatedBy" }) |
|||
@AllArgsConstructor |
|||
@NoArgsConstructor |
|||
// For local DB Server |
|||
/* @Table(name = "tbl_file_data_det", schema = "GCC") */ |
|||
// For Production DB Server |
|||
@Table(name = "tbl_oc_file_data_det", schema = "fileadjust") |
|||
public class OCFileData { |
|||
|
|||
@Id |
|||
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "oc_file_data_seq") |
|||
/* |
|||
* @SequenceGenerator(name = "oc_file_data_seq", sequenceName = |
|||
* "oc_file_data_id_seq", |
|||
* allocationSize = 1, schema = "GCC") |
|||
*/ |
|||
@SequenceGenerator(name = "oc_file_data_seq", sequenceName = "oc_file_data_id_seq", allocationSize = 1, schema = "fileadjust") |
|||
@Column(name = "oc_file_data_id", nullable = false, updatable = false) |
|||
private Long ocFileDataId; |
|||
|
|||
@Column(name = "oc_file_sub", nullable = false, length = 1000) |
|||
@NotBlank |
|||
private String ocFileSub; |
|||
|
|||
@Column(name = "oc_file_path", nullable = false, length = 1000) |
|||
@NotBlank |
|||
private String ocFilePath; |
|||
|
|||
@Column(name = "oc_file_upl_date", nullable = false) |
|||
@Temporal(TemporalType.DATE) |
|||
@NotNull |
|||
private Date ocFileUplDate; |
|||
|
|||
@OneToOne(fetch = FetchType.LAZY) |
|||
@JoinColumn(name = "file_data_id", referencedColumnName = "file_data_id", nullable = false, unique = true) |
|||
private FileData fileData; |
|||
|
|||
@CreatedBy |
|||
@ManyToOne(fetch = FetchType.LAZY) |
|||
@JoinColumn(name = "created_by_usr_id", referencedColumnName = "usr_id", updatable = false, nullable = false) |
|||
private AppUser createdBy; |
|||
|
|||
@LastModifiedBy |
|||
@ManyToOne(fetch = FetchType.LAZY) |
|||
@JoinColumn(name = "updated_by_usr_id", referencedColumnName = "usr_id", nullable = false) |
|||
private AppUser updatedBy; |
|||
|
|||
// User timestamp |
|||
@Temporal(TemporalType.TIMESTAMP) |
|||
@Column(name = "usr_created_timestamp", nullable = false, updatable = false) |
|||
private Date usrCreatedTimestamp; |
|||
|
|||
@Temporal(TemporalType.TIMESTAMP) |
|||
@Column(name = "usr_updated_timestamp", nullable = false) |
|||
private Date usrUpdatedTimestamp; |
|||
|
|||
// DB-managed timestamps |
|||
@CreatedDate |
|||
@Temporal(TemporalType.TIMESTAMP) |
|||
@Column(updatable = false, nullable = false, name = "db_created_timestamp") |
|||
private Date dbCreatedTimestamp; |
|||
|
|||
@LastModifiedDate |
|||
@Temporal(TemporalType.TIMESTAMP) |
|||
@Column(nullable = false, name = "db_updated_timestamp") |
|||
private Date dbUpdatedTimestamp; |
|||
|
|||
@PrePersist |
|||
public void onCreate() { |
|||
this.usrCreatedTimestamp = new Date(); |
|||
this.usrUpdatedTimestamp = new Date(); |
|||
} |
|||
|
|||
@PreUpdate |
|||
public void onUpdate() { |
|||
this.usrUpdatedTimestamp = new Date(); |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,11 @@ |
|||
package com.adjustment_file_management_system.springboot.repository; |
|||
|
|||
import org.springframework.data.jpa.repository.JpaRepository; |
|||
import org.springframework.stereotype.Repository; |
|||
|
|||
import com.adjustment_file_management_system.springboot.model.OCFileData; |
|||
|
|||
@Repository |
|||
public interface OCFileDataRepository extends JpaRepository<OCFileData, Long> { |
|||
|
|||
} |
|||
@ -0,0 +1,25 @@ |
|||
package com.adjustment_file_management_system.springboot.request_dto; |
|||
|
|||
import java.util.Date; |
|||
|
|||
import org.springframework.format.annotation.DateTimeFormat; |
|||
import org.springframework.web.multipart.MultipartFile; |
|||
|
|||
import lombok.AllArgsConstructor; |
|||
import lombok.Getter; |
|||
import lombok.NoArgsConstructor; |
|||
import lombok.Setter; |
|||
import lombok.ToString; |
|||
|
|||
@Getter |
|||
@Setter |
|||
@ToString(doNotUseGetters = true, includeFieldNames = true) |
|||
@AllArgsConstructor |
|||
@NoArgsConstructor |
|||
public class AddOCFileDataRequestDTO { |
|||
private MultipartFile ocFile; |
|||
@DateTimeFormat(pattern = "dd-MM-yyyy") |
|||
private Date ocFileUplDate; |
|||
private String ocFileSub; |
|||
private String fileNum; |
|||
} |
|||
@ -0,0 +1,21 @@ |
|||
package com.adjustment_file_management_system.springboot.request_dto; |
|||
|
|||
import java.util.Date; |
|||
|
|||
import lombok.AllArgsConstructor; |
|||
import lombok.Getter; |
|||
import lombok.NoArgsConstructor; |
|||
import lombok.Setter; |
|||
import lombok.ToString; |
|||
|
|||
@Getter |
|||
@Setter |
|||
@ToString(doNotUseGetters = true, includeFieldNames = true) |
|||
@NoArgsConstructor |
|||
@AllArgsConstructor |
|||
public class OCFileDataRequestDTO { |
|||
private String fileNum; |
|||
private Date ocFileUplDate; |
|||
private String ocFileSub; |
|||
private String ocFilePath; |
|||
} |
|||
@ -0,0 +1,27 @@ |
|||
package com.adjustment_file_management_system.springboot.response_dto; |
|||
|
|||
import java.util.Date; |
|||
|
|||
import lombok.AllArgsConstructor; |
|||
import lombok.Getter; |
|||
import lombok.NoArgsConstructor; |
|||
import lombok.Setter; |
|||
import lombok.ToString; |
|||
|
|||
@Getter |
|||
@Setter |
|||
@ToString(doNotUseGetters = true, includeFieldNames = true) |
|||
@AllArgsConstructor |
|||
@NoArgsConstructor |
|||
public class OCFileDataResponseDTO { |
|||
private Long ocFileDataId; |
|||
private String ocFileSub; |
|||
private String ocFilePath; |
|||
private Date ocFileUplDate; |
|||
private Long fileDataId; |
|||
private String fileDataNum; |
|||
private String finYear; |
|||
private String advType; |
|||
private String fundType; |
|||
private String vendName; |
|||
} |
|||
@ -0,0 +1,48 @@ |
|||
package com.adjustment_file_management_system.springboot.rest_controller; |
|||
|
|||
import java.text.SimpleDateFormat; |
|||
import java.util.Date; |
|||
|
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.beans.propertyeditors.CustomDateEditor; |
|||
import org.springframework.beans.propertyeditors.CustomNumberEditor; |
|||
import org.springframework.http.HttpStatus; |
|||
import org.springframework.http.ResponseEntity; |
|||
import org.springframework.web.bind.WebDataBinder; |
|||
import org.springframework.web.bind.annotation.InitBinder; |
|||
import org.springframework.web.bind.annotation.ModelAttribute; |
|||
import org.springframework.web.bind.annotation.PostMapping; |
|||
import org.springframework.web.bind.annotation.RequestMapping; |
|||
import org.springframework.web.bind.annotation.RestController; |
|||
|
|||
import com.adjustment_file_management_system.springboot.request_dto.AddOCFileDataRequestDTO; |
|||
import com.adjustment_file_management_system.springboot.response_dto.ServiceResponseDTO; |
|||
import com.adjustment_file_management_system.springboot.service.OCFileDataService; |
|||
|
|||
@RestController |
|||
@RequestMapping("/api/oc-file") |
|||
public class OCFileDataRestController { |
|||
|
|||
@Autowired |
|||
private OCFileDataService OcFileDataSer; |
|||
|
|||
@InitBinder |
|||
public void initBinder(WebDataBinder binder) { |
|||
SimpleDateFormat sdf = new SimpleDateFormat("dd-MM-yyyy"); |
|||
sdf.setLenient(false); |
|||
// true = allow empty string to be converted to null |
|||
binder.registerCustomEditor(Date.class, new CustomDateEditor(sdf, true)); |
|||
binder.registerCustomEditor(Double.class, new CustomNumberEditor(Double.class, true)); |
|||
} |
|||
|
|||
@PostMapping("/add-oc-file") |
|||
public ResponseEntity<ServiceResponseDTO> addFile(@ModelAttribute AddOCFileDataRequestDTO addOcFileDataReqDTO) { |
|||
ServiceResponseDTO serResDTO = OcFileDataSer.addOcFileData(addOcFileDataReqDTO); |
|||
if (serResDTO.isResStat()) { |
|||
return ResponseEntity.ok(serResDTO); |
|||
} else { |
|||
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(serResDTO); |
|||
} |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,195 @@ |
|||
package com.adjustment_file_management_system.springboot.service; |
|||
|
|||
import java.io.IOException; |
|||
import java.time.format.DateTimeFormatter; |
|||
import java.util.ArrayList; |
|||
import java.util.Date; |
|||
import java.util.HashMap; |
|||
import java.util.List; |
|||
import java.util.Map; |
|||
import java.util.function.Consumer; |
|||
|
|||
import org.slf4j.Logger; |
|||
import org.slf4j.LoggerFactory; |
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.stereotype.Service; |
|||
import org.springframework.transaction.annotation.Transactional; |
|||
import org.springframework.web.multipart.MultipartFile; |
|||
|
|||
import com.adjustment_file_management_system.springboot.mapper.FileDataMapper; |
|||
import com.adjustment_file_management_system.springboot.mapper.OCFileDataMapper; |
|||
import com.adjustment_file_management_system.springboot.repository.FileDataRepository; |
|||
import com.adjustment_file_management_system.springboot.repository.OCFileDataRepository; |
|||
import com.adjustment_file_management_system.springboot.repository.AdvanceTypeRepository; |
|||
import com.adjustment_file_management_system.springboot.repository.FundTypeRepository; |
|||
import com.adjustment_file_management_system.springboot.model.FileData; |
|||
import com.adjustment_file_management_system.springboot.model.AdvanceType; |
|||
import com.adjustment_file_management_system.springboot.model.FundType; |
|||
import com.adjustment_file_management_system.springboot.request_dto.AddOCFileDataRequestDTO; |
|||
import com.adjustment_file_management_system.springboot.request_dto.OCFileDataRequestDTO; |
|||
import com.adjustment_file_management_system.springboot.response_dto.FileDataResponseDTO; |
|||
import com.adjustment_file_management_system.springboot.response_dto.OCFileDataResponseDTO; |
|||
import com.adjustment_file_management_system.springboot.response_dto.ServiceResponseDTO; |
|||
import java.util.Calendar; |
|||
|
|||
@Service |
|||
public class OCFileDataService { |
|||
|
|||
private static final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd-MM-yyyy"); |
|||
|
|||
private static final Logger logger = LoggerFactory.getLogger(FileDataService.class); |
|||
|
|||
private static final Date placeHolderDate = new Date(0); |
|||
|
|||
@Autowired |
|||
private FileDataService fileDataSer; |
|||
|
|||
@Autowired |
|||
private FileDataMapper fileDataMap; |
|||
|
|||
@Autowired |
|||
private FileDataRepository fileDataRepo; |
|||
|
|||
@Autowired |
|||
private OCFileDataRepository ocFileDataRepo; |
|||
|
|||
@Autowired |
|||
private AdvanceTypeRepository advTypeRepo; |
|||
|
|||
@Autowired |
|||
private FundTypeRepository fundTypeRepo; |
|||
|
|||
@Autowired |
|||
private OCFileDataMapper ocFileDataMap; |
|||
|
|||
@Transactional(rollbackFor = Exception.class) |
|||
public ServiceResponseDTO addOcFileData(AddOCFileDataRequestDTO addOcFileDataReqDTO) { |
|||
List<String> uploadedFiles = new ArrayList<String>(); // Track uploaded file paths |
|||
try { |
|||
String fileNum = addOcFileDataReqDTO.getFileNum().trim(); |
|||
|
|||
Long fileDataId = fileDataRepo.findFileDataIdByFileNum(fileNum); |
|||
|
|||
if (fileDataId == null) { |
|||
// Create parent FileData entity |
|||
FileData newFileData = new FileData(); |
|||
newFileData.setFileDataNum(fileNum); |
|||
newFileData.setFinYear(determineFinYear(fileNum)); |
|||
|
|||
// Get or create AdvanceType |
|||
AdvanceType advType = advTypeRepo.findAdvTypeByAdvType("ELCOT") |
|||
.orElseGet(() -> advTypeRepo.findAdvTypeByAdvType("OTHER") |
|||
.orElseGet(() -> { |
|||
List<AdvanceType> all = advTypeRepo.findAll(); |
|||
if (!all.isEmpty()) return all.get(0); |
|||
AdvanceType newType = new AdvanceType(); |
|||
newType.setAdvType("ELCOT"); |
|||
return advTypeRepo.save(newType); |
|||
})); |
|||
newFileData.setAdvType(advType); |
|||
|
|||
// Get or create FundType |
|||
FundType fundType = fundTypeRepo.findAll().stream().findFirst().orElseGet(() -> { |
|||
FundType newType = new FundType(); |
|||
newType.setFundType("GENERAL FUND"); |
|||
return fundTypeRepo.save(newType); |
|||
}); |
|||
newFileData.setFundType(fundType); |
|||
|
|||
newFileData.setVendName("ELCOT"); |
|||
newFileData.setAsFilePath("-"); |
|||
newFileData.setAsFileGenDate(placeHolderDate); |
|||
newFileData.setPiFilePath("-"); |
|||
newFileData.setPiFileGenDate(placeHolderDate); |
|||
newFileData.setPiAmt(0.0); |
|||
newFileData.setFiFilePath("-"); |
|||
newFileData.setFiAmt(0.0); |
|||
newFileData.setFiFileGenDate(placeHolderDate); |
|||
newFileData.setBalAmt(0.0); |
|||
newFileData.setRtgsFilePath("-"); |
|||
newFileData.setPoFilePath("-"); |
|||
newFileData.setDcFilePath("-"); |
|||
newFileData.setSrFilePath("-"); |
|||
newFileData.setIrFilePath("-"); |
|||
newFileData.setAsFinalFilePath("-"); |
|||
newFileData.setAsFinalFileGenDate(placeHolderDate); |
|||
newFileData.setEjvNum("-"); |
|||
newFileData.setEjvNumFilePath("-"); |
|||
newFileData.setEjvNumFileGenDate(placeHolderDate); |
|||
newFileData.setTcFilePath("-"); |
|||
newFileData.setTcFileGenDate(placeHolderDate); |
|||
newFileData.setFileRmrk("-"); |
|||
|
|||
FileData savedFileData = fileDataRepo.save(newFileData); |
|||
fileDataId = savedFileData.getFileDataId(); |
|||
} |
|||
|
|||
Date ocFileUplDate = addOcFileDataReqDTO.getOcFileUplDate() != null |
|||
? addOcFileDataReqDTO.getOcFileUplDate() |
|||
: placeHolderDate; |
|||
String ocFileSub = (addOcFileDataReqDTO.getOcFileSub() != null |
|||
&& !addOcFileDataReqDTO.getOcFileSub().trim().isEmpty()) |
|||
? addOcFileDataReqDTO.getOcFileSub().trim() |
|||
: "-"; |
|||
|
|||
// Prepare DTO |
|||
OCFileDataRequestDTO ocFileDataRequestDTO = new OCFileDataRequestDTO(); |
|||
ocFileDataRequestDTO.setFileNum(fileNum); |
|||
ocFileDataRequestDTO.setOcFileUplDate(ocFileUplDate); |
|||
ocFileDataRequestDTO.setOcFileSub(ocFileSub); |
|||
|
|||
// Handle file uploads |
|||
Map<String, MultipartFile> files = new HashMap<String, MultipartFile>(); |
|||
if (addOcFileDataReqDTO.getOcFile() != null) { |
|||
files.put("OC", addOcFileDataReqDTO.getOcFile()); |
|||
} |
|||
Map<String, Consumer<String>> pathSetters = Map.of("OC", ocFileDataRequestDTO::setOcFilePath); |
|||
for (String type : files.keySet()) { |
|||
MultipartFile file = files.get(type); |
|||
// use the dates already set |
|||
fileDataSer.handleFileUpload(fileNum, type, "OC".equals(type) ? ocFileUplDate : ocFileUplDate, file, |
|||
pathSetters.get(type), uploadedFiles); |
|||
} |
|||
// Save to DB |
|||
ocFileDataRepo.save(ocFileDataMap.toOcFileDataEnt(ocFileDataRequestDTO, fileDataId)); |
|||
logger.info("OC File saved for file number: {}", fileNum); |
|||
return new ServiceResponseDTO(true, "OC File added successfully"); |
|||
} catch (IOException ioe) { |
|||
logger.error("Exception occurred in file upload part: {}", ioe.getMessage(), ioe); |
|||
ioe.printStackTrace(); |
|||
fileDataSer.cleanUpFiles(uploadedFiles.toArray(new String[0])); // Delete all uploaded files |
|||
return new ServiceResponseDTO(false, "Failed to add the file. Error in file upload: " |
|||
+ ioe.getMessage()); |
|||
} catch (Exception e) { |
|||
logger.error("Exception occurred: {}", e.getMessage(), e); |
|||
e.printStackTrace(); |
|||
fileDataSer.cleanUpFiles(uploadedFiles.toArray(new String[0])); // Delete all uploaded files |
|||
return new ServiceResponseDTO(false, "Failed to add the file. Error: " + e.getMessage()); |
|||
} |
|||
} |
|||
|
|||
private String determineFinYear(String fileNum) { |
|||
java.util.regex.Pattern pattern = java.util.regex.Pattern.compile("\\b(20\\d{2})\\b"); |
|||
java.util.regex.Matcher matcher = pattern.matcher(fileNum); |
|||
if (matcher.find()) { |
|||
int year = Integer.parseInt(matcher.group(1)); |
|||
return year + "-" + (year + 1); |
|||
} |
|||
Calendar cal = Calendar.getInstance(); |
|||
int year = cal.get(Calendar.YEAR); |
|||
int month = cal.get(Calendar.MONTH); |
|||
if (month >= Calendar.APRIL) { |
|||
return year + "-" + (year + 1); |
|||
} else { |
|||
return (year - 1) + "-" + year; |
|||
} |
|||
} |
|||
|
|||
@Transactional(readOnly = true) |
|||
public List<OCFileDataResponseDTO> getAllOcFileData() { |
|||
return ocFileDataRepo.findAll().stream() |
|||
.map(ocFileDataMap::toOcFileDataResDTO) |
|||
.toList(); |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,51 @@ |
|||
// Initialize date pickers
|
|||
["ocFileUplDate"].forEach(id => { |
|||
flatpickr("#" + id, { |
|||
dateFormat: "d-m-Y", |
|||
enableTime: false, |
|||
noCalendar: false, |
|||
allowInput: true |
|||
}); |
|||
}); |
|||
|
|||
document.addEventListener("DOMContentLoaded", function () { |
|||
const form = document.getElementById("frm-oc-file-det"); |
|||
|
|||
// Existing form submission logic...
|
|||
form.addEventListener("submit", function (e) { |
|||
e.preventDefault(); // Prevent default form submission
|
|||
|
|||
const formData = new FormData(form); |
|||
|
|||
fetch(form.getAttribute("action"), { |
|||
method: "POST", |
|||
body: formData |
|||
}) |
|||
.then(response => { |
|||
if (!response.ok) { |
|||
throw new Error("Network response was not ok: " + response.statusText); |
|||
} |
|||
return response.json(); |
|||
}) |
|||
.then(data => { |
|||
if (data.resStat) { |
|||
alert("Success: " + data.res); |
|||
form.reset(); |
|||
|
|||
// Reset flatpickr
|
|||
if (flatpickr) { |
|||
["#ocFileUplDate"].forEach(selector => { |
|||
const fp = document.querySelector(selector)?._flatpickr; |
|||
if (fp) fp.clear(); |
|||
}); |
|||
} |
|||
} else { |
|||
alert("Failed: " + data.res); |
|||
} |
|||
}) |
|||
.catch(error => { |
|||
console.error("Error submitting file:", error); |
|||
alert("Error submitting file. Please try again."); |
|||
}); |
|||
}); |
|||
}); |
|||
@ -0,0 +1,211 @@ |
|||
$(document).ready(function () { |
|||
// Initialize DataTable
|
|||
var table = $('#oc-file-det-tbl').DataTable({ |
|||
paging: true, |
|||
searching: true, |
|||
ordering: true, |
|||
lengthChange: true, |
|||
pageLength: 10, |
|||
language: { |
|||
emptyTable: "No data available", |
|||
lengthMenu: "Display _MENU_ records per page", |
|||
info: "Showing _START_ to _END_ of _TOTAL_ entries", |
|||
infoEmpty: "No records to show", |
|||
infoFiltered: "(filtered from _MAX_ total records)" |
|||
}, |
|||
columnDefs: [ |
|||
{ |
|||
orderable: false, |
|||
targets: [0, 5] // S.No and OC File columns
|
|||
} |
|||
], |
|||
buttons: [ |
|||
{ |
|||
extend: 'excelHtml5', |
|||
title: "OC File Details", |
|||
filename: "OC File Details", |
|||
exportOptions: { |
|||
columns: ':visible:not(:eq(5))', // exclude OC File column
|
|||
format: { |
|||
body: function (data, row, column, node) { |
|||
if (column === 0) { |
|||
return row + 1; |
|||
} |
|||
return $('<div>').html(data).text().trim(); |
|||
} |
|||
} |
|||
} |
|||
}, |
|||
'pdfHtml5' |
|||
] |
|||
}); |
|||
|
|||
// --- SEQUENTIAL S.NO FIX ---
|
|||
table.on('order.dt search.dt draw.dt', function () { |
|||
var pageInfo = table.page.info(); |
|||
table.column(0, { page: 'current' }).nodes().each(function (cell, i) { |
|||
cell.innerHTML = pageInfo.start + i + 1; |
|||
}); |
|||
}).draw(); |
|||
|
|||
// Move DataTable controls to custom containers
|
|||
$('#custom-pagination').html($('.dataTables_paginate')); |
|||
$('#custom-info').html($('.dataTables_info')); |
|||
$('#custom-length').html($('.dataTables_length')); |
|||
$('.dataTables_filter').hide(); // hide default search
|
|||
|
|||
// Custom search / filtering on the datatable
|
|||
$('#export-excel').on('click', function () { |
|||
table.button('.buttons-excel').trigger(); |
|||
}); |
|||
|
|||
$('#export-pdf').on('click', function () { |
|||
var originalLength = table.page.len(); |
|||
table.one('draw', function () { |
|||
var tableClone = $('#oc-file-det-tbl').clone(); |
|||
tableClone.removeClass('dataTable'); |
|||
tableClone.find('thead, tbody, tfoot').removeAttr('style'); |
|||
|
|||
// Remove "OC File" column (6th col = index 5)
|
|||
tableClone.find('tr').each(function () { |
|||
$(this).find('th:eq(5), td:eq(5)').remove(); |
|||
}); |
|||
|
|||
var pdfContainer = $('#pdf-print-container'); |
|||
if (!pdfContainer.length) { |
|||
$('body').append('<div id="pdf-print-container" style="display:none;"></div>'); |
|||
pdfContainer = $('#pdf-print-container'); |
|||
} |
|||
pdfContainer.html('<h2 style="text-align:center;">OC File Details</h2>'); |
|||
pdfContainer.append(tableClone).css({ display: 'block', padding: '20px' }); |
|||
|
|||
tableClone.css({ width: '100%', borderCollapse: 'collapse' }); |
|||
tableClone.find('th, td').css({ |
|||
border: '1px solid #ccc', |
|||
padding: '5px', |
|||
fontSize: '10px', |
|||
textAlign: 'left', |
|||
verticalAlign: 'top' |
|||
}); |
|||
tableClone.find('tr').css({ pageBreakInside: 'avoid', breakInside: 'avoid', webkitBreakInside: 'avoid' }); |
|||
|
|||
html2pdf().set({ |
|||
margin: 0.5, |
|||
filename: 'OC File Details.pdf', |
|||
image: { type: 'jpeg', quality: 0.98 }, |
|||
html2canvas: { scale: 2, useCORS: true }, |
|||
jsPDF: { unit: 'in', format: 'a3', orientation: 'landscape' } |
|||
}).from(pdfContainer[0]).save().then(function () { |
|||
table.page.len(originalLength).draw(); |
|||
pdfContainer.hide(); |
|||
}); |
|||
}); |
|||
table.page.len(-1).draw(); // show all rows for PDF
|
|||
}); |
|||
|
|||
function populateFilters() { |
|||
var numSet = {}, vendSet = {}; |
|||
|
|||
table.rows().every(function () { |
|||
var data = this.data(); |
|||
var fileNum = data[1] ? $('<div>').html(data[1]).text().trim() : ''; |
|||
var vendName = data[2] ? $('<div>').html(data[2]).text().trim() : ''; |
|||
|
|||
if (fileNum) numSet[fileNum] = true; |
|||
if (vendName) vendSet[vendName] = true; |
|||
}); |
|||
|
|||
Object.keys(numSet).sort().forEach(val => $('#file-num-list').append(`<option value="${val}">`)); |
|||
Object.keys(vendSet).sort().forEach(val => $('#vend-name-list').append(`<option value="${val}">`)); |
|||
} |
|||
populateFilters(); |
|||
|
|||
function applyFilters() { |
|||
var fileNum = $('#filter-file-num').val(); |
|||
var vendName = $('#filter-vend-name').val(); |
|||
|
|||
table.columns().search(''); // reset all
|
|||
|
|||
if (fileNum) table.column(1).search('^' + $.fn.dataTable.util.escapeRegex(fileNum) + '$', true, false); |
|||
if (vendName) table.column(2).search('^' + $.fn.dataTable.util.escapeRegex(vendName) + '$', true, false); |
|||
|
|||
table.draw(); |
|||
} |
|||
|
|||
// Event bindings for filters
|
|||
$('#filter-file-num').on('input', applyFilters); |
|||
$('#filter-vend-name').on('input', applyFilters); |
|||
}); |
|||
|
|||
// Function to view Supporting Document in modal
|
|||
function viewFileInModal(fileNum, fileType, filePath) { |
|||
var pdfModal = new bootstrap.Modal(document.getElementById('pdf-modal')); |
|||
var modalTitle = document.getElementById('pdf-modal-label'); |
|||
var pdfContainer = document.getElementById('pdf-container'); |
|||
pdfContainer.innerHTML = ''; // Clear previous pages
|
|||
pdfContainer.scrollTop = 0; // Scroll to the first page (top)
|
|||
|
|||
var decodedFilePath = decodeURIComponent(filePath); |
|||
var fileName = decodedFilePath.split(/[\\/]/).pop(); |
|||
modalTitle.textContent = fileType + " File (" + fileName + ")"; |
|||
|
|||
var params = new URLSearchParams(); |
|||
params.append('file-type', fileType); |
|||
params.append('file-path', decodedFilePath); |
|||
params.append('file-num', fileNum); |
|||
|
|||
fetch('/api/file/view-supp-docs', { |
|||
method: 'POST', |
|||
headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, |
|||
body: params.toString() |
|||
}).then(function (response) { |
|||
if (!response.ok) { |
|||
throw new Error('Failed to fetch PDF: ' + response.statusText); |
|||
} |
|||
return response.arrayBuffer(); |
|||
}).then(function (pdfData) { |
|||
console.log(pdfData); |
|||
var blob = new Blob([pdfData], { type: 'application/pdf' }); |
|||
var blobUrl = URL.createObjectURL(blob); |
|||
|
|||
// Set download link
|
|||
var downloadBtn = document.getElementById('download-pdf-btn'); |
|||
downloadBtn.href = blobUrl; |
|||
downloadBtn.download = fileName; |
|||
|
|||
return pdfjsLib.getDocument({ data: pdfData }).promise; |
|||
}).then(function (pdf) { |
|||
let renderPage = function (pageNum) { |
|||
return pdf.getPage(pageNum).then(function (page) { |
|||
var viewport = page.getViewport({ scale: 1.5 }); |
|||
var canvas = document.createElement('canvas'); |
|||
canvas.classList.add('mb-3'); |
|||
var ctx = canvas.getContext('2d'); |
|||
canvas.height = viewport.height; |
|||
canvas.width = viewport.width; |
|||
|
|||
var renderContext = { canvasContext: ctx, viewport: viewport }; |
|||
pdfContainer.appendChild(canvas); |
|||
return page.render(renderContext).promise; |
|||
}); |
|||
}; |
|||
|
|||
// Render all pages sequentially
|
|||
let sequence = Promise.resolve(); |
|||
for (let pageNum = 1; pageNum <= pdf.numPages; pageNum++) { |
|||
sequence = sequence.then(() => renderPage(pageNum)); |
|||
} |
|||
|
|||
return sequence; |
|||
}).then(function () { |
|||
var modalElement = document.getElementById('pdf-modal'); |
|||
modalElement.addEventListener('shown.bs.modal', function onShown() { |
|||
pdfContainer.scrollTop = 0; // Reset scroll position
|
|||
modalElement.scrollTop = 0; // Reset modal scroll
|
|||
modalElement.removeEventListener('shown.bs.modal', onShown); // Clean up listener
|
|||
}); |
|||
pdfModal.show(); |
|||
}).catch(function (error) { |
|||
alert('Error loading PDF: ' + error.message); |
|||
}); |
|||
} |
|||
@ -0,0 +1,78 @@ |
|||
<!DOCTYPE html> |
|||
<html xmlns:th="http://www.thymeleaf.org" lang="en"> |
|||
|
|||
<head> |
|||
<meta charset="UTF-8"> |
|||
<meta name="viewport" content="width=device-width, initial-scale=1.0"> |
|||
<title>Adjustment File Management System</title> |
|||
|
|||
<!-- Include CSS --> |
|||
<link rel="stylesheet" type="text/css" th:href="@{/css/body-form-styles.css}"> |
|||
<link rel="stylesheet" type="text/css" th:href="@{/css/home-styles.css}"> |
|||
|
|||
<!-- CSRF Tokens --> |
|||
<meta name="_csrf" th:content="${_csrf.token}" /> |
|||
<meta name="_csrf_header" th:content="${_csrf.headerName}" /> |
|||
|
|||
<!-- Header Fragment --> |
|||
<th:block th:replace="~{fragments/header :: header}"></th:block> |
|||
|
|||
</head> |
|||
|
|||
<body> |
|||
<!-- Navigation --> |
|||
<th:block th:replace="~{fragments/navbar :: navbar(${curPage})}"></th:block> |
|||
|
|||
<div class="container"> |
|||
<div class="form-container"> |
|||
<div class="header-row"> |
|||
<button class="back-button" onclick="goBack()">← Back</button> |
|||
</div> |
|||
<h2 class="form-title">Add New Elcot Remainder OC File</h2> |
|||
<form method="post" enctype="multipart/form-data" style="font-size: 14px" class="frm" |
|||
th:action="@{/api/oc-file/add-oc-file}" id="frm-oc-file-det" name="frm-oc-file-det"> |
|||
<div class="form-row"> |
|||
<!-- File Number (always enabled) --> |
|||
<div class="form-group"> |
|||
<label for="fileNum">File Number : <span class="required">*</span></label> |
|||
<input class="form-control" list="fileNumList" id="fileNum" name="fileNum" required |
|||
placeholder="Type or choose file number"> |
|||
<datalist id="fileNumList"> |
|||
<th:block th:each="file : ${fileDatum}"> |
|||
<option th:value="${file.fileDataNum}"></option> |
|||
</th:block> |
|||
</datalist> |
|||
</div> |
|||
<div class="form-group"> |
|||
<label for="ocFileUplDate">Date : <span class="required">*</span></label> |
|||
<input type="text" name="ocFileUplDate" id="ocFileUplDate" class="form-control" |
|||
placeholder="Select Date" autocomplete="off" required> |
|||
</div> |
|||
</div> |
|||
<div class="form-row"> |
|||
<div class="form-group"> |
|||
<label for="ocFileSub">Subject : <span class="required">*</span></label> |
|||
<textarea name="ocFileSub" id="ocFileSub" class="form-control" rows="4" required |
|||
placeholder="Enter subject"></textarea> |
|||
</div> |
|||
<div class="form-group"> |
|||
<label for="ocFile">OC File : <span class="required">*</span></label> |
|||
<input type="file" name="ocFile" id="ocFile" class="form-control" required accept=".pdf"> |
|||
</div> |
|||
</div> |
|||
<div class="submit-btn"> |
|||
<input type="submit" value="Submit"> |
|||
</div> |
|||
</form> |
|||
</div> |
|||
</div> |
|||
|
|||
<!-- Footer Fragment --> |
|||
<th:block th:replace="~{fragments/footer :: footer}"></th:block> |
|||
|
|||
<!-- Custom JS --> |
|||
<script th:src="@{/js/add-oc-file-script.js}" type="text/javascript"></script> |
|||
|
|||
</body> |
|||
|
|||
</html> |
|||
@ -0,0 +1,139 @@ |
|||
<!DOCTYPE html> |
|||
<html xmlns:th="http://www.thymeleaf.org" lang="en"> |
|||
|
|||
<head> |
|||
<meta charset="UTF-8"> |
|||
<meta name="viewport" content="width=device-width, initial-scale=1.0"> |
|||
<title>Adjustment File Management System</title> |
|||
|
|||
<!-- Include CSS --> |
|||
<link rel="stylesheet" type="text/css" th:href="@{/css/body-form-styles.css}"> |
|||
<link rel="stylesheet" type="text/css" th:href="@{/css/datatable-styles.css}"> |
|||
<link rel="stylesheet" type="text/css" th:href="@{/css/home-styles.css}"> |
|||
<link rel="stylesheet" type="text/css" th:href="@{/css/view-file-styles.css}"> |
|||
<link rel="stylesheet" type="text/css" th:href="@{/css/btn-secondary-styles.css}"> |
|||
|
|||
<!-- CSRF Tokens --> |
|||
<meta name="_csrf" th:content="${_csrf.token}" /> |
|||
<meta name="_csrf_header" th:content="${_csrf.headerName}" /> |
|||
|
|||
<!-- Header Fragment --> |
|||
<th:block th:replace="~{fragments/header :: header}"></th:block> |
|||
|
|||
</head> |
|||
|
|||
<body> |
|||
<!-- Navigation --> |
|||
<th:block th:replace="~{fragments/navbar :: navbar(${curPage})}"></th:block> |
|||
|
|||
<div class="container"> |
|||
<div class="form-container"> |
|||
<div class="header-row"> |
|||
<button class="back-button" onclick="goBack()">← Back</button> |
|||
</div> |
|||
<h2 class="form-title">Elcot OC Remainders Details</h2> |
|||
<!-- Button and Filter Bar --> |
|||
<div class="row mb-3"> |
|||
<!-- Adds spacing and a responsive layout --> |
|||
<div class="col-12 col-md-8 mb-2 mb-md-0"> |
|||
<!-- Buttons on the left --> |
|||
<div class="btn-group" role="group" aria-label="Options"> |
|||
<button type="button" th:onclick="|window.location.href='@{/oc-file/add-oc-file}'|" |
|||
class="btn btn-secondary"> |
|||
<i class="fa-solid fa-file-pen"></i> Add OC File |
|||
</button> |
|||
<button class="btn btn-secondary" id="export-excel" name="export-excel"> |
|||
<i class="fas fa-file-excel"></i> Export Excel |
|||
</button> |
|||
<button class="btn btn-secondary" id="export-pdf" name="export-pdf"> |
|||
<i class="fas fa-file-pdf"></i> Export PDF |
|||
</button> |
|||
</div> |
|||
</div> |
|||
<!-- <div class="col-12 col-md-8 mb-2 mb-md-0" style="padding-top:5px"> |
|||
</div> --> |
|||
</div> |
|||
<div class="row mb-3"> |
|||
<div class="col-md-6 form-group"> |
|||
<label for="filter-file-num" id="filter-file-num-lbl">Filter By File Number : </label> |
|||
<input list="file-num-list" class="form-control" id="filter-file-num" name="filter-file-num" |
|||
autocomplete="off" placeholder="Select File Number"> |
|||
<datalist id="file-num-list"> |
|||
<!-- Options will be filled by JavaScript --> |
|||
</datalist> |
|||
</div> |
|||
<div class="col-md-6 form-group"> |
|||
<label for="filter-vend-name" id="filter-vend-name-lbl">Filter By Vendor Name : </label> |
|||
<input list="vend-name-list" class="form-control" id="filter-vend-name" name="filter-vend-name" |
|||
autocomplete="off" placeholder="Select Vendor Name"> |
|||
<datalist id="vend-name-list"> |
|||
<!-- Options will be filled by JavaScript --> |
|||
</datalist> |
|||
</div> |
|||
</div> |
|||
<div class="row mb-3"> |
|||
<div class="col-md-4" id="custom-length"></div> |
|||
</div> |
|||
|
|||
<div class="table-responsive"> |
|||
<table class="det-table" id="oc-file-det-tbl"> |
|||
<thead> |
|||
<tr class="table-header"> |
|||
<th>S.No</th> |
|||
<th class="text-center">File Number</th> |
|||
<th class="text-center">Vendor Name</th> |
|||
<th class="text-center">Subject</th> |
|||
<th class="text-center">Upload Date</th> |
|||
<th class="text-center">OC File</th> |
|||
</tr> |
|||
</thead> |
|||
<tbody> |
|||
<tr th:each="ocFile : ${ocFileDataList}" th:attr="data-file-num=${ocFile.fileDataNum}"> |
|||
<!-- <tr th:each="ocFile : ${ocFileDataList}" th:attr="data-file-num=${ocFile.fileDataNum}" |
|||
onclick="if(!event.target.closest('button')) window.location.href='/file/view-file-det?file-num=' + this.dataset.fileNum" |
|||
style="cursor: pointer;"> --> |
|||
<td></td> |
|||
<!-- File Number --> |
|||
<td class="text-center" th:text="${ocFile.fileDataNum}"></td> |
|||
<!-- Vendor Name --> |
|||
<td |
|||
th:text="${ocFile.vendName != null and ocFile.vendName != '-' ? ocFile.vendName : 'None'}"> |
|||
</td> |
|||
<!-- Subject --> |
|||
<td th:text="${ocFile.ocFileSub}"></td> |
|||
<!-- Upload Date --> |
|||
<td th:with="uplDate=${#dates.format(ocFile.ocFileUplDate, 'dd-MM-yyyy')}" |
|||
th:text="${ocFile.ocFileUplDate != null and uplDate != '01-01-1970' ? uplDate : 'N/A'}"> |
|||
</td> |
|||
<!-- OC File Link --> |
|||
<td class="text-center"> |
|||
<button th:attr="data-path=${ocFile.ocFilePath}, data-file-num=${ocFile.fileDataNum}" |
|||
onclick="viewFileInModal(this.dataset.fileNum, 'OC', this.dataset.path)" class="btn" |
|||
type="button"> |
|||
<i class="fas fa-light fa-eye" style="cursor: pointer"></i> |
|||
</button> |
|||
</td> |
|||
</tr> |
|||
</tbody> |
|||
</table> |
|||
</div> |
|||
|
|||
<br /> |
|||
<div class="row mb-3"> |
|||
<div class="col-md-4 text-center" id="custom-info"></div> |
|||
<div class="col-md-8" id="custom-pagination"></div> |
|||
</div> |
|||
<th:block th:replace="~{fragments/export-pdf :: export-pdf}"></th:block> |
|||
</div> |
|||
</div> |
|||
|
|||
<th:block th:replace="~{modal/pdf-modal :: pdf-modal}"></th:block> |
|||
|
|||
<!-- Footer Fragment --> |
|||
<th:block th:replace="~{fragments/footer :: footer}"></th:block> |
|||
|
|||
<!-- Custom JS --> |
|||
<script th:src="@{/js/view-oc-file-script.js}" type="text/javascript"></script> |
|||
</body> |
|||
|
|||
</html> |
|||
Write
Preview
Loading…
Cancel
Save
Reference in new issue