Преглед изворни кода

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

# Conflicts:
#	src/main/java/com/ffii/tsms/modules/report/service/ReportService.kt
tags/Baseline_30082024_BACKEND_UAT
MSI\2Fi пре 1 година
родитељ
комит
b4e194093d
6 измењених фајлова са 267 додато и 107 уклоњено
  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. +205
    -101
      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 Прегледај датотеку

@@ -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.SalaryEffectiveRepository
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 org.springframework.stereotype.Service
import java.math.BigDecimal
import java.time.LocalDate
import java.time.LocalDateTime
import java.time.temporal.ChronoUnit

@Service
open class SalaryEffectiveService(
@@ -90,4 +93,41 @@ open class SalaryEffectiveService(
// println(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 Прегледај датотеку

@@ -4,16 +4,20 @@ import com.ffii.core.response.RecordsRes
import com.ffii.core.utils.CriteriaArgsBuilder
import com.ffii.tsms.modules.data.service.SalaryEffectiveService
import jakarta.servlet.http.HttpServletRequest
import jakarta.validation.Valid
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
@RequestMapping("/salaryEffective")
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")
// @Throws(ServletRequestBindingException::class)
// fun combo(request: HttpServletRequest?): RecordsRes<Map<String, Any>> {


+ 205
- 101
src/main/java/com/ffii/tsms/modules/report/service/ReportService.kt Прегледај датотеку

@@ -2,8 +2,8 @@ package com.ffii.tsms.modules.report.service

import com.ffii.core.support.JdbcDao
import com.ffii.tsms.modules.data.entity.*
import com.ffii.tsms.modules.data.entity.projections.SalaryEffectiveInfo
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.Milestone
import com.ffii.tsms.modules.project.entity.Project
@@ -156,11 +156,19 @@ open class ReportService(
project: Project,
invoices: List<Invoice>,
timesheets: List<Timesheet>,
monthlyStaffSalaryEffective: List<MonthlyStaffSalaryData>,
dateType: String
): ByteArray {
// Generate the Excel report with query results
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
val outputStream: ByteArrayOutputStream = ByteArrayOutputStream()
@@ -319,11 +327,20 @@ open class ReportService(
timesheets: List<Timesheet>,
teams: List<Team>,
grades: List<Grade>,
monthlyStaffSalaryEffective: List<MonthlyStaffSalaryData>,
teamId: String,
): ByteArray {
// Generate the Excel report with query results
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
val outputStream: ByteArrayOutputStream = ByteArrayOutputStream()
@@ -431,7 +448,9 @@ open class ReportService(
endDateCell.setCellValue(if (item["planEnd"] != null) item.getValue("planEnd").toString() else "N/A")

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 {
setCellValue(totalFee)
cellStyle.dataFormat = accountingStyle
@@ -693,6 +712,7 @@ open class ReportService(
project: Project,
invoices: List<Invoice>,
timesheets: List<Timesheet>,
monthlyStaffSalaryEffective: List<SalaryEffectiveService.MonthlyStaffSalaryData>,
dateType: String,
templatePath: String,
): Workbook {
@@ -763,8 +783,10 @@ open class ReportService(
rowIndex = 11
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))
// 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 {
createCell(1).apply {
@@ -805,10 +827,16 @@ open class ReportService(
timesheetEntries.map { timesheet ->
if (timesheet.normalConsumed != null) {
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) {
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 {
0.0
}
@@ -2043,47 +2071,95 @@ open class ReportService(
return jdbcDao.queryForList(sql.toString(), args)
}

open fun getProjectResourceOverconsumptionReport_projectInfo(args: Map<String, Any>): List<Map<String, Any>> {
val sql = StringBuilder("SELECT"
+ " t.projectId as id, "
+ " p.code as projectCode, "
+ " p.name as projectName, "
+ " te.code as team, "
+ " CONCAT( c.code, ' - ' ,c.name ) as client, "
+ " COALESCE(concat(sub.code, ' - ', sub.name), 'N/A') as subsidaiary, "
+ " (p.expectedTotalFee - ifnull(p.subContractFee, 0)) * 0.8 as plannedBudget "
+ " from timesheet t "
+ " left join project p on p.id = t.projectId "
+ " left join team te on te.teamLead = p.teamLead "
+ " left join customer c ON p.customerId = c.id "
+ " left join subsidiary sub on sub.id = p.customerSubsidiaryId "
+ " where t.projectid is not NULL "
+ " and p.deleted = false "
+ " and p.status = 'On-going' "
)
if (args != null) {
if (args.containsKey("teamId"))
sql.append(" and tm.id = :teamId")
if (args.containsKey("custId"))
sql.append(" and c.id = :custId")
if (args.containsKey("subsidiaryId"))
sql.append(" and ss.id = :subsidiaryId")
}
sql.append(" group by t.projectId; ")
return jdbcDao.queryForList(sql.toString(), args)
}

open fun getProjectResourceOverconsumptionReport_timesheetInfo(): List<Map<String, Any>> {
val sql = StringBuilder("SELECT"
+ " t.*, "
+ " sal.hourlyRate "
+ " from timesheet t "
+ " left join staff s on s.id = t.staffId "
+ " left join salary sal on sal.salaryPoint = s.salaryId "
+ " where t.deleted = false "
)
return jdbcDao.queryForList(sql.toString())
}

open fun getProjectResourceOverconsumptionReport(args: Map<String, Any>): List<Map<String, Any>> {
val sql = StringBuilder(
"SELECT"
+ " p.code, "
+ " p.name, "
+ " tm.code as team, "
+ " concat(c.code, ' - ',c.name) as client, "
+ " COALESCE(concat(ss.code, ' - ', ss.name), 'N/A') as subsidiary, "
+ " (p.expectedTotalFee - ifnull(p.subContractFee, 0)) * 0.8 as plannedBudget, "
+ " sum(t.consumedBudget) as actualConsumedBudget, "
+ " COALESCE(p.totalManhour, 0) as plannedManhour, "
+ " sum(t.normalConsumed + COALESCE(t.otConsumed, 0)) as actualConsumedManhour, "
+ " sum(t.consumedBudget) / ((p.expectedTotalFee - ifnull(p.subContractFee, 0)) * 0.8) as budgetConsumptionRate, "
+ " sum(t.normalConsumed + COALESCE(t.otConsumed, 0)) / COALESCE(p.totalManhour, 0) as manhourConsumptionRate, "
+ " case "
+ " when sum(t.consumedBudget) / ((p.expectedTotalFee - ifnull(p.subContractFee, 0)) * 0.8) >= 1 "
+ " or (sum(t.normalConsumed + COALESCE(t.otConsumed, 0)) / COALESCE(p.totalManhour, 0)) >= 1 "
+ " then 'Overconsumption' "
+ " when sum(t.consumedBudget) / ((p.expectedTotalFee - ifnull(p.subContractFee, 0)) * 0.8) >= :lowerLimit and sum(t.consumedBudget) / ((p.expectedTotalFee - ifnull(p.subContractFee, 0)) * 0.8) <= 1 "
+ " or (sum(t.normalConsumed + COALESCE(t.otConsumed, 0)) / COALESCE(p.totalManhour, 0)) >= :lowerLimit and (sum(t.normalConsumed + COALESCE(t.otConsumed, 0)) / COALESCE(p.totalManhour, 0)) <= 1 "
+ " then 'Potential Overconsumption' "
+ " else 'Within Budget' "
+ " END as status "
+ " from "
+ " (SELECT "
+ " t.*, "
+ " (t.normalConsumed + COALESCE(t.otConsumed, 0)) * sal.hourlyRate as consumedBudget "
+ " from timesheet t "
+ " left join staff s on s.id = t.staffId "
+ " left join salary sal on sal.salaryPoint = s.salaryId ) t "
+ " left join project p on p.id = t.projectId "
+ " left join team tm on p.teamLead = tm.teamLead "
+ " left join customer c on c.id = p.customerId "
+ " LEFT JOIN subsidiary ss on p.customerSubsidiaryId = ss.id "
+ " WHERE p.deleted = false "
+ " and p.status = 'On-going' "
"SELECT"
+ " p.code, "
+ " p.name, "
+ " tm.code as team, "
+ " concat(c.code, ' - ',c.name) as client, "
+ " COALESCE(concat(ss.code, ' - ', ss.name), 'N/A') as subsidiary, "
+ " (p.expectedTotalFee - ifnull(p.subContractFee, 0)) * 0.8 as plannedBudget, "
+ " sum(t.consumedBudget) as actualConsumedBudget, "
+ " COALESCE(p.totalManhour, 0) as plannedManhour, "
+ " sum(t.normalConsumed + COALESCE(t.otConsumed, 0)) as actualConsumedManhour, "
+ " sum(t.consumedBudget) / ((p.expectedTotalFee - ifnull(p.subContractFee, 0)) * 0.8) as budgetConsumptionRate, "
+ " sum(t.normalConsumed + COALESCE(t.otConsumed, 0)) / COALESCE(p.totalManhour, 0) as manhourConsumptionRate, "
+ " case "
+ " when sum(t.consumedBudget) / ((p.expectedTotalFee - ifnull(p.subContractFee, 0)) * 0.8) >= 1 "
+ " or (sum(t.normalConsumed + COALESCE(t.otConsumed, 0)) / COALESCE(p.totalManhour, 0)) >= 1 "
+ " then 'Overconsumption' "
+ " when sum(t.consumedBudget) / ((p.expectedTotalFee - ifnull(p.subContractFee, 0)) * 0.8) >= :lowerLimit and sum(t.consumedBudget) / ((p.expectedTotalFee - ifnull(p.subContractFee, 0)) * 0.8) <= 1 "
+ " or (sum(t.normalConsumed + COALESCE(t.otConsumed, 0)) / COALESCE(p.totalManhour, 0)) >= :lowerLimit and (sum(t.normalConsumed + COALESCE(t.otConsumed, 0)) / COALESCE(p.totalManhour, 0)) <= 1 "
+ " then 'Potential Overconsumption' "
+ " else 'Within Budget' "
+ " END as status "
+ " from "
+ " (SELECT t.*, (t.normalConsumed + COALESCE(t.otConsumed, 0)) * sal.hourlyRate as consumedBudget "
+ " FROM "
+ " ( "
+ " SELECT t.id AS tid, max(se.date) AS max_date "
+ " FROM timesheet t "
+ " INNER JOIN salary_effective se ON se.staffId = t.staffId AND se.date <= t.recordDate "
+ " GROUP BY t.id "
+ " ) max_se "
+ " LEFT JOIN timesheet t ON t.id = max_se.tid "
+ " LEFT JOIN staff s on s.id = t.staffId "
+ " inner join salary_effective se on se.date = max_se.max_date "
+ " left join salary sal on se.salaryId = sal.salaryPoint) t "
+ " left join project p on p.id = t.projectId "
+ " left join team tm on p.teamLead = tm.teamLead "
+ " left join customer c on c.id = p.customerId "
+ " LEFT JOIN subsidiary ss on p.customerSubsidiaryId = ss.id "
+ " WHERE p.deleted = false "
+ " and p.status = 'On-going' "
)
var statusFilter: String = ""
if (args != null) {
if (args.containsKey("teamId"))
sql.append(" and t.id = :teamId")
sql.append(" and tm.id = :teamId")
if (args.containsKey("custId"))
sql.append(" and c.id = :custId")
if (args.containsKey("subsidiaryId"))
@@ -2388,10 +2464,14 @@ open class ReportService(
}
}

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 ===============================")

if(salaryDataList == null){
if (salaryDataList == null) {
return financialYear.copy(
hourlyRate = (staffInfo["hourlyRate"] as BigDecimal).toDouble(),
salaryPoint = (staffInfo["salaryPoint"] as Long).toInt()
@@ -2414,15 +2494,19 @@ open class ReportService(
val previousHourlyRate = findPreviousValue(salaryDataList, financialYear.start) { it.hourlyRate }
val previousSalaryPoint = findPreviousValue(salaryDataList, financialYear.start) { it.salaryPoint }

// println("====================== staffInfo: $staffInfo ===============================")
println("====================== staffInfo: $staffInfo ===============================")
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
.filter { YearMonth.from(it.financialYear) < currentStart }
.maxByOrNull { it.financialYear }
@@ -2548,12 +2632,13 @@ open class ReportService(

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)
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)
return cell
}
@@ -2693,7 +2778,7 @@ open class ReportService(
// Average Hourly Rate by Pay Scale Point
rowNum = 8
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)
row8Cell.apply {
setCellValue("Average Hourly Rate by Pay Scale Point")
@@ -2710,9 +2795,15 @@ open class ReportService(
var column = 2
financialYears.indices.forEach { i ->
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))
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.setVerticalAlignment(row9Cell, VerticalAlignment.CENTER);
CellUtil.setCellStyleProperty(row9Cell, "borderBottom", BorderStyle.THIN)
@@ -2746,7 +2837,7 @@ open class ReportService(
var salaryColumn = 2
for (year in years) {
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 {
setCellValue("${year.salaryPoint} (${year.hourlyRate})")
}
@@ -3230,6 +3321,7 @@ open class ReportService(
timesheets: List<Timesheet>,
teams: List<Team>,
grades: List<Grade>,
monthlyStaffSalaryEffective: List<MonthlyStaffSalaryData>,
teamId: String,
templatePath: String,
): Workbook {
@@ -3304,13 +3396,19 @@ open class ReportService(
mutableMapOf<String, Double>().apply {
this["manHour"] = 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) {
mutableMapOf<String, Double>().apply {
this["manHour"] = 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 {
mutableMapOf<String, Double>().apply {
@@ -3556,13 +3654,19 @@ open class ReportService(
mutableMapOf<String, Double>().apply {
this["manHour"] = 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) {
mutableMapOf<String, Double>().apply {
this["manHour"] = 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 {
mutableMapOf<String, Double>().apply {
@@ -3667,61 +3771,61 @@ open class ReportService(
projects.forEach { project: Project ->
if (teamId.lowercase() == "all" || teamId.toLong() == project.teamLead?.team?.id || teamId.toLong() == 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,
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()
cloneStyle.cloneStyleFrom(boldFontWithBorderStyle)
cloneStyle.cloneStyleFrom(normalFontWithBorderStyle)
cellStyle = cloneStyle.apply {
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 Прегледај датотеку

@@ -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.projections.FinancialStatusReportInfo
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.TeamService
import com.ffii.tsms.modules.project.entity.*
@@ -61,7 +62,8 @@ class ReportController(
private val subsidiaryService: SubsidiaryService,
private val invoiceService: InvoiceService, private val gradeAllocationRepository: GradeAllocationRepository,
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)
@@ -86,8 +88,9 @@ class ReportController(
// val invoices = invoiceService.findAllByProjectAndPaidAmountIsNotNull(project)
val invoices = invoiceRepository.findAllByProjectCodeAndPaidAmountIsNotNull(project.code!!)
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")
return ResponseEntity.ok()
// .contentType(mediaType)
@@ -337,8 +340,9 @@ class ReportController(
(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 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()
.header("filename", "Cross Team Charge Report - " + LocalDate.now() + ".xlsx")
.body(ByteArrayResource(reportResult))


+ 3
- 0
src/main/java/com/ffii/tsms/modules/settings/entity/SettingsRepository.java Прегледај датотеку

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

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

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

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 Прегледај датотеку

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

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

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

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



Loading…
Откажи
Сачувај