瀏覽代碼

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

tags/Baseline_30082024_BACKEND_UAT
MSI\2Fi 1 年之前
父節點
當前提交
86aebc231a
共有 9 個文件被更改,包括 210 次插入50 次删除
  1. +2
    -0
      src/main/java/com/ffii/tsms/modules/common/SettingNames.java
  2. +76
    -26
      src/main/java/com/ffii/tsms/modules/data/service/DashboardService.kt
  3. +3
    -0
      src/main/java/com/ffii/tsms/modules/project/entity/projections/ProjectSearchInfo.kt
  4. +1
    -0
      src/main/java/com/ffii/tsms/modules/project/service/InvoiceService.kt
  5. +9
    -9
      src/main/java/com/ffii/tsms/modules/report/service/ReportService.kt
  6. +2
    -2
      src/main/java/com/ffii/tsms/modules/timesheet/service/LeaveService.kt
  7. +76
    -7
      src/main/java/com/ffii/tsms/modules/timesheet/service/TimesheetsService.kt
  8. +35
    -6
      src/main/java/com/ffii/tsms/modules/timesheet/web/TimesheetsController.kt
  9. +6
    -0
      src/main/resources/db/changelog/changes/20240730_01_cyril/01_update_task.sql

+ 2
- 0
src/main/java/com/ffii/tsms/modules/common/SettingNames.java 查看文件

@@ -38,6 +38,8 @@ public abstract class SettingNames {

public static final String MAIL_SMTP_RECIPIENTS = "MAIL.smtp.recipients";

public static final String MAIL_SMTP_AUTH = "MAIL.smtp.auth";

public static final String JS_VERSION = "JS.version";

public static final String REPORT_DAILYMAINT_RECIPIENTS_MECH = "REPORT.dailyMaint.recipients.mech";


+ 76
- 26
src/main/java/com/ffii/tsms/modules/data/service/DashboardService.kt 查看文件

@@ -94,7 +94,8 @@ open class DashboardService(
+ " p.name as projectName,"
+ " te.code as team,"
+ " s.name as teamLead,"
+ " GROUP_CONCAT(DISTINCT tg.name ORDER BY tg.name) as expectedStage,"
// + " GROUP_CONCAT(DISTINCT tg.name ORDER BY tg.name) as expectedStage,"
+ " taskGroup.expectedStage,"
+ " p.totalManhour as budgetedManhour,"
+ " sum(t.normalConsumed) + sum(t.otConsumed) as spentManhour,"
+ " p.totalManhour - sum(t.normalConsumed) - sum(t.otConsumed) as remainedManhour,"
@@ -109,8 +110,17 @@ open class DashboardService(
+ " left join timesheet t on pt.id = t.projectTaskId"
+ " left join team te on p.teamLead = te.teamLead"
+ " left join staff s on te.teamLead = s.id"
+ " left join milestone m on p.id = m.projectId and curdate() >= m.startDate and curdate() <= m.endDate"
// + " left join milestone m on p.id = m.projectId and curdate() >= m.startDate and curdate() <= m.endDate"
// + " left join task_group tg on m.taskGroupId = tg.id"
+ " left join ("
+ " select "
+ " m.projectId,"
+ " GROUP_CONCAT(DISTINCT tg.name ORDER BY tg.name) as expectedStage"
+ " from milestone m"
+ " left join task_group tg on m.taskGroupId = tg.id"
+ " where curdate() >= m.startDate and curdate() <= m.endDate"
+ " group by m.projectId"
+ " ) taskGroup on p.id = taskGroup.projectId"
+ " left join ("
+ " SELECT pid, MIN(comingPaymentMilestone) AS comingPaymentMilestone"
+ " FROM ("
@@ -139,7 +149,7 @@ open class DashboardService(
}
}

sql.append(" group by p.id, p.code, p.name, te.code, s.name, p.totalManhour, milestonePayment.comingPaymentMilestone")
sql.append(" group by p.id, p.code, p.name, te.code, s.name, p.totalManhour, milestonePayment.comingPaymentMilestone, taskGroup.expectedStage")

if (args["tableSorting"] == "ProjectName") {
sql.append(" ORDER BY p.name ASC")
@@ -161,7 +171,8 @@ open class DashboardService(
+ " p.name as projectName,"
+ " te.code as team,"
+ " s.name as teamLead,"
+ " GROUP_CONCAT(DISTINCT tg.name ORDER BY tg.name) as expectedStage,"
// + " GROUP_CONCAT(DISTINCT tg.name ORDER BY tg.name) as expectedStage,"
+ " taskGroup.expectedStage,"
+ " p.totalManhour as budgetedManhour,"
+ " COALESCE (sum(t.normalConsumed) + sum(t.otConsumed),0) as spentManhour,"
+ " COALESCE (p.totalManhour - sum(t.normalConsumed) - sum(t.otConsumed),0) as remainedManhour,"
@@ -176,8 +187,17 @@ open class DashboardService(
+ " left join timesheet t on pt.id = t.projectTaskId"
+ " left join team te on p.teamLead = te.teamLead"
+ " left join staff s on te.teamLead = s.id"
+ " left join milestone m on p.id = m.projectId and curdate() >= m.startDate and curdate() <= m.endDate"
// + " left join milestone m on p.id = m.projectId and curdate() >= m.startDate and curdate() <= m.endDate"
// + " left join task_group tg on m.taskGroupId = tg.id"
+ " left join ("
+ " select "
+ " m.projectId,"
+ " GROUP_CONCAT(DISTINCT tg.name ORDER BY tg.name) as expectedStage"
+ " from milestone m"
+ " left join task_group tg on m.taskGroupId = tg.id"
+ " where curdate() >= m.startDate and curdate() <= m.endDate"
+ " group by m.projectId"
+ " ) taskGroup on p.id = taskGroup.projectId"
+ " left join ("
+ " SELECT pid, MIN(comingPaymentMilestone) AS comingPaymentMilestone"
+ " FROM ("
@@ -196,7 +216,7 @@ open class DashboardService(
+ " and isNull(p.customerSubsidiaryId)"
+ " and p.status not in (\"Pending to Start\",\"Completed\",\"Deleted\")"
// + " and (tg.name != '5. Miscellaneous' or tg.name is null)"
+ " group by p.id, p.code, p.name, te.code, s.name, p.totalManhour, milestonePayment.comingPaymentMilestone"
+ " group by p.id, p.code, p.name, te.code, s.name, p.totalManhour, milestonePayment.comingPaymentMilestone, taskGroup.expectedStage"
)
if (args["tableSorting"] == "ProjectName") {
sql.append(" ORDER BY p.name ASC")
@@ -325,7 +345,8 @@ open class DashboardService(
+ " p.name as projectName,"
+ " te.code as team,"
+ " s.name as teamLead,"
+ " GROUP_CONCAT(DISTINCT tg.name ORDER BY tg.name) as expectedStage,"
// + " GROUP_CONCAT(DISTINCT tg.name ORDER BY tg.name) as expectedStage,"
+ " taskGroup.expectedStage,"
+ " p.totalManhour as budgetedManhour,"
+ " COALESCE (sum(t.normalConsumed) + sum(t.otConsumed),0) as spentManhour,"
+ " COALESCE (p.totalManhour - sum(t.normalConsumed) - sum(t.otConsumed),0) as remainedManhour,"
@@ -340,8 +361,17 @@ open class DashboardService(
+ " left join timesheet t on pt.id = t.projectTaskId"
+ " left join team te on p.teamLead = te.teamLead"
+ " left join staff s on te.teamLead = s.id"
+ " left join milestone m on p.id = m.projectId and curdate() >= m.startDate and curdate() <= m.endDate"
// + " left join milestone m on p.id = m.projectId and curdate() >= m.startDate and curdate() <= m.endDate"
// + " left join task_group tg on m.taskGroupId = tg.id"
+ " left join ("
+ " select "
+ " m.projectId,"
+ " GROUP_CONCAT(DISTINCT tg.name ORDER BY tg.name) as expectedStage"
+ " from milestone m"
+ " left join task_group tg on m.taskGroupId = tg.id"
+ " where curdate() >= m.startDate and curdate() <= m.endDate"
+ " group by m.projectId"
+ " ) taskGroup on p.id = taskGroup.projectId"
+ " left join ("
+ " SELECT pid, MIN(comingPaymentMilestone) AS comingPaymentMilestone"
+ " FROM ("
@@ -359,7 +389,7 @@ open class DashboardService(
+ " where p.teamLead = :teamLeadId"
+ " and p.status not in (\"Pending to Start\",\"Completed\",\"Deleted\")"
// + " and (tg.name != '5. Miscellaneous' or tg.name is null)"
+ " group by p.id, p.code, p.name, te.code, s.name, p.totalManhour, milestonePayment.comingPaymentMilestone"
+ " group by p.id, p.code, p.name, te.code, s.name, p.totalManhour, milestonePayment.comingPaymentMilestone, taskGroup.expectedStage"
)
if (args["tableSorting"] == "ProjectName") {
sql.append(" ORDER BY p.name ASC")
@@ -409,7 +439,8 @@ open class DashboardService(
+ " p.name as projectName,"
+ " te.code as team,"
+ " s.name as teamLead,"
+ " GROUP_CONCAT(DISTINCT tg.name ORDER BY tg.name) as expectedStage,"
// + " GROUP_CONCAT(DISTINCT tg.name ORDER BY tg.name) as expectedStage,"
+ " taskGroup.expectedStage,"
+ " p.totalManhour as budgetedManhour,"
+ " COALESCE (sum(t.normalConsumed) + sum(t.otConsumed),0) as spentManhour,"
+ " COALESCE (p.totalManhour - sum(t.normalConsumed) - sum(t.otConsumed),0) as remainedManhour,"
@@ -424,8 +455,17 @@ open class DashboardService(
+ " left join timesheet t on pt.id = t.projectTaskId"
+ " left join team te on p.teamLead = te.teamLead"
+ " left join staff s on te.teamLead = s.id"
+ " left join milestone m on p.id = m.projectId and curdate() >= m.startDate and curdate() <= m.endDate"
// + " left join milestone m on p.id = m.projectId and curdate() >= m.startDate and curdate() <= m.endDate"
// + " left join task_group tg on m.taskGroupId = tg.id"
+ " left join ("
+ " select "
+ " m.projectId,"
+ " GROUP_CONCAT(DISTINCT tg.name ORDER BY tg.name) as expectedStage"
+ " from milestone m"
+ " left join task_group tg on m.taskGroupId = tg.id"
+ " where curdate() >= m.startDate and curdate() <= m.endDate"
+ " group by m.projectId"
+ " ) taskGroup on p.id = taskGroup.projectId"
+ " left join ("
+ " SELECT pid, MIN(comingPaymentMilestone) AS comingPaymentMilestone"
+ " FROM ("
@@ -442,7 +482,7 @@ open class DashboardService(
+ " ) milestonePayment on milestonePayment.pid = p.id"
+ " where p.teamLead in (:teamIds)"
+ " and p.status not in ('Pending to Start','Completed','Deleted')"
+ " group by p.id, p.code, p.name, te.code, s.name, p.totalManhour, milestonePayment.comingPaymentMilestone"
+ " group by p.id, p.code, p.name, te.code, s.name, p.totalManhour, milestonePayment.comingPaymentMilestone, taskGroup.expectedStage"
)

// if (args["tableSorting"] == "ProjectName") {
@@ -473,7 +513,8 @@ open class DashboardService(
+ " p.name as projectName,"
+ " te.code as team,"
+ " s.name as teamLead,"
+ " GROUP_CONCAT(DISTINCT tg.name ORDER BY tg.name) as expectedStage,"
// + " GROUP_CONCAT(DISTINCT tg.name ORDER BY tg.name) as expectedStage,"
+ " taskGroup.expectedStage,"
+ " p.totalManhour as budgetedManhour,"
+ " COALESCE (sum(t.normalConsumed) + sum(t.otConsumed),0) as spentManhour,"
+ " COALESCE (p.totalManhour - sum(t.normalConsumed) - sum(t.otConsumed),0) as remainedManhour,"
@@ -488,8 +529,17 @@ open class DashboardService(
+ " left join timesheet t on pt.id = t.projectTaskId"
+ " left join team te on p.teamLead = te.teamLead"
+ " left join staff s on te.teamLead = s.id"
+ " left join milestone m on p.id = m.projectId and curdate() >= m.startDate and curdate() <= m.endDate"
// + " left join milestone m on p.id = m.projectId and curdate() >= m.startDate and curdate() <= m.endDate"
// + " left join task_group tg on m.taskGroupId = tg.id"
+ " left join ("
+ " select "
+ " m.projectId,"
+ " GROUP_CONCAT(DISTINCT tg.name ORDER BY tg.name) as expectedStage"
+ " from milestone m"
+ " left join task_group tg on m.taskGroupId = tg.id"
+ " where curdate() >= m.startDate and curdate() <= m.endDate"
+ " group by m.projectId"
+ " ) taskGroup on p.id = taskGroup.projectId"
+ " left join ("
+ " SELECT pid, MIN(comingPaymentMilestone) AS comingPaymentMilestone"
+ " FROM ("
@@ -506,7 +556,7 @@ open class DashboardService(
+ " ) milestonePayment on milestonePayment.pid = p.id"
+ " where p.teamLead in (:teamIds)"
+ " and p.status not in ('Pending to Start','Completed','Deleted')"
+ " group by p.id, p.code, p.name, te.code, s.name, p.totalManhour, milestonePayment.comingPaymentMilestone"
+ " group by p.id, p.code, p.name, te.code, s.name, p.totalManhour, milestonePayment.comingPaymentMilestone, taskGroup.expectedStage"
+ " ORDER BY te.code ASC"
)
return jdbcDao.queryForList(sql.toString(), args)
@@ -540,7 +590,7 @@ open class DashboardService(
+ " t2.id as tid,"
+ " count(p.id) as projectNo,"
+ " sum(p.expectedTotalFee) as totalFee,"
+ " sum(round((p.expectedTotalFee - p.subContractFee) * 0.8,2)) as totalBudget"
+ " sum(round((p.expectedTotalFee - ifnull(p.subContractFee, 0)) * 0.8,2)) as totalBudget"
+ " from team t2"
+ " left join project p on t2.teamLead = p.teamLead"
+ " where p.status = 'On-going'"
@@ -601,7 +651,7 @@ open class DashboardService(
+ " 'All Team' as teamName,"
+ " count(p.code) as projectNo,"
+ " sum(p.expectedTotalFee) as totalFee,"
+ " round((sum(p.expectedTotalFee) - sum(p.subContractFee)) * 0.8,2) as totalBudget,"
+ " round((sum(p.expectedTotalFee) - sum(ifnull(p.subContractFee, 0))) * 0.8,2) as totalBudget,"
+ " round(expenditure.cumulativeExpenditure,2) as cumulativeExpenditure,"
+ " sum(i.issueAmount) as totalInvoiced,"
+ " sum(p.expectedTotalFee) - sum(i.issueAmount) as unInvoiced,"
@@ -795,7 +845,7 @@ open class DashboardService(
+ " c.id as cid,"
+ " count(p.id) as projectNo,"
+ " sum(p.expectedTotalFee) as totalFee,"
+ " round((sum(p.expectedTotalFee) - sum(p.subContractFee)) * 0.8,2) as totalBudget"
+ " round((sum(p.expectedTotalFee) - sum(ifnull(p.subContractFee, 0))) * 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"
@@ -855,7 +905,7 @@ open class DashboardService(
+ " c.name as customerName,"
+ " s3.name as subsidiaryName,"
+ " p.expectedTotalFee as totalFee,"
+ " round((p.expectedTotalFee - p.subContractFee) * 0.8,2) as totalBudget,"
+ " round((p.expectedTotalFee - ifnull(p.subContractFee, 0)) * 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,"
@@ -1060,13 +1110,13 @@ open class DashboardService(
fun CashFlowReceivableAndExpenditure(args: Map<String, Any>): List<Map<String, Any>> {
val sql = StringBuilder("select"
+ " coalesce (round(sum(i.paidAmount)/sum(i.issueAmount)*100,0),0) as receivedPercentage,"
+ " coalesce (round(expenditure.expenditure/((sum(p.expectedTotalFee) - sum(p.subContractFee))*0.8)*100,0),0) as expenditurePercentage,"
+ " coalesce (round(expenditure.expenditure/((sum(p.expectedTotalFee) - sum(ifnull(p.subContractFee, 0)))*0.8)*100,0),0) as expenditurePercentage,"
+ " coalesce (sum(i.issueAmount),0) as totalInvoiced,"
+ " coalesce (sum(i.paidAmount),0) as totalReceived,"
+ " coalesce (sum(i.issueAmount) - sum(i.paidAmount),0) as receivable,"
+ " coalesce (round((sum(p.expectedTotalFee) - sum(p.subContractFee))*0.8,2),0) as totalBudget,"
+ " coalesce (round((sum(p.expectedTotalFee) - sum(ifnull(p.subContractFee, 0)))*0.8,2),0) as totalBudget,"
+ " coalesce (expenditure.expenditure,0) as totalExpenditure,"
+ " coalesce ((sum(p.expectedTotalFee) - sum(p.subContractFee))*0.8 - expenditure.expenditure,0) as expenditureReceivable,"
+ " coalesce ((sum(p.expectedTotalFee) - sum(ifnull(p.subContractFee, 0)))*0.8 - expenditure.expenditure,0) as expenditureReceivable,"
+ " sum(p.expectedTotalFee) as totalProjectFee,"
+ " coalesce (round(sum(i.issueAmount)/sum(p.expectedTotalFee)*100,0),0) as invoicedPercentage"
+ " from project p"
@@ -1164,8 +1214,8 @@ open class DashboardService(
+ " else PERIOD_DIFF(DATE_FORMAT(p.planEnd, '%Y%m'), DATE_FORMAT(p.planStart, '%Y%m'))+1"
+ " end AS 'Duration',"
+ " PERIOD_DIFF(DATE_FORMAT(p.planEnd, '%Y%m'), DATE_FORMAT(p.planStart, '%Y%m'))+1 as projectDuration,"
+ " (p.expectedTotalFee - p.subContractFee)*0.8 as totalBudget,"
+ " ((p.expectedTotalFee - p.subContractFee)*0.8) / (PERIOD_DIFF(DATE_FORMAT(p.planEnd, '%Y%m'), DATE_FORMAT(p.planStart, '%Y%m'))+1) as aniticipateExpenditure,"
+ " (p.expectedTotalFee - ifnull(p.subContractFee, 0))*0.8 as totalBudget,"
+ " ((p.expectedTotalFee - ifnull(p.subContractFee, 0))*0.8) / (PERIOD_DIFF(DATE_FORMAT(p.planEnd, '%Y%m'), DATE_FORMAT(p.planStart, '%Y%m'))+1) as aniticipateExpenditure,"
+ " ROUND(p.totalManhour / (PERIOD_DIFF(DATE_FORMAT(p.planEnd, '%Y%m'), DATE_FORMAT(p.planStart, '%Y%m'))+1), 2) AS 'AverageManhours',"
+ " p.teamLead, p.totalManhour"
+ " FROM project p"
@@ -1412,9 +1462,9 @@ open class DashboardService(
"select"
+ " concat(p.code,'-',p.name) as projectCodeAndName,"
+ " p.expectedTotalFee as totalFee,"
+ " (p.expectedTotalFee - p.subContractFee) * 0.8 as totalBudget,"
+ " (p.expectedTotalFee - ifnull(p.subContractFee, 0)) * 0.8 as totalBudget,"
+ " coalesce (expenditure.expenditure,0) as expenditure,"
+ " ((p.expectedTotalFee - p.subContractFee) * 0.8) - coalesce (expenditure.expenditure,0) as remainingBudget,"
+ " ((p.expectedTotalFee - ifnull(p.subContractFee, 0)) * 0.8) - coalesce (expenditure.expenditure,0) as remainingBudget,"
+ " case"
+ " when p.expectedTotalFee - expenditure.expenditure >= 0 then 'Within Budget'"
+ " when p.expectedTotalFee - expenditure.expenditure < 0 then 'Overconsumption'"


+ 3
- 0
src/main/java/com/ffii/tsms/modules/project/entity/projections/ProjectSearchInfo.kt 查看文件

@@ -20,6 +20,9 @@ interface ProjectSearchInfo {
@get:Value("#{target.teamLead.team.code}")
val team: String?

@get:Value("#{target.teamLead.team.code} - #{target.teamLead.team.name}")
val teamCodeName: String?

@get:Value("#{target.teamLead.team.id}")
val teamId: Long?



+ 1
- 0
src/main/java/com/ffii/tsms/modules/project/service/InvoiceService.kt 查看文件

@@ -336,6 +336,7 @@ open class InvoiceService(
val sql = StringBuilder(
"select i.id, i.invoiceNo, i.projectCode, " +
"p.name as projectName, t.code as team, i.invoiceDate, " +
" concat(t.code, ' - ', t.name) as teamCodeName, " +
"i.receiptDate, i.issueAmount , i.paidAmount " +
"from invoice i " +
"left join project p on i.projectCode = p.code " +


+ 9
- 9
src/main/java/com/ffii/tsms/modules/report/service/ReportService.kt 查看文件

@@ -2087,15 +2087,15 @@ open class ReportService(
+ " 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.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) >= :lowerLimit and (sum(t.consumedBudget) / p.expectedTotalFee) <= 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' "
+ " when (sum(t.consumedBudget) / p.expectedTotalFee) >= 1 "
+ " 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 "
@@ -2123,14 +2123,14 @@ open class ReportService(
if (args.containsKey("status"))
statusFilter = when (args.get("status")) {
"Potential Overconsumption" -> " having " +
" (sum(t.consumedBudget) / p.expectedTotalFee) >= :lowerLimit " +
" and (sum(t.consumedBudget) / p.expectedTotalFee) <= 1 " +
" 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 "

"All" -> " having " +
" (sum(t.consumedBudget) / p.expectedTotalFee) >= :lowerLimit " +
" or (sum(t.consumedBudget) / p.expectedTotalFee) >= :lowerLimit "
" sum(t.consumedBudget) / ((p.expectedTotalFee - ifnull(p.subContractFee, 0)) * 0.8) >= :lowerLimit " +
" or (sum(t.normalConsumed + COALESCE(t.otConsumed, 0)) / COALESCE(p.totalManhour, 0)) >= :lowerLimit "

else -> ""
}


+ 2
- 2
src/main/java/com/ffii/tsms/modules/timesheet/service/LeaveService.kt 查看文件

@@ -89,9 +89,9 @@ open class LeaveService(
}

open fun deleteMemberLeaveEntry(staffId: Long, entryId: Long): Map<String, List<LeaveEntry>> {
val currentStaff = staffsService.currentStaff() ?: throw BadRequestException()
// val currentStaff = staffsService.currentStaff() ?: throw BadRequestException()
// Make sure current staff is a team lead
teamService.getMyTeamForStaff(currentStaff) ?: throw BadRequestException()
// teamService.getMyTeamForStaff(currentStaff) ?: throw BadRequestException()

val memberStaff = staffsService.getStaff(staffId)



+ 76
- 7
src/main/java/com/ffii/tsms/modules/timesheet/service/TimesheetsService.kt 查看文件

@@ -69,11 +69,11 @@ open class TimesheetsService(
entry: TimeEntry,
recordDate: LocalDate?
): Map<String, List<TimeEntry>> {
val authorities = staffsService.currentAuthorities() ?: throw BadRequestException()
if (!authorities.stream().anyMatch { it.authority.equals("MAINTAIN_TIMESHEET") }) {
throw BadRequestException()
}
// val authorities = staffsService.currentAuthorities() ?: throw BadRequestException()
//
// if (!authorities.stream().anyMatch { it.authority.equals("MAINTAIN_TIMESHEET") }) {
// throw BadRequestException()
// }
// val currentStaff = staffsService.currentStaff() ?: throw BadRequestException()
// // Make sure current staff is a team lead
// teamService.getMyTeamForStaff(currentStaff) ?: throw BadRequestException()
@@ -100,9 +100,9 @@ open class TimesheetsService(
}

open fun deleteMemberTimeEntry(staffId: Long, entryId: Long): Map<String, List<TimeEntry>> {
val currentStaff = staffsService.currentStaff() ?: throw BadRequestException()
// val currentStaff = staffsService.currentStaff() ?: throw BadRequestException()
// Make sure current staff is a team lead
teamService.getMyTeamForStaff(currentStaff) ?: throw BadRequestException()
// teamService.getMyTeamForStaff(currentStaff) ?: throw BadRequestException()

val memberStaff = staffsService.getStaff(staffId)

@@ -249,6 +249,75 @@ open class TimesheetsService(
return if (sheet.lastRowNum > 0) "Import Excel success btw " + notExistProjectList.distinct().joinToString(", ") else "Import Excel failure"
}

@Transactional(rollbackFor = [Exception::class])
open fun importOSFile(workbook: Workbook?): String {
val logger = LogFactory.getLog(javaClass)

if (workbook == null) {
return "No Excel import" // if workbook is null
}

val notExistStaffIdList = mutableListOf<String>()
val sheet: Sheet = workbook.getSheetAt(0)

logger.info("---------Start Import OS Timesheets-------")
val timesheetList = mutableListOf<Timesheet>().toMutableList();
for (i in 1..<sheet.lastRowNum) {
val row = sheet.getRow(i)

if (row.getCell(1) != null && !row.getCell(
1
).stringCellValue.isNullOrBlank()
) {
logger.info("row :$i | lastCellNum" + row.lastCellNum)

// process staff
logger.info("---------staff-------")
var staff = staffRepository.findByStaffId(row.getCell(1).stringCellValue).getOrNull()
if (staff == null) {
notExistStaffIdList += row.getCell(1).stringCellValue
staff = staffRepository.findByStaffId("B000").orElseThrow()
}

// process project
logger.info("---------project-------")

val project = null

// process project task
logger.info("---------project task-------")
val projectTask = null

val nonBillableTask = taskRepository.findById(44).orElseThrow()
// process record date
logger.info("---------record date-------")
val formatter = DateTimeFormatter.ofPattern("dd-MMM-yyyy")
val recordDate = LocalDate.parse(row.getCell(7).toString(), formatter);

// normal hour + ot hour
logger.info("---------normal hour + ot hour-------")
val hours: Double = row.getCell(8).numericCellValue
val normalHours = if (hours > 8.0) 8.0 else hours
val otHours = if (hours > 8.0) hours - 8.0 else 0.0

timesheetList += Timesheet().apply {
this.staff = staff
this.recordDate = recordDate
this.normalConsumed = normalHours
this.otConsumed = otHours
this.projectTask = projectTask
this.project = project
this.nonBillableTask = nonBillableTask
}
}
}

timesheetRepository.saveAll(timesheetList)
logger.info("---------end-------")

return if (sheet.lastRowNum > 0) "Import Excel success btw staffId:" + notExistStaffIdList.distinct().joinToString(", ") else "Import Excel failure"
}

@Transactional(rollbackFor = [Exception::class])
open fun rearrangeTimesheets(): String {
val logger = LogFactory.getLog(javaClass)


+ 35
- 6
src/main/java/com/ffii/tsms/modules/timesheet/web/TimesheetsController.kt 查看文件

@@ -3,6 +3,7 @@ package com.ffii.tsms.modules.timesheet.web
import com.ffii.core.exception.BadRequestException
import com.ffii.tsms.modules.timesheet.entity.LeaveType
import com.ffii.tsms.modules.timesheet.service.LeaveService
//import com.ffii.tsms.modules.timesheet.service.MailReminderService
import com.ffii.tsms.modules.timesheet.service.TimesheetsService
import com.ffii.tsms.modules.timesheet.web.models.*
import jakarta.servlet.http.HttpServletRequest
@@ -11,18 +12,20 @@ import org.apache.poi.ss.usermodel.Workbook
import org.apache.poi.xssf.usermodel.XSSFWorkbook
import org.springframework.http.ResponseEntity
import org.springframework.web.bind.ServletRequestBindingException
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.RequestBody
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController
import org.springframework.web.bind.annotation.*
import org.springframework.web.multipart.MultipartHttpServletRequest
import java.io.UnsupportedEncodingException
import java.text.ParseException
import java.time.LocalDate
import java.time.format.DateTimeFormatter

@RestController
@RequestMapping("/timesheets")
class TimesheetsController(private val timesheetsService: TimesheetsService, private val leaveService: LeaveService) {
class TimesheetsController(
private val timesheetsService: TimesheetsService,
private val leaveService: LeaveService,
// private val mailReminderService: MailReminderService
) {
@PostMapping("/save")
fun newTimesheetEntry(@Valid @RequestBody recordTimesheet: Map<String, List<TimeEntry>>): Map<String, List<TimeEntry>> {
val parsedEntries = kotlin.runCatching {
@@ -137,6 +140,22 @@ class TimesheetsController(private val timesheetsService: TimesheetsService, pri
return ResponseEntity.ok(timesheetsService.importFile(workbook))
}

@PostMapping("/import-OS")
@Throws(ServletRequestBindingException::class)
fun importOSFile(request: HttpServletRequest): ResponseEntity<*> {
var workbook: Workbook? = null

try {
val multipartFile = (request as MultipartHttpServletRequest).getFile("multipartFileList")
workbook = XSSFWorkbook(multipartFile?.inputStream)
} catch (e: Exception) {
println("Excel Wrong")
println(e)
}

return ResponseEntity.ok(timesheetsService.importOSFile(workbook))
}

@PostMapping("/import-leave")
@Throws(ServletRequestBindingException::class)
fun importLeaveFile(request: HttpServletRequest): ResponseEntity<*> {
@@ -159,4 +178,14 @@ class TimesheetsController(private val timesheetsService: TimesheetsService, pri

return ResponseEntity.ok(timesheetsService.rearrangeTimesheets())
}

// @GetMapping("/testReminder")
// @Throws(
// UnsupportedEncodingException::class,
// ParseException::class,
// ServletRequestBindingException::class
// )
// fun testReminder(request: HttpServletRequest) {
// mailReminderService.sendTimesheetReminder()
// }
}

+ 6
- 0
src/main/resources/db/changelog/changes/20240730_01_cyril/01_update_task.sql 查看文件

@@ -0,0 +1,6 @@
-- liquibase formatted sql
-- changeset cyril:task

UPDATE task
SET name='5.8 Internal Training'
WHERE id=41;

Loading…
取消
儲存