|
|
@@ -7,6 +7,7 @@ import com.ffii.tsms.modules.project.entity.Invoice |
|
|
|
import com.ffii.tsms.modules.project.entity.Project |
|
|
|
import com.ffii.tsms.modules.timesheet.entity.Leave |
|
|
|
import com.ffii.tsms.modules.timesheet.entity.Timesheet |
|
|
|
import com.ffii.tsms.modules.timesheet.web.models.LeaveEntry |
|
|
|
import org.apache.commons.logging.Log |
|
|
|
import org.apache.commons.logging.LogFactory |
|
|
|
import org.apache.poi.ss.usermodel.* |
|
|
@@ -20,13 +21,14 @@ import java.io.IOException |
|
|
|
import java.time.LocalDate |
|
|
|
import java.time.format.DateTimeFormatter |
|
|
|
import java.util.* |
|
|
|
import kotlin.time.times |
|
|
|
|
|
|
|
data class DayInfo(val date: String?, val weekday: String?) |
|
|
|
|
|
|
|
@Service |
|
|
|
open class ReportService ( |
|
|
|
open class ReportService( |
|
|
|
private val jdbcDao: JdbcDao, |
|
|
|
) |
|
|
|
{ |
|
|
|
) { |
|
|
|
private val logger: Log = LogFactory.getLog(javaClass) |
|
|
|
private val DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy/MM/dd") |
|
|
|
private val FORMATTED_TODAY = LocalDate.now().format(DATE_FORMATTER) |
|
|
@@ -48,8 +50,13 @@ open class ReportService ( |
|
|
|
|
|
|
|
return outputStream.toByteArray() |
|
|
|
} |
|
|
|
|
|
|
|
@Throws(IOException::class) |
|
|
|
fun generateProjectCashFlowReport(project: Project, invoices: List<Invoice>, timesheets: List<Timesheet>): ByteArray { |
|
|
|
fun generateProjectCashFlowReport( |
|
|
|
project: Project, |
|
|
|
invoices: List<Invoice>, |
|
|
|
timesheets: List<Timesheet> |
|
|
|
): ByteArray { |
|
|
|
// Generate the Excel report with query results |
|
|
|
val workbook: Workbook = createProjectCashFlowReport(project, invoices, timesheets, PROJECT_CASH_FLOW_REPORT) |
|
|
|
|
|
|
@@ -62,9 +69,22 @@ open class ReportService ( |
|
|
|
} |
|
|
|
|
|
|
|
@Throws(IOException::class) |
|
|
|
fun generateStaffMonthlyWorkHourAnalysisReport(month: LocalDate, staff: Staff, timesheets: List<Timesheet>, leaves: List<Leave>, projectList: List<String>): ByteArray { |
|
|
|
fun generateStaffMonthlyWorkHourAnalysisReport( |
|
|
|
month: LocalDate, |
|
|
|
staff: Staff, |
|
|
|
timesheets: List<Timesheet>, |
|
|
|
leaves: List<Leave>, |
|
|
|
projectList: List<String> |
|
|
|
): ByteArray { |
|
|
|
// Generate the Excel report with query results |
|
|
|
val workbook: Workbook = createStaffMonthlyWorkHourAnalysisReport(month, staff, timesheets, leaves, projectList, MONTHLY_WORK_HOURS_ANALYSIS_REPORT) |
|
|
|
val workbook: Workbook = createStaffMonthlyWorkHourAnalysisReport( |
|
|
|
month, |
|
|
|
staff, |
|
|
|
timesheets, |
|
|
|
leaves, |
|
|
|
projectList, |
|
|
|
MONTHLY_WORK_HOURS_ANALYSIS_REPORT |
|
|
|
) |
|
|
|
|
|
|
|
// Write the workbook to a ByteArrayOutputStream |
|
|
|
val outputStream: ByteArrayOutputStream = ByteArrayOutputStream() |
|
|
@@ -89,7 +109,7 @@ open class ReportService ( |
|
|
|
|
|
|
|
@Throws(IOException::class) |
|
|
|
fun generateLateStartReport(project: Project): ByteArray { |
|
|
|
val workbook: Workbook = createLateStartReport(project,LATE_START_REPORT) |
|
|
|
val workbook: Workbook = createLateStartReport(project, LATE_START_REPORT) |
|
|
|
val outputStream: ByteArrayOutputStream = ByteArrayOutputStream() |
|
|
|
workbook.write(outputStream) |
|
|
|
workbook.close() |
|
|
@@ -102,7 +122,7 @@ open class ReportService ( |
|
|
|
private fun createFinancialStatusReport( |
|
|
|
templatePath: String, |
|
|
|
projectId: Long, |
|
|
|
) : Workbook { |
|
|
|
): Workbook { |
|
|
|
val resource = ClassPathResource(templatePath) |
|
|
|
val templateInputStream = resource.inputStream |
|
|
|
val workbook: Workbook = XSSFWorkbook(templateInputStream) |
|
|
@@ -125,42 +145,42 @@ open class ReportService ( |
|
|
|
val sheet: Sheet = workbook.getSheetAt(0) |
|
|
|
|
|
|
|
// accounting style + comma style |
|
|
|
val accountingStyle = workbook.createDataFormat() .getFormat("_(* #,##0.00_);_(* (#,##0.00);_(* \"-\"??_);_(@_)") |
|
|
|
val accountingStyle = workbook.createDataFormat().getFormat("_(* #,##0.00_);_(* (#,##0.00);_(* \"-\"??_);_(@_)") |
|
|
|
|
|
|
|
var rowIndex = 1 // Assuming the location is in (1,2), which is the report date field |
|
|
|
var columnIndex = 2 |
|
|
|
sheet.getRow(rowIndex).getCell(columnIndex).apply { |
|
|
|
sheet.getRow(rowIndex).createCell(columnIndex).apply { |
|
|
|
setCellValue(FORMATTED_TODAY) |
|
|
|
} |
|
|
|
|
|
|
|
rowIndex = 2 |
|
|
|
sheet.getRow(rowIndex).getCell(columnIndex).apply { |
|
|
|
sheet.getRow(rowIndex).createCell(columnIndex).apply { |
|
|
|
setCellValue(project.code) |
|
|
|
} |
|
|
|
|
|
|
|
rowIndex = 3 |
|
|
|
sheet.getRow(rowIndex).getCell(columnIndex).apply { |
|
|
|
sheet.getRow(rowIndex).createCell(columnIndex).apply { |
|
|
|
setCellValue(project.name) |
|
|
|
} |
|
|
|
|
|
|
|
rowIndex = 4 |
|
|
|
sheet.getRow(rowIndex).getCell(columnIndex).apply { |
|
|
|
sheet.getRow(rowIndex).createCell(columnIndex).apply { |
|
|
|
setCellValue(if (project.customer?.name == null) "N/A" else project.customer?.name) |
|
|
|
} |
|
|
|
|
|
|
|
rowIndex = 5 |
|
|
|
sheet.getRow(rowIndex).getCell(columnIndex).apply { |
|
|
|
sheet.getRow(rowIndex).createCell(columnIndex).apply { |
|
|
|
setCellValue(if (project.teamLead?.team?.name == null) "N/A" else project.teamLead?.team?.name) |
|
|
|
} |
|
|
|
|
|
|
|
rowIndex = 9 |
|
|
|
sheet.getRow(rowIndex).apply { |
|
|
|
getCell(1).apply { |
|
|
|
createCell(1).apply { |
|
|
|
setCellValue(project.expectedTotalFee!!) |
|
|
|
cellStyle.dataFormat = accountingStyle |
|
|
|
} |
|
|
|
|
|
|
|
getCell(2).apply { |
|
|
|
createCell(2).apply { |
|
|
|
setCellValue(project.expectedTotalFee!! / 0.8) |
|
|
|
cellStyle.dataFormat = accountingStyle |
|
|
|
} |
|
|
@@ -168,15 +188,17 @@ open class ReportService ( |
|
|
|
|
|
|
|
rowIndex = 10 |
|
|
|
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)) } |
|
|
|
val actualExpenditure = timesheets.sumOf { timesheet -> |
|
|
|
timesheet.staff!!.salary.hourlyRate.toDouble() * ((timesheet.normalConsumed ?: 0.0) + (timesheet.otConsumed |
|
|
|
?: 0.0)) |
|
|
|
} |
|
|
|
sheet.getRow(rowIndex).apply { |
|
|
|
getCell(1).apply { |
|
|
|
// TODO: Replace by actual expenditure |
|
|
|
createCell(1).apply { |
|
|
|
setCellValue(actualExpenditure) |
|
|
|
cellStyle.dataFormat = accountingStyle |
|
|
|
} |
|
|
|
|
|
|
|
getCell(2).apply { |
|
|
|
createCell(2).apply { |
|
|
|
setCellValue(actualIncome.toDouble()) |
|
|
|
cellStyle.dataFormat = accountingStyle |
|
|
|
} |
|
|
@@ -184,102 +206,137 @@ open class ReportService ( |
|
|
|
|
|
|
|
rowIndex = 11 |
|
|
|
sheet.getRow(rowIndex).apply { |
|
|
|
getCell(1).apply { |
|
|
|
// TODO: Replace by actual expenditure |
|
|
|
createCell(1).apply { |
|
|
|
cellFormula = "B10-B11" |
|
|
|
cellStyle.dataFormat = accountingStyle |
|
|
|
} |
|
|
|
|
|
|
|
getCell(2).apply { |
|
|
|
createCell(2).apply { |
|
|
|
cellFormula = "C10-C11" |
|
|
|
cellStyle.dataFormat = accountingStyle |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// TODO: Add expenditure |
|
|
|
rowIndex = 15 |
|
|
|
val combinedResults = (invoices.map { it.receiptDate } + timesheets.map { it.recordDate }).filterNotNull().sortedBy { it } |
|
|
|
logger.info("combinedResults-------------- $combinedResults") |
|
|
|
invoices.forEach{ |
|
|
|
logger.info("Invoice--------- $it. \n") |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
val dateFormatter = DateTimeFormatter.ofPattern("MMM YYYY") |
|
|
|
combinedResults.forEach { result: LocalDate -> |
|
|
|
val invoice = invoices.find { invoice: Invoice -> invoice.receiptDate == result } |
|
|
|
val timesheet = timesheets.find { timesheet: Timesheet -> timesheet.recordDate == result} |
|
|
|
|
|
|
|
logger.info("result--------------: $result") |
|
|
|
if (invoice != null) { |
|
|
|
sheet.getRow(rowIndex++)?.apply { |
|
|
|
|
|
|
|
logger.info("INVOICE NOT NULL--------------:") |
|
|
|
logger.info("getCell(0)--------------: ${getCell(0)}") |
|
|
|
logger.info("dateFormatter--------------: $dateFormatter") |
|
|
|
logger.info("result.format--------------: ${result.format(dateFormatter)}") |
|
|
|
getCell(0)?.apply { |
|
|
|
setCellValue(result.format(dateFormatter).toString()) |
|
|
|
val combinedResults = |
|
|
|
(invoices.map { it.receiptDate } + timesheets.map { it.recordDate }).filterNotNull().sortedBy { it } |
|
|
|
.map { it.format(dateFormatter) }.distinct() |
|
|
|
val groupedTimesheets = timesheets.sortedBy { it.recordDate } |
|
|
|
.groupBy { timesheetEntry -> timesheetEntry.recordDate?.format(dateFormatter).toString() } |
|
|
|
.mapValues { (_, timesheetEntries) -> |
|
|
|
timesheetEntries.map { timesheet -> |
|
|
|
if (timesheet.normalConsumed != null) { |
|
|
|
timesheet.normalConsumed!!.plus(timesheet.otConsumed ?: 0.0).times(timesheet.staff!!.salary.hourlyRate.toDouble()) |
|
|
|
} else if (timesheet.otConsumed != null) { |
|
|
|
timesheet.otConsumed!!.plus(timesheet.normalConsumed ?: 0.0).times(timesheet.staff!!.salary.hourlyRate.toDouble()) |
|
|
|
} else { |
|
|
|
0.0 |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
getCell(1)?.apply { |
|
|
|
setCellValue(0.0) |
|
|
|
cellStyle.dataFormat = accountingStyle |
|
|
|
} |
|
|
|
val groupedInvoices = invoices.sortedBy { it.receiptDate }.filter { it.paidAmount != null } |
|
|
|
.groupBy { invoiceEntry -> invoiceEntry.invoiceDate?.format(dateFormatter).toString() } |
|
|
|
.mapValues { (_, invoiceEntries) -> |
|
|
|
invoiceEntries.map { invoice -> |
|
|
|
mapOf( |
|
|
|
"paidAmount" to invoice.paidAmount?.toDouble(), |
|
|
|
"description" to invoice.milestonePayment?.description |
|
|
|
) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
logger.info("invoice.paidAmount------------: ${invoice.paidAmount}") |
|
|
|
logger.info("invoice.paidAmount------------: ${invoice.paidAmount!!.toDouble()}") |
|
|
|
getCell(2)?.apply { |
|
|
|
setCellValue(invoice.paidAmount!!.toDouble()) |
|
|
|
cellStyle.dataFormat = accountingStyle |
|
|
|
} |
|
|
|
groupedTimesheets.entries.forEach { (key, value) -> |
|
|
|
logger.info("key: $key") |
|
|
|
logger.info("value: " + value.sumOf { it }) |
|
|
|
} |
|
|
|
|
|
|
|
getCell(3)?.apply { |
|
|
|
val lastRow = rowIndex - 1 |
|
|
|
if (lastRow == 15) { |
|
|
|
cellFormula = "C{currentRow}-B{currentRow}".replace("{currentRow}", rowIndex.toString()) |
|
|
|
} else { |
|
|
|
cellFormula = "IF(B{currentRow}>0,D{lastRow}-B{currentRow},D{lastRow}+C{currentRow})".replace("{currentRow}", rowIndex.toString()).replace("{lastRow}", lastRow.toString()) |
|
|
|
groupedInvoices.entries.forEach { (key, value) -> |
|
|
|
logger.info("key: $key") |
|
|
|
logger.info("value: " + value) |
|
|
|
groupedInvoices[key]!!.forEachIndexed { index, invoice -> |
|
|
|
logger.info("index: $index") |
|
|
|
logger.info("invoice: $invoice") |
|
|
|
invoice.get("paidAmount") |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
combinedResults.forEach { result: String -> |
|
|
|
|
|
|
|
if (groupedInvoices.containsKey(result)) { |
|
|
|
groupedInvoices[result]!!.forEachIndexed { _, invoice -> |
|
|
|
sheet.getRow(rowIndex++).apply { |
|
|
|
createCell(0).apply { |
|
|
|
setCellValue(result) |
|
|
|
} |
|
|
|
|
|
|
|
createCell(1).apply { |
|
|
|
setCellValue(0.0) |
|
|
|
cellStyle.dataFormat = accountingStyle |
|
|
|
} |
|
|
|
|
|
|
|
createCell(2).apply { |
|
|
|
setCellValue(invoice["paidAmount"] as Double) |
|
|
|
cellStyle.dataFormat = accountingStyle |
|
|
|
} |
|
|
|
cellStyle.dataFormat = accountingStyle |
|
|
|
} |
|
|
|
|
|
|
|
getCell(4)?.apply { |
|
|
|
setCellValue(invoice.milestonePayment!!.description!!) |
|
|
|
createCell(3).apply { |
|
|
|
val lastRow = rowIndex - 1 |
|
|
|
if (lastRow == 15) { |
|
|
|
cellFormula = |
|
|
|
"C{currentRow}-B{currentRow}".replace("{currentRow}", rowIndex.toString()) |
|
|
|
} else { |
|
|
|
cellFormula = |
|
|
|
"IF(B{currentRow}>0,D{lastRow}-B{currentRow},D{lastRow}+C{currentRow})".replace( |
|
|
|
"{currentRow}", |
|
|
|
rowIndex.toString() |
|
|
|
).replace("{lastRow}", lastRow.toString()) |
|
|
|
} |
|
|
|
cellStyle.dataFormat = accountingStyle |
|
|
|
} |
|
|
|
|
|
|
|
createCell(4)?.apply { |
|
|
|
setCellValue(invoice["description"].toString()) |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
if (timesheet != null) { |
|
|
|
sheet.getRow(rowIndex++)?.apply { |
|
|
|
if (groupedTimesheets.containsKey(result)) { |
|
|
|
sheet.getRow(rowIndex++).apply { |
|
|
|
|
|
|
|
logger.info("TIMESHEET NOT NULL--------------:") |
|
|
|
logger.info("getCell(0)--------------: ${getCell(0)}") |
|
|
|
logger.info("dateFormatter--------------: $dateFormatter") |
|
|
|
logger.info("result.format--------------: ${result.format(dateFormatter)}") |
|
|
|
getCell(0)?.apply { |
|
|
|
createCell(0).apply { |
|
|
|
setCellValue(result.format(dateFormatter)) |
|
|
|
} |
|
|
|
|
|
|
|
getCell(1)?.apply { |
|
|
|
setCellValue(timesheet.staff!!.salary.hourlyRate.toDouble() * ((timesheet.normalConsumed ?: 0.0) + (timesheet.otConsumed ?: 0.0))) |
|
|
|
createCell(1).apply { |
|
|
|
setCellValue(groupedTimesheets[result]!!.sum()) |
|
|
|
cellStyle.dataFormat = accountingStyle |
|
|
|
} |
|
|
|
|
|
|
|
getCell(2)?.apply { |
|
|
|
createCell(2).apply { |
|
|
|
setCellValue(0.0) |
|
|
|
cellStyle.dataFormat = accountingStyle |
|
|
|
} |
|
|
|
|
|
|
|
getCell(3)?.apply { |
|
|
|
createCell(3).apply { |
|
|
|
val lastRow = rowIndex - 1 |
|
|
|
if (lastRow == 15) { |
|
|
|
cellFormula = "C{currentRow}-B{currentRow}".replace("{currentRow}", rowIndex.toString()) |
|
|
|
} else { |
|
|
|
cellFormula = "IF(B{currentRow}>0,D{lastRow}-B{currentRow},D{lastRow}+C{currentRow})".replace("{currentRow}", rowIndex.toString()).replace("{lastRow}", lastRow.toString()) |
|
|
|
cellFormula = |
|
|
|
"IF(B{currentRow}>0,D{lastRow}-B{currentRow},D{lastRow}+C{currentRow})".replace( |
|
|
|
"{currentRow}", |
|
|
|
rowIndex.toString() |
|
|
|
).replace("{lastRow}", lastRow.toString()) |
|
|
|
} |
|
|
|
cellStyle.dataFormat = accountingStyle |
|
|
|
} |
|
|
|
|
|
|
|
getCell(4)?.apply { |
|
|
|
createCell(4).apply { |
|
|
|
setCellValue("Monthly Manpower Expenditure") |
|
|
|
} |
|
|
|
} |
|
|
@@ -392,7 +449,7 @@ open class ReportService ( |
|
|
|
// cellStyle.borderBottom = BorderStyle.DOUBLE |
|
|
|
cellStyle.alignment = HorizontalAlignment.CENTER |
|
|
|
} |
|
|
|
sheet.addMergedRegion(CellRangeAddress(rowIndex,rowIndex , 0, 1)) |
|
|
|
sheet.addMergedRegion(CellRangeAddress(rowIndex, rowIndex, 0, 1)) |
|
|
|
// |
|
|
|
rowIndex += 1 |
|
|
|
sheet.getRow(rowIndex).getCell(0).apply { |
|
|
@@ -414,7 +471,7 @@ open class ReportService ( |
|
|
|
cellStyle.alignment = HorizontalAlignment.CENTER |
|
|
|
cellStyle.dataFormat = accountingStyle |
|
|
|
} |
|
|
|
sheet.addMergedRegion(CellRangeAddress(rowIndex,rowIndex , 0, 1)) |
|
|
|
sheet.addMergedRegion(CellRangeAddress(rowIndex, rowIndex, 0, 1)) |
|
|
|
// |
|
|
|
rowIndex += 1 |
|
|
|
sheet.getRow(rowIndex).getCell(0).apply { |
|
|
@@ -428,7 +485,7 @@ open class ReportService ( |
|
|
|
cellStyle.dataFormat = accountingStyle |
|
|
|
} |
|
|
|
|
|
|
|
sheet.addMergedRegion(CellRangeAddress(rowIndex,rowIndex , 0, 1)) |
|
|
|
sheet.addMergedRegion(CellRangeAddress(rowIndex, rowIndex, 0, 1)) |
|
|
|
|
|
|
|
rowIndex += 1 |
|
|
|
sheet.getRow(rowIndex).getCell(0).apply { |
|
|
@@ -453,12 +510,12 @@ open class ReportService ( |
|
|
|
// cellStyle.borderBottom = BorderStyle.DOUBLE |
|
|
|
} |
|
|
|
sheet.getRow(rowIndex).getCell(2).apply { |
|
|
|
cellFormula = "C${rowIndex-2}+C${rowIndex-1}" |
|
|
|
cellFormula = "C${rowIndex - 2}+C${rowIndex - 1}" |
|
|
|
cellStyle.dataFormat = accountingStyle |
|
|
|
// cellStyle.borderTop = BorderStyle.THIN |
|
|
|
// cellStyle.borderBottom = BorderStyle.DOUBLE |
|
|
|
} |
|
|
|
sheet.addMergedRegion(CellRangeAddress(rowIndex,rowIndex , 0, 1)) |
|
|
|
sheet.addMergedRegion(CellRangeAddress(rowIndex, rowIndex, 0, 1)) |
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
|
|
|
rowIndex = 7 |
|
|
|
columnIndex = 2 |
|
|
@@ -487,21 +544,21 @@ open class ReportService ( |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
|
|
|
sheet.getRow(rowIndex).apply { |
|
|
|
getCell(columnIndex).setCellValue("Leave Hours") |
|
|
|
getCell(columnIndex).cellStyle.alignment = HorizontalAlignment.CENTER |
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
|
|
|
sheet.getRow(rowIndex).apply { |
|
|
|
getCell(columnIndex).setCellValue("Leave Hours") |
|
|
|
getCell(columnIndex).cellStyle.alignment = HorizontalAlignment.CENTER |
|
|
|
|
|
|
|
columnIndex += 1 |
|
|
|
getCell(columnIndex).setCellValue("Daily Manhour Spent\n" + "(Excluding Leave Hours)") |
|
|
|
getCell(columnIndex).cellStyle.alignment = HorizontalAlignment.CENTER |
|
|
|
columnSize = columnIndex |
|
|
|
} |
|
|
|
sheet.addMergedRegion(CellRangeAddress(6,6 , 2, columnIndex)) |
|
|
|
columnIndex += 1 |
|
|
|
getCell(columnIndex).setCellValue("Daily Manhour Spent\n" + "(Excluding Leave Hours)") |
|
|
|
getCell(columnIndex).cellStyle.alignment = HorizontalAlignment.CENTER |
|
|
|
columnSize = columnIndex |
|
|
|
} |
|
|
|
sheet.addMergedRegion(CellRangeAddress(6, 6, 2, columnIndex)) |
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
|
|
|
for (i in 2 until columnIndex) { |
|
|
|
for (k in 0 until rowSize) { |
|
|
|
sheet.getRow(8+k).getCell(i).apply { |
|
|
|
sheet.getRow(8 + k).getCell(i).apply { |
|
|
|
setCellValue(0.0) |
|
|
|
cellStyle.dataFormat = accountingStyle |
|
|
|
} |
|
|
@@ -512,14 +569,17 @@ open class ReportService ( |
|
|
|
if (sheet.getRow(rowIndex - 1).getCell(2).stringCellValue != "Leave Hours") { |
|
|
|
// cal daily spent manhour |
|
|
|
for (i in 0 until rowSize) { |
|
|
|
val cell = sheet.getRow(rowIndex)?.getCell(columnIndex) ?: sheet.getRow(rowIndex + i)?.createCell(columnIndex) |
|
|
|
cell?.cellFormula = "SUM(${getColumnAlphabet(2)}${rowIndex+1}:${getColumnAlphabet(columnIndex)}${rowIndex+1})" // should columnIndex - 2 |
|
|
|
val cell = |
|
|
|
sheet.getRow(rowIndex)?.getCell(columnIndex) ?: sheet.getRow(rowIndex + i)?.createCell(columnIndex) |
|
|
|
cell?.cellFormula = |
|
|
|
"SUM(${getColumnAlphabet(2)}${rowIndex + 1}:${getColumnAlphabet(columnIndex)}${rowIndex + 1})" // should columnIndex - 2 |
|
|
|
rowIndex++ |
|
|
|
} |
|
|
|
// cal subtotal |
|
|
|
for (i in 0 until columnSize) { // minus last col |
|
|
|
val cell = sheet.getRow(rowIndex)?.getCell(2) ?: sheet.getRow(rowIndex)?.createCell(2) |
|
|
|
cell?.cellFormula = "SUM(${getColumnAlphabet(2)}${rowIndex}:${getColumnAlphabet(columnIndex + i)}${rowIndex})" |
|
|
|
cell?.cellFormula = |
|
|
|
"SUM(${getColumnAlphabet(2)}${rowIndex}:${getColumnAlphabet(columnIndex + i)}${rowIndex})" |
|
|
|
cell?.cellStyle?.dataFormat = accountingStyle |
|
|
|
cell?.cellStyle?.setFont(boldFont) |
|
|
|
// cell?.cellStyle?.borderTop = BorderStyle.THIN |
|
|
@@ -528,7 +588,8 @@ open class ReportService ( |
|
|
|
} else { // just for preview when no data |
|
|
|
// cal daily spent manhour |
|
|
|
for (i in 0 until rowSize) { |
|
|
|
val cell = sheet.getRow(rowIndex)?.getCell(columnIndex) ?: sheet.getRow(rowIndex + i)?.createCell(columnIndex) |
|
|
|
val cell = |
|
|
|
sheet.getRow(rowIndex)?.getCell(columnIndex) ?: sheet.getRow(rowIndex + i)?.createCell(columnIndex) |
|
|
|
cell?.setCellValue("daily spent manhour") |
|
|
|
rowIndex++ |
|
|
|
} |
|
|
@@ -548,7 +609,7 @@ open class ReportService ( |
|
|
|
|
|
|
|
@Throws(IOException::class) |
|
|
|
private fun createSalaryList( |
|
|
|
salarys : List<Salary>, |
|
|
|
salarys: List<Salary>, |
|
|
|
templatePath: String, |
|
|
|
): Workbook { |
|
|
|
|
|
|
@@ -588,19 +649,19 @@ open class ReportService ( |
|
|
|
private fun createLateStartReport( |
|
|
|
project: Project, |
|
|
|
templatePath: String |
|
|
|
):Workbook{ |
|
|
|
): Workbook { |
|
|
|
|
|
|
|
project |
|
|
|
val resource = ClassPathResource(templatePath) |
|
|
|
val templateInputStream = resource.inputStream |
|
|
|
val workbook: Workbook = XSSFWorkbook(templateInputStream) |
|
|
|
val sheet = workbook.getSheetAt(0) |
|
|
|
|
|
|
|
|
|
|
|
// Formatting the current date to "YYYY/MM/DD" and setting it to cell C2 |
|
|
|
val formattedToday = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy/MM/dd")) |
|
|
|
val dateCell = sheet.getRow(1)?.getCell(2) ?: sheet.getRow(1).createCell(2) |
|
|
|
dateCell.setCellValue(formattedToday) |
|
|
|
|
|
|
|
|
|
|
|
// Styling for cell A1: Font size 16 and bold |
|
|
|
val headerFont = workbook.createFont().apply { |
|
|
|
bold = true |
|
|
@@ -612,7 +673,7 @@ open class ReportService ( |
|
|
|
val headerCell = sheet.getRow(0)?.getCell(0) ?: sheet.getRow(0).createCell(0) |
|
|
|
headerCell.cellStyle = headerCellStyle |
|
|
|
headerCell.setCellValue("Report Title") |
|
|
|
|
|
|
|
|
|
|
|
// Apply styles from A2 to A4 (bold) |
|
|
|
val boldFont = workbook.createFont().apply { bold = true } |
|
|
|
val boldCellStyle = workbook.createCellStyle().apply { setFont(boldFont) } |
|
|
@@ -621,7 +682,7 @@ open class ReportService ( |
|
|
|
val cell = row?.getCell(0) ?: row.createCell(0) |
|
|
|
cell.cellStyle = boldCellStyle |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Apply styles from A6 to J6 (bold, bottom border, center alignment) |
|
|
|
val styleA6ToJ6 = workbook.createCellStyle().apply { |
|
|
|
setFont(boldFont) |
|
|
@@ -634,7 +695,7 @@ open class ReportService ( |
|
|
|
val cell = row?.getCell(cellAddress.column) ?: row.createCell(cellAddress.column) |
|
|
|
cell.cellStyle = styleA6ToJ6 |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Setting column widths dynamically based on content length (example logic) |
|
|
|
val maxContentWidths = IntArray(10) { 8 } // Initial widths for A to J |
|
|
|
for (rowIndex in 0..sheet.lastRowNum) { |
|
|
@@ -652,31 +713,31 @@ open class ReportService ( |
|
|
|
for (colIndex in 0..9) { |
|
|
|
sheet.setColumnWidth(colIndex, (maxContentWidths[colIndex] + 2) * 256) // Set the width for each column |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
return workbook |
|
|
|
} |
|
|
|
|
|
|
|
open fun getFinancialStatus(projectId: Long?): List<Map<String, Any>> { |
|
|
|
val sql = StringBuilder( |
|
|
|
"with cte_invoice as (select p.code, sum(i.issueAmount) as sumIssuedAmount , sum(i.paidAmount) as sumPaidAmount" |
|
|
|
+ "from invoice i" |
|
|
|
+ "left join project p on p.code = i.projectCode" |
|
|
|
+ "group by p.code" |
|
|
|
+ ")" |
|
|
|
+ " Select p.code, p.description, c.name, t2.name, p.planStart , p.planEnd , p.expectedTotalFee ," |
|
|
|
+ " s.name , IFNULL(t.normalConsumed, 0) as normalConsumed, IFNULL(t.otConsumed , 0) as otConsumed, s2.hourlyRate," |
|
|
|
+ " cte_i.sumIssuedAmount, cte_i.sumPaidAmount" |
|
|
|
+ " from timesheet t" |
|
|
|
+ " left join project_task pt on pt.id = t.projectTaskId" |
|
|
|
+ " left join project p ON p.id = pt.project_id" |
|
|
|
+ " left join staff s on s.id = t.staffId" |
|
|
|
+ " left join salary s2 on s.salaryId = s2.salaryPoint" |
|
|
|
+ " left join customer c on c.id = p.customerId" |
|
|
|
+ " left join team t2 on t2.id = s.teamId" |
|
|
|
+ " left join cte_invoice cte_i on cte_i.code = p.code" |
|
|
|
+ "from invoice i" |
|
|
|
+ "left join project p on p.code = i.projectCode" |
|
|
|
+ "group by p.code" |
|
|
|
+ ")" |
|
|
|
+ " Select p.code, p.description, c.name, t2.name, p.planStart , p.planEnd , p.expectedTotalFee ," |
|
|
|
+ " s.name , IFNULL(t.normalConsumed, 0) as normalConsumed, IFNULL(t.otConsumed , 0) as otConsumed, s2.hourlyRate," |
|
|
|
+ " cte_i.sumIssuedAmount, cte_i.sumPaidAmount" |
|
|
|
+ " from timesheet t" |
|
|
|
+ " left join project_task pt on pt.id = t.projectTaskId" |
|
|
|
+ " left join project p ON p.id = pt.project_id" |
|
|
|
+ " left join staff s on s.id = t.staffId" |
|
|
|
+ " left join salary s2 on s.salaryId = s2.salaryPoint" |
|
|
|
+ " left join customer c on c.id = p.customerId" |
|
|
|
+ " left join team t2 on t2.id = s.teamId" |
|
|
|
+ " left join cte_invoice cte_i on cte_i.code = p.code" |
|
|
|
) |
|
|
|
|
|
|
|
if (projectId!! > 0){ |
|
|
|
if (projectId!! > 0) { |
|
|
|
sql.append(" where p.id = :projectId ") |
|
|
|
} |
|
|
|
sql.append(" order by p.code") |
|
|
|