@@ -10,6 +10,7 @@ import com.ffii.tsms.modules.data.entity.Team
import com.ffii.tsms.modules.project.entity.Invoice
import com.ffii.tsms.modules.project.entity.Milestone
import com.ffii.tsms.modules.project.entity.Project
import com.ffii.tsms.modules.project.entity.ProjectRepository
import com.ffii.tsms.modules.report.web.model.costAndExpenseRequest
import com.ffii.tsms.modules.timesheet.entity.Timesheet
import org.apache.commons.logging.Log
@@ -22,12 +23,14 @@ import org.apache.poi.ss.usermodel.FormulaEvaluator
import org.apache.poi.ss.util.CellReference
import org.apache.poi.ss.util.RegionUtil
import org.apache.poi.xssf.usermodel.XSSFCellStyle
import org.apache.poi.xssf.usermodel.XSSFColor
import org.springframework.core.io.ClassPathResource
import org.springframework.stereotype.Service
import java.io.ByteArrayOutputStream
import java.io.IOException
import java.math.BigDecimal
import java.time.LocalDate
import java.time.Year
import java.time.YearMonth
import java.time.format.DateTimeFormatter
import java.time.ZoneId
@@ -42,6 +45,7 @@ data class DayInfo(val date: String?, val weekday: String?)
@Service
open class ReportService(
private val jdbcDao: JdbcDao,
private val projectRepository: ProjectRepository
) {
private val logger: Log = LogFactory.getLog(javaClass)
private val DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy/MM/dd")
@@ -487,16 +491,22 @@ open class ReportService(
cellStyle = boldFontCellStyle
}
val receivedAmountCell = row.createCell(14)
val projectedCpiCell = row.createCell(14)
projectedCpiCell.apply {
cellFormula = "(H${rowNum}/J${rowNum})"
cellStyle = boldFontCellStyle
}
val receivedAmountCell = row.createCell(15)
val paidAmount = item["paidAmount"]?.let { it as BigDecimal } ?: BigDecimal(0)
receivedAmountCell.apply {
setCellValue(paidAmount.toDouble())
cellStyle.dataFormat = accountingStyle
}
val unsettledAmountCell = row.createCell(15 )
val unsettledAmountCell = row.createCell(16 )
unsettledAmountCell.apply {
cellFormula = "L${rowNum}-O ${rowNum}"
cellFormula = "L${rowNum}-P ${rowNum}"
cellStyle = boldFontCellStyle
cellStyle.dataFormat = accountingStyle
}
@@ -578,17 +588,27 @@ open class ReportService(
CellUtil.setCellStyleProperty(lastCpiCell, "borderTop", BorderStyle.THIN)
CellUtil.setCellStyleProperty(lastCpiCell, "borderBottom", BorderStyle.DOUBLE)
val sumRAmoun tCell = row.createCell(14)
sumRAmoun tCell.apply {
val la stPCpi Cell = row.createCell(14)
la stPCpi Cell.apply {
cellFormula = "SUM(O15:O${rowNum})"
cellStyle = boldFontCellStyle
cellStyle.dataFormat = accountingStyle
}
CellUtil.setCellStyleProperty(lastPCpiCell, "borderTop", BorderStyle.THIN)
CellUtil.setCellStyleProperty(lastPCpiCell, "borderBottom", BorderStyle.DOUBLE)
val sumRAmountCell = row.createCell(15)
sumRAmountCell.apply {
cellFormula = "SUM(P15:P${rowNum})"
cellStyle.dataFormat = accountingStyle
}
CellUtil.setCellStyleProperty(sumRAmountCell, "borderTop", BorderStyle.THIN)
CellUtil.setCellStyleProperty(sumRAmountCell, "borderBottom", BorderStyle.DOUBLE)
val sumUnSettleCell = row.createCell(15)
val sumUnSettleCell = row.createCell(16 )
sumUnSettleCell.apply {
cellFormula = "SUM(P15:P${rowNum})"
cellFormula = "SUM(Q15:Q ${rowNum})"
cellStyle = boldFontCellStyle
cellStyle.dataFormat = accountingStyle
}
@@ -1434,6 +1454,13 @@ open class ReportService(
val workbook: Workbook = XSSFWorkbook(templateInputStream)
val accountingStyle = workbook.createDataFormat().getFormat("_(* #,##0.00_);_(* (#,##0.00);_(* \"-\"??_);_(@_)")
val defaultStyle = workbook.createDataFormat().getFormat("###,##0")
// Fill Orange Style
val fillOrange = workbook.createCellStyle()
val customColor = XSSFColor(byteArrayOf(255.toByte(), 192.toByte(), 0.toByte()))
fillOrange.setFillForegroundColor(customColor);
fillOrange.setFillPattern(FillPatternType.SOLID_FOREGROUND)
val sheet: Sheet = workbook.getSheetAt(0)
var rowIndex = 2
@@ -1446,30 +1473,46 @@ open class ReportService(
val cell = getCell(0) ?: createCell(0)
cell.setCellValue(salary.salaryPoint.toDouble())
CellUtil.setAlignment(cell, HorizontalAlignment.CENTER)
cell.cellStyle.dataFormat = defaultStyle
when (index) {
0 -> {
val cell1 = getCell(1) ?: createCell(1)
cell1.setCellValue(salary.lowerLimit.toDouble())
CellUtil.setAlignment(cell1, HorizontalAlignment.CENTER)
cell1.cellStyle.dataFormat = defaultStyle
}
else -> {
val cell1 = getCell(1) ?: createCell(1)
cell1.cellFormula =
"(C{previousRow}+1)".replace("{previousRow}", (rowIndex).toString())
CellUtil.setAlignment(cell1, HorizontalAlignment.CENTER)
cell1.cellStyle.dataFormat = defaultStyle
}
}
val cell2 = getCell(2) ?: createCell(2)
cell2.cellFormula = "(B{currentRow}+D{currentRow})-1".replace("{currentRow}", (rowIndex+1).toString())
CellUtil.setAlignment(cell2, HorizontalAlignment.CENTER)
cell2.cellStyle.dataFormat = defaultStyle
// getCell(2).cellStyle.dataFormat = accountingStyle
val cell3 = getCell(3)?:createCell(3)
cell3.setCellValue(salary.increment.toDouble())
cell3.cellStyle = fillOrange
cell3.cellStyle.dataFormat = defaultStyle
CellUtil.setAlignment(cell3, HorizontalAlignment.CENTER)
val cell4 = getCell(4)?:createCell(4)
cell4.cellFormula =
"(((C{currentRow}+B{currentRow})/2)/20)/8".replace("{currentRow}", (rowIndex+1).toString())
// getCell(4).cellStyle.dataFormat = accountingStyle
cell4.cellStyle.dataFormat = accountingStyle
CellUtil.setAlignment(cell4, HorizontalAlignment.CENTER)
}
rowIndex++;
}
@@ -2042,6 +2085,7 @@ open class ReportService(
+ " select p.code, p.description, c.name as client, IFNULL(s2.name, \"N/A\") as subsidiary, concat(t.code, \" - \", t.name) as teamLead,"
+ " IFNULL(cte_ts.normalConsumed, 0) as normalConsumed, IFNULL(cte_ts.otConsumed, 0) as otConsumed, DATE_FORMAT(cte_ts.recordDate, '%Y-%m') as recordDate, "
+ " IFNULL(cte_ts.salaryPoint, 0) as salaryPoint, "
+ " (p.expectedTotalFee - IFNULL(cte_i.sumIssuedAmount, 0)) as expectedTotalFee, "
+ " IFNULL(cte_ts.hourlyRate, 0) as hourlyRate, IFNULL(cte_i.sumIssuedAmount, 0) as sumIssuedAmount, IFNULL(cte_i.sumPaidAmount, 0) as sumPaidAmount, IFNULL(cte_tss.sumManhourExpenditure, 0) as sumManhourExpenditure,"
+ " s.name, s.staffId, g.code as gradeCode, g.name as gradeName, t2.code as teamCode, t2.name as teamName"
+ " from project p"
@@ -2060,11 +2104,23 @@ open class ReportService(
+ " and p.id = :projectId"
+ " order by g.code, s.name, cte_ts.recordDate"
)
val projectPlanStartEndMonth = projectRepository.findProjectPlanStartEndByIdAndDeletedFalse(projectId)
val compareStartMonth= YearMonth.parse(startMonth)
val compareEndMonth= YearMonth.parse(endMonth)
val actualStartMonth: YearMonth
(if (projectPlanStartEndMonth.actualStart == null){
YearMonth.now().plusMonths(1)
}else{
YearMonth.from(projectPlanStartEndMonth.actualStart)
}).also { actualStartMonth = it }
val queryStartMonth = if(compareStartMonth.isAfter(actualStartMonth)) compareStartMonth else actualStartMonth
val queryEndMonth = if(compareEndMonth.isAfter(YearMonth.now())) YearMonth.now() else compareEndMonth
val args = mapOf(
"projectId" to projectId,
"startMonth" to startMonth,
"endMonth" to endMonth,
"startMonth" to queryStartMonth.toString() ,
"endMonth" to qu eryE ndMonth.toString() ,
)
val manHoursSpent = jdbcDao.queryForList(sql.toString(), args)
@@ -2088,7 +2144,7 @@ open class ReportService(
"startMonth" to startMonth,
"endMonth" to endMonth,
"code" to projectsCode["code"],
"description" to projectsCode["description"],
"description" to projectsCode["description"]
)
val staffInfoList = mutableListOf<Map<String, Any>>()
@@ -2109,6 +2165,9 @@ open class ReportService(
if (info["code"] == item["code"] && "subsidiary" !in info) {
info["subsidiary"] = item.getValue("subsidiary")
}
if (info["code"] == item["code"] && "expectedTotalFee" !in info) {
info["expectedTotalFee"] = item.getValue("expectedTotalFee")
}
if (info["manhourExpenditure"] != item.getValue("sumManhourExpenditure")) {
info["manhourExpenditure"] = item.getValue("sumManhourExpenditure")
}
@@ -2188,9 +2247,11 @@ open class ReportService(
}
fun genPandLReport(projectId: Long, startMonth: String, endMonth: String): ByteArray {
val manhoursSpentList = getManhoursSpent(projectId, startMonth, endMonth)
val workbook: Workbook = createPandLReportWorkbook(PandL_REPORT, manhoursSpentList, startMonth, endMonth)
val workbook: Workbook = createPandLReportWorkbook(PandL_REPORT, manhoursSpentList, startMonth, endMonth, projectId )
val outputStream: ByteArrayOutputStream = ByteArrayOutputStream()
workbook.write(outputStream)
@@ -2203,7 +2264,8 @@ open class ReportService(
templatePath: String,
manhoursSpent: List<Map<String, Any>>,
startMonth: String,
endMonth: String
endMonth: String,
projectId: Long
): Workbook {
val resource = ClassPathResource(templatePath)
val templateInputStream = resource.inputStream
@@ -2211,14 +2273,35 @@ open class ReportService(
val accountingStyle = workbook.createDataFormat().getFormat("_(* #,##0.00_);_(* (#,##0.00);_(* \"-\"??_);_(@_)")
val projectPlanStartEndMonth = projectRepository.findProjectPlanStartEndByIdAndDeletedFalse(projectId)
val compareStartMonth= YearMonth.parse(startMonth)
val compareEndMonth= YearMonth.parse(endMonth)
val actualStartMonth: YearMonth
(if (projectPlanStartEndMonth.actualStart == null){
YearMonth.now().plusMonths(1)
}else{
YearMonth.from(projectPlanStartEndMonth.actualStart)
}).also { actualStartMonth = it }
val queryStartMonth = if(compareStartMonth.isAfter(actualStartMonth)) compareStartMonth else actualStartMonth
val queryEndMonth = if(compareEndMonth.isAfter(YearMonth.now())) YearMonth.now() else compareEndMonth
val monthFormat = DateTimeFormatter.ofPattern("MMM yyyy", Locale.ENGLISH)
val startDate = YearMonth.parse(startMonth, DateTimeFormatter.ofPattern("yyyy-MM"))
val startDate = YearMonth.parse(queryStartMonth.toString() , DateTimeFormatter.ofPattern("yyyy-MM"))
val convertStartMonth = YearMonth.of(startDate.year, startDate.month)
val endDate = YearMonth.parse(endMonth, DateTimeFormatter.ofPattern("yyyy-MM"))
val endDate = YearMonth.parse(qu eryE ndMonth.toString() , DateTimeFormatter.ofPattern("yyyy-MM"))
val convertEndMonth = YearMonth.of(endDate.year, endDate.month)
val monthRange: MutableList<Map<String, Any>> = ArrayList()
var currentDate = startDate
if (currentDate == endDate){
monthRange.add(
mapOf(
"display" to YearMonth.of(currentDate.year, currentDate.month).format(monthFormat),
"date" to currentDate
)
)
}
while (!currentDate.isAfter(endDate)) {
monthRange.add(
mapOf(
@@ -2468,7 +2551,7 @@ open class ReportService(
val lastColumnIndex = getColumnAlphabet(3 + monthRange.size)
val totalManhourETitleCell = totalManhourERow.getCell(0) ?: totalManhourERow.createCell(0)
totalManhourETitleCell.apply {
setCellValue("Total Manhour Expenditure (HKD)")
setCellValue("Less: Total Manhour Expenditure (HKD)")
}
val totalManhourECell = totalManhourERow.getCell(1) ?: totalManhourERow.createCell(1)
totalManhourECell.apply {
@@ -2490,8 +2573,36 @@ open class ReportService(
cellFormula = "B${rowNum - 1}-B${rowNum}"
cellStyle.dataFormat = accountingStyle
}
CellUtil.setCellStyleProperty(panlCellTitle, "borderBottom", BorderStyle.DOUBLE)
CellUtil.setCellStyleProperty(panlCell, "borderBottom", BorderStyle.DOUBLE)
// CellUtil.setCellStyleProperty(panlCellTitle, "borderBottom", BorderStyle.DOUBLE)
// CellUtil.setCellStyleProperty(panlCell, "borderBottom", BorderStyle.DOUBLE)
val profitAndLossRowNum = rowNum+1
rowNum += 1
val uninvoicedAmountRow: Row = sheet.getRow(rowNum) ?: sheet.createRow(rowNum)
val uninvoicedAmountTitleCell = uninvoicedAmountRow.getCell(0) ?: uninvoicedAmountRow.createCell(0)
uninvoicedAmountTitleCell.apply {
setCellValue("Add: Uninvoice Amount (HKD)")
}
val uninvoicedAmountCell = uninvoicedAmountRow.getCell(1) ?: uninvoicedAmountRow.createCell(1)
uninvoicedAmountCell.apply {
setCellValue(if ((info.getValue("expectedTotalFee") as Double) < 0.0 ) 0.0 else info.getValue("expectedTotalFee") as Double)
cellStyle.dataFormat = accountingStyle
}
rowNum += 1
val projectedPnLRow: Row = sheet.getRow(rowNum) ?: sheet.createRow(rowNum)
val projectedPnLTitleCell = projectedPnLRow.getCell(0) ?: projectedPnLRow.createCell(0)
projectedPnLTitleCell.apply {
setCellValue("Projected Profit / (Loss)")
}
val projectedPnLCell = projectedPnLRow.getCell(1) ?: projectedPnLRow.createCell(1)
projectedPnLCell.apply {
cellFormula = "B${profitAndLossRowNum}+B${profitAndLossRowNum+1}"
cellStyle.dataFormat = accountingStyle
}
CellUtil.setCellStyleProperty(projectedPnLTitleCell, "borderBottom", BorderStyle.DOUBLE)
CellUtil.setCellStyleProperty(projectedPnLCell, "borderBottom", BorderStyle.DOUBLE)
conditionalFormattingNegative(sheet)