|
|
@@ -5,6 +5,7 @@ import com.ffii.tsms.modules.data.entity.Customer |
|
|
|
import com.ffii.tsms.modules.data.entity.Salary |
|
|
|
import com.ffii.tsms.modules.data.entity.Staff |
|
|
|
import com.ffii.tsms.modules.data.entity.Team |
|
|
|
import com.ffii.tsms.modules.project.entity.GradeAllocation |
|
|
|
import com.ffii.tsms.modules.project.entity.Invoice |
|
|
|
import com.ffii.tsms.modules.project.entity.Milestone |
|
|
|
import com.ffii.tsms.modules.project.entity.Project |
|
|
@@ -32,6 +33,7 @@ import java.sql.Time |
|
|
|
import java.time.LocalDate |
|
|
|
import java.time.YearMonth |
|
|
|
import java.time.format.DateTimeFormatter |
|
|
|
import java.time.temporal.ChronoUnit |
|
|
|
import java.util.* |
|
|
|
import kotlin.jvm.optionals.getOrElse |
|
|
|
|
|
|
@@ -56,7 +58,8 @@ open class ReportService( |
|
|
|
private val SALART_LIST_TEMPLATE = "templates/report/Salary Template.xlsx" |
|
|
|
private val LATE_START_REPORT = "templates/report/AR01_Late Start Report v01.xlsx" |
|
|
|
private val RESOURCE_OVERCONSUMPTION_REPORT = "templates/report/AR03_Resource Overconsumption.xlsx" |
|
|
|
private val COMPLETE_PROJECT_OUTSTANDING_RECEIVABLE = "templates/report/AR06_Project Completion Report with Outstanding Accounts Receivable v02.xlsx" |
|
|
|
private val COMPLETE_PROJECT_OUTSTANDING_RECEIVABLE = |
|
|
|
"templates/report/AR06_Project Completion Report with Outstanding Accounts Receivable v02.xlsx" |
|
|
|
private val COMPLETION_PROJECT = "templates/report/AR05_Project Completion Report.xlsx" |
|
|
|
|
|
|
|
// ==============================|| GENERATE REPORT ||============================== // |
|
|
@@ -145,6 +148,8 @@ open class ReportService( |
|
|
|
searchedClient: String, |
|
|
|
projects: List<Project>, |
|
|
|
timesheets: List<Timesheet>, |
|
|
|
numberOfDays: Int, |
|
|
|
projectCompletion: Int, |
|
|
|
): ByteArray { |
|
|
|
// Generate the Excel report with query results |
|
|
|
val workbook: Workbook = createProjectPotentialDelayReport( |
|
|
@@ -152,6 +157,8 @@ open class ReportService( |
|
|
|
searchedClient, |
|
|
|
projects, |
|
|
|
timesheets, |
|
|
|
numberOfDays, |
|
|
|
projectCompletion, |
|
|
|
PROJECT_POTENTIAL_DELAY_REPORT |
|
|
|
) |
|
|
|
|
|
|
@@ -209,6 +216,7 @@ open class ReportService( |
|
|
|
|
|
|
|
return outputStream.toByteArray() |
|
|
|
} |
|
|
|
|
|
|
|
@Throws(IOException::class) |
|
|
|
fun generateProjectCompletionReport( |
|
|
|
args: MutableMap<String, Any>, |
|
|
@@ -811,6 +819,8 @@ open class ReportService( |
|
|
|
searchedClient: String, |
|
|
|
projects: List<Project>, |
|
|
|
timesheets: List<Timesheet>, |
|
|
|
numberOfDays: Int, |
|
|
|
projectCompletion: Int, |
|
|
|
templatePath: String, |
|
|
|
): Workbook { |
|
|
|
// please create a new function for each report template |
|
|
@@ -897,35 +907,36 @@ open class ReportService( |
|
|
|
} |
|
|
|
|
|
|
|
project.milestones.forEach { milestone: Milestone -> |
|
|
|
logger.info(milestone.id) |
|
|
|
|
|
|
|
val tempRow = sheet.getRow(rowIndex) ?: sheet.createRow(rowIndex) |
|
|
|
rowIndex++ |
|
|
|
|
|
|
|
tempRow.apply { |
|
|
|
createCell(7).apply { |
|
|
|
setCellValue(milestone.taskGroup?.name ?: "N/A") |
|
|
|
} |
|
|
|
|
|
|
|
createCell(8).apply { |
|
|
|
setCellValue(milestone.endDate?.format(DATE_FORMATTER) ?: "N/A") |
|
|
|
} |
|
|
|
|
|
|
|
createCell(9).apply { |
|
|
|
cellStyle.dataFormat = workbook.createDataFormat().getFormat("0.00%") |
|
|
|
val manHoursSpent = groupedTimesheets[Pair(project.id, milestone.id)]?.sum() ?: 0.0 |
|
|
|
val resourceUtilization = manHoursSpent / (milestone.stagePercentAllocation!! / 100 * project.totalManhour!!) |
|
|
|
// logger.info(project.name + " : " + milestone.taskGroup?.name + " : " + ChronoUnit.DAYS.between(LocalDate.now(), milestone.endDate)) |
|
|
|
// logger.info(numberOfDays) |
|
|
|
if (ChronoUnit.DAYS.between(LocalDate.now(), milestone.endDate) <= numberOfDays.toLong() && resourceUtilization <= projectCompletion.toDouble() / 100.0) { |
|
|
|
val tempRow = sheet.getRow(rowIndex) ?: sheet.createRow(rowIndex) |
|
|
|
rowIndex++ |
|
|
|
|
|
|
|
tempRow.apply { |
|
|
|
createCell(7).apply { |
|
|
|
setCellValue(milestone.taskGroup?.name ?: "N/A") |
|
|
|
} |
|
|
|
|
|
|
|
if (groupedTimesheets.containsKey(Pair(project.id, milestone.id))) { |
|
|
|
val manHoursSpent = groupedTimesheets[Pair(project.id, milestone.id)]!!.sum() |
|
|
|
createCell(8).apply { |
|
|
|
setCellValue(milestone.endDate?.format(DATE_FORMATTER) ?: "N/A") |
|
|
|
} |
|
|
|
|
|
|
|
logger.info("manHoursSpent: $manHoursSpent") |
|
|
|
logger.info("milestone.stagePercentAllocation: " + milestone.stagePercentAllocation) |
|
|
|
logger.info("project.totalManhour: " + project.totalManhour) |
|
|
|
createCell(9).apply { |
|
|
|
cellStyle.dataFormat = workbook.createDataFormat().getFormat("0.00%") |
|
|
|
|
|
|
|
val resourceUtilization = |
|
|
|
manHoursSpent / (milestone.stagePercentAllocation!! / 100 * project.totalManhour!!) |
|
|
|
setCellValue(resourceUtilization ?: 0.0) |
|
|
|
} else { |
|
|
|
setCellValue(0.0) |
|
|
|
// if (groupedTimesheets.containsKey(Pair(project.id, milestone.id))) { |
|
|
|
// val manHoursSpent = groupedTimesheets[Pair(project.id, milestone.id)]!!.sum() |
|
|
|
// |
|
|
|
// val resourceUtilization = |
|
|
|
// manHoursSpent / (milestone.stagePercentAllocation!! / 100 * project.totalManhour!!) |
|
|
|
setCellValue(resourceUtilization) |
|
|
|
// } else { |
|
|
|
// setCellValue(0.0) |
|
|
|
// } |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
@@ -1259,6 +1270,7 @@ open class ReportService( |
|
|
|
|
|
|
|
return workbook |
|
|
|
} |
|
|
|
|
|
|
|
private fun createProjectCompletionReport( |
|
|
|
args: MutableMap<String, Any>, |
|
|
|
result: List<Map<String, Any>>, |
|
|
@@ -1291,10 +1303,11 @@ open class ReportService( |
|
|
|
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) |
|
|
|
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 ) |
|
|
|
else -> tempCell.setCellValue(obj[key] as String) |
|
|
|
} |
|
|
|
} |
|
|
|
rowIndex++ |
|
|
@@ -1516,107 +1529,111 @@ 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 = "" |
|
|
@@ -2109,10 +2126,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" |
|
|
@@ -2134,13 +2151,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 " |
|
|
|
) |
|
|
@@ -2159,9 +2176,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"], |
|
|
@@ -2169,22 +2186,27 @@ 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) |
|
|
@@ -2202,7 +2224,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) |
|
|
@@ -2220,9 +2242,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 " |
|
|
|
) |
|
|
@@ -2233,9 +2255,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 " |
|
|
|
) |
|
|
@@ -2245,15 +2267,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) |
|
|
@@ -2298,13 +2320,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) |
|
|
|
|
|
|
@@ -2314,11 +2336,17 @@ 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) |
|
|
|