| @@ -0,0 +1,109 @@ | |||||
| package com.ffii.fpsms.modules.common.internalSetup | |||||
| import com.ffii.core.utils.ExcelUtils | |||||
| import org.apache.poi.ss.usermodel.Cell | |||||
| import org.apache.poi.ss.usermodel.Row | |||||
| import org.apache.poi.ss.usermodel.Sheet | |||||
| import org.apache.poi.ss.usermodel.Workbook | |||||
| import org.apache.poi.xssf.usermodel.XSSFWorkbook | |||||
| import org.springframework.stereotype.Component | |||||
| import java.io.File | |||||
| import java.io.FileInputStream | |||||
| import java.io.IOException | |||||
| import com.ffii.fpsms.modules.user.entity.User | |||||
| import com.ffii.fpsms.modules.user.entity.UserRepository | |||||
| import org.springframework.beans.factory.annotation.Autowired | |||||
| import org.springframework.security.crypto.password.PasswordEncoder | |||||
| import org.springframework.transaction.annotation.Transactional | |||||
| @Component | |||||
| open class UsersSetup { | |||||
| @Autowired | |||||
| private lateinit var userRepository: UserRepository | |||||
| @Autowired | |||||
| private lateinit var passwordEncoder: PasswordEncoder | |||||
| data class UserData( | |||||
| val staffNo: String, | |||||
| val name: String, | |||||
| val title: String | |||||
| ) | |||||
| @Transactional(rollbackFor = [Exception::class]) | |||||
| open fun importExcelFromLocal(filePath: String): Int { | |||||
| val file = File(filePath) | |||||
| if (!file.exists()) { | |||||
| throw IOException("File not found: $filePath") | |||||
| } | |||||
| FileInputStream(file).use { inputStream -> | |||||
| val workbook: Workbook = XSSFWorkbook(inputStream) | |||||
| try { | |||||
| val sheet: Sheet = workbook.getSheetAt(0) | |||||
| val userDataList = mutableListOf<UserData>() | |||||
| for (rowIndex in 1..sheet.lastRowNum) { | |||||
| val row: Row? = sheet.getRow(rowIndex) | |||||
| if (row != null) { | |||||
| val staffNoCell: Cell? = row.getCell(1) // Column B | |||||
| val nameCell: Cell? = row.getCell(2) // Column C | |||||
| val titleCell: Cell? = row.getCell(3) // Column D | |||||
| val staffNo = if (staffNoCell != null) { | |||||
| ExcelUtils.getStringValue(staffNoCell).trim() | |||||
| } else { | |||||
| "" | |||||
| } | |||||
| val name = if (nameCell != null) { | |||||
| ExcelUtils.getStringValue(nameCell).trim() | |||||
| } else { | |||||
| "" | |||||
| } | |||||
| val title = if (titleCell != null) { | |||||
| ExcelUtils.getStringValue(titleCell).trim() | |||||
| } else { | |||||
| "" | |||||
| } | |||||
| if (staffNo.isNotEmpty()) { | |||||
| userDataList.add(UserData(staffNo, name, title)) | |||||
| } | |||||
| } | |||||
| } | |||||
| // Create users in database | |||||
| var createdCount = 0 | |||||
| for (userData in userDataList) { | |||||
| // Check if username (staffNo) already exists | |||||
| val existingUser = userRepository.findByUsernameAndDeletedFalse(userData.staffNo) | |||||
| if (existingUser.isEmpty) { | |||||
| val user = User() | |||||
| user.username = userData.staffNo | |||||
| user.password = passwordEncoder.encode("Pings2026!") | |||||
| user.locked = false | |||||
| user.name = userData.name | |||||
| user.title = userData.title | |||||
| user.lotusNotesUser = false | |||||
| user.staffNo = userData.staffNo | |||||
| userRepository.save(user) | |||||
| createdCount++ | |||||
| } | |||||
| } | |||||
| return createdCount | |||||
| } finally { | |||||
| workbook.close() | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -33,4 +33,7 @@ open class Printer : BaseEntity<Long>() { | |||||
| @Size(max = 10) | @Size(max = 10) | ||||
| @Column(name = "port", length = 10) | @Column(name = "port", length = 10) | ||||
| open var port: Int? = null | open var port: Int? = null | ||||
| @Column(name = "dpi", nullable = true) | |||||
| open var dpi: Int? = null | |||||
| } | } | ||||
| @@ -115,4 +115,9 @@ open class StockInLine : BaseEntity<Long>() { | |||||
| @OneToMany(mappedBy = "stockInLine", cascade = [CascadeType.ALL], orphanRemoval = true) | @OneToMany(mappedBy = "stockInLine", cascade = [CascadeType.ALL], orphanRemoval = true) | ||||
| open var escalationLog: MutableList<EscalationLog>? = mutableListOf() | open var escalationLog: MutableList<EscalationLog>? = mutableListOf() | ||||
| @JsonBackReference | |||||
| @ManyToOne | |||||
| @JoinColumn(name = "stockTransferId") | |||||
| open var stockTransferRecord: StockTransferRecord? = null | |||||
| } | } | ||||
| @@ -48,6 +48,12 @@ open class StockOutLine: BaseEntity<Long>() { | |||||
| @Column(name = "type") | @Column(name = "type") | ||||
| open var type: String? = null | open var type: String? = null | ||||
| @JsonBackReference | |||||
| @ManyToOne | |||||
| @JoinColumn(name = "stockTransferId") | |||||
| open var stockTransferRecord: StockTransferRecord? = null | |||||
| @Column(name = "startTime") | @Column(name = "startTime") | ||||
| open var startTime: LocalDateTime? = null | open var startTime: LocalDateTime? = null | ||||
| @Column(name = "endTime") | @Column(name = "endTime") | ||||
| @@ -0,0 +1,47 @@ | |||||
| package com.ffii.fpsms.modules.stock.entity | |||||
| import com.fasterxml.jackson.annotation.JsonBackReference | |||||
| import com.ffii.core.entity.BaseEntity | |||||
| import com.ffii.fpsms.modules.master.entity.Items | |||||
| import jakarta.persistence.* | |||||
| import jakarta.validation.constraints.NotNull | |||||
| import java.math.BigDecimal | |||||
| @Entity | |||||
| @Table(name = "stock_transfer_record") | |||||
| open class StockTransferRecord : BaseEntity<Long>() { | |||||
| @NotNull | |||||
| @ManyToOne | |||||
| @JoinColumn(name = "itemId", nullable = false) | |||||
| open var item: Items? = null | |||||
| @Column(name = "itemCode", length = 50) | |||||
| open var itemCode: String? = null | |||||
| @Column(name = "itemName", length = 200) | |||||
| open var itemName: String? = null | |||||
| @Column(name = "lotNo", length = 512) | |||||
| open var lotNo: String? = null | |||||
| @NotNull | |||||
| @Column(name = "transferredQty", precision = 14, scale = 2, nullable = false) | |||||
| open var transferredQty: BigDecimal? = null | |||||
| @Column(name = "startLocation", length = 255) | |||||
| open var startLocation: String? = null | |||||
| @Column(name = "targetLocation", length = 255) | |||||
| open var targetLocation: String? = null | |||||
| @JsonBackReference | |||||
| @ManyToOne | |||||
| @JoinColumn(name = "stockInLineId") | |||||
| open var stockInLine: StockInLine? = null | |||||
| @JsonBackReference | |||||
| @ManyToOne | |||||
| @JoinColumn(name = "stockOutLineId") | |||||
| open var stockOutLine: StockOutLine? = null | |||||
| } | |||||
| @@ -0,0 +1,7 @@ | |||||
| package com.ffii.fpsms.modules.stock.entity | |||||
| import com.ffii.core.support.AbstractRepository | |||||
| import org.springframework.stereotype.Repository | |||||
| @Repository | |||||
| interface StockTransferRecordRepository : AbstractRepository<StockTransferRecord, Long> | |||||
| @@ -53,6 +53,8 @@ import java.io.UnsupportedEncodingException; | |||||
| import jakarta.validation.Valid; | import jakarta.validation.Valid; | ||||
| import jakarta.validation.constraints.NotBlank; | import jakarta.validation.constraints.NotBlank; | ||||
| import com.ffii.fpsms.modules.common.internalSetup.UsersSetup; | |||||
| @RestController | @RestController | ||||
| @RequestMapping("/user") | @RequestMapping("/user") | ||||
| public class UserController{ | public class UserController{ | ||||
| @@ -62,16 +64,19 @@ public class UserController{ | |||||
| private PasswordEncoder passwordEncoder; | private PasswordEncoder passwordEncoder; | ||||
| private SettingsService settingsService; | private SettingsService settingsService; | ||||
| private UserQrCodeService userQrCodeService; | private UserQrCodeService userQrCodeService; | ||||
| private UsersSetup usersSetup; | |||||
| public UserController( | public UserController( | ||||
| UserService userService, | UserService userService, | ||||
| PasswordEncoder passwordEncoder, | PasswordEncoder passwordEncoder, | ||||
| SettingsService settingsService, | SettingsService settingsService, | ||||
| UserQrCodeService userQrCodeService) { | |||||
| this.userService = userService; | |||||
| UserQrCodeService userQrCodeService, | |||||
| UsersSetup usersSetup) { | |||||
| this.userService = userService; | |||||
| this.passwordEncoder = passwordEncoder; | this.passwordEncoder = passwordEncoder; | ||||
| this.settingsService = settingsService; | this.settingsService = settingsService; | ||||
| this.userQrCodeService = userQrCodeService; | this.userQrCodeService = userQrCodeService; | ||||
| this.usersSetup = usersSetup; | |||||
| } | } | ||||
| // @Operation(summary = "list user", responses = { @ApiResponse(responseCode = "200"), | // @Operation(summary = "list user", responses = { @ApiResponse(responseCode = "200"), | ||||
| @@ -293,4 +298,31 @@ public class UserController{ | |||||
| } | } | ||||
| @PostMapping("/users-setup") | |||||
| public ResponseEntity<Map<String, Object>> importUsersFromExcel(@RequestBody Map<String, String> request) { | |||||
| String filePath = request.get("filePath"); | |||||
| if (filePath == null || filePath.isEmpty()) { | |||||
| Map<String, Object> errorResponse = new HashMap<>(); | |||||
| errorResponse.put("success", false); | |||||
| errorResponse.put("error", "filePath is required"); | |||||
| return ResponseEntity.badRequest().body(errorResponse); | |||||
| } | |||||
| try { | |||||
| int createdCount = usersSetup.importExcelFromLocal(filePath); | |||||
| Map<String, Object> response = new HashMap<>(); | |||||
| response.put("success", true); | |||||
| response.put("message", "Users imported successfully"); | |||||
| response.put("createdCount", createdCount); | |||||
| return ResponseEntity.ok(response); | |||||
| } catch (Exception e) { | |||||
| logger.error("Error importing users from Excel", e); | |||||
| Map<String, Object> errorResponse = new HashMap<>(); | |||||
| errorResponse.put("success", false); | |||||
| errorResponse.put("error", e.getMessage()); | |||||
| return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(errorResponse); | |||||
| } | |||||
| } | |||||
| } | } | ||||
| @@ -0,0 +1,5 @@ | |||||
| -- liquibase formatted sql | |||||
| -- changeset KelvinY:add_dpi_to_printer | |||||
| ALTER TABLE `fpsmsdb`.`printer` | |||||
| ADD COLUMN `dpi` INT NULL; | |||||
| @@ -0,0 +1,5 @@ | |||||
| -- liquibase formatted sql | |||||
| -- changeset KelvinY:add_latestMupUpdatedDate_to_items | |||||
| ALTER TABLE `fpsmsdb`.`items` | |||||
| ADD COLUMN `latestMupUpdatedDate` DATETIME NULL AFTER `LatestMarketUnitPrice`; | |||||
| @@ -0,0 +1,29 @@ | |||||
| -- liquibase formatted sql | |||||
| -- changeset KelvinY:create_stock_transfer_record_table | |||||
| CREATE TABLE IF NOT EXISTS `stock_transfer_record` ( | |||||
| `id` INT NOT NULL AUTO_INCREMENT, | |||||
| `created` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, | |||||
| `createdBy` VARCHAR(30) DEFAULT NULL, | |||||
| `version` INT NOT NULL DEFAULT '0', | |||||
| `modified` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, | |||||
| `modifiedBy` VARCHAR(30) DEFAULT NULL, | |||||
| `deleted` TINYINT(1) NOT NULL DEFAULT '0', | |||||
| `itemId` INT NOT NULL, | |||||
| `itemCode` VARCHAR(50), | |||||
| `itemName` VARCHAR(200), | |||||
| `lotNo` VARCHAR(512), | |||||
| `transferredQty` DECIMAL(14,2) NOT NULL, | |||||
| `startLocation` VARCHAR(255), | |||||
| `targetLocation` VARCHAR(255), | |||||
| `stockInLineId` INT, | |||||
| `stockOutLineId` INT, | |||||
| PRIMARY KEY (`id`), | |||||
| INDEX `idx_stock_transfer_record_itemId` (`itemId`), | |||||
| INDEX `idx_stock_transfer_record_stockInLineId` (`stockInLineId`), | |||||
| INDEX `idx_stock_transfer_record_stockOutLineId` (`stockOutLineId`) | |||||
| ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; | |||||
| @@ -0,0 +1,16 @@ | |||||
| -- liquibase formatted sql | |||||
| -- changeset KelvinY:add_stockTransferId_to_stock_lines | |||||
| ALTER TABLE `stock_in_line` | |||||
| ADD COLUMN `stockTransferId` INT NULL; | |||||
| ALTER TABLE `stock_out_line` | |||||
| ADD COLUMN `stockTransferId` INT NULL; | |||||
| ALTER TABLE `stock_in_line` | |||||
| ADD CONSTRAINT `fk_stock_in_line_stock_transfer_record` | |||||
| FOREIGN KEY (`stockTransferId`) REFERENCES `stock_transfer_record`(`id`); | |||||
| ALTER TABLE `stock_out_line` | |||||
| ADD CONSTRAINT `fk_stock_out_line_stock_transfer_record` | |||||
| FOREIGN KEY (`stockTransferId`) REFERENCES `stock_transfer_record`(`id`); | |||||