Browse Source

update api

tags/Baseline_30082024_BACKEND_UAT
Mac\David 1 year ago
parent
commit
f36efde136
1 changed files with 155 additions and 40 deletions
  1. +155
    -40
      src/main/java/com/ffii/tsms/modules/data/service/DashboardService.kt

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

@@ -68,14 +68,14 @@ open class DashboardService(


fun searchCustomerSubsidiaryProject(args: Map<String, Any>): List<Map<String, Any>> { fun searchCustomerSubsidiaryProject(args: Map<String, Any>): List<Map<String, Any>> {
val sql = StringBuilder("select" val sql = StringBuilder("select"
+ " ROW_NUMBER() OVER (ORDER BY p.id, p.code, p.name, te.code, s.name, tg.name, p.totalManhour, milestonePayment.comingPaymentMilestone) AS id,"
+ " ROW_NUMBER() OVER (ORDER BY p.id, p.code, p.name, te.code, s.name, p.totalManhour, milestonePayment.comingPaymentMilestone) AS id,"
+ " p.id as id," + " p.id as id,"
+ " p.id as projectId," + " p.id as projectId,"
+ " p.code as projectCode," + " p.code as projectCode,"
+ " p.name as projectName," + " p.name as projectName,"
+ " te.code as team," + " te.code as team,"
+ " s.name as teamLead," + " s.name as teamLead,"
+ " tg.name as expectedStage,"
+ " GROUP_CONCAT(DISTINCT tg.name ORDER BY tg.name) as expectedStage,"
+ " p.totalManhour as budgetedManhour," + " p.totalManhour as budgetedManhour,"
+ " sum(t.normalConsumed) + sum(t.otConsumed) as spentManhour," + " sum(t.normalConsumed) + sum(t.otConsumed) as spentManhour,"
+ " p.totalManhour - sum(t.normalConsumed) - sum(t.otConsumed) as remainedManhour," + " p.totalManhour - sum(t.normalConsumed) - sum(t.otConsumed) as remainedManhour,"
@@ -116,20 +116,20 @@ open class DashboardService(
} }
} }


sql.append(" group by p.id, p.code, p.name, te.code, s.name, tg.name, p.totalManhour, milestonePayment.comingPaymentMilestone")
sql.append(" group by p.id, p.code, p.name, te.code, s.name, p.totalManhour, milestonePayment.comingPaymentMilestone")
return jdbcDao.queryForList(sql.toString(), args) return jdbcDao.queryForList(sql.toString(), args)
} }


fun searchCustomerNonSubsidiaryProject(args: Map<String, Any>): List<Map<String, Any>> { fun searchCustomerNonSubsidiaryProject(args: Map<String, Any>): List<Map<String, Any>> {
val sql = StringBuilder("select" val sql = StringBuilder("select"
+ " ROW_NUMBER() OVER (ORDER BY p.id, p.code, p.name, te.code, s.name, tg.name, p.totalManhour, milestonePayment.comingPaymentMilestone) AS id,"
+ " ROW_NUMBER() OVER (ORDER BY p.id, p.code, p.name, te.code, s.name, p.totalManhour, milestonePayment.comingPaymentMilestone) AS id,"
+ " p.id as id," + " p.id as id,"
+ " p.id as projectId," + " p.id as projectId,"
+ " p.code as projectCode," + " p.code as projectCode,"
+ " p.name as projectName," + " p.name as projectName,"
+ " te.code as team," + " te.code as team,"
+ " s.name as teamLead," + " s.name as teamLead,"
+ " tg.name as expectedStage,"
+ " GROUP_CONCAT(DISTINCT tg.name ORDER BY tg.name) as expectedStage,"
+ " p.totalManhour as budgetedManhour," + " p.totalManhour as budgetedManhour,"
+ " COALESCE (sum(t.normalConsumed) + sum(t.otConsumed),0) as spentManhour," + " COALESCE (sum(t.normalConsumed) + sum(t.otConsumed),0) as spentManhour,"
+ " COALESCE (p.totalManhour - sum(t.normalConsumed) - sum(t.otConsumed),0) as remainedManhour," + " COALESCE (p.totalManhour - sum(t.normalConsumed) - sum(t.otConsumed),0) as remainedManhour,"
@@ -160,7 +160,7 @@ open class DashboardService(
+ " and isNull(p.customerSubsidiaryId)" + " and isNull(p.customerSubsidiaryId)"
+ " and p.status not in (\"Pending to Start\",\"Completed\",\"Deleted\")" + " and p.status not in (\"Pending to Start\",\"Completed\",\"Deleted\")"
+ " and (tg.name != '5. Miscellaneous' or tg.name is null)" + " and (tg.name != '5. Miscellaneous' or tg.name is null)"
+ " group by p.id, p.code, p.name, te.code, s.name, tg.name, p.totalManhour, milestonePayment.comingPaymentMilestone"
+ " group by p.id, p.code, p.name, te.code, s.name, p.totalManhour, milestonePayment.comingPaymentMilestone"
) )


return jdbcDao.queryForList(sql.toString(), args) return jdbcDao.queryForList(sql.toString(), args)
@@ -276,14 +276,14 @@ open class DashboardService(
fun searchTeamProject(args: Map<String, Any>): List<Map<String, Any>> { fun searchTeamProject(args: Map<String, Any>): List<Map<String, Any>> {
val sql = StringBuilder( val sql = StringBuilder(
"select" "select"
+ " ROW_NUMBER() OVER (ORDER BY p.id, p.code, p.name, te.code, s.name, tg.name, p.totalManhour, milestonePayment.comingPaymentMilestone) AS id,"
+ " ROW_NUMBER() OVER (ORDER BY p.id, p.code, p.name, te.code, s.name, p.totalManhour, milestonePayment.comingPaymentMilestone) AS id,"
+ " p.id as id," + " p.id as id,"
+ " p.id as projectId," + " p.id as projectId,"
+ " p.code as projectCode," + " p.code as projectCode,"
+ " p.name as projectName," + " p.name as projectName,"
+ " te.code as team," + " te.code as team,"
+ " s.name as teamLead," + " s.name as teamLead,"
+ " tg.name as expectedStage,"
+ " GROUP_CONCAT(DISTINCT tg.name ORDER BY tg.name) as expectedStage,"
+ " p.totalManhour as budgetedManhour," + " p.totalManhour as budgetedManhour,"
+ " COALESCE (sum(t.normalConsumed) + sum(t.otConsumed),0) as spentManhour," + " COALESCE (sum(t.normalConsumed) + sum(t.otConsumed),0) as spentManhour,"
+ " COALESCE (p.totalManhour - sum(t.normalConsumed) - sum(t.otConsumed),0) as remainedManhour," + " COALESCE (p.totalManhour - sum(t.normalConsumed) - sum(t.otConsumed),0) as remainedManhour,"
@@ -313,7 +313,7 @@ open class DashboardService(
+ " where p.teamLead = :teamLeadId" + " where p.teamLead = :teamLeadId"
+ " and p.status not in (\"Pending to Start\",\"Completed\",\"Deleted\")" + " and p.status not in (\"Pending to Start\",\"Completed\",\"Deleted\")"
+ " and (tg.name != '5. Miscellaneous' or tg.name is null)" + " and (tg.name != '5. Miscellaneous' or tg.name is null)"
+ " group by p.id, p.code, p.name, te.code, s.name, tg.name, p.totalManhour, milestonePayment.comingPaymentMilestone"
+ " group by p.id, p.code, p.name, te.code, s.name, p.totalManhour, milestonePayment.comingPaymentMilestone"
) )


return jdbcDao.queryForList(sql.toString(), args) return jdbcDao.queryForList(sql.toString(), args)
@@ -445,16 +445,103 @@ open class DashboardService(
return jdbcDao.queryForList(sql.toString(), args) return jdbcDao.queryForList(sql.toString(), args)
} }


// " case"
// + " when coalesce(round(sum(p.expectedTotalFee) * 0.8,2),0) <= COALESCE(expenditure.cumulativeExpenditure,0) then coalesce(round(sum(p.expectedTotalFee) * 0.8,2),0) - coalesce(sum(i.issueAmount),0)"
// + " when coalesce(round(sum(p.expectedTotalFee) * 0.8,2),0) > COALESCE(expenditure.cumulativeExpenditure,0) and COALESCE(expenditure.cumulativeExpenditure,0) < coalesce(sum(i.issueAmount),0) then 0"
// + " when coalesce(round(sum(p.expectedTotalFee) * 0.8,2),0) > COALESCE(expenditure.cumulativeExpenditure,0) and COALESCE(expenditure.cumulativeExpenditure,0) >= coalesce(sum(i.issueAmount),0) then COALESCE(expenditure.cumulativeExpenditure,0) - coalesce(sum(i.issueAmount),0)"
// + " end as totalUninvoiced"

fun searchFinancialSummaryByClient(args: Map<String, Any>): List<Map<String, Any>> { fun searchFinancialSummaryByClient(args: Map<String, Any>): List<Map<String, Any>> {
val sql = StringBuilder( "select"
+ " ROW_NUMBER() OVER (ORDER BY t.id, c.id) AS id,"
// val sql = StringBuilder( "select"
// + " ROW_NUMBER() OVER (ORDER BY t.id, c.id) AS id,"
// + " t.id as teamId,"
// + " c.id as cid,"
// + " c.code as customerCode,"
// + " c.name as customerName,"
// + " count(p.name) as projectNo,"
// + " sum(p.expectedTotalFee) as totalFee,"
// + " round(sum(p.expectedTotalFee) * 0.8,2) as totalBudget,"
// + " COALESCE(round(expenditure.cumulativeExpenditure,2),0) as cumulativeExpenditure,"
// + " coalesce(sum(i.issueAmount),0) as totalInvoiced,"
// + " coalesce(sum(i.paidAmount),0) as totalReceived,"
// + " case"
// + " when coalesce(round(sum(i.issueAmount) / (COALESCE(expenditure.cumulativeExpenditure,0)),2),0) >= 1 then 'Positive'"
// + " when coalesce(round(sum(i.issueAmount) / (COALESCE(expenditure.cumulativeExpenditure,0)),2),0) < 1 then 'Negative'"
// + " end as cashFlowStatus,"
// + " coalesce(round(sum(i.issueAmount) / (COALESCE(expenditure.cumulativeExpenditure,0)),2),0) as cpi,"
// + " case"
// + " when coalesce(round(sum(p.expectedTotalFee) * 0.8,2),0) <= COALESCE(expenditure.cumulativeExpenditure,0) then coalesce(round(sum(p.expectedTotalFee) * 0.8,2),0) - coalesce(sum(i.issueAmount),0)"
// + " when coalesce(round(sum(p.expectedTotalFee) * 0.8,2),0) > COALESCE(expenditure.cumulativeExpenditure,0) and COALESCE(expenditure.cumulativeExpenditure,0) < coalesce(sum(i.issueAmount),0) then 0"
// + " when coalesce(round(sum(p.expectedTotalFee) * 0.8,2),0) > COALESCE(expenditure.cumulativeExpenditure,0) and COALESCE(expenditure.cumulativeExpenditure,0) >= coalesce(sum(i.issueAmount),0) then COALESCE(expenditure.cumulativeExpenditure,0) - coalesce(sum(i.issueAmount),0)"
// + " end as totalUninvoiced"
// + " from team t"
// + " left join project p on t.teamLead = p.teamLead"
// + " left join customer c on p.customerId = c.id"
// + " left join ("
// + " select"
// + " t3.id as tid,"
// + " c3.id as cid,"
// + " sum(i3.issueAmount) as issueAmount,"
// + " sum(i3.paidAmount) as paidAmount"
// + " from team t3"
// + " left join project p3 on t3.teamLead = p3.teamLead"
// + " left join customer c3 on p3.customerId = c3.id"
// + " left join invoice i3 on p3.code = i3.projectCode"
// + " where t3.deleted = 0"
// + " and p3.status = 'On-going'"
//
// )
//
// if (args != null) {
// if (args.containsKey("teamId"))
// sql.append(" AND t3.id = :teamId");
// }
// sql.append( " group by c3.id, t3.id"
// + " ) as i on i.cid = c.id and i.tid = t.id"
// + " left join ("
// + " select"
// + " r.teamId as teamId,"
// + " r.customerId as customerId,"
// + " sum(r.cumulativeExpenditure) as cumulativeExpenditure"
// + " from ("
// + " select"
// + " t1.id as teamId,"
// + " c2.id as customerId,"
// + " (coalesce(sum(t2.normalConsumed),0) * s2.hourlyRate) + (coalesce(sum(t2.otConsumed),0) * s2.hourlyRate * 1.0) as cumulativeExpenditure,"
// + " s2.hourlyRate as hourlyRate,"
// + " sum(t2.normalConsumed) as normalConsumed,"
// + " sum(t2.otConsumed) as otConsumed"
// + " from team t1"
// + " left join project p2 on t1.teamLead = p2.teamLead"
// + " left join customer c2 on p2.customerId = c2 .id"
// + " left join project_task pt ON p2.id = pt.project_id"
// + " left join timesheet t2 on pt.id = t2.projectTaskId"
// + " left join staff s on t2.staffId = s.id"
// + " left join salary s2 on s.salaryId = s2.salaryPoint"
// + " where p2.status = 'On-going'"
// + " and t2.id is not null"
// + " group by s2.hourlyRate,t1.id,c2.id"
// + " ) as r"
// + " group by r.teamId, r.customerId"
// + " ) as expenditure on expenditure.teamId = t.id and expenditure.customerId = c.id"
// + " where t.deleted = 0"
// + " and p.status = 'On-going'")
//
// if (args != null) {
// if (args.containsKey("teamId"))
// sql.append(" AND t.id = :teamId");
// }
// sql.append(" group by t.id, c.id, c.code, c.name")
val sql = StringBuilder(
"select"
+ " ROW_NUMBER() OVER (ORDER BY t.id, i.cid) AS id,"
+ " t.id as teamId," + " t.id as teamId,"
+ " c.id as cid,"
+ " c.code as customerCode,"
+ " c.name as customerName,"
+ " count(p.name) as projectNo,"
+ " sum(p.expectedTotalFee) as totalFee,"
+ " round(sum(p.expectedTotalFee) * 0.8,2) as totalBudget,"
+ " i.cid as cid,"
+ " i.customerCode as customerCode,"
+ " i.customerName as customerName,"
+ " p.projectNo as projectNo,"
+ " p.totalFee as totalFee,"
+ " p.totalBudget as totalBudget,"
+ " COALESCE(round(expenditure.cumulativeExpenditure,2),0) as cumulativeExpenditure," + " COALESCE(round(expenditure.cumulativeExpenditure,2),0) as cumulativeExpenditure,"
+ " coalesce(sum(i.issueAmount),0) as totalInvoiced," + " coalesce(sum(i.issueAmount),0) as totalInvoiced,"
+ " coalesce(sum(i.paidAmount),0) as totalReceived," + " coalesce(sum(i.paidAmount),0) as totalReceived,"
@@ -464,17 +551,17 @@ open class DashboardService(
+ " end as cashFlowStatus," + " end as cashFlowStatus,"
+ " coalesce(round(sum(i.issueAmount) / (COALESCE(expenditure.cumulativeExpenditure,0)),2),0) as cpi," + " coalesce(round(sum(i.issueAmount) / (COALESCE(expenditure.cumulativeExpenditure,0)),2),0) as cpi,"
+ " case" + " case"
+ " when coalesce(round(sum(p.expectedTotalFee) * 0.8,2),0) <= COALESCE(expenditure.cumulativeExpenditure,0) then coalesce(round(sum(p.expectedTotalFee) * 0.8,2),0) - coalesce(sum(i.issueAmount),0)"
+ " when coalesce(round(sum(p.expectedTotalFee) * 0.8,2),0) > COALESCE(expenditure.cumulativeExpenditure,0) and COALESCE(expenditure.cumulativeExpenditure,0) < coalesce(sum(i.issueAmount),0) then 0"
+ " when coalesce(round(sum(p.expectedTotalFee) * 0.8,2),0) > COALESCE(expenditure.cumulativeExpenditure,0) and COALESCE(expenditure.cumulativeExpenditure,0) >= coalesce(sum(i.issueAmount),0) then COALESCE(expenditure.cumulativeExpenditure,0) - coalesce(sum(i.issueAmount),0)"
+ " when p.totalFee - sum(i.issueAmount) >= 0 then coalesce(round(p.totalFee - sum(i.issueAmount),2),0)"
+ " when p.totalFee - sum(i.issueAmount) < 0 then 0"
+ " when p.totalFee - sum(i.issueAmount) is null then 0"
+ " end as totalUninvoiced" + " end as totalUninvoiced"
+ " from team t" + " from team t"
+ " left join project p on t.teamLead = p.teamLead"
+ " left join customer c on p.customerId = c.id"
+ " left join (" + " left join ("
+ " select" + " select"
+ " t3.id as tid," + " t3.id as tid,"
+ " c3.id as cid," + " c3.id as cid,"
+ " c3.code as customerCode,"
+ " c3.name as customerName,"
+ " sum(i3.issueAmount) as issueAmount," + " sum(i3.issueAmount) as issueAmount,"
+ " sum(i3.paidAmount) as paidAmount" + " sum(i3.paidAmount) as paidAmount"
+ " from team t3" + " from team t3"
@@ -483,15 +570,34 @@ open class DashboardService(
+ " left join invoice i3 on p3.code = i3.projectCode" + " left join invoice i3 on p3.code = i3.projectCode"
+ " where t3.deleted = 0" + " where t3.deleted = 0"
+ " and p3.status = 'On-going'" + " and p3.status = 'On-going'"

)

if (args != null) {
if (args.containsKey("teamId"))
)
if (args != null) {
if (args.containsKey("teamId"))
sql.append(" AND t3.id = :teamId"); sql.append(" AND t3.id = :teamId");
}
sql.append( " group by c3.id, t3.id"
+ " ) as i on i.cid = c.id and i.tid = t.id"
}
sql.append(
" group by c3.id, t3.id, customerCode, customerName"
+ " ) as i on i.tid = t.id"
+ " left join ("
+ " select"
+ " t.id as tid,"
+ " c.id as cid,"
+ " count(p.id) as projectNo,"
+ " sum(p.expectedTotalFee) as totalFee,"
+ " round(sum(p.expectedTotalFee) * 0.8,2) as totalBudget"
+ " from team t"
+ " left join project p on t.teamLead = p.teamLead"
+ " left join customer c on p.customerId = c.id"
+ " where t.deleted = 0"
+ " and p.status = 'On-going'"
)
if (args != null) {
if (args.containsKey("teamId"))
sql.append(" AND t.id = :teamId");
}
sql.append(
" group by t.id, c.id"
+ " ) as p on p.tid = t.id and p.cid = i.cid"
+ " left join (" + " left join ("
+ " select" + " select"
+ " r.teamId as teamId," + " r.teamId as teamId,"
@@ -517,15 +623,15 @@ open class DashboardService(
+ " group by s2.hourlyRate,t1.id,c2.id" + " group by s2.hourlyRate,t1.id,c2.id"
+ " ) as r" + " ) as r"
+ " group by r.teamId, r.customerId" + " group by r.teamId, r.customerId"
+ " ) as expenditure on expenditure.teamId = t.id and expenditure.customerId = c.id"
+ " ) as expenditure on expenditure.teamId = t.id and expenditure.customerId = i.cid"
+ " where t.deleted = 0" + " where t.deleted = 0"
+ " and p.status = 'On-going'")
if (args != null) {
if (args.containsKey("teamId"))
+ " and i.cid is not null"
)
if (args != null) {
if (args.containsKey("teamId"))
sql.append(" AND t.id = :teamId"); sql.append(" AND t.id = :teamId");
}
sql.append(" group by t.id, c.id, c.code, c.name")
}
sql.append( " group by t.id, i.cid, i.customerCode, i.customerName, p.projectNo, p.totalFee, p.totalBudget")


return jdbcDao.queryForList(sql.toString(), args) return jdbcDao.queryForList(sql.toString(), args)
} }
@@ -547,10 +653,15 @@ open class DashboardService(
+ " when coalesce(round(sum(i.issueAmount) / (COALESCE(expenditure.cumulativeExpenditure,0)),2),0) < 1 then 'Negative'" + " when coalesce(round(sum(i.issueAmount) / (COALESCE(expenditure.cumulativeExpenditure,0)),2),0) < 1 then 'Negative'"
+ " end as cashFlowStatus," + " end as cashFlowStatus,"
+ " coalesce(round(sum(i.issueAmount) / (COALESCE(expenditure.cumulativeExpenditure,0)),2),0) as cpi," + " coalesce(round(sum(i.issueAmount) / (COALESCE(expenditure.cumulativeExpenditure,0)),2),0) as cpi,"
// + " case"
// + " when coalesce(round(sum(p.expectedTotalFee) * 0.8,2),0) <= COALESCE(expenditure.cumulativeExpenditure,0) then coalesce(round(sum(p.expectedTotalFee) * 0.8,2),0) - coalesce(sum(i.issueAmount),0)"
// + " when coalesce(round(sum(p.expectedTotalFee) * 0.8,2),0) > COALESCE(expenditure.cumulativeExpenditure,0) and COALESCE(expenditure.cumulativeExpenditure,0) < coalesce(sum(i.issueAmount),0) then 0"
// + " when coalesce(round(sum(p.expectedTotalFee) * 0.8,2),0) > COALESCE(expenditure.cumulativeExpenditure,0) and COALESCE(expenditure.cumulativeExpenditure,0) >= coalesce(sum(i.issueAmount),0) then COALESCE(expenditure.cumulativeExpenditure,0) - coalesce(sum(i.issueAmount),0)"
// + " end as totalUninvoiced"
+ " case" + " case"
+ " when coalesce(round(sum(p.expectedTotalFee) * 0.8,2),0) <= COALESCE(expenditure.cumulativeExpenditure,0) then coalesce(round(sum(p.expectedTotalFee) * 0.8,2),0) - coalesce(sum(i.issueAmount),0)"
+ " when coalesce(round(sum(p.expectedTotalFee) * 0.8,2),0) > COALESCE(expenditure.cumulativeExpenditure,0) and COALESCE(expenditure.cumulativeExpenditure,0) < coalesce(sum(i.issueAmount),0) then 0"
+ " when coalesce(round(sum(p.expectedTotalFee) * 0.8,2),0) > COALESCE(expenditure.cumulativeExpenditure,0) and COALESCE(expenditure.cumulativeExpenditure,0) >= coalesce(sum(i.issueAmount),0) then COALESCE(expenditure.cumulativeExpenditure,0) - coalesce(sum(i.issueAmount),0)"
+ " when p.expectedTotalFee - sum(i.issueAmount) >= 0 then coalesce(round(p.expectedTotalFee - sum(i.issueAmount),2),0)"
+ " when p.expectedTotalFee - sum(i.issueAmount) < 0 then 0"
+ " when p.expectedTotalFee - sum(i.issueAmount) is null then 0"
+ " end as totalUninvoiced" + " end as totalUninvoiced"
+ " from team t" + " from team t"
+ " left join project p on t.teamLead = p.teamLead" + " left join project p on t.teamLead = p.teamLead"
@@ -1346,6 +1457,7 @@ open class DashboardService(
+ " AND dates.missing_date = ts.recordDate" + " AND dates.missing_date = ts.recordDate"
+ " WHERE" + " WHERE"
+ " st.teamId = :teamId" + " st.teamId = :teamId"
+ " and st.deleted = 0"
+ " AND ts.recordDate IS NULL" + " AND ts.recordDate IS NULL"
+ " GROUP BY st.id, st.name" + " GROUP BY st.id, st.name"
+ " ORDER BY" + " ORDER BY"
@@ -1595,6 +1707,7 @@ open class DashboardService(
+ " group by p.id, p.name" + " group by p.id, p.name"
+ " ) as result on result.pid = p2.id" + " ) as result on result.pid = p2.id"
+ " where s2.id = :staffId" + " where s2.id = :staffId"
+ " and result.manhours > 0"
+ " group by p2.id, p2.name, result.manhours" + " group by p2.id, p2.name, result.manhours"
) )


@@ -1626,6 +1739,7 @@ open class DashboardService(
+ " group by p.id, p.name" + " group by p.id, p.name"
+ " ) as result on result.pid = p2.id" + " ) as result on result.pid = p2.id"
+ " where s2.id = :staffId" + " where s2.id = :staffId"
+ " and result.manhours > 0"
+ " group by p2.id, p2.name, result.manhours" + " group by p2.id, p2.name, result.manhours"
) )


@@ -1656,6 +1770,7 @@ open class DashboardService(
+ " group by p.id, p.name" + " group by p.id, p.name"
+ " ) as result on result.pid = p2.id" + " ) as result on result.pid = p2.id"
+ " where s2.id = :staffId" + " where s2.id = :staffId"
+ " and result.manhours > 0"
+ " group by p2.id, p2.name, result.manhours" + " group by p2.id, p2.name, result.manhours"
) )




Loading…
Cancel
Save