Pārlūkot izejas kodu

add export dashboard (finanical status by client) excel , update project & timesheet & authority

tags/Baseline_30082024_BACKEND_UAT
cyril.tsui pirms 1 gada
vecāks
revīzija
fa4e9d70ba
9 mainītis faili ar 222 papildinājumiem un 6 dzēšanām
  1. +171
    -0
      src/main/java/com/ffii/tsms/modules/data/service/DashboardService.kt
  2. +13
    -3
      src/main/java/com/ffii/tsms/modules/data/web/DashboardController.kt
  3. +15
    -0
      src/main/java/com/ffii/tsms/modules/data/web/models/ExportDashboardExcelRequest.kt
  4. +9
    -3
      src/main/java/com/ffii/tsms/modules/project/service/ProjectsService.kt
  5. +2
    -0
      src/main/java/com/ffii/tsms/modules/project/web/models/AssignedProject.kt
  6. +3
    -0
      src/main/java/com/ffii/tsms/modules/timesheet/entity/TimesheetRepository.kt
  7. +9
    -0
      src/main/resources/db/changelog/changes/20240710_01_cyril/01_update_authority.sql
  8. Binārs
      src/main/resources/templates/report/[Dashboard] Financial Summary for client.xlsx
  9. Binārs
      src/main/resources/templates/report/[Dashboard] Financial Summary for project.xlsx

+ 171
- 0
src/main/java/com/ffii/tsms/modules/data/service/DashboardService.kt Parādīt failu

@@ -6,11 +6,27 @@ import com.ffii.tsms.modules.data.entity.Customer
import com.ffii.tsms.modules.data.entity.CustomerType import com.ffii.tsms.modules.data.entity.CustomerType
import com.ffii.tsms.modules.data.entity.CustomerRepository import com.ffii.tsms.modules.data.entity.CustomerRepository
import com.ffii.tsms.modules.data.entity.CustomerTypeRepository import com.ffii.tsms.modules.data.entity.CustomerTypeRepository
import com.ffii.tsms.modules.data.web.models.FinancialSummaryByClient
import com.ffii.tsms.modules.data.web.models.SaveCustomerResponse import com.ffii.tsms.modules.data.web.models.SaveCustomerResponse
import com.ffii.tsms.modules.project.entity.Invoice
import com.ffii.tsms.modules.project.entity.Project
import com.ffii.tsms.modules.project.web.models.SaveCustomerRequest import com.ffii.tsms.modules.project.web.models.SaveCustomerRequest
import com.ffii.tsms.modules.timesheet.entity.Timesheet
import org.apache.poi.ss.usermodel.BorderStyle
import org.apache.poi.ss.usermodel.HorizontalAlignment
import org.apache.poi.ss.usermodel.Sheet
import org.apache.poi.ss.usermodel.Workbook
import org.apache.poi.ss.util.CellRangeAddress
import org.apache.poi.ss.util.CellUtil
import org.apache.poi.xssf.usermodel.XSSFWorkbook
import org.springframework.beans.BeanUtils import org.springframework.beans.BeanUtils
import org.springframework.core.io.ClassPathResource
import org.springframework.stereotype.Service import org.springframework.stereotype.Service
import java.io.ByteArrayOutputStream
import java.io.IOException
import java.math.BigDecimal import java.math.BigDecimal
import java.time.LocalDate
import java.time.format.DateTimeFormatter
import java.util.Optional import java.util.Optional


@Service @Service
@@ -22,6 +38,11 @@ open class DashboardService(
private val staffsService: StaffsService, private val staffsService: StaffsService,
private val jdbcDao: JdbcDao private val jdbcDao: JdbcDao
) { ) {
private val DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy/MM/dd")
private val FORMATTED_TODAY = LocalDate.now().format(DATE_FORMATTER)

private val FINANCIAL_SUMMARY_FOR_CLIENT = "templates/report/[Dashboard] Financial Summary for client.xlsx"
private val FINANCIAL_SUMMARY_FOR_PROJET = "templates/report/[Dashboard] Financial Summary for project.xlsx"


fun CustomerSubsidiary(args: Map<String, Any>): List<Map<String, Any>> { fun CustomerSubsidiary(args: Map<String, Any>): List<Map<String, Any>> {
val sql = StringBuilder("select" val sql = StringBuilder("select"
@@ -2105,6 +2126,156 @@ open class DashboardService(
"no_authority" "no_authority"
} }
} }

@Throws(IOException::class)
fun exportFinancialSummaryByClientExcel(
financialSummaryByClients: List<FinancialSummaryByClient>,
): ByteArray {
// Generate the Excel report with query results
val workbook: Workbook =
createFinancialSummaryByClientExcel(financialSummaryByClients, FINANCIAL_SUMMARY_FOR_CLIENT)

// Write the workbook to a ByteArrayOutputStream
val outputStream: ByteArrayOutputStream = ByteArrayOutputStream()
workbook.write(outputStream)
workbook.close()

return outputStream.toByteArray()
}

@Throws(IOException::class)
private fun createFinancialSummaryByClientExcel(
financialSummaryByClients: List<FinancialSummaryByClient>,
templatePath: String,
): Workbook {
// please create a new function for each report template
val resource = ClassPathResource(templatePath)
val templateInputStream = resource.inputStream
val workbook: Workbook = XSSFWorkbook(templateInputStream)

val sheet: Sheet = workbook.getSheetAt(0)

// accounting style + comma style
val accountingStyle = workbook.createDataFormat().getFormat("_(* #,##0.00_);_(* (#,##0.00);_(* \"-\"??_);_(@_)")

// normal font
val normalFont = workbook.createFont().apply {
fontName = "Times New Roman"
}

val normalFontStyle = workbook.createCellStyle().apply {
setFont(normalFont)
}

var rowIndex = 1 // Assuming the location is in (1,2), which is the report date field
var columnIndex = 1
sheet.getRow(rowIndex).createCell(columnIndex).apply {
setCellValue(FORMATTED_TODAY)
}

rowIndex = 4
financialSummaryByClients.forEach { financialSummaryByClient: FinancialSummaryByClient ->
sheet.createRow(rowIndex++).apply {
createCell(0).apply {
setCellValue(financialSummaryByClient.customerCode)
cellStyle = normalFontStyle
CellUtil.setAlignment(this, HorizontalAlignment.LEFT)
}

createCell(1).apply {
setCellValue(financialSummaryByClient.customerName)
cellStyle = normalFontStyle
CellUtil.setAlignment(this, HorizontalAlignment.LEFT)
}

createCell(2).apply {
setCellValue(financialSummaryByClient.projectNo)
cellStyle = normalFontStyle.apply {
dataFormat = accountingStyle
}
CellUtil.setAlignment(this, HorizontalAlignment.LEFT)
}

createCell(3).apply {
cellFormula = "IF(E${rowIndex}>=1,\"Positive\",\"Negative\")"
cellStyle = normalFontStyle
CellUtil.setAlignment(this, HorizontalAlignment.CENTER)
}

createCell(4).apply {
cellFormula = "IFERROR(IF(K${rowIndex}=1, 0, K${rowIndex}/J${rowIndex}),0)"
cellStyle = normalFontStyle.apply {
dataFormat = accountingStyle
}
CellUtil.setAlignment(this, HorizontalAlignment.RIGHT)
}

createCell(5).apply {
cellFormula = "IF(G${rowIndex}>=1,\"Positive\",\"Negative\")"
cellStyle = normalFontStyle
CellUtil.setAlignment(this, HorizontalAlignment.CENTER)
}

createCell(6).apply {
cellFormula = "IFERROR(H${rowIndex}/J${rowIndex},0)"
cellStyle = normalFontStyle.apply {
dataFormat = accountingStyle
}
CellUtil.setAlignment(this, HorizontalAlignment.RIGHT)
}

createCell(7).apply {
setCellValue(financialSummaryByClient.totalFee)
cellStyle = normalFontStyle.apply {
dataFormat = accountingStyle
}
CellUtil.setAlignment(this, HorizontalAlignment.RIGHT)
}

createCell(8).apply {
cellFormula = "H${rowIndex}*80%"
cellStyle = normalFontStyle.apply {
dataFormat = accountingStyle
}
CellUtil.setAlignment(this, HorizontalAlignment.RIGHT)
}

createCell(9).apply {
setCellValue(financialSummaryByClient.cumulativeExpenditure)
cellStyle = normalFontStyle.apply {
dataFormat = accountingStyle
}
CellUtil.setAlignment(this, HorizontalAlignment.RIGHT)
}

createCell(10).apply {
setCellValue(financialSummaryByClient.totalInvoiced)
cellStyle = normalFontStyle.apply {
dataFormat = accountingStyle
}
CellUtil.setAlignment(this, HorizontalAlignment.RIGHT)
}

createCell(11).apply {
cellFormula = "IF(H${rowIndex}-K${rowIndex}<0,0,H${rowIndex}-K${rowIndex})"
cellStyle = normalFontStyle.apply {
dataFormat = accountingStyle
}
CellUtil.setAlignment(this, HorizontalAlignment.RIGHT)
}

createCell(12).apply {
setCellValue(financialSummaryByClient.totalReceived)
cellStyle = normalFontStyle.apply {
dataFormat = accountingStyle
}
CellUtil.setAlignment(this, HorizontalAlignment.RIGHT)
}
}
}

return workbook
}
} }





+ 13
- 3
src/main/java/com/ffii/tsms/modules/data/web/DashboardController.kt Parādīt failu

@@ -2,9 +2,6 @@ package com.ffii.tsms.modules.data.web


import com.ffii.tsms.modules.data.entity.Customer import com.ffii.tsms.modules.data.entity.Customer
import com.ffii.tsms.modules.data.entity.CustomerType import com.ffii.tsms.modules.data.entity.CustomerType
import com.ffii.tsms.modules.data.web.models.CustomerResponse
import com.ffii.tsms.modules.data.web.models.SaveCustomerResponse
import com.ffii.tsms.modules.project.web.models.SaveCustomerRequest
import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.RequestMapping import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController import org.springframework.web.bind.annotation.RestController
@@ -19,6 +16,11 @@ import org.springframework.web.bind.annotation.ResponseStatus
import com.ffii.core.response.RecordsRes import com.ffii.core.response.RecordsRes
import com.ffii.core.utils.CriteriaArgsBuilder import com.ffii.core.utils.CriteriaArgsBuilder
import com.ffii.tsms.modules.data.service.* import com.ffii.tsms.modules.data.service.*
import com.ffii.tsms.modules.data.web.models.ExportFinancialSummaryByClientExcelRequest
import org.springframework.core.io.ByteArrayResource
import org.springframework.core.io.Resource
import org.springframework.http.ResponseEntity
import java.time.LocalDate


@RestController @RestController
@RequestMapping("/dashboard") @RequestMapping("/dashboard")
@@ -440,4 +442,12 @@ class DashboardController(
val args = mutableMapOf<String, Any>() val args = mutableMapOf<String, Any>()
return dashboardService.staffCombo(args) return dashboardService.staffCombo(args)
} }

@PostMapping("/exportFinancialSummaryByClientExcel")
fun exportFinancialSummaryByClientExcel(@Valid @RequestBody request: ExportFinancialSummaryByClientExcelRequest): ResponseEntity<Resource> {
val reportResult: ByteArray = dashboardService.exportFinancialSummaryByClientExcel(request.financialSummaryByClients)
return ResponseEntity.ok()
.header("filename", "Financial Summary for Client - " + LocalDate.now() + ".xlsx")
.body(ByteArrayResource(reportResult))
}
} }

+ 15
- 0
src/main/java/com/ffii/tsms/modules/data/web/models/ExportDashboardExcelRequest.kt Parādīt failu

@@ -0,0 +1,15 @@
package com.ffii.tsms.modules.data.web.models

data class FinancialSummaryByClient (
val customerCode: String,
val customerName: String,
val projectNo: Double,
val totalFee: Double,
val cumulativeExpenditure: Double,
val totalInvoiced: Double,
val totalReceived: Double,
)

data class ExportFinancialSummaryByClientExcelRequest (
val financialSummaryByClients: List<FinancialSummaryByClient>
)

+ 9
- 3
src/main/java/com/ffii/tsms/modules/project/service/ProjectsService.kt Parādīt failu

@@ -58,6 +58,7 @@ open class ProjectsService(
private val customerRepository: CustomerRepository, private val customerRepository: CustomerRepository,
private val subsidiaryRepository: SubsidiaryRepository, private val subsidiaryRepository: SubsidiaryRepository,
private val customerSubsidiaryService: CustomerSubsidiaryService, private val customerSubsidiaryService: CustomerSubsidiaryService,
private val staffsService: StaffsService,
) { ) {
open fun allProjects(): List<ProjectSearchInfo> { open fun allProjects(): List<ProjectSearchInfo> {
return projectRepository.findProjectSearchInfoByDeletedIsFalseOrderByCodeDesc() return projectRepository.findProjectSearchInfoByDeletedIsFalseOrderByCodeDesc()
@@ -83,12 +84,15 @@ open class ProjectsService(
} }


open fun allAssignedProjects(): List<AssignedProject> { open fun allAssignedProjects(): List<AssignedProject> {
val currentStaff = staffsService.currentStaff()
return SecurityUtils.getUser().getOrNull()?.let { user -> return SecurityUtils.getUser().getOrNull()?.let { user ->
staffRepository.findByUserId(user.id).getOrNull()?.let { staff -> staffRepository.findByUserId(user.id).getOrNull()?.let { staff ->
staffAllocationRepository.findOnGoingAssignedProjectsByStaff(staff).map { project -> staffAllocationRepository.findOnGoingAssignedProjectsByStaff(staff).map { project ->
val timesheetHours = timesheetRepository.totalHoursConsumedByProject(project) val timesheetHours = timesheetRepository.totalHoursConsumedByProject(project)
val staffTimesheetHours = timesheetRepository.totalHoursConsumedByProjectAndStaff(project, currentStaff!!)


AssignedProject(id = project.id!!,
AssignedProject(
id = project.id!!,
code = project.code!!, code = project.code!!,
name = project.name!!, name = project.name!!,
tasks = projectTaskRepository.findAllByProject(project).mapNotNull { pt -> pt.task }, tasks = projectTaskRepository.findAllByProject(project).mapNotNull { pt -> pt.task },
@@ -104,7 +108,9 @@ open class ProjectsService(
hoursAllocated = project.totalManhour ?: 0.0, hoursAllocated = project.totalManhour ?: 0.0,
hoursAllocatedOther = 0.0, hoursAllocatedOther = 0.0,
hoursSpent = timesheetHours.normalConsumed, hoursSpent = timesheetHours.normalConsumed,
hoursSpentOther = timesheetHours.otConsumed
hoursSpentOther = timesheetHours.otConsumed,
currentStaffHoursSpent = staffTimesheetHours.normalConsumed,
currentStaffHoursSpentOther = staffTimesheetHours.otConsumed
) )
} }
} }
@@ -213,7 +219,7 @@ open class ProjectsService(
project.apply { project.apply {
name = request.projectName name = request.projectName
description = request.projectDescription description = request.projectDescription
code = if (request.mainProjectId != null && mainProject != null) createSubProjectCode(
code = if (request.mainProjectId != null && mainProject != null && request.projectCode == null) createSubProjectCode(
mainProject, mainProject,
project project
) else request.projectCode ) else request.projectCode


+ 2
- 0
src/main/java/com/ffii/tsms/modules/project/web/models/AssignedProject.kt Parādīt failu

@@ -14,6 +14,8 @@ data class AssignedProject(
val hoursAllocatedOther: Double, val hoursAllocatedOther: Double,
val hoursSpent: Double, val hoursSpent: Double,
val hoursSpentOther: Double, val hoursSpentOther: Double,
val currentStaffHoursSpent: Double,
val currentStaffHoursSpentOther: Double,
) )


data class MilestoneInfo( data class MilestoneInfo(


+ 3
- 0
src/main/java/com/ffii/tsms/modules/timesheet/entity/TimesheetRepository.kt Parādīt failu

@@ -21,6 +21,9 @@ interface TimesheetRepository : AbstractRepository<Timesheet, Long> {
@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")
fun totalHoursConsumedByProject(project: Project): TimesheetHours fun totalHoursConsumedByProject(project: Project): TimesheetHours


@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 and t.staff = ?2")
fun totalHoursConsumedByProjectAndStaff(project: Project, staff: Staff): TimesheetHours

fun findByStaffAndRecordDateBetweenOrderByRecordDate(staff: Staff, start: LocalDate, end: LocalDate): List<Timesheet> fun findByStaffAndRecordDateBetweenOrderByRecordDate(staff: Staff, start: LocalDate, end: LocalDate): List<Timesheet>


fun findDistinctProjectTaskByStaffAndRecordDateBetweenOrderByRecordDate(staff: Staff, start: LocalDate, end: LocalDate): List<Timesheet> fun findDistinctProjectTaskByStaffAndRecordDateBetweenOrderByRecordDate(staff: Staff, start: LocalDate, end: LocalDate): List<Timesheet>

+ 9
- 0
src/main/resources/db/changelog/changes/20240710_01_cyril/01_update_authority.sql Parādīt failu

@@ -0,0 +1,9 @@
-- liquibase formatted sql
-- changeset cyril:authority, user_authority

INSERT INTO authority (authority,name)
VALUES ('MAINTAIN_NORMAL_STAFF_WORKSPACE','Maintain Normal Staff Workspace');
INSERT INTO authority (authority,name)
VALUES ('MAINTAIN_MANAGEMENT_STAFF_WORKSPACE','Maintain Management Staff Workspace');

INSERT INTO `user_authority` VALUES (1,43);

Binārs
src/main/resources/templates/report/[Dashboard] Financial Summary for client.xlsx Parādīt failu


Binārs
src/main/resources/templates/report/[Dashboard] Financial Summary for project.xlsx Parādīt failu


Notiek ielāde…
Atcelt
Saglabāt