Przeglądaj źródła

update report

tags/Baseline_30082024_BACKEND_UAT
cyril.tsui 1 rok temu
rodzic
commit
ea09340e3b
3 zmienionych plików z 80 dodań i 30 usunięć
  1. +69
    -28
      src/main/java/com/ffii/tsms/modules/report/service/ReportService.kt
  2. +8
    -2
      src/main/java/com/ffii/tsms/modules/report/web/ReportController.kt
  3. +3
    -0
      src/main/java/com/ffii/tsms/modules/timesheet/entity/TimesheetRepository.kt

+ 69
- 28
src/main/java/com/ffii/tsms/modules/report/service/ReportService.kt Wyświetl plik

@@ -1,12 +1,11 @@
package com.ffii.tsms.modules.report.service package com.ffii.tsms.modules.report.service


import com.ffii.tsms.modules.data.entity.Salary import com.ffii.tsms.modules.data.entity.Salary
import com.ffii.tsms.modules.data.entity.projections.SalarySearchInfo
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.Timesheet
import org.apache.poi.ss.usermodel.Sheet import org.apache.poi.ss.usermodel.Sheet
import org.apache.poi.ss.usermodel.Workbook import org.apache.poi.ss.usermodel.Workbook
import org.apache.poi.xssf.usermodel.XSSFDataFormat
import org.apache.poi.xssf.usermodel.XSSFWorkbook import org.apache.poi.xssf.usermodel.XSSFWorkbook
import org.springframework.core.io.ClassPathResource import org.springframework.core.io.ClassPathResource
import org.springframework.stereotype.Service import org.springframework.stereotype.Service
@@ -25,9 +24,9 @@ open class ReportService {


// ==============================|| GENERATE REPORT ||============================== // // ==============================|| GENERATE REPORT ||============================== //
@Throws(IOException::class) @Throws(IOException::class)
fun generateProjectCashFlowReport(project: Project, invoices: List<Invoice>): ByteArray {
fun generateProjectCashFlowReport(project: Project, invoices: List<Invoice>, timesheets: List<Timesheet>): ByteArray {
// Generate the Excel report with query results // Generate the Excel report with query results
val workbook: Workbook = createProjectCashFlowReport(project, invoices, PROJECT_CASH_FLOW_REPORT)
val workbook: Workbook = createProjectCashFlowReport(project, invoices, timesheets, PROJECT_CASH_FLOW_REPORT)


// Write the workbook to a ByteArrayOutputStream // Write the workbook to a ByteArrayOutputStream
val outputStream: ByteArrayOutputStream = ByteArrayOutputStream() val outputStream: ByteArrayOutputStream = ByteArrayOutputStream()
@@ -55,6 +54,7 @@ open class ReportService {
private fun createProjectCashFlowReport( private fun createProjectCashFlowReport(
project: Project, project: Project,
invoices: List<Invoice>, invoices: List<Invoice>,
timesheets: List<Timesheet>,
templatePath: String, templatePath: String,
): Workbook { ): Workbook {
// please create a new function for each report template // please create a new function for each report template
@@ -108,10 +108,11 @@ open class ReportService {


rowIndex = 10 rowIndex = 10
val actualIncome = invoices.sumOf { invoice -> invoice.paidAmount!! } val actualIncome = invoices.sumOf { invoice -> invoice.paidAmount!! }
val actualExpenditure = timesheets.sumOf { timesheet -> timesheet.staff!!.salary.hourlyRate.toDouble() * ((timesheet.normalConsumed ?: 0.0) + (timesheet.otConsumed ?: 0.0)) }
sheet.getRow(rowIndex).apply { sheet.getRow(rowIndex).apply {
getCell(1).apply { getCell(1).apply {
// TODO: Replace by actual expenditure // TODO: Replace by actual expenditure
setCellValue(actualIncome * 0.8)
setCellValue(actualExpenditure)
cellStyle.dataFormat = accountingStyle cellStyle.dataFormat = accountingStyle
} }


@@ -136,37 +137,77 @@ open class ReportService {
} }


// TODO: Add expenditure // TODO: Add expenditure
// formula =IF(B17>0,D16-B17,D16+C17)
rowIndex = 15 rowIndex = 15
val combinedResults = (invoices.map { it.receiptDate } + timesheets.map { it.recordDate }).filterNotNull().sortedBy { it }

val dateFormatter = DateTimeFormatter.ofPattern("MMM YYYY") val dateFormatter = DateTimeFormatter.ofPattern("MMM YYYY")
invoices.forEach { invoice: Invoice ->
sheet.getRow(rowIndex++).apply {
getCell(0).apply {
setCellValue(invoice.receiptDate!!.format(dateFormatter))
}
combinedResults.forEach { result: LocalDate ->
val invoice = invoices.find { invoice: Invoice -> invoice.receiptDate == result }
val timesheet = timesheets.find { timesheet: Timesheet -> timesheet.recordDate == result}


getCell(1).apply {
setCellValue(0.0)
cellStyle.dataFormat = accountingStyle
}
if (invoice != null) {
sheet.getRow(rowIndex++).apply {


getCell(2).apply {
setCellValue(invoice.paidAmount!!)
cellStyle.dataFormat = accountingStyle
}
getCell(0).apply {
setCellValue(result.format(dateFormatter))
}

getCell(1).apply {
setCellValue(0.0)
cellStyle.dataFormat = accountingStyle
}

getCell(2).apply {
setCellValue(invoice.paidAmount!!)
cellStyle.dataFormat = accountingStyle
}

getCell(3).apply {
val lastRow = rowIndex - 1
if (lastRow == 15) {
cellFormula = "C{currentRow}-B{currentRow}".replace("{currentRow}", rowIndex.toString())
} else {
cellFormula = "IF(B{currentRow}>0,D{lastRow}-B{currentRow},D{lastRow}+C{currentRow})".replace("{currentRow}", rowIndex.toString()).replace("{lastRow}", lastRow.toString())
}
cellStyle.dataFormat = accountingStyle
}


getCell(3).apply {
val lastRow = rowIndex - 1
if (lastRow == 15) {
cellFormula = "C{currentRow}".replace("{currentRow}", rowIndex.toString())
} else {
cellFormula = "IF(B{currentRow}>0,D{lastRow}-B{currentRow},D{lastRow}+C{currentRow})".replace("{currentRow}", rowIndex.toString()).replace("{lastRow}", lastRow.toString())
getCell(4).apply {
setCellValue(invoice.milestonePayment!!.description!!)
} }
cellStyle.dataFormat = accountingStyle
} }
}

if (timesheet != null) {
sheet.getRow(rowIndex++).apply {

getCell(0).apply {
setCellValue(result.format(dateFormatter))
}

getCell(1).apply {
setCellValue(timesheet.staff!!.salary.hourlyRate.toDouble() * ((timesheet.normalConsumed ?: 0.0) + (timesheet.otConsumed ?: 0.0)))
cellStyle.dataFormat = accountingStyle
}


getCell(4).apply {
setCellValue(invoice.milestonePayment!!.description!!)
getCell(2).apply {
setCellValue(0.0)
cellStyle.dataFormat = accountingStyle
}

getCell(3).apply {
val lastRow = rowIndex - 1
if (lastRow == 15) {
cellFormula = "C{currentRow}-B{currentRow}".replace("{currentRow}", rowIndex.toString())
} else {
cellFormula = "IF(B{currentRow}>0,D{lastRow}-B{currentRow},D{lastRow}+C{currentRow})".replace("{currentRow}", rowIndex.toString()).replace("{lastRow}", lastRow.toString())
}
cellStyle.dataFormat = accountingStyle
}

getCell(4).apply {
setCellValue("Monthly Manpower Expenditure")
}
} }
} }
} }


+ 8
- 2
src/main/java/com/ffii/tsms/modules/report/web/ReportController.kt Wyświetl plik

@@ -4,6 +4,7 @@ import com.ffii.tsms.modules.project.entity.*
import com.ffii.tsms.modules.report.service.ReportService import com.ffii.tsms.modules.report.service.ReportService
import com.ffii.tsms.modules.project.service.InvoiceService import com.ffii.tsms.modules.project.service.InvoiceService
import com.ffii.tsms.modules.report.web.model.ProjectCashFlowReportRequest import com.ffii.tsms.modules.report.web.model.ProjectCashFlowReportRequest
import com.ffii.tsms.modules.timesheet.entity.TimesheetRepository
import jakarta.validation.Valid import jakarta.validation.Valid
import org.springframework.core.io.ByteArrayResource import org.springframework.core.io.ByteArrayResource
import org.springframework.core.io.Resource import org.springframework.core.io.Resource
@@ -21,16 +22,21 @@ import java.time.LocalDate


@RestController @RestController
@RequestMapping("/reports") @RequestMapping("/reports")
class ReportController(private val invoiceRepository: InvoiceRepository, private val milestonePaymentRepository: MilestonePaymentRepository, private val excelReportService: ReportService, private val projectRepository: ProjectRepository, private val invoiceService: InvoiceService) {
class ReportController(private val invoiceRepository: InvoiceRepository, private val milestonePaymentRepository: MilestonePaymentRepository, private val excelReportService: ReportService, private val projectRepository: ProjectRepository, private val invoiceService: InvoiceService,
private val timesheetRepository: TimesheetRepository,
private val projectTaskRepository: ProjectTaskRepository
) {


@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> {


val project = projectRepository.findById(request.projectId).orElseThrow() val project = projectRepository.findById(request.projectId).orElseThrow()
val projectTasks = projectTaskRepository.findAllByProject(project)
val invoices = invoiceService.findAllByProjectAndPaidAmountIsNotNull(project) val invoices = invoiceService.findAllByProjectAndPaidAmountIsNotNull(project)
val timesheets = timesheetRepository.findAllByProjectTaskIn(projectTasks)


val reportResult: ByteArray = excelReportService.generateProjectCashFlowReport(project, invoices)
val reportResult: ByteArray = excelReportService.generateProjectCashFlowReport(project, invoices, timesheets)
// val mediaType: MediaType = MediaType.parseMediaType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet") // val mediaType: MediaType = MediaType.parseMediaType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
return ResponseEntity.ok() return ResponseEntity.ok()
// .contentType(mediaType) // .contentType(mediaType)


+ 3
- 0
src/main/java/com/ffii/tsms/modules/timesheet/entity/TimesheetRepository.kt Wyświetl plik

@@ -4,6 +4,7 @@ import com.ffii.core.support.AbstractRepository
import com.ffii.tsms.modules.data.entity.Staff import com.ffii.tsms.modules.data.entity.Staff
import com.ffii.tsms.modules.project.entity.Project import com.ffii.tsms.modules.project.entity.Project
import com.ffii.tsms.modules.timesheet.entity.projections.TimesheetHours import com.ffii.tsms.modules.timesheet.entity.projections.TimesheetHours
import com.ffii.tsms.modules.project.entity.ProjectTask
import org.springframework.data.jpa.repository.Query import org.springframework.data.jpa.repository.Query
import java.time.LocalDate import java.time.LocalDate


@@ -11,6 +12,8 @@ interface TimesheetRepository : AbstractRepository<Timesheet, Long> {


fun findAllByStaff(staff: Staff): List<Timesheet> fun findAllByStaff(staff: Staff): List<Timesheet>


fun findAllByProjectTaskIn(projectTasks: List<ProjectTask>): List<Timesheet>

fun deleteAllByStaffAndRecordDate(staff: Staff, recordDate: LocalDate) fun deleteAllByStaffAndRecordDate(staff: Staff, recordDate: LocalDate)


@Query("SELECT new com.ffii.tsms.modules.timesheet.entity.projections.TimesheetHours(IFNULL(SUM(normalConsumed), 0), IFNULL(SUM(otConsumed), 0)) FROM Timesheet t JOIN ProjectTask pt on t.projectTask = pt WHERE pt.project = ?1") @Query("SELECT new com.ffii.tsms.modules.timesheet.entity.projections.TimesheetHours(IFNULL(SUM(normalConsumed), 0), IFNULL(SUM(otConsumed), 0)) FROM Timesheet t JOIN ProjectTask pt on t.projectTask = pt WHERE pt.project = ?1")


Ładowanie…
Anuluj
Zapisz