From af563cddad0e26662be169d22707f1f249aa631a Mon Sep 17 00:00:00 2001 From: "cyril.tsui" Date: Fri, 20 Jun 2025 16:30:12 +0800 Subject: [PATCH 1/6] update --- .../web/models/ConsoPickOrderResponse.kt | 82 +++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100644 src/main/java/com/ffii/fpsms/modules/pickOrder/web/models/ConsoPickOrderResponse.kt diff --git a/src/main/java/com/ffii/fpsms/modules/pickOrder/web/models/ConsoPickOrderResponse.kt b/src/main/java/com/ffii/fpsms/modules/pickOrder/web/models/ConsoPickOrderResponse.kt new file mode 100644 index 0000000..0a5bc61 --- /dev/null +++ b/src/main/java/com/ffii/fpsms/modules/pickOrder/web/models/ConsoPickOrderResponse.kt @@ -0,0 +1,82 @@ +package com.ffii.fpsms.modules.pickOrder.web.models + +import java.math.BigDecimal +import java.time.LocalDateTime + +// Final Response +data class ConsoPickOrderResponse( + val consoCode: String, + val pickOrders: List, + val items: List +) + +// Components +data class PickOrderInConso( + val id: Long, + val code: String, + val targetDate: LocalDateTime, + val completeDate: LocalDateTime, + val releasedDate: LocalDateTime, + val releasedById: IdName, + val assignTo: IdName, + val pickOrderLines: List, + val type: String, + val status: String, +) + +data class PickOrderLineInConso( + val id: Long, + val item: IdName, + val qty: BigDecimal, + val uom: IdCodeDesc, + val status: String, + val suggestPickLots: List, + val actualPickLots: List, +) + +data class SuggestPickLotInConso( + val id: Long, + val type: String, + val inventoryLotLine: InventoryLotLineInConso, + val qty: BigDecimal, + val pickSuggested: Boolean?, +) + +data class ActualPickLotInConso( + val id: Long, + val type: String, + val inventoryLotLine: InventoryLotLineInConso, + val qty: BigDecimal, +) + +data class InventoryLotLineInConso( + val id: Long, + val inQty: BigDecimal, + val outQty: BigDecimal, + val holdQty: BigDecimal, + val remainingQty: BigDecimal, + val stockUom: IdCodeDesc, + val status: String, + val remarks: String, +) + +data class ItemInConso( + val id: Long, + val code: String, + val name: String, + val qty: BigDecimal, + val suggestPickLots: List, + val actualPickLots: List, +) + +// Common +data class IdName( + val id: Long, + val name: String, +) + +data class IdCodeDesc( + val id: Long, + val code: String, + val desc: String, +) \ No newline at end of file From 05dc2a611f921d2a8b1355138c5d36ebb9c18efb Mon Sep 17 00:00:00 2001 From: "MSI\\derek" Date: Mon, 23 Jun 2025 12:47:47 +0800 Subject: [PATCH 2/6] =?UTF-8?q?=EF=BD=95=EF=BD=90=EF=BD=84=EF=BD=81?= =?UTF-8?q?=EF=BD=94=EF=BD=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/ffii/fpsms/modules/stock/entity/StockInLine.kt | 6 +++++- .../ffii/fpsms/modules/stock/service/StockInLineService.kt | 2 ++ .../01_add_lot_line_id_to_stock_in_line.sql | 7 +++++++ 3 files changed, 14 insertions(+), 1 deletion(-) create mode 100644 src/main/resources/db/changelog/changes/20250620_01_derek/01_add_lot_line_id_to_stock_in_line.sql 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 6ce0170..443d83e 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 @@ -69,10 +69,14 @@ open class StockInLine : BaseEntity() { @JoinColumn(name = "userId") open var user: User? = null - @OneToOne + @ManyToOne @JoinColumn(name = "inventoryLotId") open var inventoryLot: InventoryLot? = null + @OneToOne + @JoinColumn(name = "inventoryLotLineId") + open var inventoryLotLine: InventoryLotLine? = null + @Column(name = "lotNo") open var lotNo: String? = null diff --git a/src/main/java/com/ffii/fpsms/modules/stock/service/StockInLineService.kt b/src/main/java/com/ffii/fpsms/modules/stock/service/StockInLineService.kt index 4fd6e6f..adc2a40 100644 --- a/src/main/java/com/ffii/fpsms/modules/stock/service/StockInLineService.kt +++ b/src/main/java/com/ffii/fpsms/modules/stock/service/StockInLineService.kt @@ -251,6 +251,7 @@ open class StockInLineService( this.status = request.status this.expiryDate = stockInLine.expiryDate ?: request.expiryDate this.inventoryLot = stockInLine.inventoryLot ?: savedInventoryLot + this.inventoryLotLine = savedInventoryLotLine this.lotNo = stockInLine.lotNo ?: savedInventoryLot?.lotNo } val savedStockInLine = saveAndFlush(stockInLine) @@ -320,6 +321,7 @@ open class StockInLineService( this.acceptedQty = request.acceptedQty this.status = request.status this.inventoryLot = savedInventoryLot ?: stockInLine.inventoryLot + this.inventoryLotLine = savedInventoryLotLine this.lotNo = savedInventoryLot?.lotNo ?: stockInLine.lotNo this.expiryDate = stockInLine.expiryDate ?: request.expiryDate this.productLotNo = stockInLine.productLotNo ?: request.productLotNo diff --git a/src/main/resources/db/changelog/changes/20250620_01_derek/01_add_lot_line_id_to_stock_in_line.sql b/src/main/resources/db/changelog/changes/20250620_01_derek/01_add_lot_line_id_to_stock_in_line.sql new file mode 100644 index 0000000..15b065b --- /dev/null +++ b/src/main/resources/db/changelog/changes/20250620_01_derek/01_add_lot_line_id_to_stock_in_line.sql @@ -0,0 +1,7 @@ +-- liquibase formatted sql +-- changeset derek:add_lot_line_id_to_stock_in_line + +ALTER TABLE `stock_in_line` + ADD COLUMN `inventoryLotLineId` INT(11) NULL AFTER `inventoryLotId`, + ADD CONSTRAINT FK_STOCK_IN_LINE_ON_LOT_LINE_ID FOREIGN KEY (inventoryLotLineId) REFERENCES inventory_lot_line (id) +; \ No newline at end of file From 0064be31f256791b0df807a64996e52112eb5b3e Mon Sep 17 00:00:00 2001 From: "cyril.tsui" Date: Mon, 23 Jun 2025 13:29:26 +0800 Subject: [PATCH 3/6] Add suggested pick lot & conso pick order detail --- .../modules/pickOrder/entity/PickOrderLine.kt | 6 +- .../pickOrder/entity/PickOrderRepository.kt | 2 + .../pickOrder/service/PickOrderService.kt | 147 +++++++++++++++++- .../pickOrder/web/PickOrderController.kt | 7 + .../web/models/ConsoPickOrderResponse.kt | 68 ++++---- .../modules/stock/entity/InventoryLotLine.kt | 8 +- .../entity/InventoryLotLineRepository.kt | 3 + .../stock/entity/SuggestPickLotRepository.kt | 2 + .../modules/stock/entity/SuggestedPickLot.kt | 6 +- .../stock/entity/enum/InventoryLotLineEnum.kt | 6 + .../enum/InventoryLotLineEnumConverter.kt | 17 ++ .../entity/projection/InventoryLotLineInfo.kt | 36 +++++ .../stock/service/InventoryLotLineService.kt | 64 ++++++++ .../stock/service/StockInLineService.kt | 3 +- .../stock/service/SuggestedPickLotService.kt | 124 +++++++++++++++ .../stock/web/InventoryLotLineController.kt | 23 +++ .../stock/web/SuggestedPickLotController.kt | 25 +++ .../web/model/SaveInventoryLotLineRequest.kt | 15 ++ .../web/model/SaveSuggestedPickLotRequest.kt | 12 ++ .../model/SuggestInventoryLotLineResponse.kt | 13 ++ 20 files changed, 542 insertions(+), 45 deletions(-) create mode 100644 src/main/java/com/ffii/fpsms/modules/stock/entity/enum/InventoryLotLineEnum.kt create mode 100644 src/main/java/com/ffii/fpsms/modules/stock/entity/enum/InventoryLotLineEnumConverter.kt create mode 100644 src/main/java/com/ffii/fpsms/modules/stock/entity/projection/InventoryLotLineInfo.kt create mode 100644 src/main/java/com/ffii/fpsms/modules/stock/service/InventoryLotLineService.kt create mode 100644 src/main/java/com/ffii/fpsms/modules/stock/service/SuggestedPickLotService.kt create mode 100644 src/main/java/com/ffii/fpsms/modules/stock/web/InventoryLotLineController.kt create mode 100644 src/main/java/com/ffii/fpsms/modules/stock/web/SuggestedPickLotController.kt create mode 100644 src/main/java/com/ffii/fpsms/modules/stock/web/model/SaveInventoryLotLineRequest.kt create mode 100644 src/main/java/com/ffii/fpsms/modules/stock/web/model/SaveSuggestedPickLotRequest.kt create mode 100644 src/main/java/com/ffii/fpsms/modules/stock/web/model/SuggestInventoryLotLineResponse.kt diff --git a/src/main/java/com/ffii/fpsms/modules/pickOrder/entity/PickOrderLine.kt b/src/main/java/com/ffii/fpsms/modules/pickOrder/entity/PickOrderLine.kt index 6b2004b..3c93033 100644 --- a/src/main/java/com/ffii/fpsms/modules/pickOrder/entity/PickOrderLine.kt +++ b/src/main/java/com/ffii/fpsms/modules/pickOrder/entity/PickOrderLine.kt @@ -5,6 +5,8 @@ import com.fasterxml.jackson.annotation.JsonManagedReference import com.ffii.core.entity.BaseEntity import com.ffii.fpsms.modules.master.entity.Items import com.ffii.fpsms.modules.master.entity.UomConversion +import com.ffii.fpsms.modules.pickOrder.enums.PickOrderLineStatus +import com.ffii.fpsms.modules.pickOrder.enums.PickOrderLineStatusConverter import com.ffii.fpsms.modules.stock.entity.StockOutLine import com.ffii.fpsms.modules.stock.entity.SuggestedPickLot import jakarta.persistence.* @@ -36,10 +38,10 @@ open class PickOrderLine : BaseEntity() { @JoinColumn(name = "uomId", nullable = false) open var uom: UomConversion? = null - @Size(max = 30) + @Convert(converter = PickOrderLineStatusConverter::class) @NotNull @Column(name = "status", nullable = false, length = 30) - open var status: String? = null + open var status: PickOrderLineStatus? = null @JsonManagedReference @OneToMany(mappedBy = "pickOrderLine", cascade = [CascadeType.ALL], orphanRemoval = true) diff --git a/src/main/java/com/ffii/fpsms/modules/pickOrder/entity/PickOrderRepository.kt b/src/main/java/com/ffii/fpsms/modules/pickOrder/entity/PickOrderRepository.kt index 77f0866..e34a196 100644 --- a/src/main/java/com/ffii/fpsms/modules/pickOrder/entity/PickOrderRepository.kt +++ b/src/main/java/com/ffii/fpsms/modules/pickOrder/entity/PickOrderRepository.kt @@ -53,4 +53,6 @@ interface PickOrderRepository : AbstractRepository { fun findAllByIdInAndConsoCodeIsNullAndStatus(id: List, status: PickOrderStatus): List fun findPickOrderInfoByIdIn(id: List): List + + fun findAllByConsoCode(consoCode: String): List } \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/pickOrder/service/PickOrderService.kt b/src/main/java/com/ffii/fpsms/modules/pickOrder/service/PickOrderService.kt index 0cc8bf8..950be8f 100644 --- a/src/main/java/com/ffii/fpsms/modules/pickOrder/service/PickOrderService.kt +++ b/src/main/java/com/ffii/fpsms/modules/pickOrder/service/PickOrderService.kt @@ -6,12 +6,15 @@ import com.ffii.fpsms.modules.pickOrder.entity.PickOrderRepository import com.ffii.fpsms.modules.pickOrder.entity.projection.PickOrderInfo import com.ffii.fpsms.modules.pickOrder.entity.projection.PickOrderLineInfo import com.ffii.fpsms.modules.pickOrder.enums.PickOrderStatus -import com.ffii.fpsms.modules.pickOrder.web.models.ConsoPickOrderRequest -import com.ffii.fpsms.modules.pickOrder.web.models.SearchPickOrderRequest +import com.ffii.fpsms.modules.pickOrder.web.models.* +import com.ffii.fpsms.modules.stock.service.StockOutLineService +import com.ffii.fpsms.modules.stock.service.SuggestedPickLotService +import com.ffii.fpsms.modules.stock.web.model.SuggestInventoryLotLineResponse import org.springframework.context.annotation.Lazy import org.springframework.data.domain.PageRequest import org.springframework.stereotype.Service import java.io.Serializable +import java.math.BigDecimal import java.time.LocalDate import java.time.LocalDateTime import java.time.format.DateTimeFormatter @@ -19,10 +22,12 @@ import java.time.format.DateTimeFormatter @Service open class PickOrderService( val pickOrderRepository: PickOrderRepository, + val stockOutLineService: StockOutLineService, + val suggestedPickLotService: SuggestedPickLotService, ) { open fun LocalDateTimeParse(dateTime: String?, pattern: String? = "YYYY-MM-DD hh:mm:ss"): LocalDateTime? { try { - val formatter = DateTimeFormatter.ofPattern(pattern) + val formatter = DateTimeFormatter.ofPattern(pattern!!) return LocalDateTime.parse(dateTime, formatter) } catch (e: Exception) { return null @@ -76,26 +81,154 @@ open class PickOrderService( open fun consoPickOrders(request: ConsoPickOrderRequest): List { val newConsoCode = assignConsoCode() - val pickOrders = pickOrderRepository.findAllByIdInAndConsoCodeIsNullAndStatus(request.ids, PickOrderStatus.PENDING) + val pickOrders = pickOrderRepository.findAllByIdInAndStatus(request.ids, PickOrderStatus.PENDING) pickOrders.forEach { it.consoCode = newConsoCode } val updatedPickOrders = pickOrderRepository.saveAll(pickOrders) - val updatedPickOrderInfos = updatedPickOrders.map { po -> po.id as Serializable }.let { pickOrderRepository.findPickOrderInfoByIdIn(it) } + val updatedPickOrderInfos = updatedPickOrders.map { po -> po.id as Serializable } + .let { pickOrderRepository.findPickOrderInfoByIdIn(it) } return updatedPickOrderInfos } open fun deconsoPickOrders(request: ConsoPickOrderRequest): List { - val pickOrders = pickOrderRepository.findAllByIdInAndStatus(request.ids, PickOrderStatus.PENDING) + val pickOrders = pickOrderRepository.findAllByIdInAndStatus(request.ids, PickOrderStatus.CONSOLIDATED) pickOrders.forEach { it.consoCode = null } val updatedPickOrders = pickOrderRepository.saveAll(pickOrders) - val updatedPickOrderInfos = updatedPickOrders.map { po -> po.id as Serializable }.let { pickOrderRepository.findPickOrderInfoByIdIn(it) } + val updatedPickOrderInfos = updatedPickOrders.map { po -> po.id as Serializable } + .let { pickOrderRepository.findPickOrderInfoByIdIn(it) } return updatedPickOrderInfos } + + open fun consoPickOrderDetail(consoCode: String): ConsoPickOrderResponse { + // Conso code + // Pick orders with items + // Items + + val zero = BigDecimal.ZERO + + // pick orders + // Mapping: PickOrder -> PickOrderInConso + val pos = pickOrderRepository.findAllByConsoCode(consoCode) + val finalPos = pos.map { po -> + val pols = po.pickOrderLines + + // Suggestions for Pick Order Line + val suggestions = suggestedPickLotService.suggestionForPickOrderLines(pols) + + // Pick Order Lines + // Mapping: PickOrderLine -> PickOrderLineInConso + val finalPols = pols.map { pol -> + val item = pol.item + val uom = pol.uom + + // Check If already have suggestion + var suggestion = pol.suggestedPickLots + if (suggestion.isEmpty()) { + suggestion = suggestions.filter { it.pickOrderLine?.id == pol.id }.toMutableList() + } + + // Mapping: SuggestedPickLot -> SuggestPickLotInConso + val finalSuggestion = suggestion.map { + val inventoryLotLine = it.suggestedLotLine + val remainingQty = (inventoryLotLine?.inQty ?: zero) + .minus(inventoryLotLine?.outQty ?: zero) + .minus(inventoryLotLine?.holdQty ?: zero) + val stockUom = inventoryLotLine?.stockUom?.uom + + // Mapping: InventoryLotLine -> InventoryLotLineInConso + val finalInventoryLotLine = InventoryLotLineInConso( + id = inventoryLotLine?.id, + inQty = inventoryLotLine?.inQty, + outQty = inventoryLotLine?.outQty, + holdQty = inventoryLotLine?.holdQty, + remainingQty = remainingQty, + stockUom = IdCodeDesc(stockUom?.id, stockUom?.code, stockUom?.udfudesc), + status = inventoryLotLine?.status?.value, + remarks = inventoryLotLine?.remarks + ) + + // Return + SuggestPickLotInConso( + id = it.id, + type = it.type?.value, + qty = it.qty, + inventoryLotLine = finalInventoryLotLine, + pickSuggested = it.pickSuggested + ) + } + + // Return + PickOrderLineInConso( + id = pol.id, + item = IdCodeName(item?.id, item?.code, item?.name), + qty = pol.qty, + uom = IdCodeDesc(uom?.id, uom?.code, uom?.udfudesc), + suggestPickLots = finalSuggestion, + status = pol.status?.value, + actualPickLots = mutableListOf() + ) + } + + val releasedBy = po.releasedBy + val assignTo = po.assignTo + + // Return + PickOrderInConso( + id = po.id, + code = po.code, + targetDate = po.targetDate, + completeDate = po.completeDate, + releasedDate = po.releasedDate, + releasedBy = IdName(releasedBy?.id, releasedBy?.name), + assignTo = IdName(assignTo?.id, assignTo?.name), + pickOrderLines = finalPols, + type = po.type?.value, + status = po.status?.value, + ) + } + + // Items + val finalItems = finalPos + .flatMap { it.pickOrderLines } + .groupBy { it.item.id } + .map { (_itemId, _line) -> + val itemSuggestions = _line + .flatMap { it.suggestPickLots } + .groupBy { it.inventoryLotLine.id to it.pickSuggested } + .map{ (_key, _lot) -> + SuggestPickLotInConso( + id = _key.first, + type = _lot.first().type, + inventoryLotLine = _lot.first().inventoryLotLine, + qty = _lot.fold(zero) { sum, item -> sum + (item.qty ?: zero)}, + pickSuggested = _key.second + ) + } + + ItemInConso( + id = _line.first().item.id, + code = _line.first().item.code, + name = _line.first().item.name, + qty = _line.fold(zero) { sum, item -> sum + (item.qty ?: zero)}, + suggestPickLots = _line + .flatMap { it.suggestPickLots }, + actualPickLots = mutableListOf() + ) + } + + val response = ConsoPickOrderResponse( + consoCode = consoCode, + pickOrders = finalPos, + items = finalItems + ) + + return response + } } \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/pickOrder/web/PickOrderController.kt b/src/main/java/com/ffii/fpsms/modules/pickOrder/web/PickOrderController.kt index 39b55e3..272ccbe 100644 --- a/src/main/java/com/ffii/fpsms/modules/pickOrder/web/PickOrderController.kt +++ b/src/main/java/com/ffii/fpsms/modules/pickOrder/web/PickOrderController.kt @@ -5,6 +5,7 @@ import com.ffii.fpsms.modules.pickOrder.entity.PickOrderRepository import com.ffii.fpsms.modules.pickOrder.entity.projection.PickOrderInfo import com.ffii.fpsms.modules.pickOrder.service.PickOrderService import com.ffii.fpsms.modules.pickOrder.web.models.ConsoPickOrderRequest +import com.ffii.fpsms.modules.pickOrder.web.models.ConsoPickOrderResponse import com.ffii.fpsms.modules.pickOrder.web.models.SearchPickOrderRequest import jakarta.validation.Valid import org.springframework.data.domain.Page @@ -12,6 +13,7 @@ import org.springframework.data.domain.PageRequest import org.springframework.data.domain.Pageable import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.bind.annotation.ModelAttribute +import org.springframework.web.bind.annotation.PathVariable import org.springframework.web.bind.annotation.PostMapping import org.springframework.web.bind.annotation.RequestBody import org.springframework.web.bind.annotation.RequestMapping @@ -47,4 +49,9 @@ class PickOrderController( fun deconsoPickOrders(@Valid @RequestBody request: ConsoPickOrderRequest): List { return pickOrderService.deconsoPickOrders(request) } + + @GetMapping("/consoDetail/{consoCode}") + fun consoPickOrderDetail(@PathVariable consoCode: String): ConsoPickOrderResponse { + return pickOrderService.consoPickOrderDetail(consoCode); + } } \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/pickOrder/web/models/ConsoPickOrderResponse.kt b/src/main/java/com/ffii/fpsms/modules/pickOrder/web/models/ConsoPickOrderResponse.kt index 0a5bc61..01607f6 100644 --- a/src/main/java/com/ffii/fpsms/modules/pickOrder/web/models/ConsoPickOrderResponse.kt +++ b/src/main/java/com/ffii/fpsms/modules/pickOrder/web/models/ConsoPickOrderResponse.kt @@ -12,33 +12,33 @@ data class ConsoPickOrderResponse( // Components data class PickOrderInConso( - val id: Long, - val code: String, - val targetDate: LocalDateTime, - val completeDate: LocalDateTime, - val releasedDate: LocalDateTime, - val releasedById: IdName, + val id: Long?, + val code: String?, + val targetDate: LocalDateTime?, + val completeDate: LocalDateTime?, + val releasedDate: LocalDateTime?, + val releasedBy: IdName, val assignTo: IdName, val pickOrderLines: List, - val type: String, - val status: String, + val type: String?, + val status: String?, ) data class PickOrderLineInConso( - val id: Long, - val item: IdName, - val qty: BigDecimal, + val id: Long?, + val item: IdCodeName, + val qty: BigDecimal?, val uom: IdCodeDesc, - val status: String, + val status: String?, val suggestPickLots: List, val actualPickLots: List, ) data class SuggestPickLotInConso( - val id: Long, - val type: String, + val id: Long?, + val type: String?, val inventoryLotLine: InventoryLotLineInConso, - val qty: BigDecimal, + val qty: BigDecimal?, val pickSuggested: Boolean?, ) @@ -50,33 +50,39 @@ data class ActualPickLotInConso( ) data class InventoryLotLineInConso( - val id: Long, - val inQty: BigDecimal, - val outQty: BigDecimal, - val holdQty: BigDecimal, - val remainingQty: BigDecimal, + val id: Long?, + val inQty: BigDecimal?, + val outQty: BigDecimal?, + val holdQty: BigDecimal?, + val remainingQty: BigDecimal?, val stockUom: IdCodeDesc, - val status: String, - val remarks: String, + val status: String?, + val remarks: String?, ) data class ItemInConso( - val id: Long, - val code: String, - val name: String, - val qty: BigDecimal, + val id: Long?, + val code: String?, + val name: String?, + val qty: BigDecimal?, val suggestPickLots: List, val actualPickLots: List, ) // Common data class IdName( - val id: Long, - val name: String, + val id: Long?, + val name: String?, +) + +data class IdCodeName( + val id: Long?, + val code: String?, + val name: String?, ) data class IdCodeDesc( - val id: Long, - val code: String, - val desc: String, + val id: Long?, + val code: String?, + val desc: String?, ) \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/stock/entity/InventoryLotLine.kt b/src/main/java/com/ffii/fpsms/modules/stock/entity/InventoryLotLine.kt index 2995c6a..123f6ce 100644 --- a/src/main/java/com/ffii/fpsms/modules/stock/entity/InventoryLotLine.kt +++ b/src/main/java/com/ffii/fpsms/modules/stock/entity/InventoryLotLine.kt @@ -1,9 +1,13 @@ 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.ItemUom import com.ffii.fpsms.modules.master.entity.Warehouse +import com.ffii.fpsms.modules.stock.entity.enum.InventoryLotLineStatus +import com.ffii.fpsms.modules.stock.entity.enum.InventoryLotLineStringConverter import jakarta.persistence.Column +import jakarta.persistence.Convert import jakarta.persistence.Entity import jakarta.persistence.JoinColumn import jakarta.persistence.ManyToOne @@ -16,6 +20,7 @@ import java.math.BigDecimal @Entity @Table(name = "inventory_lot_line") open class InventoryLotLine : BaseEntity() { +// @JsonBackReference @NotNull @ManyToOne @JoinColumn(name = "inventoryLotId") @@ -40,8 +45,9 @@ open class InventoryLotLine : BaseEntity() { @Column(name = "holdQty") open var holdQty: BigDecimal? = null + @Convert(converter = InventoryLotLineStringConverter::class) @Column(name = "status") - open var status: String? = null + open var status: InventoryLotLineStatus? = null @Column(name = "remarks") open var remarks: String? = null diff --git a/src/main/java/com/ffii/fpsms/modules/stock/entity/InventoryLotLineRepository.kt b/src/main/java/com/ffii/fpsms/modules/stock/entity/InventoryLotLineRepository.kt index 6ea1e52..a47532d 100644 --- a/src/main/java/com/ffii/fpsms/modules/stock/entity/InventoryLotLineRepository.kt +++ b/src/main/java/com/ffii/fpsms/modules/stock/entity/InventoryLotLineRepository.kt @@ -1,8 +1,11 @@ package com.ffii.fpsms.modules.stock.entity import com.ffii.core.support.AbstractRepository +import com.ffii.fpsms.modules.stock.entity.projection.InventoryLotLineInfo import org.springframework.stereotype.Repository +import java.io.Serializable @Repository interface InventoryLotLineRepository : AbstractRepository { + fun findInventoryLotLineInfoByInventoryLotItemIdIn(ids: List): List } \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/stock/entity/SuggestPickLotRepository.kt b/src/main/java/com/ffii/fpsms/modules/stock/entity/SuggestPickLotRepository.kt index 23ca200..c477824 100644 --- a/src/main/java/com/ffii/fpsms/modules/stock/entity/SuggestPickLotRepository.kt +++ b/src/main/java/com/ffii/fpsms/modules/stock/entity/SuggestPickLotRepository.kt @@ -1,8 +1,10 @@ package com.ffii.fpsms.modules.stock.entity import com.ffii.core.support.AbstractRepository +import com.ffii.fpsms.modules.pickOrder.entity.PickOrderLine import org.springframework.stereotype.Repository @Repository interface SuggestPickLotRepository : AbstractRepository { + fun findAllByPickOrderLineIn(lines: List): List } \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/stock/entity/SuggestedPickLot.kt b/src/main/java/com/ffii/fpsms/modules/stock/entity/SuggestedPickLot.kt index db087e9..dbaac21 100644 --- a/src/main/java/com/ffii/fpsms/modules/stock/entity/SuggestedPickLot.kt +++ b/src/main/java/com/ffii/fpsms/modules/stock/entity/SuggestedPickLot.kt @@ -19,14 +19,14 @@ open class SuggestedPickLot: BaseEntity() { @Column(name = "type", nullable = false, length = 100) open var type: SuggestedPickLotType? = null - @JsonBackReference +// @JsonBackReference @ManyToOne @JoinColumn(name = "stockOutLineId") open var stockOutLine: StockOutLine? = null - @JsonBackReference +// @JsonBackReference @ManyToOne - @JoinColumn(name = "suggestedLotLineId", nullable = false) + @JoinColumn(name = "suggestedLotLineId") open var suggestedLotLine: InventoryLotLine? = null @JsonBackReference diff --git a/src/main/java/com/ffii/fpsms/modules/stock/entity/enum/InventoryLotLineEnum.kt b/src/main/java/com/ffii/fpsms/modules/stock/entity/enum/InventoryLotLineEnum.kt new file mode 100644 index 0000000..764cbf0 --- /dev/null +++ b/src/main/java/com/ffii/fpsms/modules/stock/entity/enum/InventoryLotLineEnum.kt @@ -0,0 +1,6 @@ +package com.ffii.fpsms.modules.stock.entity.enum + +enum class InventoryLotLineStatus(val value: String) { + AVAILABLE ("available"), + UNAVAILABLE ("unavailable") +} \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/stock/entity/enum/InventoryLotLineEnumConverter.kt b/src/main/java/com/ffii/fpsms/modules/stock/entity/enum/InventoryLotLineEnumConverter.kt new file mode 100644 index 0000000..6191da1 --- /dev/null +++ b/src/main/java/com/ffii/fpsms/modules/stock/entity/enum/InventoryLotLineEnumConverter.kt @@ -0,0 +1,17 @@ +package com.ffii.fpsms.modules.stock.entity.enum + +import jakarta.persistence.AttributeConverter +import jakarta.persistence.Converter + +@Converter(autoApply = true) +class InventoryLotLineStringConverter: AttributeConverter { + override fun convertToDatabaseColumn(status: InventoryLotLineStatus?): String? { + return status?.value + } + + override fun convertToEntityAttribute(value: String?): InventoryLotLineStatus? { + return value?.let { v -> + InventoryLotLineStatus.entries.find { it.value == v } + } + } +} \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/stock/entity/projection/InventoryLotLineInfo.kt b/src/main/java/com/ffii/fpsms/modules/stock/entity/projection/InventoryLotLineInfo.kt new file mode 100644 index 0000000..fbd4343 --- /dev/null +++ b/src/main/java/com/ffii/fpsms/modules/stock/entity/projection/InventoryLotLineInfo.kt @@ -0,0 +1,36 @@ +package com.ffii.fpsms.modules.stock.entity.projection + +import org.springframework.beans.factory.annotation.Value +import java.math.BigDecimal +import java.time.LocalDate + +interface InventoryLotLineItemInfo { + val id: Long + val code: String + val name: String +} + +interface InventoryLotLineWarehouseInfo { + val id: Long + val code: String + val name: String + val description: String + val capacity: BigDecimal +} + +interface InventoryLotLineInfo { + val id: Long? + @get:Value("#{target.inventoryLot.item}") + val item: InventoryLotLineItemInfo? + val warehouse: InventoryLotLineWarehouseInfo? + var inQty: BigDecimal? + var outQty: BigDecimal? + var holdQty: BigDecimal? + @get:Value("#{target.status.value}") + val status: String? + val remarks: String? + @get:Value("#{target.stockUom.uom.udfudesc}") + val uom: String? + @get:Value("#{target.inventoryLot.expiryDate}") + val expiryDate: LocalDate +} \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/stock/service/InventoryLotLineService.kt b/src/main/java/com/ffii/fpsms/modules/stock/service/InventoryLotLineService.kt new file mode 100644 index 0000000..b469a6b --- /dev/null +++ b/src/main/java/com/ffii/fpsms/modules/stock/service/InventoryLotLineService.kt @@ -0,0 +1,64 @@ +package com.ffii.fpsms.modules.stock.service + +import com.ffii.fpsms.modules.master.entity.ItemUomRespository +import com.ffii.fpsms.modules.master.entity.WarehouseRepository +import com.ffii.fpsms.modules.stock.entity.InventoryLotLine +import com.ffii.fpsms.modules.stock.entity.InventoryLotLineRepository +import com.ffii.fpsms.modules.stock.entity.InventoryLotRepository +import com.ffii.fpsms.modules.stock.entity.enum.InventoryLotLineStatus +import com.ffii.fpsms.modules.stock.entity.projection.InventoryLotLineInfo +import com.ffii.fpsms.modules.stock.web.model.SaveInventoryLotLineRequest +import org.springframework.stereotype.Service +import org.springframework.transaction.annotation.Propagation +import org.springframework.transaction.annotation.Transactional +import java.math.BigDecimal +import java.util.Optional +import kotlin.jvm.optionals.getOrNull + +@Service +open class InventoryLotLineService( + private val inventoryLotLineRepository: InventoryLotLineRepository, + private val inventoryLotRepository: InventoryLotRepository, + private val warehouseRepository: WarehouseRepository, + private val itemUomRespository: ItemUomRespository +) { + open fun findById(id: Long): Optional { + return inventoryLotLineRepository.findById(id) + } + + open fun allInventoryLotLinesByItemIdIn(itemIds: List): List { + return inventoryLotLineRepository.findInventoryLotLineInfoByInventoryLotItemIdIn(itemIds) + } + + open fun saveInventoryLotLine(request: SaveInventoryLotLineRequest): InventoryLotLine { + val inventoryLotLine = + request.id?.let { inventoryLotLineRepository.findById(it).getOrNull() } ?: InventoryLotLine() + val inventoryLot = request.inventoryLotId?.let { inventoryLotRepository.findById(it).getOrNull() } + val warehouse = request.warehouseId?.let { warehouseRepository.findById(it).getOrNull() } + val stockUom = request.stockUomId?.let { itemUomRespository.findById(it).getOrNull() } + val remainingQty = + (request.inQty ?: BigDecimal(0)) - (request.outQty ?: BigDecimal(0)) - (request.holdQty ?: BigDecimal(0)) + val status = request.status?.let { _status -> InventoryLotLineStatus.entries.find { it.value == _status } } + val qtyStatus = when (remainingQty > BigDecimal(0)) { + true -> InventoryLotLineStatus.AVAILABLE + else -> InventoryLotLineStatus.UNAVAILABLE + } + + inventoryLotLine.apply { + this.inventoryLot = inventoryLot + this.warehouse = warehouse + inQty = request.inQty + outQty = request.outQty + holdQty = request.holdQty + this.stockUom = stockUom + this.status = + when (status == InventoryLotLineStatus.AVAILABLE && qtyStatus == InventoryLotLineStatus.AVAILABLE) { + true -> InventoryLotLineStatus.AVAILABLE + else -> InventoryLotLineStatus.UNAVAILABLE + } + remarks = request.remarks + } + + return inventoryLotLineRepository.save(inventoryLotLine) + } +} \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/stock/service/StockInLineService.kt b/src/main/java/com/ffii/fpsms/modules/stock/service/StockInLineService.kt index adc2a40..4c20021 100644 --- a/src/main/java/com/ffii/fpsms/modules/stock/service/StockInLineService.kt +++ b/src/main/java/com/ffii/fpsms/modules/stock/service/StockInLineService.kt @@ -30,6 +30,7 @@ import com.ffii.fpsms.modules.master.entity.WarehouseRepository import com.ffii.fpsms.modules.purchaseOrder.entity.PurchaseOrderRepository import com.ffii.fpsms.modules.purchaseOrder.enums.PurchaseOrderLineStatus import com.ffii.fpsms.modules.purchaseOrder.enums.PurchaseOrderStatus +import com.ffii.fpsms.modules.stock.entity.enum.InventoryLotLineStatus import com.ffii.fpsms.modules.stock.entity.projection.StockInLineInfo import java.io.FileNotFoundException import java.time.format.DateTimeFormatter @@ -143,7 +144,7 @@ open class StockInLineService( this.inventoryLot = stockInLine.inventoryLot this.warehouse = warehouse this.inQty = convertedBaseQty - this.status = "available" + this.status = InventoryLotLineStatus.AVAILABLE this.stockUom = stockItemUom } val savedInventoryLotLine = inventoryLotLineRepository.saveAndFlush(inventoryLotLine) diff --git a/src/main/java/com/ffii/fpsms/modules/stock/service/SuggestedPickLotService.kt b/src/main/java/com/ffii/fpsms/modules/stock/service/SuggestedPickLotService.kt new file mode 100644 index 0000000..ba22505 --- /dev/null +++ b/src/main/java/com/ffii/fpsms/modules/stock/service/SuggestedPickLotService.kt @@ -0,0 +1,124 @@ +package com.ffii.fpsms.modules.stock.service + +import com.ffii.fpsms.modules.pickOrder.entity.PickOrderLine +import com.ffii.fpsms.modules.pickOrder.entity.PickOrderLineRepository +import com.ffii.fpsms.modules.stock.entity.InventoryLotLineRepository +import com.ffii.fpsms.modules.stock.entity.StockOutLIneRepository +import com.ffii.fpsms.modules.stock.entity.SuggestPickLotRepository +import com.ffii.fpsms.modules.stock.entity.SuggestedPickLot +import com.ffii.fpsms.modules.stock.entity.enum.InventoryLotLineStatus +import com.ffii.fpsms.modules.stock.entity.projection.InventoryLotLineInfo +import com.ffii.fpsms.modules.stock.enums.SuggestedPickLotType +import com.ffii.fpsms.modules.stock.web.model.SaveSuggestedPickLotRequest +import com.ffii.fpsms.modules.stock.web.model.SuggestInventoryLotLineResponse +import org.springframework.stereotype.Service +import java.math.BigDecimal +import kotlin.jvm.optionals.getOrDefault +import kotlin.jvm.optionals.getOrNull + +@Service +open class SuggestedPickLotService( + val suggestedPickLotRepository: SuggestPickLotRepository, + val stockOutLIneRepository: StockOutLIneRepository, + val inventoryLotLineRepository: InventoryLotLineRepository, + val pickOrderLineRepository: PickOrderLineRepository, + val inventoryLotLineService: InventoryLotLineService, +) { + + open fun suggestionForPickOrderLines(pickOrderlines: List): List { + val itemIds = pickOrderlines.mapNotNull { it.item?.id } + val zero = BigDecimal.ZERO + + val suggestionList: MutableList = mutableListOf() + + // get current inventory lot line qty & grouped by item Id + val availableInventoryLotLines = inventoryLotLineService + .allInventoryLotLinesByItemIdIn(itemIds) + .filter { (it.inQty ?: zero).minus(it.outQty ?: zero).minus(it.holdQty ?: zero) > zero } + .filter { it.status == InventoryLotLineStatus.AVAILABLE.value } + .sortedBy { it.expiryDate } + .groupBy { it.item?.id } + + // loop for suggest pick lot line + pickOrderlines.forEach { line -> + val lotLines = availableInventoryLotLines[line.item?.id].orEmpty() + var remainingQty = line.qty ?: zero + val updatedLotLines = mutableListOf() + + lotLines.forEachIndexed { index, lotLine -> + if (remainingQty <= zero) return@forEachIndexed + + val availableQty = (lotLine.inQty ?: zero) + .minus(lotLine.outQty ?: zero) + .minus(lotLine.holdQty ?: zero) + + if (availableQty <= zero) { + updatedLotLines += lotLine + return@forEachIndexed + } + val inventoryLotLine = lotLine.id?.let { inventoryLotLineService.findById(it).getOrNull() } +// println("1:${inventoryLotLine?.id}: ${inventoryLotLine?.holdQty}") + val originalHoldQty = inventoryLotLine?.holdQty + + // Update Qty + val assignQty = minOf(availableQty, remainingQty) + remainingQty = remainingQty.minus(assignQty) + lotLine.holdQty = lotLine.holdQty?.plus(assignQty) +// println("2:${inventoryLotLine?.id}: ${inventoryLotLine?.holdQty}") +// println("3:${inventoryLotLine?.id}: ${originalHoldQty}") + suggestionList += SuggestedPickLot().apply { + type = SuggestedPickLotType.PICK_ORDER + suggestedLotLine = inventoryLotLine?.apply { + holdQty = originalHoldQty + } + pickOrderLine = line + qty = assignQty + } +// suggestionList += SuggestInventoryLotLineResponse( +// type = SuggestedPickLotType.PICK_ORDER.value, +// suggestedLotLine = inventoryLotLine, +// pickOrderLine = line, +// qty = assignQty +// ) + } + + // if still have remainingQty + if (remainingQty > zero) { + suggestionList += SuggestedPickLot().apply { + type = SuggestedPickLotType.PICK_ORDER + suggestedLotLine = null + pickOrderLine = line + qty = null + } +// suggestionList += SuggestInventoryLotLineResponse( +// type = SuggestedPickLotType.PICK_ORDER.value, +// suggestedLotLine = null, +// pickOrderLine = line, +// qty = null +// ) + } + } + return suggestionList + } + + open fun saveSuggestedPickLot(request: SaveSuggestedPickLotRequest): SuggestedPickLot { + val suggestedPickLot = + request.id?.let { id -> suggestedPickLotRepository.findById(id).getOrDefault(SuggestedPickLot()) } + ?: SuggestedPickLot() + val type = request.type?.let { _type -> SuggestedPickLotType.entries.find { it.value == _type } } + val stockOutLine = request.stockOutLineId?.let { id -> stockOutLIneRepository.findById(id).getOrNull() } + val suggestedLotLine = + request.suggestedLotLineId?.let { id -> inventoryLotLineRepository.findById(id).getOrNull() } + val pickOrderLine = request.pickOrderLineId?.let { id -> pickOrderLineRepository.findById(id).getOrNull() } + + suggestedPickLot.apply { + this.type = type + this.stockOutLine = stockOutLine + this.suggestedLotLine = suggestedLotLine + this.pickOrderLine = pickOrderLine + qty = request.qty + } + + return suggestedPickLotRepository.save(suggestedPickLot) + } +} \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/stock/web/InventoryLotLineController.kt b/src/main/java/com/ffii/fpsms/modules/stock/web/InventoryLotLineController.kt new file mode 100644 index 0000000..4f64ca6 --- /dev/null +++ b/src/main/java/com/ffii/fpsms/modules/stock/web/InventoryLotLineController.kt @@ -0,0 +1,23 @@ +package com.ffii.fpsms.modules.stock.web + +import com.ffii.fpsms.modules.pickOrder.web.models.ConsoPickOrderRequest +import com.ffii.fpsms.modules.stock.entity.InventoryLotLineRepository +import jakarta.validation.Valid +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.RequestParam +import org.springframework.web.bind.annotation.RestController + +@RequestMapping("/inventoryLotLine") +@RestController +class InventoryLotLineController ( + private val inventoryLotLineRepository: InventoryLotLineRepository + +){ + @PostMapping("/test") + fun test(@Valid @RequestBody request: ConsoPickOrderRequest) :Any { + return inventoryLotLineRepository.findInventoryLotLineInfoByInventoryLotItemIdIn(request.ids) + } +} \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/stock/web/SuggestedPickLotController.kt b/src/main/java/com/ffii/fpsms/modules/stock/web/SuggestedPickLotController.kt new file mode 100644 index 0000000..e1b4268 --- /dev/null +++ b/src/main/java/com/ffii/fpsms/modules/stock/web/SuggestedPickLotController.kt @@ -0,0 +1,25 @@ +package com.ffii.fpsms.modules.stock.web + +import com.ffii.fpsms.modules.pickOrder.entity.PickOrderLineRepository +import com.ffii.fpsms.modules.pickOrder.entity.PickOrderRepository +import com.ffii.fpsms.modules.pickOrder.enums.PickOrderStatus +import com.ffii.fpsms.modules.purchaseOrder.service.PurchaseOrderLineService +import com.ffii.fpsms.modules.stock.service.SuggestedPickLotService +import org.springframework.web.bind.annotation.GetMapping +import org.springframework.web.bind.annotation.PathVariable +import org.springframework.web.bind.annotation.RequestMapping +import org.springframework.web.bind.annotation.RestController + +@RequestMapping("/suggestedPickLot") +@RestController +class SuggestedPickLotController( + val suggestedPickLotService: SuggestedPickLotService, + val pickOrderLineService: PurchaseOrderLineService, + val pickOrderRepository: PickOrderRepository +) { + @GetMapping("/test/{conso}") + fun test(@PathVariable conso: String): Any { + val test1 = pickOrderRepository.findAllByConsoCode(conso).flatMap { it.pickOrderLines } + return suggestedPickLotService.suggestionForPickOrderLines(test1) + } +} \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/stock/web/model/SaveInventoryLotLineRequest.kt b/src/main/java/com/ffii/fpsms/modules/stock/web/model/SaveInventoryLotLineRequest.kt new file mode 100644 index 0000000..b41176e --- /dev/null +++ b/src/main/java/com/ffii/fpsms/modules/stock/web/model/SaveInventoryLotLineRequest.kt @@ -0,0 +1,15 @@ +package com.ffii.fpsms.modules.stock.web.model + +import java.math.BigDecimal + +data class SaveInventoryLotLineRequest( + val id: Long?, + val inventoryLotId: Long?, + val warehouseId: Long?, + val inQty: BigDecimal?, + val outQty: BigDecimal?, + val holdQty: BigDecimal?, + val stockUomId: Long?, + val status: String?, + val remarks: String? +) diff --git a/src/main/java/com/ffii/fpsms/modules/stock/web/model/SaveSuggestedPickLotRequest.kt b/src/main/java/com/ffii/fpsms/modules/stock/web/model/SaveSuggestedPickLotRequest.kt new file mode 100644 index 0000000..a5e33b2 --- /dev/null +++ b/src/main/java/com/ffii/fpsms/modules/stock/web/model/SaveSuggestedPickLotRequest.kt @@ -0,0 +1,12 @@ +package com.ffii.fpsms.modules.stock.web.model + +import java.math.BigDecimal + +data class SaveSuggestedPickLotRequest ( + val id: Long?, + val type: String?, + val stockOutLineId: Long?, + val suggestedLotLineId: Long?, + val pickOrderLineId: Long?, + val qty: BigDecimal? +) \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/stock/web/model/SuggestInventoryLotLineResponse.kt b/src/main/java/com/ffii/fpsms/modules/stock/web/model/SuggestInventoryLotLineResponse.kt new file mode 100644 index 0000000..61dedbe --- /dev/null +++ b/src/main/java/com/ffii/fpsms/modules/stock/web/model/SuggestInventoryLotLineResponse.kt @@ -0,0 +1,13 @@ +package com.ffii.fpsms.modules.stock.web.model + +import com.ffii.fpsms.modules.pickOrder.entity.PickOrderLine +import com.ffii.fpsms.modules.stock.entity.InventoryLotLine +import com.ffii.fpsms.modules.stock.enums.SuggestedPickLotType +import java.math.BigDecimal + +data class SuggestInventoryLotLineResponse( + val type: String?, + val suggestedLotLine: InventoryLotLine?, + val pickOrderLine: PickOrderLine?, + val qty: BigDecimal? +) From d2cdfcae778624e464af7b481328f81f645ad98a Mon Sep 17 00:00:00 2001 From: "cyril.tsui" Date: Mon, 23 Jun 2025 13:31:16 +0800 Subject: [PATCH 4/6] comment test function --- .../modules/stock/web/InventoryLotLineController.kt | 8 ++++---- .../modules/stock/web/SuggestedPickLotController.kt | 10 +++++----- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/main/java/com/ffii/fpsms/modules/stock/web/InventoryLotLineController.kt b/src/main/java/com/ffii/fpsms/modules/stock/web/InventoryLotLineController.kt index 4f64ca6..c9886b7 100644 --- a/src/main/java/com/ffii/fpsms/modules/stock/web/InventoryLotLineController.kt +++ b/src/main/java/com/ffii/fpsms/modules/stock/web/InventoryLotLineController.kt @@ -16,8 +16,8 @@ class InventoryLotLineController ( private val inventoryLotLineRepository: InventoryLotLineRepository ){ - @PostMapping("/test") - fun test(@Valid @RequestBody request: ConsoPickOrderRequest) :Any { - return inventoryLotLineRepository.findInventoryLotLineInfoByInventoryLotItemIdIn(request.ids) - } +// @PostMapping("/test") +// fun test(@Valid @RequestBody request: ConsoPickOrderRequest) :Any { +// return inventoryLotLineRepository.findInventoryLotLineInfoByInventoryLotItemIdIn(request.ids) +// } } \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/stock/web/SuggestedPickLotController.kt b/src/main/java/com/ffii/fpsms/modules/stock/web/SuggestedPickLotController.kt index e1b4268..14f2b13 100644 --- a/src/main/java/com/ffii/fpsms/modules/stock/web/SuggestedPickLotController.kt +++ b/src/main/java/com/ffii/fpsms/modules/stock/web/SuggestedPickLotController.kt @@ -17,9 +17,9 @@ class SuggestedPickLotController( val pickOrderLineService: PurchaseOrderLineService, val pickOrderRepository: PickOrderRepository ) { - @GetMapping("/test/{conso}") - fun test(@PathVariable conso: String): Any { - val test1 = pickOrderRepository.findAllByConsoCode(conso).flatMap { it.pickOrderLines } - return suggestedPickLotService.suggestionForPickOrderLines(test1) - } +// @GetMapping("/test/{conso}") +// fun test(@PathVariable conso: String): Any { +// val test1 = pickOrderRepository.findAllByConsoCode(conso).flatMap { it.pickOrderLines } +// return suggestedPickLotService.suggestionForPickOrderLines(test1) +// } } \ No newline at end of file From a8053c760b60d660531d7fe1dc3d9348fc8cb1a1 Mon Sep 17 00:00:00 2001 From: "cyril.tsui" Date: Mon, 23 Jun 2025 17:17:17 +0800 Subject: [PATCH 5/6] Update for suggested pick lot --- .../pickOrder/service/PickOrderService.kt | 31 ++++---- .../web/models/ConsoPickOrderRequest.kt | 9 +++ .../stock/service/SuggestedPickLotService.kt | 78 ++++++++++++------- .../stock/web/SuggestedPickLotController.kt | 1 + .../model/SuggestInventoryLotLineResponse.kt | 13 ---- .../web/model/SuggestedPickLotRequest.kt | 17 ++++ .../web/model/SuggestedPickLotResponse.kt | 16 ++++ 7 files changed, 109 insertions(+), 56 deletions(-) delete mode 100644 src/main/java/com/ffii/fpsms/modules/stock/web/model/SuggestInventoryLotLineResponse.kt create mode 100644 src/main/java/com/ffii/fpsms/modules/stock/web/model/SuggestedPickLotRequest.kt create mode 100644 src/main/java/com/ffii/fpsms/modules/stock/web/model/SuggestedPickLotResponse.kt diff --git a/src/main/java/com/ffii/fpsms/modules/pickOrder/service/PickOrderService.kt b/src/main/java/com/ffii/fpsms/modules/pickOrder/service/PickOrderService.kt index 950be8f..17fd78a 100644 --- a/src/main/java/com/ffii/fpsms/modules/pickOrder/service/PickOrderService.kt +++ b/src/main/java/com/ffii/fpsms/modules/pickOrder/service/PickOrderService.kt @@ -9,7 +9,7 @@ import com.ffii.fpsms.modules.pickOrder.enums.PickOrderStatus import com.ffii.fpsms.modules.pickOrder.web.models.* import com.ffii.fpsms.modules.stock.service.StockOutLineService import com.ffii.fpsms.modules.stock.service.SuggestedPickLotService -import com.ffii.fpsms.modules.stock.web.model.SuggestInventoryLotLineResponse +import com.ffii.fpsms.modules.stock.web.model.SuggestedPickLotForPoRequest import org.springframework.context.annotation.Lazy import org.springframework.data.domain.PageRequest import org.springframework.stereotype.Service @@ -25,7 +25,7 @@ open class PickOrderService( val stockOutLineService: StockOutLineService, val suggestedPickLotService: SuggestedPickLotService, ) { - open fun LocalDateTimeParse(dateTime: String?, pattern: String? = "YYYY-MM-DD hh:mm:ss"): LocalDateTime? { + open fun localDateTimeParse(dateTime: String?, pattern: String? = "YYYY-MM-DD hh:mm:ss"): LocalDateTime? { try { val formatter = DateTimeFormatter.ofPattern(pattern!!) return LocalDateTime.parse(dateTime, formatter) @@ -43,8 +43,8 @@ open class PickOrderService( val response = pickOrderRepository.findPickOrderInfoByConditionsAndPageable( code = request.code ?: "all", - targetDateFrom = LocalDateTimeParse(request.targetDateFrom), - targetDateTo = LocalDateTimeParse(request.targetDateTo), + targetDateFrom = localDateTimeParse(request.targetDateFrom), + targetDateTo = localDateTimeParse(request.targetDateTo), type = request.type ?: "all", status = request.status ?: "all", itemName = request.itemName ?: "all", @@ -106,21 +106,23 @@ open class PickOrderService( return updatedPickOrderInfos } + // TODO: Add actual pick lots open fun consoPickOrderDetail(consoCode: String): ConsoPickOrderResponse { - // Conso code - // Pick orders with items - // Items - val zero = BigDecimal.ZERO // pick orders - // Mapping: PickOrder -> PickOrderInConso val pos = pickOrderRepository.findAllByConsoCode(consoCode) + + // Suggestions for Pick Order + val suggestions = suggestedPickLotService.suggestionForPickOrders(SuggestedPickLotForPoRequest(pickOrders = pos)) + val suggestedList = suggestions.suggestedList + + // Mapping: PickOrder -> PickOrderInConso val finalPos = pos.map { po -> val pols = po.pickOrderLines // Suggestions for Pick Order Line - val suggestions = suggestedPickLotService.suggestionForPickOrderLines(pols) + // val suggestions = suggestedPickLotService.suggestionForPickOrderLines(pols) // Pick Order Lines // Mapping: PickOrderLine -> PickOrderLineInConso @@ -131,7 +133,7 @@ open class PickOrderService( // Check If already have suggestion var suggestion = pol.suggestedPickLots if (suggestion.isEmpty()) { - suggestion = suggestions.filter { it.pickOrderLine?.id == pol.id }.toMutableList() + suggestion = suggestedList.filter { it.pickOrderLine?.id == pol.id }.toMutableList() } // Mapping: SuggestedPickLot -> SuggestPickLotInConso @@ -217,8 +219,7 @@ open class PickOrderService( code = _line.first().item.code, name = _line.first().item.name, qty = _line.fold(zero) { sum, item -> sum + (item.qty ?: zero)}, - suggestPickLots = _line - .flatMap { it.suggestPickLots }, + suggestPickLots = itemSuggestions, actualPickLots = mutableListOf() ) } @@ -231,4 +232,8 @@ open class PickOrderService( return response } + + open fun releaseConsoPickOrder(request: ReleaseConsoPickOrderRequest) { + val pos = pickOrderRepository.findAllByConsoCode(request.consoCode) + } } \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/pickOrder/web/models/ConsoPickOrderRequest.kt b/src/main/java/com/ffii/fpsms/modules/pickOrder/web/models/ConsoPickOrderRequest.kt index 41d6a7c..91d7fd7 100644 --- a/src/main/java/com/ffii/fpsms/modules/pickOrder/web/models/ConsoPickOrderRequest.kt +++ b/src/main/java/com/ffii/fpsms/modules/pickOrder/web/models/ConsoPickOrderRequest.kt @@ -1,5 +1,14 @@ package com.ffii.fpsms.modules.pickOrder.web.models +import com.ffii.fpsms.modules.stock.web.model.SaveSuggestedPickLotRequest + +// Consolidated / De-consolidated data class ConsoPickOrderRequest ( val ids: List +) + +// Release Consolidated Pick Order +data class ReleaseConsoPickOrderRequest ( + val consoCode: String, + val suggestedPickLots: List ) \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/stock/service/SuggestedPickLotService.kt b/src/main/java/com/ffii/fpsms/modules/stock/service/SuggestedPickLotService.kt index ba22505..f40e39d 100644 --- a/src/main/java/com/ffii/fpsms/modules/stock/service/SuggestedPickLotService.kt +++ b/src/main/java/com/ffii/fpsms/modules/stock/service/SuggestedPickLotService.kt @@ -1,5 +1,6 @@ package com.ffii.fpsms.modules.stock.service +import com.ffii.fpsms.modules.pickOrder.entity.PickOrder import com.ffii.fpsms.modules.pickOrder.entity.PickOrderLine import com.ffii.fpsms.modules.pickOrder.entity.PickOrderLineRepository import com.ffii.fpsms.modules.stock.entity.InventoryLotLineRepository @@ -10,7 +11,9 @@ import com.ffii.fpsms.modules.stock.entity.enum.InventoryLotLineStatus import com.ffii.fpsms.modules.stock.entity.projection.InventoryLotLineInfo import com.ffii.fpsms.modules.stock.enums.SuggestedPickLotType import com.ffii.fpsms.modules.stock.web.model.SaveSuggestedPickLotRequest -import com.ffii.fpsms.modules.stock.web.model.SuggestInventoryLotLineResponse +import com.ffii.fpsms.modules.stock.web.model.SuggestedPickLotForPoRequest +import com.ffii.fpsms.modules.stock.web.model.SuggestedPickLotForPolRequest +import com.ffii.fpsms.modules.stock.web.model.SuggestedPickLotResponse import org.springframework.stereotype.Service import java.math.BigDecimal import kotlin.jvm.optionals.getOrDefault @@ -24,12 +27,31 @@ open class SuggestedPickLotService( val pickOrderLineRepository: PickOrderLineRepository, val inventoryLotLineService: InventoryLotLineService, ) { + open fun suggestionForPickOrders(request: SuggestedPickLotForPoRequest): SuggestedPickLotResponse { + val pos = request.pickOrders + val suggestedList = mutableListOf() + var holdQtyMap = request.holdQtyMap - open fun suggestionForPickOrderLines(pickOrderlines: List): List { - val itemIds = pickOrderlines.mapNotNull { it.item?.id } + pos.forEach { + val response = suggestionForPickOrderLines(SuggestedPickLotForPolRequest( + holdQtyMap = holdQtyMap, + pickOrderLines = it.pickOrderLines + )) + + holdQtyMap = response.holdQtyMap + suggestedList += response.suggestedList + } + + return SuggestedPickLotResponse(holdQtyMap = holdQtyMap, suggestedList = suggestedList) + } + + open fun suggestionForPickOrderLines(request: SuggestedPickLotForPolRequest): SuggestedPickLotResponse { + val pols = request.pickOrderLines + val itemIds = pols.mapNotNull { it.item?.id } val zero = BigDecimal.ZERO - val suggestionList: MutableList = mutableListOf() + val suggestedList: MutableList = mutableListOf() + val holdQtyMap: MutableMap = request.holdQtyMap // get current inventory lot line qty & grouped by item Id val availableInventoryLotLines = inventoryLotLineService @@ -40,7 +62,7 @@ open class SuggestedPickLotService( .groupBy { it.item?.id } // loop for suggest pick lot line - pickOrderlines.forEach { line -> + pols.forEach { line -> val lotLines = availableInventoryLotLines[line.item?.id].orEmpty() var remainingQty = line.qty ?: zero val updatedLotLines = mutableListOf() @@ -50,58 +72,44 @@ open class SuggestedPickLotService( val availableQty = (lotLine.inQty ?: zero) .minus(lotLine.outQty ?: zero) - .minus(lotLine.holdQty ?: zero) + .minus((lotLine.holdQty ?: zero) + .plus(holdQtyMap[lotLine.id] ?: zero) + ) if (availableQty <= zero) { updatedLotLines += lotLine return@forEachIndexed } val inventoryLotLine = lotLine.id?.let { inventoryLotLineService.findById(it).getOrNull() } -// println("1:${inventoryLotLine?.id}: ${inventoryLotLine?.holdQty}") val originalHoldQty = inventoryLotLine?.holdQty // Update Qty val assignQty = minOf(availableQty, remainingQty) remainingQty = remainingQty.minus(assignQty) - lotLine.holdQty = lotLine.holdQty?.plus(assignQty) -// println("2:${inventoryLotLine?.id}: ${inventoryLotLine?.holdQty}") -// println("3:${inventoryLotLine?.id}: ${originalHoldQty}") - suggestionList += SuggestedPickLot().apply { + holdQtyMap[lotLine.id] = (holdQtyMap[lotLine.id] ?: zero).plus(assignQty) +// lotLine.holdQty = lotLine.holdQty?.plus(assignQty) + suggestedList += SuggestedPickLot().apply { type = SuggestedPickLotType.PICK_ORDER - suggestedLotLine = inventoryLotLine?.apply { - holdQty = originalHoldQty - } + suggestedLotLine = inventoryLotLine pickOrderLine = line qty = assignQty } -// suggestionList += SuggestInventoryLotLineResponse( -// type = SuggestedPickLotType.PICK_ORDER.value, -// suggestedLotLine = inventoryLotLine, -// pickOrderLine = line, -// qty = assignQty -// ) } // if still have remainingQty if (remainingQty > zero) { - suggestionList += SuggestedPickLot().apply { + suggestedList += SuggestedPickLot().apply { type = SuggestedPickLotType.PICK_ORDER suggestedLotLine = null pickOrderLine = line - qty = null + qty = remainingQty } -// suggestionList += SuggestInventoryLotLineResponse( -// type = SuggestedPickLotType.PICK_ORDER.value, -// suggestedLotLine = null, -// pickOrderLine = line, -// qty = null -// ) } } - return suggestionList + return SuggestedPickLotResponse(holdQtyMap = holdQtyMap, suggestedList = suggestedList) } - open fun saveSuggestedPickLot(request: SaveSuggestedPickLotRequest): SuggestedPickLot { + open fun convertRequestToEntity(request: SaveSuggestedPickLotRequest): SuggestedPickLot{ val suggestedPickLot = request.id?.let { id -> suggestedPickLotRepository.findById(id).getOrDefault(SuggestedPickLot()) } ?: SuggestedPickLot() @@ -119,6 +127,16 @@ open class SuggestedPickLotService( qty = request.qty } + return suggestedPickLot + } + + open fun saveSuggestedPickLot(request: SaveSuggestedPickLotRequest): SuggestedPickLot { + val suggestedPickLot = convertRequestToEntity(request) + return suggestedPickLotRepository.save(suggestedPickLot) } + + open fun saveAll(request: List): List { + return suggestedPickLotRepository.saveAll(request) + } } \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/stock/web/SuggestedPickLotController.kt b/src/main/java/com/ffii/fpsms/modules/stock/web/SuggestedPickLotController.kt index 14f2b13..68a129d 100644 --- a/src/main/java/com/ffii/fpsms/modules/stock/web/SuggestedPickLotController.kt +++ b/src/main/java/com/ffii/fpsms/modules/stock/web/SuggestedPickLotController.kt @@ -7,6 +7,7 @@ import com.ffii.fpsms.modules.purchaseOrder.service.PurchaseOrderLineService import com.ffii.fpsms.modules.stock.service.SuggestedPickLotService import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.bind.annotation.PathVariable +import org.springframework.web.bind.annotation.PostMapping import org.springframework.web.bind.annotation.RequestMapping import org.springframework.web.bind.annotation.RestController diff --git a/src/main/java/com/ffii/fpsms/modules/stock/web/model/SuggestInventoryLotLineResponse.kt b/src/main/java/com/ffii/fpsms/modules/stock/web/model/SuggestInventoryLotLineResponse.kt deleted file mode 100644 index 61dedbe..0000000 --- a/src/main/java/com/ffii/fpsms/modules/stock/web/model/SuggestInventoryLotLineResponse.kt +++ /dev/null @@ -1,13 +0,0 @@ -package com.ffii.fpsms.modules.stock.web.model - -import com.ffii.fpsms.modules.pickOrder.entity.PickOrderLine -import com.ffii.fpsms.modules.stock.entity.InventoryLotLine -import com.ffii.fpsms.modules.stock.enums.SuggestedPickLotType -import java.math.BigDecimal - -data class SuggestInventoryLotLineResponse( - val type: String?, - val suggestedLotLine: InventoryLotLine?, - val pickOrderLine: PickOrderLine?, - val qty: BigDecimal? -) diff --git a/src/main/java/com/ffii/fpsms/modules/stock/web/model/SuggestedPickLotRequest.kt b/src/main/java/com/ffii/fpsms/modules/stock/web/model/SuggestedPickLotRequest.kt new file mode 100644 index 0000000..361fe42 --- /dev/null +++ b/src/main/java/com/ffii/fpsms/modules/stock/web/model/SuggestedPickLotRequest.kt @@ -0,0 +1,17 @@ +package com.ffii.fpsms.modules.stock.web.model + +import com.ffii.fpsms.modules.pickOrder.entity.PickOrder +import com.ffii.fpsms.modules.pickOrder.entity.PickOrderLine +import java.math.BigDecimal + +data class SuggestedPickLotForPoRequest( + val holdQtyMap: MutableMap = mutableMapOf(), + val pickOrders: List +) + +data class SuggestedPickLotForPolRequest( + val holdQtyMap: MutableMap = mutableMapOf(), + val pickOrderLines: List +) + + diff --git a/src/main/java/com/ffii/fpsms/modules/stock/web/model/SuggestedPickLotResponse.kt b/src/main/java/com/ffii/fpsms/modules/stock/web/model/SuggestedPickLotResponse.kt new file mode 100644 index 0000000..3cad900 --- /dev/null +++ b/src/main/java/com/ffii/fpsms/modules/stock/web/model/SuggestedPickLotResponse.kt @@ -0,0 +1,16 @@ +package com.ffii.fpsms.modules.stock.web.model + +import com.ffii.fpsms.modules.stock.entity.SuggestedPickLot +import java.math.BigDecimal + +//data class SuggestInventoryLotLine( +// val type: String?, +// val suggestedLotLine: InventoryLotLine?, +// val pickOrderLine: PickOrderLine?, +// val qty: BigDecimal? +//) + +data class SuggestedPickLotResponse( + val holdQtyMap: MutableMap, + val suggestedList: List +) From 961b6f813e9017bf0a7b70249ed3e27ff85a38be Mon Sep 17 00:00:00 2001 From: "cyril.tsui" Date: Tue, 24 Jun 2025 11:42:17 +0800 Subject: [PATCH 6/6] update --- .../modules/pickOrder/service/PickOrderService.kt | 14 ++++++++++---- .../pickOrder/web/models/ConsoPickOrderResponse.kt | 1 + 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/ffii/fpsms/modules/pickOrder/service/PickOrderService.kt b/src/main/java/com/ffii/fpsms/modules/pickOrder/service/PickOrderService.kt index 17fd78a..d3dadf4 100644 --- a/src/main/java/com/ffii/fpsms/modules/pickOrder/service/PickOrderService.kt +++ b/src/main/java/com/ffii/fpsms/modules/pickOrder/service/PickOrderService.kt @@ -1,6 +1,7 @@ package com.ffii.fpsms.modules.pickOrder.service import com.ffii.core.response.RecordsRes +import com.ffii.fpsms.modules.master.web.models.MessageResponse import com.ffii.fpsms.modules.pickOrder.entity.PickOrder import com.ffii.fpsms.modules.pickOrder.entity.PickOrderRepository import com.ffii.fpsms.modules.pickOrder.entity.projection.PickOrderInfo @@ -112,7 +113,7 @@ open class PickOrderService( // pick orders val pos = pickOrderRepository.findAllByConsoCode(consoCode) - + // Suggestions for Pick Order val suggestions = suggestedPickLotService.suggestionForPickOrders(SuggestedPickLotForPoRequest(pickOrders = pos)) val suggestedList = suggestions.suggestedList @@ -147,6 +148,7 @@ open class PickOrderService( // Mapping: InventoryLotLine -> InventoryLotLineInConso val finalInventoryLotLine = InventoryLotLineInConso( id = inventoryLotLine?.id, + lotNo = inventoryLotLine?.inventoryLot?.lotNo, inQty = inventoryLotLine?.inQty, outQty = inventoryLotLine?.outQty, holdQty = inventoryLotLine?.holdQty, @@ -233,7 +235,11 @@ open class PickOrderService( return response } - open fun releaseConsoPickOrder(request: ReleaseConsoPickOrderRequest) { - val pos = pickOrderRepository.findAllByConsoCode(request.consoCode) - } +// open fun releaseConsoPickOrder(request: ReleaseConsoPickOrderRequest): MessageResponse { +// val pos = pickOrderRepository.findAllByConsoCode(request.consoCode) +// val suggestedPickLots = suggestedPickLotService.convertRequestsToEntities(request.suggestedPickLots) +// +// suggestedPickLotService.saveAll(suggestedPickLots) +// // TODO: Add Response +// } } \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/pickOrder/web/models/ConsoPickOrderResponse.kt b/src/main/java/com/ffii/fpsms/modules/pickOrder/web/models/ConsoPickOrderResponse.kt index 01607f6..0e3d44e 100644 --- a/src/main/java/com/ffii/fpsms/modules/pickOrder/web/models/ConsoPickOrderResponse.kt +++ b/src/main/java/com/ffii/fpsms/modules/pickOrder/web/models/ConsoPickOrderResponse.kt @@ -51,6 +51,7 @@ data class ActualPickLotInConso( data class InventoryLotLineInConso( val id: Long?, + val lotNo: String?, val inQty: BigDecimal?, val outQty: BigDecimal?, val holdQty: BigDecimal?,