Bladeren bron

Merge branch 'master' of https://git.2fi-solutions.com/davidhui/TSMS-backend

tags/Baseline_30082024_BACKEND_UAT
cyril.tsui 1 jaar geleden
bovenliggende
commit
af365b4605
1 gewijzigde bestanden met toevoegingen van 145 en 159 verwijderingen
  1. +145
    -159
      src/main/java/com/ffii/tsms/modules/report/service/ReportService.kt

+ 145
- 159
src/main/java/com/ffii/tsms/modules/report/service/ReportService.kt Bestand weergeven

@@ -63,6 +63,30 @@ open class ReportService(
private val COMPLETION_PROJECT = "templates/report/AR05_Project Completion Report.xlsx"

// ==============================|| GENERATE REPORT ||============================== //
fun generalCreateReportIndexed( // just loop through query records one by one, return rowIndex
sheet: Sheet,
result: List<Map<String, Any>>,
startRow: Int,
startColumn: Int
): Int {
var rowIndex = startRow
var columnIndex = startColumn
result.forEachIndexed { index, obj ->
var tempCell = sheet.getRow(rowIndex).createCell(columnIndex)
tempCell.setCellValue((index + 1).toDouble())
val keys = obj.keys.toList()
keys.forEachIndexed { keyIndex, key ->
tempCell = sheet.getRow(rowIndex).getCell(columnIndex + keyIndex + 1) ?: sheet.getRow(rowIndex)
.createCell(columnIndex + keyIndex + 1)
when (obj[key]) {
is Double -> tempCell.setCellValue(obj[key] as Double)
else -> tempCell.setCellValue(obj[key] as String)
}
}
rowIndex++
}
return rowIndex
}

fun genFinancialStatusReport(teamLeadId: Long): ByteArray {

@@ -1298,20 +1322,8 @@ open class ReportService(

rowIndex = 5
columnIndex = 0
result.forEachIndexed { index, obj ->
tempCell = sheet.getRow(rowIndex).createCell(columnIndex)
tempCell.setCellValue((index + 1).toDouble())
val keys = obj.keys.toList()
keys.forEachIndexed { keyIndex, key ->
tempCell = sheet.getRow(rowIndex).getCell(columnIndex + keyIndex + 1) ?: sheet.getRow(rowIndex)
.createCell(columnIndex + keyIndex + 1)
when (obj[key]) {
is Double -> tempCell.setCellValue(obj[key] as Double)
else -> tempCell.setCellValue(obj[key] as String)
}
}
rowIndex++
}

generalCreateReportIndexed(sheet, result, rowIndex, columnIndex)
return workbook
}

@@ -1350,20 +1362,9 @@ open class ReportService(

rowIndex = 6
columnIndex = 0
result.forEachIndexed { index, obj ->
tempCell = sheet.getRow(rowIndex).createCell(columnIndex)
tempCell.setCellValue((index + 1).toDouble())
val keys = obj.keys.toList()
keys.forEachIndexed { keyIndex, key ->
tempCell = sheet.getRow(rowIndex).getCell(columnIndex + keyIndex + 1) ?: sheet.getRow(rowIndex)
.createCell(columnIndex + keyIndex + 1)
when (obj[key]) {
is Double -> tempCell.setCellValue(obj[key] as Double)
else -> tempCell.setCellValue(obj[key] as String)
}
}
rowIndex++
}

// val currRow = generalCreateReportIndexed(sheet, result, rowIndex, columnIndex)
generalCreateReportIndexed(sheet, result, rowIndex, columnIndex)

val sheetCF = sheet.sheetConditionalFormatting
val rule1 = sheetCF.createConditionalFormattingRule("AND(J7 >= $lowerLimit, J7 <= 1)")
@@ -1529,111 +1530,107 @@ open class ReportService(
)
return jdbcDao.queryForList(sql.toString(), args)
}

open fun getProjectCompletionReport(args: Map<String, Any>): List<Map<String, Any>> {
val sql = StringBuilder(
"select"
+ " result.code, "
+ " result.name, "
+ " result.teamCode, "
+ " result.custCode, "
)
val sql = StringBuilder("select"
+ " result.code, "
+ " result.name, "
+ " result.teamCode, "
+ " result.custCode, " )
if (args.get("outstanding") as Boolean) {
sql.append(" result.projectFee - (result.totalBudget - COALESCE(i.issueAmount , 0) + COALESCE(i.issueAmount, 0) - COALESCE(i.paidAmount, 0)) as `Receivable Remained`, ")
}
sql.append(
" DATE_FORMAT(result.actualEnd, '%d/%m/%Y') as actualEnd "
+ " from ( "
+ " SELECT "
+ " pt.project_id, "
+ " min(p.code) as code, "
+ " min(p.name) as name, "
+ " min(t.code) as teamCode, "
+ " min(c.code) as custCode, "
+ " min(p.actualEnd) as actualEnd, "
+ " min(p.expectedTotalFee) as projectFee, "
+ " sum(COALESCE(tns.totalConsumed*sal.hourlyRate, 0)) as totalBudget "
+ " FROM ( "
+ " SELECT "
+ " t.staffId, "
+ " sum(t.normalConsumed + COALESCE(t.otConsumed, 0)) as totalConsumed, "
+ " t.projectTaskId AS taskId "
+ " FROM timesheet t "
+ " LEFT JOIN staff s ON t.staffId = s.id "
+ " LEFT JOIN team te on s.teamId = te.id "
+ " GROUP BY t.staffId, t.projectTaskId "
+ " order by t.staffId "
+ " ) AS tns "
+ " right join project_task pt ON tns.taskId = pt.id "
+ " left join project p on p.id = pt.project_id "
+ " left JOIN staff s ON p.teamLead = s.id "
+ " left join salary sal on s.salaryId = sal.salaryPoint "
+ " left JOIN team t ON s.teamId = t.id "
+ " left join customer c on c.id = p.customerId "
+ " where p.deleted = false "
+ " and p.status = 'Completed' "
+ " and p.actualEnd BETWEEN :startDate and :endDate "
+ " group by pt.project_id "
+ " ) as result "
+ " left join invoice i on result.code = i.projectCode "
+ " order by result.actualEnd "
+ " from ( "
+ " SELECT "
+ " pt.project_id, "
+ " min(p.code) as code, "
+ " min(p.name) as name, "
+ " min(t.code) as teamCode, "
+ " min(c.code) as custCode, "
+ " min(p.actualEnd) as actualEnd, "
+ " min(p.expectedTotalFee) as projectFee, "
+ " sum(COALESCE(tns.totalConsumed*sal.hourlyRate, 0)) as totalBudget "
+ " FROM ( "
+ " SELECT "
+ " t.staffId, "
+ " sum(t.normalConsumed + COALESCE(t.otConsumed, 0)) as totalConsumed, "
+ " t.projectTaskId AS taskId "
+ " FROM timesheet t "
+ " LEFT JOIN staff s ON t.staffId = s.id "
+ " LEFT JOIN team te on s.teamId = te.id "
+ " GROUP BY t.staffId, t.projectTaskId "
+ " order by t.staffId "
+ " ) AS tns "
+ " right join project_task pt ON tns.taskId = pt.id "
+ " left join project p on p.id = pt.project_id "
+ " left JOIN staff s ON p.teamLead = s.id "
+ " left join salary sal on s.salaryId = sal.salaryPoint "
+ " left JOIN team t ON s.teamId = t.id "
+ " left join customer c on c.id = p.customerId "
+ " where p.deleted = false "
+ " and p.status = 'Completed' "
+ " and p.actualEnd BETWEEN :startDate and :endDate "
+ " group by pt.project_id "
+ " ) as result "
+ " left join invoice i on result.code = i.projectCode "
+ " order by result.actualEnd "
)

return jdbcDao.queryForList(sql.toString(), args)
}

open fun getProjectResourceOverconsumptionReport(args: Map<String, Any>): List<Map<String, Any>> {
val sql = StringBuilder(
"WITH teamNormalConsumed AS ("
+ " SELECT "
+ " s.teamId, "
+ " pt.project_id, "
+ " SUM(tns.totalConsumed) AS totalConsumed, "
+ " sum(tns.totalBudget) as totalBudget "
+ " FROM ( "
+ " SELECT "
+ " t.staffId, "
+ " sum(t.normalConsumed + COALESCE(t.otConsumed, 0)) as totalConsumed, "
+ " t.projectTaskId AS taskId, "
+ " sum(t.normalConsumed + COALESCE(t.otConsumed, 0)) * min(sal.hourlyRate) as totalBudget "
+ " FROM timesheet t "
+ " LEFT JOIN staff s ON t.staffId = s.id "
+ " left join salary sal on sal.salaryPoint = s.salaryId "
+ " GROUP BY t.staffId, t.projectTaskId "
+ " ) AS tns "
+ " INNER JOIN project_task pt ON tns.taskId = pt.id "
+ " JOIN staff s ON tns.staffId = s.id "
+ " JOIN team t ON s.teamId = t.id "
+ " GROUP BY teamId, project_id "
+ " ) "
+ " SELECT "
+ " p.code, "
+ " p.name, "
+ " t.code as team, "
+ " c.code as client, "
+ " p.expectedTotalFee * 0.8 as plannedBudget, "
+ " COALESCE(tns.totalBudget, 0) as actualConsumedBudget, "
+ " COALESCE(p.totalManhour, 0) as plannedManhour, "
+ " COALESCE(tns.totalConsumed, 0) as actualConsumedManhour, "
+ " (COALESCE((tns.totalConsumed * sa.hourlyRate), 0) / p.expectedTotalFee) as budgetConsumptionRate, "
+ " (COALESCE(tns.totalConsumed, 0) / COALESCE(p.totalManhour, 0)) as manhourConsumptionRate, "
+ " CASE "
+ " when (COALESCE((tns.totalConsumed * sa.hourlyRate), 0) / p.expectedTotalFee) >= :lowerLimit and (COALESCE((tns.totalConsumed * sa.hourlyRate), 0) / p.expectedTotalFee) <= 1 "
+ " or (COALESCE(tns.totalConsumed, 0) / COALESCE(p.totalManhour, 0)) >= :lowerLimit and (COALESCE(tns.totalConsumed, 0) / COALESCE(p.totalManhour, 0)) <= 1 "
+ " then 'Potential Overconsumption' "
+ " when (COALESCE((tns.totalConsumed * sa.hourlyRate), 0) / p.expectedTotalFee) >= 1 "
+ " or (COALESCE(tns.totalConsumed, 0) / COALESCE(p.totalManhour, 0)) >= 1 "
+ " then 'Overconsumption' "
+ " else 'Within Budget' "
+ " END as status "
+ " FROM project p "
+ " LEFT JOIN team t ON p.teamLead = t.teamLead "
+ " LEFT JOIN staff s ON p.teamLead = s.id "
+ " LEFT JOIN salary sa ON s.salaryId = sa.salaryPoint "
+ " LEFT JOIN customer c ON p.customerId = c.id "
+ " left join teamNormalConsumed tns on tns.project_id = p.id "
+ " WHERE p.deleted = false "
+ " and p.status = 'On-going' "
val sql = StringBuilder("WITH teamNormalConsumed AS ("
+ " SELECT "
+ " s.teamId, "
+ " pt.project_id, "
+ " SUM(tns.totalConsumed) AS totalConsumed, "
+ " sum(tns.totalBudget) as totalBudget "
+ " FROM ( "
+ " SELECT "
+ " t.staffId, "
+ " sum(t.normalConsumed + COALESCE(t.otConsumed, 0)) as totalConsumed, "
+ " t.projectTaskId AS taskId, "
+ " sum(t.normalConsumed + COALESCE(t.otConsumed, 0)) * min(sal.hourlyRate) as totalBudget "
+ " FROM timesheet t "
+ " LEFT JOIN staff s ON t.staffId = s.id "
+ " left join salary sal on sal.salaryPoint = s.salaryId "
+ " GROUP BY t.staffId, t.projectTaskId "
+ " ) AS tns "
+ " INNER JOIN project_task pt ON tns.taskId = pt.id "
+ " JOIN staff s ON tns.staffId = s.id "
+ " JOIN team t ON s.teamId = t.id "
+ " GROUP BY teamId, project_id "
+ " ) "
+ " SELECT "
+ " p.code, "
+ " p.name, "
+ " t.code as team, "
+ " c.code as client, "
+ " p.expectedTotalFee * 0.8 as plannedBudget, "
+ " COALESCE(tns.totalBudget, 0) as actualConsumedBudget, "
+ " COALESCE(p.totalManhour, 0) as plannedManhour, "
+ " COALESCE(tns.totalConsumed, 0) as actualConsumedManhour, "
+ " (COALESCE((tns.totalConsumed * sa.hourlyRate), 0) / p.expectedTotalFee) as budgetConsumptionRate, "
+ " (COALESCE(tns.totalConsumed, 0) / COALESCE(p.totalManhour, 0)) as manhourConsumptionRate, "
+ " CASE "
+ " when (COALESCE((tns.totalConsumed * sa.hourlyRate), 0) / p.expectedTotalFee) >= :lowerLimit and (COALESCE((tns.totalConsumed * sa.hourlyRate), 0) / p.expectedTotalFee) <= 1 "
+ " or (COALESCE(tns.totalConsumed, 0) / COALESCE(p.totalManhour, 0)) >= :lowerLimit and (COALESCE(tns.totalConsumed, 0) / COALESCE(p.totalManhour, 0)) <= 1 "
+ " then 'Potential Overconsumption' "
+ " when (COALESCE((tns.totalConsumed * sa.hourlyRate), 0) / p.expectedTotalFee) >= 1 "
+ " or (COALESCE(tns.totalConsumed, 0) / COALESCE(p.totalManhour, 0)) >= 1 "
+ " then 'Overconsumption' "
+ " else 'Within Budget' "
+ " END as status "
+ " FROM project p "
+ " LEFT JOIN team t ON p.teamLead = t.teamLead "
+ " LEFT JOIN staff s ON p.teamLead = s.id "
+ " LEFT JOIN salary sa ON s.salaryId = sa.salaryPoint "
+ " LEFT JOIN customer c ON p.customerId = c.id "
+ " left join teamNormalConsumed tns on tns.project_id = p.id "
+ " WHERE p.deleted = false "
+ " and p.status = 'On-going' "
)
if (args != null) {
var statusFilter: String = ""
@@ -2126,10 +2123,10 @@ open class ReportService(
return workbook
}

fun getCostAndExpense(clientId: Long?, teamId: Long?): List<Map<String, Any?>> {
fun getCostAndExpense(clientId: Long?, teamId: Long?): List<Map<String,Any?>>{
val sql = StringBuilder(
" with cte_timesheet as ( "
+ " Select p.code, s.name as staff, IFNULL(t.normalConsumed, 0) as normalConsumed, IFNULL(t.otConsumed , 0) as otConsumed, s2.salaryPoint, s2.hourlyRate, t.staffId,"
+ " Select p.code, s.name as staff, IFNULL(t.normalConsumed, 0) as normalConsumed, IFNULL(t.otConsumed , 0) as otConsumed, s2.salaryPoint, s2.hourlyRate, t.staffId,"
+ " t.recordDate"
+ " from timesheet t"
+ " left join project_task pt on pt.id = t.projectTaskId"
@@ -2151,13 +2148,13 @@ open class ReportService(
+ " left join team t2 on t2.id = s.teamId"
+ " where ISNULL(p.code) = False"
)
if (clientId != null) {
if(clientId != null){
sql.append(
" and c.id = :clientId "
)
}

if (teamId != null) {
if(teamId != null){
sql.append(
" and p.teamLead = :teamId "
)
@@ -2176,9 +2173,9 @@ open class ReportService(
val queryList = jdbcDao.queryForList(sql.toString(), args)
val costAndExpenseList = mutableListOf<Map<String, Any?>>()

for (item in queryList) {
for(item in queryList){
val hourlyRate = (item.getValue("hourlyRate") as BigDecimal).toDouble()
if (item["code"] !in costAndExpenseList) {
if(item["code"] !in costAndExpenseList){
costAndExpenseList.add(
mapOf(
"code" to item["code"],
@@ -2186,27 +2183,22 @@ open class ReportService(
"client" to item["client"],
"teamLead" to item["teamLead"],
"budget" to item["expectedTotalFee"],
"totalManhours" to item["normalConsumed"] as Double + item["otConsumed"] as Double,
"manhourExpenditure" to (hourlyRate * item["normalConsumed"] as Double)
+ (hourlyRate * item["otConsumed"] as Double * otFactor)
"totalManhours" to item["normalConsumed"] as Double + item["otConsumed"] as Double,
"manhourExpenditure" to (hourlyRate * item["normalConsumed"] as Double )
+ (hourlyRate * item["otConsumed"]as Double * otFactor)
)
)
} else {
}else{
val existingMap = costAndExpenseList.find { it.containsValue(item["code"]) }!!
costAndExpenseList[costAndExpenseList.indexOf(existingMap)] = existingMap.toMutableMap().apply {
put(
"totalManhours",
get("manhours") as Double + (item["normalConsumed"] as Double + item["otConsumed"] as Double)
)
put(
"manhourExpenditure",
get("manhourExpenditure") as Double + ((hourlyRate * item["normalConsumed"] as Double)
+ (hourlyRate * item["otConsumed"] as Double * otFactor))
)
put("totalManhours", get("manhours") as Double + (item["normalConsumed"] as Double + item["otConsumed"] as Double))
put("manhourExpenditure", get("manhourExpenditure") as Double + ((hourlyRate * item["normalConsumed"] as Double )
+ (hourlyRate * item["otConsumed"]as Double * otFactor)))
}
}
}
val result = costAndExpenseList.map { item ->
val result = costAndExpenseList.map {
item ->
val budget = (item["budget"] as? Double)?.times(0.8) ?: 0.0
val budgetRemain = budget - (item["manhourExpenditure"] as? Double ?: 0.0)
val remainingPercent = (budgetRemain / budget)
@@ -2224,7 +2216,7 @@ open class ReportService(
teamId: Long?,
clientId: Long?,
budgetPercentage: Double?
): Workbook {
): Workbook{
val resource = ClassPathResource(templatePath)
val templateInputStream = resource.inputStream
val workbook: Workbook = XSSFWorkbook(templateInputStream)
@@ -2242,9 +2234,9 @@ open class ReportService(
rowNum = 2
val row2: Row = sheet.getRow(rowNum)
val row2Cell = row2.getCell(2)
if (teamId == null) {
if(teamId == null){
row2Cell.setCellValue("All")
} else {
}else{
val sql = StringBuilder(
" select t.id, t.code, t.name, concat(t.code, \" - \" ,t.name) as teamLead from team t where t.id = :teamId "
)
@@ -2255,9 +2247,9 @@ open class ReportService(
rowNum = 3
val row3: Row = sheet.getRow(rowNum)
val row3Cell = row3.getCell(2)
if (clientId == null) {
if(clientId == null){
row3Cell.setCellValue("All")
} else {
}else{
val sql = StringBuilder(
" select c.id, c.name from customer c where c.id = :clientId "
)
@@ -2267,15 +2259,15 @@ open class ReportService(


val filterList: List<Map<String, Any?>>
if (budgetPercentage != null) {
if(budgetPercentage != null){
filterList = costAndExpenseList.filter { ((it["budgetPercentage"] as? Double) ?: 0.0) > budgetPercentage }
} else {
}else{
filterList = costAndExpenseList
}


rowNum = 6
for (item in filterList) {
for(item in filterList){
val index = filterList.indexOf(item)
val row: Row = sheet.getRow(rowNum) ?: sheet.createRow(rowNum)
val cell = row.getCell(0) ?: row.createCell(0)
@@ -2320,13 +2312,13 @@ open class ReportService(

val cell7 = row.getCell(7) ?: row.createCell(7)
cell7.apply {
cellFormula = "F${rowNum + 1}-G${rowNum + 1}"
cellFormula = "F${rowNum+1}-G${rowNum+1}"
}
CellUtil.setCellStyleProperty(cell7, "dataFormat", accountingStyle)

val cell8 = row.getCell(8) ?: row.createCell(8)
cell8.apply {
cellFormula = "H${rowNum + 1}/F${rowNum + 1}"
cellFormula = "H${rowNum+1}/F${rowNum+1}"
}
CellUtil.setCellStyleProperty(cell8, "dataFormat", percentStyle)

@@ -2336,17 +2328,11 @@ open class ReportService(
return workbook
}

fun genCostAndExpenseReport(request: costAndExpenseRequest): ByteArray {
fun genCostAndExpenseReport(request: costAndExpenseRequest): ByteArray{

val costAndExpenseList = getCostAndExpense(request.clientId, request.teamId)

val workbook: Workbook = createCostAndExpenseWorkbook(
COSTANDEXPENSE_REPORT,
costAndExpenseList,
request.teamId,
request.clientId,
request.budgetPercentage
)
val workbook: Workbook = createCostAndExpenseWorkbook(COSTANDEXPENSE_REPORT, costAndExpenseList, request.teamId, request.clientId, request.budgetPercentage)

val outputStream: ByteArrayOutputStream = ByteArrayOutputStream()
workbook.write(outputStream)


Laden…
Annuleren
Opslaan