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? +)