作者 | SHA1 | 备注 | 提交日期 |
---|---|---|---|
|
d6636c72cb | Merge branch 'master' of https://git.2fi-solutions.com/davidhui/TSMS-backend | 1年前 |
|
652612e10c | report update | 1年前 |
@@ -8,7 +8,7 @@ import org.springframework.data.repository.query.Param; | |||||
import com.ffii.core.support.AbstractRepository; | import com.ffii.core.support.AbstractRepository; | ||||
public interface CustomerRepository extends AbstractRepository<Customer, Long> { | public interface CustomerRepository extends AbstractRepository<Customer, Long> { | ||||
List<Customer> findAllByDeletedFalse(); | List<Customer> findAllByDeletedFalse(); | ||||
Optional<Customer> findByCode(@Param("code") String code); | Optional<Customer> findByCode(@Param("code") String code); | ||||
} | |||||
Optional<Customer> findByName(@Param("name") String name); | |||||
} |
@@ -5,6 +5,7 @@ import com.ffii.tsms.modules.project.entity.projections.InvoiceInfoSearchInfo | |||||
import com.ffii.tsms.modules.project.entity.projections.InvoiceSearchInfo | import com.ffii.tsms.modules.project.entity.projections.InvoiceSearchInfo | ||||
import com.ffii.tsms.modules.project.entity.projections.ProjectSearchInfo | import com.ffii.tsms.modules.project.entity.projections.ProjectSearchInfo | ||||
import java.io.Serializable | import java.io.Serializable | ||||
import java.time.LocalDate | |||||
interface ProjectRepository : AbstractRepository<Project, Long> { | interface ProjectRepository : AbstractRepository<Project, Long> { | ||||
fun findProjectSearchInfoByOrderByCreatedDesc(): List<ProjectSearchInfo> | fun findProjectSearchInfoByOrderByCreatedDesc(): List<ProjectSearchInfo> | ||||
@@ -16,4 +17,8 @@ interface ProjectRepository : AbstractRepository<Project, Long> { | |||||
fun findInvoiceInfoSearchInfoById(id: Long): List<InvoiceInfoSearchInfo> | fun findInvoiceInfoSearchInfoById(id: Long): List<InvoiceInfoSearchInfo> | ||||
fun findFirstByIsClpProjectAndIdIsNotOrderByIdDesc(isClpProject: Boolean, id: Serializable?): Project? | fun findFirstByIsClpProjectAndIdIsNotOrderByIdDesc(isClpProject: Boolean, id: Serializable?): Project? | ||||
fun findAllByPlanStartLessThanEqualAndPlanEndGreaterThanEqual(remainedDateFrom: LocalDate?, remainedDateTo: LocalDate?) | |||||
fun findByDateRange(start: LocalDate, end: LocalDate): List<Project> | |||||
} | } |
@@ -3,6 +3,8 @@ package com.ffii.tsms.modules.report.service | |||||
import com.ffii.core.support.JdbcDao | import com.ffii.core.support.JdbcDao | ||||
import com.ffii.tsms.modules.data.entity.Salary | import com.ffii.tsms.modules.data.entity.Salary | ||||
import com.ffii.tsms.modules.data.entity.Staff | import com.ffii.tsms.modules.data.entity.Staff | ||||
import com.ffii.tsms.modules.data.entity.Team | |||||
import com.ffii.tsms.modules.data.entity.Customer | |||||
import com.ffii.tsms.modules.project.entity.Invoice | import com.ffii.tsms.modules.project.entity.Invoice | ||||
import com.ffii.tsms.modules.project.entity.Project | import com.ffii.tsms.modules.project.entity.Project | ||||
import com.ffii.tsms.modules.timesheet.entity.Leave | import com.ffii.tsms.modules.timesheet.entity.Leave | ||||
@@ -159,8 +161,16 @@ open class ReportService( | |||||
} | } | ||||
@Throws(IOException::class) | @Throws(IOException::class) | ||||
fun generateLateStartReport(project: Project?): ByteArray { | |||||
val workbook: Workbook = createLateStartReport(project,LATE_START_REPORT) | |||||
fun generateLateStartReport( | |||||
team: Team, | |||||
customer: Customer, | |||||
project: List<Project>, | |||||
): ByteArray { | |||||
val workbook: Workbook = createLateStartReport( | |||||
team, | |||||
customer, | |||||
project, | |||||
LATE_START_REPORT) | |||||
val outputStream: ByteArrayOutputStream = ByteArrayOutputStream() | val outputStream: ByteArrayOutputStream = ByteArrayOutputStream() | ||||
workbook.write(outputStream) | workbook.write(outputStream) | ||||
workbook.close() | workbook.close() | ||||
@@ -1021,34 +1031,27 @@ open class ReportService( | |||||
return workbook | return workbook | ||||
} | } | ||||
//createLateStartReport | |||||
private fun createLateStartReport( | private fun createLateStartReport( | ||||
project: Project?, | |||||
team: Team, | |||||
customer: Customer, | |||||
project: List<Project>, | |||||
templatePath: String | templatePath: String | ||||
):Workbook{ | ):Workbook{ | ||||
val resource = ClassPathResource(templatePath) | val resource = ClassPathResource(templatePath) | ||||
val templateInputStream = resource.inputStream | val templateInputStream = resource.inputStream | ||||
val workbook: Workbook = XSSFWorkbook(templateInputStream) | val workbook: Workbook = XSSFWorkbook(templateInputStream) | ||||
val sheet = workbook.getSheetAt(0) | val sheet = workbook.getSheetAt(0) | ||||
// Formatting the current date to "YYYY/MM/DD" and setting it to cell C2 | // Formatting the current date to "YYYY/MM/DD" and setting it to cell C2 | ||||
val formattedToday = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy/MM/dd")) | val formattedToday = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy/MM/dd")) | ||||
val dateCell = sheet.getRow(1)?.getCell(2) ?: sheet.getRow(1).createCell(2) | val dateCell = sheet.getRow(1)?.getCell(2) ?: sheet.getRow(1).createCell(2) | ||||
dateCell.setCellValue(formattedToday) | dateCell.setCellValue(formattedToday) | ||||
// // Start populating project data starting at row 7 | |||||
// val startRow = 6 // 0-based index for row 7 | |||||
// val projectDataRow = sheet.createRow(startRow) | |||||
// // Populate the row with project data | |||||
// projectDataRow.createCell(1).setCellValue(project.code) // Column B | |||||
// projectDataRow.createCell(2).setCellValue(project.name) // Column C | |||||
// projectDataRow.createCell(3).setCellValue(project.teamLead?.name) // Column D | |||||
// projectDataRow.createCell(4).setCellValue(project.custLeadName) // Column E | |||||
// projectDataRow.createCell(5).setCellValue( | |||||
// project.planStart?.format(DateTimeFormatter.ofPattern("yyyy/MM/dd")) ?: "N/A" // Column F | |||||
// ) | |||||
// Start populating project data starting at row 7 | |||||
val startRow = 6 // 0-based index for row 7 | |||||
val projectDataRow = sheet.createRow(startRow) | |||||
// Styling for cell A1: Font size 16 and bold | // Styling for cell A1: Font size 16 and bold | ||||
val headerFont = workbook.createFont().apply { | val headerFont = workbook.createFont().apply { | ||||
@@ -1060,7 +1063,7 @@ open class ReportService( | |||||
} | } | ||||
val headerCell = sheet.getRow(0)?.getCell(0) ?: sheet.getRow(0).createCell(0) | val headerCell = sheet.getRow(0)?.getCell(0) ?: sheet.getRow(0).createCell(0) | ||||
headerCell.cellStyle = headerCellStyle | headerCell.cellStyle = headerCellStyle | ||||
headerCell.setCellValue("Report Title") | |||||
//headerCell.setCellValue("Report Title") | |||||
// Apply styles from A2 to A4 (bold) | // Apply styles from A2 to A4 (bold) | ||||
val boldFont = workbook.createFont().apply { bold = true } | val boldFont = workbook.createFont().apply { bold = true } | ||||
@@ -1,7 +1,7 @@ | |||||
package com.ffii.tsms.modules.report.web | package com.ffii.tsms.modules.report.web | ||||
import com.ffii.tsms.modules.data.entity.StaffRepository | import com.ffii.tsms.modules.data.entity.StaffRepository | ||||
import com.ffii.tsms.modules.data.entity.projections.FinancialStatusReportInfo | |||||
//import com.ffii.tsms.modules.data.entity.projections.FinancialStatusReportInfo | |||||
import com.ffii.tsms.modules.data.entity.projections.StaffSearchInfo | import com.ffii.tsms.modules.data.entity.projections.StaffSearchInfo | ||||
import com.ffii.tsms.modules.project.entity.* | import com.ffii.tsms.modules.project.entity.* | ||||
import com.ffii.tsms.modules.report.service.ReportService | import com.ffii.tsms.modules.report.service.ReportService | ||||
@@ -9,6 +9,8 @@ import com.ffii.tsms.modules.project.service.InvoiceService | |||||
import com.ffii.tsms.modules.report.web.model.FinancialStatusReportRequest | import com.ffii.tsms.modules.report.web.model.FinancialStatusReportRequest | ||||
import com.ffii.tsms.modules.report.web.model.ProjectCashFlowReportRequest | import com.ffii.tsms.modules.report.web.model.ProjectCashFlowReportRequest | ||||
import com.ffii.tsms.modules.report.web.model.StaffMonthlyWorkHourAnalysisReportRequest | import com.ffii.tsms.modules.report.web.model.StaffMonthlyWorkHourAnalysisReportRequest | ||||
import com.ffii.tsms.modules.report.web.model.LateStartReportRequest | |||||
import com.ffii.tsms.modules.project.entity.ProjectRepository | |||||
import com.ffii.tsms.modules.timesheet.entity.LeaveRepository | import com.ffii.tsms.modules.timesheet.entity.LeaveRepository | ||||
import com.ffii.tsms.modules.timesheet.entity.TimesheetRepository | import com.ffii.tsms.modules.timesheet.entity.TimesheetRepository | ||||
import com.ffii.tsms.modules.timesheet.entity.projections.ProjectMonthlyHoursWithDate | import com.ffii.tsms.modules.timesheet.entity.projections.ProjectMonthlyHoursWithDate | ||||
@@ -28,6 +30,14 @@ import org.springframework.http.HttpHeaders | |||||
import org.springframework.http.MediaType | import org.springframework.http.MediaType | ||||
import java.io.IOException | import java.io.IOException | ||||
import java.time.LocalDate | import java.time.LocalDate | ||||
import java.net.URLEncoder | |||||
import java.time.format.DateTimeFormatter | |||||
import org.springframework.stereotype.Controller | |||||
import com.ffii.tsms.modules.data.entity.TeamRepository | |||||
import com.ffii.tsms.modules.data.entity.CustomerRepository | |||||
import org.springframework.data.jpa.repository.JpaRepository | |||||
import com.ffii.tsms.modules.data.entity.Customer | |||||
import com.ffii.tsms.modules.project.entity.Project | |||||
@RestController | @RestController | ||||
@RequestMapping("/reports") | @RequestMapping("/reports") | ||||
@@ -38,6 +48,8 @@ class ReportController( | |||||
private val projectRepository: ProjectRepository, | private val projectRepository: ProjectRepository, | ||||
private val timesheetRepository: TimesheetRepository, | private val timesheetRepository: TimesheetRepository, | ||||
private val projectTaskRepository: ProjectTaskRepository, | private val projectTaskRepository: ProjectTaskRepository, | ||||
private val teamRepository: TeamRepository, | |||||
private val customerRepository: CustomerRepository, | |||||
private val staffRepository: StaffRepository, | private val staffRepository: StaffRepository, | ||||
private val leaveRepository: LeaveRepository, | private val leaveRepository: LeaveRepository, | ||||
private val invoiceService: InvoiceService) { | private val invoiceService: InvoiceService) { | ||||
@@ -54,7 +66,6 @@ class ReportController( | |||||
.body(ByteArrayResource(reportResult)) | .body(ByteArrayResource(reportResult)) | ||||
} | } | ||||
@PostMapping("/ProjectCashFlowReport") | @PostMapping("/ProjectCashFlowReport") | ||||
@Throws(ServletRequestBindingException::class, IOException::class) | @Throws(ServletRequestBindingException::class, IOException::class) | ||||
fun getProjectCashFlowReport(@RequestBody @Valid request: ProjectCashFlowReportRequest): ResponseEntity<Resource> { | fun getProjectCashFlowReport(@RequestBody @Valid request: ProjectCashFlowReportRequest): ResponseEntity<Resource> { | ||||
@@ -102,18 +113,59 @@ class ReportController( | |||||
} | } | ||||
@PostMapping("/downloadLateStartReport") | @PostMapping("/downloadLateStartReport") | ||||
fun downloadLateStartReport(): ResponseEntity<ByteArrayResource> { | |||||
val reportBytes = excelReportService.generateLateStartReport(null) | |||||
@Throws(ServletRequestBindingException::class, IOException::class) | |||||
// fun downloadLateStartReport(@RequestBody @Valid request: LateStartReportRequest): ResponseEntity<ByteArrayResource> { | |||||
// //val reportDate = projectRepository.findAllByPlanStartLessThanEqualAndPlanEndGreaterThanEqual().orElseThrow()//to do | |||||
// val team = teamRepository.findById(request.teamId).orElseThrow() | |||||
// //val customer = customerRepository.findById(request.clientId).orElseThrow() | |||||
// val reportResult: ByteArray = excelReportService.generateLateStartReport(team,customer,reportDate) | |||||
// val headers = HttpHeaders() | |||||
// val filename = "Late_Start_Report_${LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd"))}.xlsx" | |||||
// headers.add("filename", "${URLEncoder.encode(filename, "UTF-8")}") | |||||
// // Logging the filename to ensure it's correctly formatted | |||||
// println("Generated filename for download: $filename") | |||||
// return ResponseEntity.ok() | |||||
// .headers(headers) | |||||
// .contentLength(reportResult.size.toLong()) | |||||
// .contentType(MediaType.parseMediaType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")) | |||||
// .body(ByteArrayResource(reportResult)) | |||||
// } | |||||
fun downloadLateStartReport(@RequestBody @Valid request: LateStartReportRequest): ResponseEntity<ByteArrayResource> { | |||||
val team = teamRepository.findById(request.teamId).orElseThrow { | |||||
Exception("Team not found with ID: ${request.teamId}") | |||||
} | |||||
val customer = customerRepository.findByName(request.customer).orElseThrow() | |||||
// ?: throw Exception("Customer not found with name: ${request.customer}") | |||||
val projects = projectRepository.findAllByDateRange(request.remainedDateFrom, request.remainedDateTo) | |||||
if (projects.isEmpty()) { | |||||
throw Exception("No projects found for the given date: ${request.reportDate}") | |||||
} | |||||
val reportResult: ByteArray = try { | |||||
excelReportService.generateLateStartReport(team, customer, projects) | |||||
} catch (e: Exception) { | |||||
throw Exception("Error generating report", e) | |||||
} | |||||
val headers = HttpHeaders() | val headers = HttpHeaders() | ||||
headers.add("Content-Disposition", "attachment; filename=Late_Start_Report_${LocalDate.now()}.xlsx") | |||||
val formattedDate = request.reportDate.format(DateTimeFormatter.ofPattern("yyyyMMdd")) | |||||
val filename = "Late_Start_Report_$formattedDate.xlsx" | |||||
headers.add("Content-Disposition", "attachment; filename=${URLEncoder.encode(filename, "UTF-8")}") | |||||
println("Generated filename for download: $filename") | |||||
return ResponseEntity.ok() | return ResponseEntity.ok() | ||||
.headers(headers) | .headers(headers) | ||||
.contentLength(reportBytes.size.toLong()) | |||||
.contentLength(reportResult.size.toLong()) | |||||
.contentType(MediaType.parseMediaType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")) | .contentType(MediaType.parseMediaType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")) | ||||
.body(ByteArrayResource(reportBytes)) | |||||
.body(ByteArrayResource(reportResult)) | |||||
} | } | ||||
@GetMapping("/financialReport/{id}") | @GetMapping("/financialReport/{id}") | ||||
fun getFinancialReport(@PathVariable id: Long): List<Map<String, Any>> { | fun getFinancialReport(@PathVariable id: Long): List<Map<String, Any>> { | ||||
// println(excelReportService.genFinancialStatusReport(id)) | // println(excelReportService.genFinancialStatusReport(id)) | ||||
@@ -1,5 +1,6 @@ | |||||
package com.ffii.tsms.modules.report.web.model | package com.ffii.tsms.modules.report.web.model | ||||
import java.time.LocalDate | |||||
import java.time.YearMonth | import java.time.YearMonth | ||||
data class FinancialStatusReportRequest ( | data class FinancialStatusReportRequest ( | ||||
@@ -13,4 +14,13 @@ data class ProjectCashFlowReportRequest ( | |||||
data class StaffMonthlyWorkHourAnalysisReportRequest ( | data class StaffMonthlyWorkHourAnalysisReportRequest ( | ||||
val id: Long, | val id: Long, | ||||
val yearMonth: YearMonth | val yearMonth: YearMonth | ||||
) | |||||
data class LateStartReportRequest ( | |||||
val teamId: Long, | |||||
val clientId: Long, | |||||
val remainedDateFrom: LocalDate, | |||||
val remainedDateTo: LocalDate, | |||||
val customer: String, | |||||
val reportDate: LocalDate | |||||
) | ) |