diff --git a/src/main/java/com/ffii/fpsms/modules/common/internalSetup/usersSetup.kt b/src/main/java/com/ffii/fpsms/modules/common/internalSetup/usersSetup.kt new file mode 100644 index 0000000..b7d1c2c --- /dev/null +++ b/src/main/java/com/ffii/fpsms/modules/common/internalSetup/usersSetup.kt @@ -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() + + 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() + } + } + } +} diff --git a/src/main/java/com/ffii/fpsms/modules/master/entity/Printer.kt b/src/main/java/com/ffii/fpsms/modules/master/entity/Printer.kt index e2b2314..5458240 100644 --- a/src/main/java/com/ffii/fpsms/modules/master/entity/Printer.kt +++ b/src/main/java/com/ffii/fpsms/modules/master/entity/Printer.kt @@ -33,4 +33,7 @@ open class Printer : BaseEntity() { @Size(max = 10) @Column(name = "port", length = 10) open var port: Int? = null + + @Column(name = "dpi", nullable = true) + open var dpi: Int? = null } \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/stock/entity/StockInLine.kt b/src/main/java/com/ffii/fpsms/modules/stock/entity/StockInLine.kt index 66df5ba..f0ac9fb 100644 --- a/src/main/java/com/ffii/fpsms/modules/stock/entity/StockInLine.kt +++ b/src/main/java/com/ffii/fpsms/modules/stock/entity/StockInLine.kt @@ -115,4 +115,9 @@ open class StockInLine : BaseEntity() { @OneToMany(mappedBy = "stockInLine", cascade = [CascadeType.ALL], orphanRemoval = true) open var escalationLog: MutableList? = mutableListOf() + @JsonBackReference + @ManyToOne + @JoinColumn(name = "stockTransferId") + open var stockTransferRecord: StockTransferRecord? = null + } \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/stock/entity/StockOutLine.kt b/src/main/java/com/ffii/fpsms/modules/stock/entity/StockOutLine.kt index 1370407..da1c05e 100644 --- a/src/main/java/com/ffii/fpsms/modules/stock/entity/StockOutLine.kt +++ b/src/main/java/com/ffii/fpsms/modules/stock/entity/StockOutLine.kt @@ -48,6 +48,12 @@ open class StockOutLine: BaseEntity() { @Column(name = "type") open var type: String? = null + + @JsonBackReference + @ManyToOne + @JoinColumn(name = "stockTransferId") + open var stockTransferRecord: StockTransferRecord? = null + @Column(name = "startTime") open var startTime: LocalDateTime? = null @Column(name = "endTime") diff --git a/src/main/java/com/ffii/fpsms/modules/stock/entity/StockTransferRecord.kt b/src/main/java/com/ffii/fpsms/modules/stock/entity/StockTransferRecord.kt new file mode 100644 index 0000000..3a198e8 --- /dev/null +++ b/src/main/java/com/ffii/fpsms/modules/stock/entity/StockTransferRecord.kt @@ -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() { + + @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 +} \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/stock/entity/StockTransferRecordRepository.kt b/src/main/java/com/ffii/fpsms/modules/stock/entity/StockTransferRecordRepository.kt new file mode 100644 index 0000000..a9c07da --- /dev/null +++ b/src/main/java/com/ffii/fpsms/modules/stock/entity/StockTransferRecordRepository.kt @@ -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 \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/stock/service/StockTransferRecordService.kt b/src/main/java/com/ffii/fpsms/modules/stock/service/StockTransferRecordService.kt new file mode 100644 index 0000000..e69de29 diff --git a/src/main/java/com/ffii/fpsms/modules/user/web/UserController.java b/src/main/java/com/ffii/fpsms/modules/user/web/UserController.java index 9d8164e..8df4dd5 100644 --- a/src/main/java/com/ffii/fpsms/modules/user/web/UserController.java +++ b/src/main/java/com/ffii/fpsms/modules/user/web/UserController.java @@ -53,6 +53,8 @@ import java.io.UnsupportedEncodingException; import jakarta.validation.Valid; import jakarta.validation.constraints.NotBlank; +import com.ffii.fpsms.modules.common.internalSetup.UsersSetup; + @RestController @RequestMapping("/user") public class UserController{ @@ -62,16 +64,19 @@ public class UserController{ private PasswordEncoder passwordEncoder; private SettingsService settingsService; private UserQrCodeService userQrCodeService; - + private UsersSetup usersSetup; + public UserController( UserService userService, PasswordEncoder passwordEncoder, SettingsService settingsService, - UserQrCodeService userQrCodeService) { - this.userService = userService; + UserQrCodeService userQrCodeService, + UsersSetup usersSetup) { + this.userService = userService; this.passwordEncoder = passwordEncoder; this.settingsService = settingsService; this.userQrCodeService = userQrCodeService; + this.usersSetup = usersSetup; } // @Operation(summary = "list user", responses = { @ApiResponse(responseCode = "200"), @@ -293,4 +298,31 @@ public class UserController{ } + @PostMapping("/users-setup") + public ResponseEntity> importUsersFromExcel(@RequestBody Map request) { + String filePath = request.get("filePath"); + + if (filePath == null || filePath.isEmpty()) { + Map 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 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 errorResponse = new HashMap<>(); + errorResponse.put("success", false); + errorResponse.put("error", e.getMessage()); + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(errorResponse); + } + } + } diff --git a/src/main/resources/db/changelog/changes/20260109_01_KelvinY/01_add_dpi_to_printer.sql b/src/main/resources/db/changelog/changes/20260109_01_KelvinY/01_add_dpi_to_printer.sql new file mode 100644 index 0000000..4f59423 --- /dev/null +++ b/src/main/resources/db/changelog/changes/20260109_01_KelvinY/01_add_dpi_to_printer.sql @@ -0,0 +1,5 @@ +-- liquibase formatted sql +-- changeset KelvinY:add_dpi_to_printer + +ALTER TABLE `fpsmsdb`.`printer` +ADD COLUMN `dpi` INT NULL; \ No newline at end of file diff --git a/src/main/resources/db/changelog/changes/20260109_01_KelvinY/02_add_latestMupUpdatedDate_to_items.sql b/src/main/resources/db/changelog/changes/20260109_01_KelvinY/02_add_latestMupUpdatedDate_to_items.sql new file mode 100644 index 0000000..ead0706 --- /dev/null +++ b/src/main/resources/db/changelog/changes/20260109_01_KelvinY/02_add_latestMupUpdatedDate_to_items.sql @@ -0,0 +1,5 @@ +-- liquibase formatted sql +-- changeset KelvinY:add_latestMupUpdatedDate_to_items + +ALTER TABLE `fpsmsdb`.`items` +ADD COLUMN `latestMupUpdatedDate` DATETIME NULL AFTER `LatestMarketUnitPrice`; \ No newline at end of file diff --git a/src/main/resources/db/changelog/changes/20260112_01_KelvinY/01_create_stock_transfer_record.sql b/src/main/resources/db/changelog/changes/20260112_01_KelvinY/01_create_stock_transfer_record.sql new file mode 100644 index 0000000..01d3c9d --- /dev/null +++ b/src/main/resources/db/changelog/changes/20260112_01_KelvinY/01_create_stock_transfer_record.sql @@ -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; \ No newline at end of file diff --git a/src/main/resources/db/changelog/changes/20260112_01_KelvinY/02_add_stockTransferId_to_stock_lines.sql b/src/main/resources/db/changelog/changes/20260112_01_KelvinY/02_add_stockTransferId_to_stock_lines.sql new file mode 100644 index 0000000..2b50466 --- /dev/null +++ b/src/main/resources/db/changelog/changes/20260112_01_KelvinY/02_add_stockTransferId_to_stock_lines.sql @@ -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`); \ No newline at end of file