From bb18fd66f29544ba9aa6ae9320cac67df94e6291 Mon Sep 17 00:00:00 2001 From: "cyril.tsui" Date: Thu, 7 Nov 2024 18:08:20 +0800 Subject: [PATCH] Update Import timesheet function --- .../entity/FormerStaffTimesheetHistory.kt | 25 ++++ .../FormerStaffTimesheetHistoryRepository.kt | 7 + .../timesheet/service/TimesheetsService.kt | 130 +++++++++++++----- ...e_former_staff_timesheet_history_table.sql | 22 +++ 4 files changed, 150 insertions(+), 34 deletions(-) create mode 100644 src/main/java/com/ffii/tsms/modules/timesheet/entity/FormerStaffTimesheetHistory.kt create mode 100644 src/main/java/com/ffii/tsms/modules/timesheet/entity/FormerStaffTimesheetHistoryRepository.kt create mode 100644 src/main/resources/db/changelog/changes/20241107_01_cyril/01_create_former_staff_timesheet_history_table.sql diff --git a/src/main/java/com/ffii/tsms/modules/timesheet/entity/FormerStaffTimesheetHistory.kt b/src/main/java/com/ffii/tsms/modules/timesheet/entity/FormerStaffTimesheetHistory.kt new file mode 100644 index 0000000..1289f45 --- /dev/null +++ b/src/main/java/com/ffii/tsms/modules/timesheet/entity/FormerStaffTimesheetHistory.kt @@ -0,0 +1,25 @@ +package com.ffii.tsms.modules.timesheet.entity + +import com.ffii.core.entity.BaseEntity +import jakarta.persistence.Column +import jakarta.persistence.Entity +import jakarta.persistence.JoinColumn +import jakarta.persistence.OneToOne +import jakarta.persistence.Table + +// Update when 'Timesheet' migration +@Entity +@Table(name = "former_staff_timesheet_history") +open class FormerStaffTimesheetHistory : BaseEntity() { + + @JoinColumn(name = "timesheetId") + @OneToOne + open var timesheet: Timesheet? = null + + @Column(name = "staffId") + open var staffId: String? = null + + // Master Id from other company + @Column(name = "mtsid") + open var mtsid: Int? = null +} \ No newline at end of file diff --git a/src/main/java/com/ffii/tsms/modules/timesheet/entity/FormerStaffTimesheetHistoryRepository.kt b/src/main/java/com/ffii/tsms/modules/timesheet/entity/FormerStaffTimesheetHistoryRepository.kt new file mode 100644 index 0000000..ccccc46 --- /dev/null +++ b/src/main/java/com/ffii/tsms/modules/timesheet/entity/FormerStaffTimesheetHistoryRepository.kt @@ -0,0 +1,7 @@ +package com.ffii.tsms.modules.timesheet.entity + +import com.ffii.core.support.AbstractRepository + +interface FormerStaffTimesheetHistoryRepository : AbstractRepository { + +} \ No newline at end of file diff --git a/src/main/java/com/ffii/tsms/modules/timesheet/service/TimesheetsService.kt b/src/main/java/com/ffii/tsms/modules/timesheet/service/TimesheetsService.kt index 4028151..9f51ed7 100644 --- a/src/main/java/com/ffii/tsms/modules/timesheet/service/TimesheetsService.kt +++ b/src/main/java/com/ffii/tsms/modules/timesheet/service/TimesheetsService.kt @@ -11,13 +11,11 @@ import com.ffii.tsms.modules.data.service.StaffsService import com.ffii.tsms.modules.data.service.TeamLogService import com.ffii.tsms.modules.data.service.TeamService import com.ffii.tsms.modules.project.entity.* -import com.ffii.tsms.modules.timesheet.entity.Leave -import com.ffii.tsms.modules.timesheet.entity.LeaveRepository -import com.ffii.tsms.modules.timesheet.entity.Timesheet -import com.ffii.tsms.modules.timesheet.entity.TimesheetRepository +import com.ffii.tsms.modules.timesheet.entity.* import com.ffii.tsms.modules.timesheet.web.models.TeamMemberTimeEntries import com.ffii.tsms.modules.timesheet.web.models.TimeEntry import org.apache.commons.logging.LogFactory +import org.apache.poi.ss.usermodel.CellType import org.apache.poi.ss.usermodel.Sheet import org.apache.poi.ss.usermodel.Workbook import org.springframework.stereotype.Service @@ -38,7 +36,9 @@ open class TimesheetsService( private val teamService: TeamService, private val teamLogService: TeamLogService, private val salaryEffectiveService: SalaryEffectiveService, - private val staffRepository: StaffRepository, private val leaveRepository: LeaveRepository, + private val staffRepository: StaffRepository, + private val leaveRepository: LeaveRepository, + private val formerStaffTimesheetHistoryRepository: FormerStaffTimesheetHistoryRepository, private val jdbcDao: JdbcDao, ) : AbstractBaseEntityService(jdbcDao, timesheetRepository) { @Transactional @@ -173,15 +173,19 @@ open class TimesheetsService( } } + data class FormerStaff(val staffId: String, val mtsid: Int) + @Transactional(rollbackFor = [Exception::class]) open fun importFile(workbook: Workbook?): String { val logger = LogFactory.getLog(javaClass) + val formatter = DateTimeFormatter.ofPattern("dd-MMM-yyyy") if (workbook == null) { return "No Excel import" // if workbook is null } val notExistProjectList = mutableListOf() + val formerStaffList = mutableListOf() val sheet: Sheet = workbook.getSheetAt(0) logger.info("---------Start Import Timesheets-------") @@ -189,30 +193,50 @@ open class TimesheetsService( for (i in 1.. "" + false -> StringBuilder(row.getCell(4).stringCellValue).insert(1, '-').toString() + } + val projectSubCode = when (row.getCell(5)?.cellType) { + CellType.STRING -> row.getCell(5).stringCellValue.padStart(3, '0') + CellType.NUMERIC -> row.getCell(5).numericCellValue.toString().padStart(3, '0') + else -> "" + } + val recordDate = when (row.getCell(7).toString().isBlank()) { + true -> null + false -> LocalDate.parse(row.getCell(7).toString(), formatter); + } + val hours: Double = row.getCell(8).numericCellValue + + // Start Process + if (!staffId.isNullOrBlank() && projectCode.isNotBlank()) { logger.info("row :$i | lastCellNum" + row.lastCellNum) // process staff logger.info("---------staff-------") - var staff = staffRepository.findByStaffId(row.getCell(0).stringCellValue).getOrNull() + logger.info("mtsid: $mtsid") + logger.info("Staff ID: $staffId") + var staff = staffRepository.findByStaffId(staffId).getOrNull() if (staff == null) { staff = staffRepository.findByStaffId("B000").orElseThrow() + formerStaffList += FormerStaff( + staffId = staffId, + mtsid = mtsid + ) } // process project logger.info("---------project-------") - var projectCode = StringBuilder(row.getCell(3).stringCellValue).insert(1, '-').toString() - if (row.getCell(4) != null && row.getCell(4).toString().isNotBlank()) { - val subCode = row.getCell(4).numericCellValue + if (projectSubCode.isNotBlank()) { val splitMainProjectCode = projectCode.split('-') projectCode = splitMainProjectCode[0] + '-' + String.format( "%04d", splitMainProjectCode[1].toInt() - ) + '-' + String.format("%03d", subCode.toInt()) + ) + '-' + projectSubCode } else { val splitProjectCode = projectCode.split('-') projectCode = splitProjectCode[0] + '-' + String.format("%04d", splitProjectCode[1].toInt()) @@ -229,14 +253,13 @@ open class TimesheetsService( // process record date logger.info("---------record date-------") - val formatter = DateTimeFormatter.ofPattern("dd-MMM-yyyy") - val recordDate = LocalDate.parse(row.getCell(6).toString(), formatter); + logger.info("Recode Date: $recordDate") // normal hour + ot hour logger.info("---------normal hour + ot hour-------") - val hours: Double = row.getCell(7).numericCellValue val normalHours = if (hours > 8.0) 8.0 else hours val otHours = if (hours > 8.0) hours - 8.0 else 0.0 + logger.info("Normal Hours: $normalHours | OT Hours: $otHours") if (project != null) { timesheetList += Timesheet().apply { @@ -253,22 +276,40 @@ open class TimesheetsService( } } - timesheetRepository.saveAll(timesheetList) - logger.info("---------end-------") - logger.info("Not Exist Project List: "+ notExistProjectList.distinct().joinToString(", ")) + val savedTimesheetList = timesheetRepository.saveAll(timesheetList) + logger.info("---------End Of Import Timesheet-------") + logger.info("---------Start Insert Former Staff Timesheet History-------") + + val formerStaffTimesheetHistoryList = mutableListOf() + savedTimesheetList.filter { it.staff?.staffId == "B000" }.zip(formerStaffList).forEach {(savedTimesheet: Timesheet, formerStaff: FormerStaff) -> + logger.info("Staff ID: ${formerStaff.staffId} | mtsid: ${formerStaff.mtsid}") + formerStaffTimesheetHistoryList += FormerStaffTimesheetHistory().apply { + this.timesheet = savedTimesheet + this.staffId = formerStaff.staffId + this.mtsid = formerStaff.mtsid + } + } + + formerStaffTimesheetHistoryRepository.saveAll(formerStaffTimesheetHistoryList.filter { it.mtsid != null }) + logger.info("---------End-------") + val notExistProjects = notExistProjectList.distinct().joinToString(", ") + val notExistStaffs = formerStaffTimesheetHistoryList.map { it.staffId }.distinct().joinToString(", ") + logger.info("Not Exist Project List: $notExistProjects") + logger.info("Not Exist Staff List: $notExistStaffs") - return if (sheet.lastRowNum > 0) "Import Excel success btw " + notExistProjectList.distinct().joinToString(", ") else "Import Excel failure" + return if (sheet.lastRowNum > 0) "Import Excel success btw not exist project list: $notExistProjects ; not exist staff list: $notExistStaffs" else "Import Excel failure" } @Transactional(rollbackFor = [Exception::class]) open fun importOSFile(workbook: Workbook?): String { val logger = LogFactory.getLog(javaClass) + val formatter = DateTimeFormatter.ofPattern("dd-MMM-yyyy") if (workbook == null) { return "No Excel import" // if workbook is null } - val notExistStaffIdList = mutableListOf() + val formerStaffList = mutableListOf() val sheet: Sheet = workbook.getSheetAt(0) logger.info("---------Start Import OS Timesheets-------") @@ -276,18 +317,26 @@ open class TimesheetsService( for (i in 1.. null + false -> LocalDate.parse(row.getCell(7).toString(), formatter); + } + val hours: Double = row.getCell(8).numericCellValue + + if (!staffId.isNullOrBlank()) { logger.info("row :$i | lastCellNum" + row.lastCellNum) // process staff logger.info("---------staff-------") - var staff = staffRepository.findByStaffId(row.getCell(1).stringCellValue).getOrNull() + var staff = staffRepository.findByStaffId(staffId).getOrNull() if (staff == null) { - notExistStaffIdList += row.getCell(1).stringCellValue staff = staffRepository.findByStaffId("B000").orElseThrow() + formerStaffList += FormerStaff( + staffId = staffId, + mtsid = mtsid + ) } // process project @@ -300,14 +349,12 @@ open class TimesheetsService( val projectTask = null val nonBillableTask = taskRepository.findById(42).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 @@ -323,10 +370,25 @@ open class TimesheetsService( } } - timesheetRepository.saveAll(timesheetList) - logger.info("---------end-------") + val savedTimesheetList = timesheetRepository.saveAll(timesheetList) + logger.info("---------End Of Import Timesheet-------") + logger.info("---------Start Insert Former Staff Timesheet History-------") + val formerStaffTimesheetHistoryList = mutableListOf() + savedTimesheetList.filter { it.staff?.staffId == "B000" }.zip(formerStaffList).forEach {(savedTimesheet: Timesheet, formerStaffList: FormerStaff) -> + formerStaffTimesheetHistoryList += FormerStaffTimesheetHistory().apply { + this.timesheet = savedTimesheet + this.staffId = formerStaffList.staffId + this.mtsid = formerStaffList.mtsid + } + } + + formerStaffTimesheetHistoryRepository.saveAll(formerStaffTimesheetHistoryList) + logger.info("---------End-------") + + val notExistStaffs = formerStaffTimesheetHistoryList.map { it.staffId }.distinct().joinToString(", ") + logger.info("Not Exist Staff List: $notExistStaffs") - return if (sheet.lastRowNum > 0) "Import Excel success btw staffId:" + notExistStaffIdList.distinct().joinToString(", ") else "Import Excel failure" + return if (sheet.lastRowNum > 0) "Import Excel success btw staffId: : $notExistStaffs" else "Import Excel failure" } open fun getManpowerExpenseByProjectId( projectId: Long, diff --git a/src/main/resources/db/changelog/changes/20241107_01_cyril/01_create_former_staff_timesheet_history_table.sql b/src/main/resources/db/changelog/changes/20241107_01_cyril/01_create_former_staff_timesheet_history_table.sql new file mode 100644 index 0000000..63d988f --- /dev/null +++ b/src/main/resources/db/changelog/changes/20241107_01_cyril/01_create_former_staff_timesheet_history_table.sql @@ -0,0 +1,22 @@ +-- liquibase formatted sql + +-- changeset cyril:former_staff_timesheet_history +CREATE TABLE `former_staff_timesheet_history` ( + `id` INT NOT NULL AUTO_INCREMENT, + `version` INT NOT NULL DEFAULT '0', + `created` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + `createdBy` VARCHAR(30) CHARACTER SET 'utf8mb4' COLLATE 'utf8mb4_unicode_ci' NULL DEFAULT NULL, + `modified` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + `modifiedBy` VARCHAR(30) CHARACTER SET 'utf8mb4' COLLATE 'utf8mb4_unicode_ci' NULL DEFAULT NULL, + `deleted` TINYINT(1) NOT NULL DEFAULT '0', + `timesheetId` INT NULL, + `staffId` VARCHAR(30) NULL, + `mtsid` INT NULL, + PRIMARY KEY (`id`), + INDEX `FK_STAFF_ON_TIMESHEETID` (`timesheetId` ASC) VISIBLE, + CONSTRAINT `FK_STAFF_ON_TIMESHEETID` + FOREIGN KEY (`timesheetId`) + REFERENCES `timesheet` (`id`) + ON DELETE RESTRICT + ON UPDATE RESTRICT) +;