Browse Source

update settings, report (salary effective)

tags/Baseline_30082024_BACKEND_UAT
cyril.tsui 1 year ago
parent
commit
01c738b8e7
6 changed files with 214 additions and 83 deletions
  1. +40
    -0
      src/main/java/com/ffii/tsms/modules/data/service/SalaryEffectiveService.kt
  2. +7
    -3
      src/main/java/com/ffii/tsms/modules/data/web/SalaryEffectiveController.kt
  3. +152
    -77
      src/main/java/com/ffii/tsms/modules/report/service/ReportService.kt
  4. +7
    -3
      src/main/java/com/ffii/tsms/modules/report/web/ReportController.kt
  5. +3
    -0
      src/main/java/com/ffii/tsms/modules/settings/entity/SettingsRepository.java
  6. +5
    -0
      src/main/java/com/ffii/tsms/modules/settings/service/SettingsService.java

+ 40
- 0
src/main/java/com/ffii/tsms/modules/data/service/SalaryEffectiveService.kt View File

@@ -6,10 +6,13 @@ import com.ffii.tsms.modules.data.entity.Salary
import com.ffii.tsms.modules.data.entity.SalaryEffective import com.ffii.tsms.modules.data.entity.SalaryEffective
import com.ffii.tsms.modules.data.entity.SalaryEffectiveRepository import com.ffii.tsms.modules.data.entity.SalaryEffectiveRepository
import com.ffii.tsms.modules.data.entity.SalaryRepository import com.ffii.tsms.modules.data.entity.SalaryRepository
import com.ffii.tsms.modules.data.entity.Staff
import com.ffii.tsms.modules.data.entity.StaffRepository import com.ffii.tsms.modules.data.entity.StaffRepository
import org.springframework.stereotype.Service import org.springframework.stereotype.Service
import java.math.BigDecimal import java.math.BigDecimal
import java.time.LocalDate import java.time.LocalDate
import java.time.LocalDateTime
import java.time.temporal.ChronoUnit


@Service @Service
open class SalaryEffectiveService( open class SalaryEffectiveService(
@@ -90,4 +93,41 @@ open class SalaryEffectiveService(
// println(salaryEffectiveLists) // println(salaryEffectiveLists)
return salaryEffectiveLists return salaryEffectiveLists
} }

data class MonthlyStaffSalaryData(
val staff: Staff,
val date: LocalDate,
val hourlyRate: Double,
)

open fun getMonthlyStaffSalaryData(startDate: LocalDate, endDate: LocalDate): List<MonthlyStaffSalaryData> {
val salaryEffective = salaryEffectiveRepository.findAll()
.groupBy { Pair(it.date.year, it.date.month) }
.map { (_, se) -> se.maxByOrNull { it.date }!!}
salaryEffective.sortedWith(compareBy({it.staff.staffId}, {it.date}))

val staffs = staffRepository.findAll().sortedBy { it.staffId }.filter { it.deleted == false }

val result = mutableListOf<MonthlyStaffSalaryData>()
val dateList = (0..ChronoUnit.MONTHS.between(startDate, endDate)).map { startDate.withDayOfMonth(1).plusMonths(it) }

staffs.forEach { staff ->
dateList.forEach{ date ->
val staffSalaryEffective = salaryEffective.filter { it.staff.staffId.equals(staff.staffId) }
var currentHourlyRate = staffSalaryEffective.filter { date.isEqual(it.date.withDayOfMonth(1)) || date.isAfter(it.date.withDayOfMonth(1)) }.minByOrNull { it.date }

if (currentHourlyRate == null) {
currentHourlyRate = staffSalaryEffective.minByOrNull { it.date }
}

result += MonthlyStaffSalaryData(
staff = staff,
date = date,
hourlyRate = currentHourlyRate?.salary?.hourlyRate?.toDouble() ?: staff?.salary?.hourlyRate?.toDouble() ?: 0.0
)
}
}

return result
}
} }

+ 7
- 3
src/main/java/com/ffii/tsms/modules/data/web/SalaryEffectiveController.kt View File

@@ -4,16 +4,20 @@ 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.SalaryEffectiveService import com.ffii.tsms.modules.data.service.SalaryEffectiveService
import jakarta.servlet.http.HttpServletRequest import jakarta.servlet.http.HttpServletRequest
import jakarta.validation.Valid
import org.springframework.web.bind.ServletRequestBindingException import org.springframework.web.bind.ServletRequestBindingException
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController
import org.springframework.web.bind.annotation.*
import java.time.LocalDate




@RestController @RestController
@RequestMapping("/salaryEffective") @RequestMapping("/salaryEffective")
class SalaryEffectiveController(private val salaryEffectiveService: SalaryEffectiveService) { class SalaryEffectiveController(private val salaryEffectiveService: SalaryEffectiveService) {


@GetMapping("/test")
fun test(@RequestParam startDate: LocalDate, @RequestParam endDate: LocalDate): List<SalaryEffectiveService.MonthlyStaffSalaryData> {
return salaryEffectiveService.getMonthlyStaffSalaryData(startDate, endDate)
}
// @GetMapping("/combo") // @GetMapping("/combo")
// @Throws(ServletRequestBindingException::class) // @Throws(ServletRequestBindingException::class)
// fun combo(request: HttpServletRequest?): RecordsRes<Map<String, Any>> { // fun combo(request: HttpServletRequest?): RecordsRes<Map<String, Any>> {


+ 152
- 77
src/main/java/com/ffii/tsms/modules/report/service/ReportService.kt View File

@@ -3,6 +3,7 @@ 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.* import com.ffii.tsms.modules.data.entity.*
import com.ffii.tsms.modules.data.service.SalaryEffectiveService import com.ffii.tsms.modules.data.service.SalaryEffectiveService
import com.ffii.tsms.modules.data.service.SalaryEffectiveService.MonthlyStaffSalaryData
import com.ffii.tsms.modules.project.entity.Invoice import com.ffii.tsms.modules.project.entity.Invoice
import com.ffii.tsms.modules.project.entity.Milestone import com.ffii.tsms.modules.project.entity.Milestone
import com.ffii.tsms.modules.project.entity.Project import com.ffii.tsms.modules.project.entity.Project
@@ -186,11 +187,19 @@ open class ReportService(
project: Project, project: Project,
invoices: List<Invoice>, invoices: List<Invoice>,
timesheets: List<Timesheet>, timesheets: List<Timesheet>,
monthlyStaffSalaryEffective: List<MonthlyStaffSalaryData>,
dateType: String dateType: String
): ByteArray { ): ByteArray {
// Generate the Excel report with query results // Generate the Excel report with query results
val workbook: Workbook = val workbook: Workbook =
createProjectCashFlowReport(project, invoices, timesheets, dateType, PROJECT_CASH_FLOW_REPORT)
createProjectCashFlowReport(
project,
invoices,
timesheets,
monthlyStaffSalaryEffective,
dateType,
PROJECT_CASH_FLOW_REPORT
)


// Write the workbook to a ByteArrayOutputStream // Write the workbook to a ByteArrayOutputStream
val outputStream: ByteArrayOutputStream = ByteArrayOutputStream() val outputStream: ByteArrayOutputStream = ByteArrayOutputStream()
@@ -349,11 +358,20 @@ open class ReportService(
timesheets: List<Timesheet>, timesheets: List<Timesheet>,
teams: List<Team>, teams: List<Team>,
grades: List<Grade>, grades: List<Grade>,
monthlyStaffSalaryEffective: List<MonthlyStaffSalaryData>,
teamId: String, teamId: String,
): ByteArray { ): ByteArray {
// Generate the Excel report with query results // Generate the Excel report with query results
val workbook: Workbook = val workbook: Workbook =
createCrossTeamChargeReport(month, timesheets, teams, grades, teamId, CROSS_TEAM_CHARGE_REPORT)
createCrossTeamChargeReport(
month,
timesheets,
teams,
grades,
monthlyStaffSalaryEffective,
teamId,
CROSS_TEAM_CHARGE_REPORT
)


// Write the workbook to a ByteArrayOutputStream // Write the workbook to a ByteArrayOutputStream
val outputStream: ByteArrayOutputStream = ByteArrayOutputStream() val outputStream: ByteArrayOutputStream = ByteArrayOutputStream()
@@ -461,7 +479,9 @@ open class ReportService(
endDateCell.setCellValue(if (item["planEnd"] != null) item.getValue("planEnd").toString() else "N/A") endDateCell.setCellValue(if (item["planEnd"] != null) item.getValue("planEnd").toString() else "N/A")


val totalFeeCell = row.createCell(7) val totalFeeCell = row.createCell(7)
val totalFee = (item["expectedTotalFee"]?.let { it as Double } ?: 0.0) - (item["subContractFee"]?.let { it as Double } ?: 0.0)
val totalFee =
(item["expectedTotalFee"]?.let { it as Double } ?: 0.0) - (item["subContractFee"]?.let { it as Double }
?: 0.0)
totalFeeCell.apply { totalFeeCell.apply {
setCellValue(totalFee) setCellValue(totalFee)
cellStyle.dataFormat = accountingStyle cellStyle.dataFormat = accountingStyle
@@ -722,6 +742,7 @@ open class ReportService(
project: Project, project: Project,
invoices: List<Invoice>, invoices: List<Invoice>,
timesheets: List<Timesheet>, timesheets: List<Timesheet>,
monthlyStaffSalaryEffective: List<SalaryEffectiveService.MonthlyStaffSalaryData>,
dateType: String, dateType: String,
templatePath: String, templatePath: String,
): Workbook { ): Workbook {
@@ -792,8 +813,10 @@ open class ReportService(
rowIndex = 11 rowIndex = 11
val actualIncome = invoices.sumOf { invoice -> invoice.paidAmount!! } val actualIncome = invoices.sumOf { invoice -> invoice.paidAmount!! }
val actualExpenditure = timesheets.sumOf { timesheet -> val actualExpenditure = timesheets.sumOf { timesheet ->
timesheet.staff!!.salary.hourlyRate.toDouble() * ((timesheet.normalConsumed ?: 0.0) + (timesheet.otConsumed
?: 0.0))
// timesheet.staff!!.salary.hourlyRate.toDouble() * ((timesheet.normalConsumed ?: 0.0) + (timesheet.otConsumed ?: 0.0))
(monthlyStaffSalaryEffective.find {
it.staff.staffId == timesheet.staff?.staffId && it.date.year == timesheet.recordDate?.year && it.date.month == timesheet.recordDate?.month
}?.hourlyRate ?: 0.0) * ((timesheet.normalConsumed ?: 0.0) + (timesheet.otConsumed ?: 0.0))
} }
sheet.getRow(rowIndex).apply { sheet.getRow(rowIndex).apply {
createCell(1).apply { createCell(1).apply {
@@ -834,10 +857,16 @@ open class ReportService(
timesheetEntries.map { timesheet -> timesheetEntries.map { timesheet ->
if (timesheet.normalConsumed != null) { if (timesheet.normalConsumed != null) {
timesheet.normalConsumed!!.plus(timesheet.otConsumed ?: 0.0) timesheet.normalConsumed!!.plus(timesheet.otConsumed ?: 0.0)
.times(timesheet.staff!!.salary.hourlyRate.toDouble())
// .times(timesheet.staff!!.salary.hourlyRate.toDouble())
.times(monthlyStaffSalaryEffective.find {
it.staff.staffId == timesheet.staff?.staffId && it.date.year == timesheet.recordDate?.year && it.date.month == timesheet.recordDate?.month
}?.hourlyRate ?: 0.0)
} else if (timesheet.otConsumed != null) { } else if (timesheet.otConsumed != null) {
timesheet.otConsumed!!.plus(timesheet.normalConsumed ?: 0.0) timesheet.otConsumed!!.plus(timesheet.normalConsumed ?: 0.0)
.times(timesheet.staff!!.salary.hourlyRate.toDouble())
// .times(timesheet.staff!!.salary.hourlyRate.toDouble())
.times(monthlyStaffSalaryEffective.find {
it.staff.staffId == timesheet.staff?.staffId && it.date.year == timesheet.recordDate?.year && it.date.month == timesheet.recordDate?.month
}?.hourlyRate ?: 0.0)
} else { } else {
0.0 0.0
} }
@@ -2302,7 +2331,13 @@ open class ReportService(
for (item in manHoursSpent) { for (item in manHoursSpent) {
updateInfo(info, item) updateInfo(info, item)


val hourlyRate = getSalaryForMonth(item.getValue("recordDate") as String, item.getValue("staffId") as String, staffSalaryLists, queryStartMonth, queryEndMonth) ?: (item.getValue("hourlyRate") as BigDecimal).toDouble()
val hourlyRate = getSalaryForMonth(
item.getValue("recordDate") as String,
item.getValue("staffId") as String,
staffSalaryLists,
queryStartMonth,
queryEndMonth
) ?: (item.getValue("hourlyRate") as BigDecimal).toDouble()


if (!staffInfoList.any { it["staffId"] == item["staffId"] && it["name"] == item["name"] }) { if (!staffInfoList.any { it["staffId"] == item["staffId"] && it["name"] == item["name"] }) {


@@ -2374,7 +2409,13 @@ open class ReportService(
return tempList return tempList
} }


fun getSalaryForMonth(recordDate: String, staffId: String, staffSalaryLists: List<SalaryEffectiveService.StaffSalaryData>, start: YearMonth, end: YearMonth): Double? {
fun getSalaryForMonth(
recordDate: String,
staffId: String,
staffSalaryLists: List<SalaryEffectiveService.StaffSalaryData>,
start: YearMonth,
end: YearMonth
): Double? {
val formatter = DateTimeFormatter.ofPattern("yyyy-MM") val formatter = DateTimeFormatter.ofPattern("yyyy-MM")
val monthDate = YearMonth.parse(recordDate, formatter) val monthDate = YearMonth.parse(recordDate, formatter)


@@ -2383,7 +2424,12 @@ open class ReportService(
return findSalaryForMonth(staffSalaryData, monthDate, start, end) return findSalaryForMonth(staffSalaryData, monthDate, start, end)
} }


private fun findSalaryForMonth(salaryDataList: List<SalaryEffectiveService.SalaryData>, targetMonth: YearMonth, start: YearMonth, end: YearMonth): Double? {
private fun findSalaryForMonth(
salaryDataList: List<SalaryEffectiveService.SalaryData>,
targetMonth: YearMonth,
start: YearMonth,
end: YearMonth
): Double? {
if (salaryDataList.isEmpty()) return null if (salaryDataList.isEmpty()) return null


val periodStartMonth = 10 val periodStartMonth = 10
@@ -2416,23 +2462,27 @@ open class ReportService(
val staffSalaryData = staffSalaryDataList.find { it.staffId == staffId } val staffSalaryData = staffSalaryDataList.find { it.staffId == staffId }


// if (staffSalaryData != null) { // if (staffSalaryData != null) {
val updatedFinancialYears = (staffInfo["financialYears"] as List<FinancialYear>).map { financialYear ->
updateFinancialYear(financialYear, staffSalaryData?.salaryData, staffInfo)
}
val updatedFinancialYears = (staffInfo["financialYears"] as List<FinancialYear>).map { financialYear ->
updateFinancialYear(financialYear, staffSalaryData?.salaryData, staffInfo)
}


val updatedStaffInfo = staffInfo.toMutableMap().apply {
this["financialYears"] = updatedFinancialYears
}
val updatedStaffInfo = staffInfo.toMutableMap().apply {
this["financialYears"] = updatedFinancialYears
}


staffInfoList[index] = updatedStaffInfo
staffInfoList[index] = updatedStaffInfo
// } // }
} }
} }


fun updateFinancialYear(financialYear: FinancialYear, salaryDataList: List<SalaryEffectiveService.SalaryData>?, staffInfo: Map<String, Any>): FinancialYear {
fun updateFinancialYear(
financialYear: FinancialYear,
salaryDataList: List<SalaryEffectiveService.SalaryData>?,
staffInfo: Map<String, Any>
): FinancialYear {
println("====================== staffInfo: $staffInfo ===============================") println("====================== staffInfo: $staffInfo ===============================")


if(salaryDataList == null){
if (salaryDataList == null) {
return financialYear.copy( return financialYear.copy(
hourlyRate = (staffInfo["hourlyRate"] as BigDecimal).toDouble(), hourlyRate = (staffInfo["hourlyRate"] as BigDecimal).toDouble(),
salaryPoint = (staffInfo["salaryPoint"] as Long).toInt() salaryPoint = (staffInfo["salaryPoint"] as Long).toInt()
@@ -2457,13 +2507,17 @@ open class ReportService(


println("====================== staffInfo: $staffInfo ===============================") println("====================== staffInfo: $staffInfo ===============================")
return financialYear.copy( return financialYear.copy(
hourlyRate = previousHourlyRate?.toDouble() ?: financialYear.hourlyRate,
salaryPoint = previousSalaryPoint ?: financialYear.salaryPoint
hourlyRate = previousHourlyRate?.toDouble() ?: (staffInfo["hourlyRate"] as BigDecimal).toDouble(),
salaryPoint = previousSalaryPoint ?: (staffInfo["salaryPoint"] as Long).toInt()
) )
} }
} }


fun <T> findPreviousValue(salaryDataList: List<SalaryEffectiveService.SalaryData>, currentStart: YearMonth, valueSelector: (SalaryEffectiveService.SalaryData) -> T): T? {
fun <T> findPreviousValue(
salaryDataList: List<SalaryEffectiveService.SalaryData>,
currentStart: YearMonth,
valueSelector: (SalaryEffectiveService.SalaryData) -> T
): T? {
return salaryDataList return salaryDataList
.filter { YearMonth.from(it.financialYear) < currentStart } .filter { YearMonth.from(it.financialYear) < currentStart }
.maxByOrNull { it.financialYear } .maxByOrNull { it.financialYear }
@@ -2486,10 +2540,11 @@ open class ReportService(
} }


// For Calculating the Financial Year // For Calculating the Financial Year
data class FinancialYear(val start: YearMonth, val end: YearMonth, var hourlyRate: Double, var salaryPoint: Int){
fun isYearMonthInFinancialYear(salaryEffectiveMonth: YearMonth) :Boolean{
data class FinancialYear(val start: YearMonth, val end: YearMonth, var hourlyRate: Double, var salaryPoint: Int) {
fun isYearMonthInFinancialYear(salaryEffectiveMonth: YearMonth): Boolean {
if ((salaryEffectiveMonth.isAfter(start) || salaryEffectiveMonth.equals(start)) && if ((salaryEffectiveMonth.isAfter(start) || salaryEffectiveMonth.equals(start)) &&
(salaryEffectiveMonth.isBefore(end) || salaryEffectiveMonth.equals(end))){
(salaryEffectiveMonth.isBefore(end) || salaryEffectiveMonth.equals(end))
) {
return true return true
} }
return false return false
@@ -2588,12 +2643,13 @@ open class ReportService(


return financialYearDates return financialYearDates
} }
private fun getOrCreateRow(sheet: Sheet, startRow: Int): Row{

private fun getOrCreateRow(sheet: Sheet, startRow: Int): Row {
val row: Row = sheet.getRow(startRow) ?: sheet.createRow(startRow) val row: Row = sheet.getRow(startRow) ?: sheet.createRow(startRow)
return row return row
} }


private fun getOrCreateCell(row: Row, columnIndex: Int): Cell{
private fun getOrCreateCell(row: Row, columnIndex: Int): Cell {
val cell: Cell = row.getCell(columnIndex) ?: row.createCell(columnIndex) val cell: Cell = row.getCell(columnIndex) ?: row.createCell(columnIndex)
return cell return cell
} }
@@ -2733,7 +2789,7 @@ open class ReportService(
// Average Hourly Rate by Pay Scale Point // Average Hourly Rate by Pay Scale Point
rowNum = 8 rowNum = 8
val row8: Row = sheet.getRow(rowNum) ?: sheet.createRow(rowNum) val row8: Row = sheet.getRow(rowNum) ?: sheet.createRow(rowNum)
sheet.addMergedRegion(CellRangeAddress(rowNum, rowNum, 2, (financialYears.size+1)*2-1))
sheet.addMergedRegion(CellRangeAddress(rowNum, rowNum, 2, (financialYears.size + 1) * 2 - 1))
val row8Cell = row8.getCell(2) ?: row8.createCell(2) val row8Cell = row8.getCell(2) ?: row8.createCell(2)
row8Cell.apply { row8Cell.apply {
setCellValue("Average Hourly Rate by Pay Scale Point") setCellValue("Average Hourly Rate by Pay Scale Point")
@@ -2750,9 +2806,15 @@ open class ReportService(
var column = 2 var column = 2
financialYears.indices.forEach { i -> financialYears.indices.forEach { i ->
val row9Cell = row9.getCell(column) ?: row9.createCell(column) val row9Cell = row9.getCell(column) ?: row9.createCell(column)
val row9Cell2 = row9.getCell(column+1) ?: row9.createCell(column+1)
val row9Cell2 = row9.getCell(column + 1) ?: row9.createCell(column + 1)
sheet.addMergedRegion(CellRangeAddress(rowNum, rowNum, column, column + 1)) sheet.addMergedRegion(CellRangeAddress(rowNum, rowNum, column, column + 1))
row9Cell.setCellValue("${financialYears[i].start.format(monthFormat)} - ${financialYears[i].end.format(monthFormat)}")
row9Cell.setCellValue(
"${financialYears[i].start.format(monthFormat)} - ${
financialYears[i].end.format(
monthFormat
)
}"
)
CellUtil.setAlignment(row9Cell, HorizontalAlignment.CENTER); CellUtil.setAlignment(row9Cell, HorizontalAlignment.CENTER);
CellUtil.setVerticalAlignment(row9Cell, VerticalAlignment.CENTER); CellUtil.setVerticalAlignment(row9Cell, VerticalAlignment.CENTER);
CellUtil.setCellStyleProperty(row9Cell, "borderBottom", BorderStyle.THIN) CellUtil.setCellStyleProperty(row9Cell, "borderBottom", BorderStyle.THIN)
@@ -2786,7 +2848,7 @@ open class ReportService(
var salaryColumn = 2 var salaryColumn = 2
for (year in years) { for (year in years) {
val salaryPointCell = row.getCell(salaryColumn) ?: row.createCell(salaryColumn) val salaryPointCell = row.getCell(salaryColumn) ?: row.createCell(salaryColumn)
sheet.addMergedRegion(CellRangeAddress(rowNum, rowNum, salaryColumn, salaryColumn+1))
sheet.addMergedRegion(CellRangeAddress(rowNum, rowNum, salaryColumn, salaryColumn + 1))
salaryPointCell.apply { salaryPointCell.apply {
setCellValue("${year.salaryPoint} (${year.hourlyRate})") setCellValue("${year.salaryPoint} (${year.hourlyRate})")
} }
@@ -3300,6 +3362,7 @@ open class ReportService(
timesheets: List<Timesheet>, timesheets: List<Timesheet>,
teams: List<Team>, teams: List<Team>,
grades: List<Grade>, grades: List<Grade>,
monthlyStaffSalaryEffective: List<MonthlyStaffSalaryData>,
teamId: String, teamId: String,
templatePath: String, templatePath: String,
): Workbook { ): Workbook {
@@ -3374,13 +3437,19 @@ open class ReportService(
mutableMapOf<String, Double>().apply { mutableMapOf<String, Double>().apply {
this["manHour"] = timesheet.normalConsumed!!.plus(timesheet.otConsumed ?: 0.0) this["manHour"] = timesheet.normalConsumed!!.plus(timesheet.otConsumed ?: 0.0)
this["salary"] = timesheet.normalConsumed!!.plus(timesheet.otConsumed ?: 0.0) this["salary"] = timesheet.normalConsumed!!.plus(timesheet.otConsumed ?: 0.0)
.times(timesheet.staff!!.salary.hourlyRate.toDouble())
// .times(timesheet.staff!!.salary.hourlyRate.toDouble())
.times(monthlyStaffSalaryEffective.find {
it.staff.staffId == timesheet.staff?.staffId && it.date.year == timesheet.recordDate?.year && it.date.month == timesheet.recordDate?.month
}?.hourlyRate ?: 0.0)
} }
} else if (timesheet.otConsumed != null) { } else if (timesheet.otConsumed != null) {
mutableMapOf<String, Double>().apply { mutableMapOf<String, Double>().apply {
this["manHour"] = timesheet.otConsumed!!.plus(timesheet.normalConsumed ?: 0.0) this["manHour"] = timesheet.otConsumed!!.plus(timesheet.normalConsumed ?: 0.0)
this["salary"] = timesheet.otConsumed!!.plus(timesheet.normalConsumed ?: 0.0) this["salary"] = timesheet.otConsumed!!.plus(timesheet.normalConsumed ?: 0.0)
.times(timesheet.staff!!.salary.hourlyRate.toDouble())
// .times(timesheet.staff!!.salary.hourlyRate.toDouble())
.times(monthlyStaffSalaryEffective.find {
it.staff.staffId == timesheet.staff?.staffId && it.date.year == timesheet.recordDate?.year && it.date.month == timesheet.recordDate?.month
}?.hourlyRate ?: 0.0)
} }
} else { } else {
mutableMapOf<String, Double>().apply { mutableMapOf<String, Double>().apply {
@@ -3626,13 +3695,19 @@ open class ReportService(
mutableMapOf<String, Double>().apply { mutableMapOf<String, Double>().apply {
this["manHour"] = timesheet.normalConsumed!!.plus(timesheet.otConsumed ?: 0.0) this["manHour"] = timesheet.normalConsumed!!.plus(timesheet.otConsumed ?: 0.0)
this["salary"] = timesheet.normalConsumed!!.plus(timesheet.otConsumed ?: 0.0) this["salary"] = timesheet.normalConsumed!!.plus(timesheet.otConsumed ?: 0.0)
.times(timesheet.staff!!.salary.hourlyRate.toDouble())
// .times(timesheet.staff!!.salary.hourlyRate.toDouble())
.times(monthlyStaffSalaryEffective.find {
it.staff.staffId == timesheet.staff?.staffId && it.date.year == timesheet.recordDate?.year && it.date.month == timesheet.recordDate?.month
}?.hourlyRate ?: 0.0)
} }
} else if (timesheet.otConsumed != null) { } else if (timesheet.otConsumed != null) {
mutableMapOf<String, Double>().apply { mutableMapOf<String, Double>().apply {
this["manHour"] = timesheet.otConsumed!!.plus(timesheet.normalConsumed ?: 0.0) this["manHour"] = timesheet.otConsumed!!.plus(timesheet.normalConsumed ?: 0.0)
this["salary"] = timesheet.otConsumed!!.plus(timesheet.normalConsumed ?: 0.0) this["salary"] = timesheet.otConsumed!!.plus(timesheet.normalConsumed ?: 0.0)
.times(timesheet.staff!!.salary.hourlyRate.toDouble())
// .times(timesheet.staff!!.salary.hourlyRate.toDouble())
.times(monthlyStaffSalaryEffective.find {
it.staff.staffId == timesheet.staff?.staffId && it.date.year == timesheet.recordDate?.year && it.date.month == timesheet.recordDate?.month
}?.hourlyRate ?: 0.0)
} }
} else { } else {
mutableMapOf<String, Double>().apply { mutableMapOf<String, Double>().apply {
@@ -3737,61 +3812,61 @@ open class ReportService(
projects.forEach { project: Project -> projects.forEach { project: Project ->
if (teamId.lowercase() == "all" || teamId.toLong() == project.teamLead?.team?.id || teamId.toLong() == team.id) { if (teamId.lowercase() == "all" || teamId.toLong() == project.teamLead?.team?.id || teamId.toLong() == team.id) {
// if (team.id == project.teamLead?.team?.id) { // if (team.id == project.teamLead?.team?.id) {
endRow++
sheet.createRow(rowIndex++).apply {
columnIndex = 0
createCell(columnIndex++).apply {
setCellValue("${project.code}: ${project.name}")
val cloneStyle = workbook.createCellStyle()
cloneStyle.cloneStyleFrom(normalFontWithBorderStyle)
cellStyle = cloneStyle.apply {
alignment = HorizontalAlignment.LEFT
}
endRow++
sheet.createRow(rowIndex++).apply {
columnIndex = 0
createCell(columnIndex++).apply {
setCellValue("${project.code}: ${project.name}")
val cloneStyle = workbook.createCellStyle()
cloneStyle.cloneStyleFrom(normalFontWithBorderStyle)
cellStyle = cloneStyle.apply {
alignment = HorizontalAlignment.LEFT
} }
}


var totalSalary = 0.0
staffs.forEach { staff: Staff ->
logger.info("Staff: ${staff.staffId}")
createCell(columnIndex++).apply {
setCellValue(
groupedTimesheetsIndividual[Pair(
project.id,
staff.id,
)]?.sumOf { it.getValue("manHour") } ?: 0.0)

totalSalary += groupedTimesheetsIndividual[Pair(
var totalSalary = 0.0
staffs.forEach { staff: Staff ->
logger.info("Staff: ${staff.staffId}")
createCell(columnIndex++).apply {
setCellValue(
groupedTimesheetsIndividual[Pair(
project.id, project.id,
staff.id
)]?.sumOf { it.getValue("salary") } ?: 0.0

val cloneStyle = workbook.createCellStyle()
cloneStyle.cloneStyleFrom(normalFontWithBorderStyle)
cellStyle = cloneStyle.apply {
dataFormat = accountingStyle
}
}
}
staff.id,
)]?.sumOf { it.getValue("manHour") } ?: 0.0)


createCell(columnIndex++).apply {
val lastCellLetter = CellReference.convertNumToColString(this.columnIndex - 1)
cellFormula = "sum(B${this.rowIndex + 1}:${lastCellLetter}${this.rowIndex + 1})"
totalSalary += groupedTimesheetsIndividual[Pair(
project.id,
staff.id
)]?.sumOf { it.getValue("salary") } ?: 0.0


val cloneStyle = workbook.createCellStyle() val cloneStyle = workbook.createCellStyle()
cloneStyle.cloneStyleFrom(boldFontWithBorderStyle)
cloneStyle.cloneStyleFrom(normalFontWithBorderStyle)
cellStyle = cloneStyle.apply { cellStyle = cloneStyle.apply {
dataFormat = accountingStyle dataFormat = accountingStyle
} }
} }
}


createCell(columnIndex).apply {
setCellValue(totalSalary)
val cloneStyle = workbook.createCellStyle()
cloneStyle.cloneStyleFrom(boldFontWithBorderStyle)
cellStyle = cloneStyle.apply {
dataFormat = accountingStyle
}
createCell(columnIndex++).apply {
val lastCellLetter = CellReference.convertNumToColString(this.columnIndex - 1)
cellFormula = "sum(B${this.rowIndex + 1}:${lastCellLetter}${this.rowIndex + 1})"

val cloneStyle = workbook.createCellStyle()
cloneStyle.cloneStyleFrom(boldFontWithBorderStyle)
cellStyle = cloneStyle.apply {
dataFormat = accountingStyle
} }
} }

createCell(columnIndex).apply {
setCellValue(totalSalary)
val cloneStyle = workbook.createCellStyle()
cloneStyle.cloneStyleFrom(boldFontWithBorderStyle)
cellStyle = cloneStyle.apply {
dataFormat = accountingStyle
}
}
}
// } // }
} }
} }


+ 7
- 3
src/main/java/com/ffii/tsms/modules/report/web/ReportController.kt View File

@@ -6,6 +6,7 @@ import com.ffii.tsms.modules.common.SecurityUtils
import com.ffii.tsms.modules.data.entity.* import com.ffii.tsms.modules.data.entity.*
//import com.ffii.tsms.modules.data.entity.projections.FinancialStatusReportInfo //import com.ffii.tsms.modules.data.entity.projections.FinancialStatusReportInfo
import com.ffii.tsms.modules.data.service.CustomerService import com.ffii.tsms.modules.data.service.CustomerService
import com.ffii.tsms.modules.data.service.SalaryEffectiveService
import com.ffii.tsms.modules.data.service.StaffsService import com.ffii.tsms.modules.data.service.StaffsService
import com.ffii.tsms.modules.data.service.TeamService import com.ffii.tsms.modules.data.service.TeamService
import com.ffii.tsms.modules.project.entity.* import com.ffii.tsms.modules.project.entity.*
@@ -60,7 +61,8 @@ class ReportController(
private val subsidiaryService: SubsidiaryService, private val subsidiaryService: SubsidiaryService,
private val invoiceService: InvoiceService, private val gradeAllocationRepository: GradeAllocationRepository, private val invoiceService: InvoiceService, private val gradeAllocationRepository: GradeAllocationRepository,
private val subsidiaryRepository: SubsidiaryRepository, private val staffAllocationRepository: StaffAllocationRepository, private val subsidiaryRepository: SubsidiaryRepository, private val staffAllocationRepository: StaffAllocationRepository,
private val gradeRepository: GradeRepository
private val gradeRepository: GradeRepository,
private val salaryEffectiveService: SalaryEffectiveService,
) { ) {


private val logger: Log = LogFactory.getLog(javaClass) private val logger: Log = LogFactory.getLog(javaClass)
@@ -85,8 +87,9 @@ class ReportController(
// val invoices = invoiceService.findAllByProjectAndPaidAmountIsNotNull(project) // val invoices = invoiceService.findAllByProjectAndPaidAmountIsNotNull(project)
val invoices = invoiceRepository.findAllByProjectCodeAndPaidAmountIsNotNull(project.code!!) val invoices = invoiceRepository.findAllByProjectCodeAndPaidAmountIsNotNull(project.code!!)
val timesheets = timesheetRepository.findAllByProjectTaskIn(projectTasks) val timesheets = timesheetRepository.findAllByProjectTaskIn(projectTasks)
val monthlyStaffSalaryEffective = salaryEffectiveService.getMonthlyStaffSalaryData(timesheets.minByOrNull { it.recordDate!! }?.recordDate ?: LocalDate.parse("2012-01-01"), timesheets.maxByOrNull { it.recordDate!! }?.recordDate ?: LocalDate.now())


val reportResult: ByteArray = excelReportService.generateProjectCashFlowReport(project, invoices, timesheets, request.dateType)
val reportResult: ByteArray = excelReportService.generateProjectCashFlowReport(project, invoices, timesheets, monthlyStaffSalaryEffective, request.dateType)
// 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)
@@ -336,8 +339,9 @@ class ReportController(
(it.recordDate!!.isEqual(startDate) || it.recordDate!!.isEqual(endDate) || (it.recordDate!!.isAfter(startDate) && it.recordDate!!.isBefore(endDate)))} (it.recordDate!!.isEqual(startDate) || it.recordDate!!.isEqual(endDate) || (it.recordDate!!.isAfter(startDate) && it.recordDate!!.isBefore(endDate)))}
val teams = teamRepository.findAll().filter { it.deleted == false } val teams = teamRepository.findAll().filter { it.deleted == false }
val grades = gradeRepository.findAll().filter { it.deleted == false } val grades = gradeRepository.findAll().filter { it.deleted == false }
val monthlyStaffSalaryEffective = salaryEffectiveService.getMonthlyStaffSalaryData(startDate, endDate)


val reportResult: ByteArray = excelReportService.generateCrossTeamChargeReport(request.month, timesheets, teams, grades, request.teamId)
val reportResult: ByteArray = excelReportService.generateCrossTeamChargeReport(request.month, timesheets, teams, grades, monthlyStaffSalaryEffective, request.teamId)
return ResponseEntity.ok() return ResponseEntity.ok()
.header("filename", "Cross Team Charge Report - " + LocalDate.now() + ".xlsx") .header("filename", "Cross Team Charge Report - " + LocalDate.now() + ".xlsx")
.body(ByteArrayResource(reportResult)) .body(ByteArrayResource(reportResult))


+ 3
- 0
src/main/java/com/ffii/tsms/modules/settings/entity/SettingsRepository.java View File

@@ -1,5 +1,6 @@
package com.ffii.tsms.modules.settings.entity; package com.ffii.tsms.modules.settings.entity;


import java.util.List;
import java.util.Optional; import java.util.Optional;


import org.springframework.data.repository.query.Param; import org.springframework.data.repository.query.Param;
@@ -9,4 +10,6 @@ import com.ffii.core.support.AbstractRepository;
public interface SettingsRepository extends AbstractRepository<Settings, Long> { public interface SettingsRepository extends AbstractRepository<Settings, Long> {


Optional<Settings> findByName(@Param("name") String name); Optional<Settings> findByName(@Param("name") String name);

List<Settings> findAllByCategory(@Param("category") String category);
} }

+ 5
- 0
src/main/java/com/ffii/tsms/modules/settings/service/SettingsService.java View File

@@ -5,6 +5,7 @@ import java.time.LocalDateTime;
import java.time.LocalTime; import java.time.LocalTime;
import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException; import java.time.format.DateTimeParseException;
import java.util.List;
import java.util.Optional; import java.util.Optional;


import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
@@ -29,6 +30,10 @@ public class SettingsService extends AbstractIdEntityService<Settings, Long, Set
return this.repository.findByName(name); return this.repository.findByName(name);
} }


public List<Settings> findAllByCategory(String category) {
return this.repository.findAllByCategory(category);
}

public boolean validateType(String type, String value) { public boolean validateType(String type, String value) {
if (StringUtils.isBlank(type)) return true; if (StringUtils.isBlank(type)) return true;




Loading…
Cancel
Save