Procházet zdrojové kódy

update

tags/Baseline_30082024_BACKEND_UAT
cyril.tsui před 1 rokem
rodič
revize
d18f4c0a6b
3 změnil soubory, kde provedl 184 přidání a 122 odebrání
  1. +2
    -1
      src/main/java/com/ffii/tsms/modules/data/entity/StaffRepository.java
  2. +1
    -1
      src/main/java/com/ffii/tsms/modules/data/service/StaffsService.kt
  3. +181
    -120
      src/main/java/com/ffii/tsms/modules/report/service/ReportService.kt

+ 2
- 1
src/main/java/com/ffii/tsms/modules/data/entity/StaffRepository.java Zobrazit soubor

@@ -5,6 +5,7 @@ import com.ffii.core.support.AbstractRepository;
import com.ffii.tsms.modules.data.entity.projections.StaffSearchInfo;
import org.springframework.data.repository.query.Param;

import java.io.Serializable;
import java.util.List;
import java.util.Map;
import java.util.Optional;
@@ -13,7 +14,7 @@ public interface StaffRepository extends AbstractRepository<Staff, Long> {
List<StaffSearchInfo> findStaffSearchInfoByAndDeletedFalse();
List<StaffSearchInfo> findStaffSearchInfoByAndDeletedFalseAndTeamIdIsNull();

List<StaffSearchInfo> findAllStaffSearchInfoByIdIn(List<Long> ids);
List<StaffSearchInfo> findAllStaffSearchInfoByIdIn(List<Serializable> ids);
Optional<Staff> findByStaffId(@Param("staffId") String staffId);

Optional<StaffSearchInfo> findStaffSearchInfoById(@Param("id") Long id);


+ 1
- 1
src/main/java/com/ffii/tsms/modules/data/service/StaffsService.kt Zobrazit soubor

@@ -35,7 +35,7 @@ open class StaffsService(
) : AbstractBaseEntityService<Staff, Long, StaffRepository>(jdbcDao, staffRepository) {
open fun getTeamLeads(): List<StaffSearchInfo> {
// TODO: Replace by actual logic
val teamIds = teamRepository.findAll().map { team -> team.staff.id }
val teamIds = teamRepository.findAll().filter { team -> team.deleted == false }.map { team -> team.staff.id }
return staffRepository.findAllStaffSearchInfoByIdIn(teamIds)
}



+ 181
- 120
src/main/java/com/ffii/tsms/modules/report/service/ReportService.kt Zobrazit soubor

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


Načítá se…
Zrušit
Uložit