| @@ -15,7 +15,7 @@ open class M18TokenService( | |||
| private val m18Config: M18Config | |||
| ) { | |||
| @Bean | |||
| // @Bean | |||
| fun run() { | |||
| // val params: MutableMap<String, String> = mutableMapOf( | |||
| // "grant_type" to m18Config.GRANT_TYPE, | |||
| @@ -153,6 +153,7 @@ open class PickOrderService( | |||
| + " i.name as itemName, " | |||
| + " pol.qty as qty, " | |||
| + " max(uc.code) as uom, " | |||
| + " group_concat(JSON_ARRAY(ill.id)) as lotLineId, " | |||
| + " group_concat(JSON_ARRAY(w.name)) as warehouse, " | |||
| + " group_concat(JSON_ARRAY(il.lotNo)) as suggestedLotNo " | |||
| + " from pick_order po " | |||
| @@ -8,4 +8,5 @@ import org.springframework.stereotype.Repository | |||
| @Repository | |||
| interface QcResultRepository: AbstractRepository<QcResult, Long> { | |||
| fun findQcResultInfoByStockInLineIdAndDeletedFalse(stockInLineId: Long): List<QcResultInfo> | |||
| fun findQcResultInfoByStockOutLineIdAndDeletedFalse(stockOutLineId: Long): List<QcResultInfo> | |||
| } | |||
| @@ -10,7 +10,9 @@ interface QcResultInfo { | |||
| val name: String | |||
| @get:Value("#{target.qcItem.code}") | |||
| val code: String | |||
| @get:Value("#{target.stockInLine.id}") | |||
| val stockInLineId: Long | |||
| @get:Value("#{target.stockInLine?.id}") | |||
| val stockInLineId: Long? | |||
| @get:Value("#{target.stockOutLine?.id}") | |||
| val stockOutLineId: Long? | |||
| val failQty: Double | |||
| } | |||
| @@ -57,4 +57,7 @@ open class QcResultService( | |||
| open fun getAllQcResultInfoByStockInLineId(stockInLineId: Long): List<QcResultInfo> { | |||
| return qcResultRepository.findQcResultInfoByStockInLineIdAndDeletedFalse(stockInLineId) | |||
| } | |||
| open fun getAllQcResultInfoByStockOutLineId(stockOutLineId: Long): List<QcResultInfo> { | |||
| return qcResultRepository.findQcResultInfoByStockOutLineIdAndDeletedFalse(stockOutLineId) | |||
| } | |||
| } | |||
| @@ -23,4 +23,9 @@ class QcResultController( | |||
| fun getAllQcResultInfoByStockInLineId(@PathVariable stockInLineId: Long): List<QcResultInfo> { | |||
| return qcResultService.getAllQcResultInfoByStockInLineId(stockInLineId) | |||
| } | |||
| @GetMapping("/pick-order/{stockOutLineId}") | |||
| fun getAllQcResultInfoByStockOutLineId(@PathVariable stockOutLineId: Long): List<QcResultInfo> { | |||
| return qcResultService.getAllQcResultInfoByStockOutLineId(stockOutLineId) | |||
| } | |||
| } | |||
| @@ -10,4 +10,8 @@ interface StockOutLIneRepository: AbstractRepository<StockOutLine, Long> { | |||
| fun findAllByStockOutIdAndDeletedFalse(stockOutId: Long): List<StockOutLine> | |||
| // fun findAllByStockOutIdAndDeletedFalse(stockOutId: Long, status: StockOutStatus): List<StockOutLine> | |||
| fun findAllByPickOrderLineIdAndDeletedFalse(pickOrderLineId: Long): List<StockOutLineInfo> | |||
| fun findAllByIdIn(id: List<Long>): List<StockOutLineInfo> | |||
| fun findStockOutLineInfoById(id: Long): StockOutLineInfo | |||
| } | |||
| @@ -20,11 +20,13 @@ open class StockOutLine: BaseEntity<Long>() { | |||
| @Column(name = "qty") | |||
| open var qty: Double? = null | |||
| @JsonBackReference | |||
| @NotNull | |||
| @ManyToOne | |||
| @JoinColumn(name = "stockOutId") | |||
| open var stockOut: StockOut? = null | |||
| @JsonBackReference | |||
| @ManyToOne | |||
| @JoinColumn(name = "inventoryLotLineId") | |||
| open var inventoryLotLine: InventoryLotLine? = null | |||
| @@ -10,17 +10,21 @@ interface StockOutLineInfo { | |||
| val itemId: Long | |||
| @get:Value("#{target.item?.name}") | |||
| val itemName: String? | |||
| @get:Value("#{target.item?.code}") | |||
| val itemNo: String | |||
| val qty: BigDecimal | |||
| @get:Value("#{target.stockOut?.id}") | |||
| val stockOutId: Long | |||
| @get:Value("#{target.pickOrderLine?.id}") | |||
| val pickOrderLineId: Long | |||
| val pickOrderLineId: Long? | |||
| @get:Value("#{target.inventoryLotLine?.id}") | |||
| val inventoryLotLineId: Long? | |||
| @get:Value("#{target.inventoryLotLine?.inventoryLot?.lotNo}") | |||
| val lotNo: String? | |||
| val status: String | |||
| val pickTime: LocalDateTime? | |||
| } | |||
| @@ -193,6 +193,26 @@ open class StockInLineService( | |||
| @Transactional | |||
| fun updatePurchaseOrderLineStatus(request: SaveStockInLineRequest) { | |||
| println(request.status) | |||
| if (request.status == StockInLineStatus.RECEIVING.status) { | |||
| val unQcedLines = stockInLineRepository.findStockInLineInfoByPurchaseOrderLineIdAndDeletedFalse(purchaseOrderLineId = request.purchaseOrderLineId) | |||
| .filter { | |||
| it.status != StockInLineStatus.RECEIVING.status | |||
| && it.status != StockInLineStatus.RECEIVED.status | |||
| && it.status != StockInLineStatus.COMPLETE.status | |||
| && it.status != StockInLineStatus.REJECT.status | |||
| } | |||
| if (unQcedLines.isEmpty()) { | |||
| // all stock in lines finished | |||
| // change status of purchase order line | |||
| val purchaseOrderLine = polRepository.findById(request.purchaseOrderLineId).orElseThrow() | |||
| purchaseOrderLine.apply { | |||
| status = PurchaseOrderLineStatus.RECEIVING | |||
| } | |||
| polRepository.saveAndFlush(purchaseOrderLine) | |||
| } else { | |||
| // still have unQcedLines lines | |||
| } | |||
| } | |||
| if (request.status == StockInLineStatus.COMPLETE.status || request.status == StockInLineStatus.REJECT.status) { | |||
| // val unfinishedLines = stockInLineRepository.findStockInLineInfoByPurchaseOrderLineIdAndStatusNotAndDeletedFalse(purchaseOrderLineId = request.purchaseOrderLineId, status = request.status!!) | |||
| val unfinishedLines = stockInLineRepository.findStockInLineInfoByPurchaseOrderLineIdAndDeletedFalse(purchaseOrderLineId = request.purchaseOrderLineId) | |||
| @@ -5,12 +5,11 @@ import com.ffii.core.support.JdbcDao | |||
| import com.ffii.fpsms.modules.master.entity.ItemsRepository | |||
| import com.ffii.fpsms.modules.master.web.models.MessageResponse | |||
| import com.ffii.fpsms.modules.pickOrder.entity.PickOrderLineRepository | |||
| import com.ffii.fpsms.modules.pickOrder.enums.PickOrderLineStatus | |||
| import com.ffii.fpsms.modules.stock.entity.* | |||
| import com.ffii.fpsms.modules.stock.entity.projection.StockOutLineInfo | |||
| import com.ffii.fpsms.modules.stock.web.model.SaveStockOutLineRequest | |||
| import com.ffii.fpsms.modules.stock.web.model.SaveStockOutRequest | |||
| import com.ffii.fpsms.modules.stock.web.model.StockOutLineStatus | |||
| import com.ffii.fpsms.modules.stock.web.model.StockOutStatus | |||
| import com.ffii.fpsms.modules.stock.web.model.* | |||
| import org.springframework.data.repository.query.Param | |||
| import org.springframework.stereotype.Service | |||
| import org.springframework.transaction.annotation.Transactional | |||
| import java.io.IOException | |||
| @@ -61,21 +60,30 @@ open class StockOutLineService( | |||
| } | |||
| @Transactional | |||
| open fun create(request: SaveStockOutLineRequest): MessageResponse { | |||
| open fun create(request: CreateStockOutLineRequest): MessageResponse { | |||
| // pick flow step 1 | |||
| // println(request.pickOrderLineId) | |||
| val stockOut = stockOutRepository.findByConsoPickOrderCode(request.consoCode).orElseThrow() | |||
| val pickOrderLine = pickOrderLineRepository.findById(request.pickOrderLineId).orElseThrow() | |||
| val item = itemRepository.findById(request.itemId).orElseThrow() | |||
| val pickOrderLine = pickOrderLineRepository.saveAndFlush( | |||
| pickOrderLineRepository.findById(request.pickOrderLineId).orElseThrow() | |||
| .apply { | |||
| this.status = PickOrderLineStatus.PICKING | |||
| } | |||
| ) | |||
| val item = itemRepository.findById(pickOrderLine.item!!.id!!).orElseThrow() | |||
| val inventoryLotLine = inventoryLotLineRepository.findById(request.inventoryLotLineId).orElseThrow() | |||
| val stockOutLine = StockOutLine() | |||
| .apply { | |||
| this.item = item | |||
| this.qty = request.qty | |||
| this.stockOut = stockOut | |||
| this.inventoryLotLine | |||
| this.inventoryLotLine = inventoryLotLine | |||
| this.pickOrderLine = pickOrderLine | |||
| this.status = StockOutLineStatus.PENDING.status | |||
| } | |||
| val savedStockOutLine = saveAndFlush(stockOutLine) | |||
| val mappedSavedStockOutLine = stockOutLineRepository.findStockOutLineInfoById(savedStockOutLine.id!!) | |||
| // println("triggering") | |||
| return MessageResponse( | |||
| id = savedStockOutLine.id, | |||
| name = savedStockOutLine.inventoryLotLine!!.inventoryLot!!.lotNo, | |||
| @@ -83,13 +91,126 @@ open class StockOutLineService( | |||
| type = savedStockOutLine.status, | |||
| message = "success", | |||
| errorPosition = null, | |||
| entity = savedStockOutLine, | |||
| entity = mappedSavedStockOutLine, | |||
| ) | |||
| } | |||
| @Transactional | |||
| open fun update(request: SaveStockOutLineRequest) { | |||
| val stockOutLine = stockOutLineRepository.findById(request.id!!).orElseThrow() | |||
| fun handleQc(stockOutLine: StockOutLine, request: UpdateStockOutLineRequest): List<StockOutLine?> { | |||
| var newStockOutLine: StockOutLine? = null | |||
| if (request.qty < stockOutLine.qty!!) { | |||
| newStockOutLine = StockOutLine().apply { | |||
| this.pickOrderLine = stockOutLine.pickOrderLine | |||
| this.stockOut = stockOutLine.stockOut | |||
| this.item = stockOutLine.item | |||
| this.qty = stockOutLine.qty!! - request.qty | |||
| this.status = StockOutLineStatus.DETERMINE1.status // escalated | |||
| } | |||
| } | |||
| val inventoryLotLine = if (request.inventoryLotLineId != null) | |||
| inventoryLotLineRepository.findById(request.inventoryLotLineId).orElseThrow() | |||
| else null | |||
| stockOutLine.apply { | |||
| this.inventoryLotLine = inventoryLotLine ?: stockOutLine.inventoryLotLine | |||
| this.qty = request.qty | |||
| this.status = StockOutLineStatus.COMPLETE.status // complete | |||
| } | |||
| return listOf(stockOutLine, newStockOutLine) | |||
| } | |||
| @Transactional | |||
| fun handleLotChangeApprovalOrReject(stockOutLine: StockOutLine, request: UpdateStockOutLineRequest): List<StockOutLine?> { | |||
| /** | |||
| * @param request.qty | |||
| * qty that requires lot change | |||
| */ | |||
| var newStockOutLine: StockOutLine? = null | |||
| if (request.qty < stockOutLine.qty!!) { | |||
| newStockOutLine = StockOutLine().apply { | |||
| this.pickOrderLine = stockOutLine.pickOrderLine | |||
| this.stockOut = stockOutLine.stockOut | |||
| this.item = stockOutLine.item | |||
| this.qty = stockOutLine.qty!! - request.qty | |||
| this.status = StockOutLineStatus.REJECTED.status // original status | |||
| } | |||
| } | |||
| stockOutLine.apply { | |||
| this.qty = request.qty | |||
| this.status = StockOutLineStatus.LOT_CHANGE_APPROVAL.status // require new lot no | |||
| } | |||
| return listOf(stockOutLine, newStockOutLine) | |||
| } | |||
| // @Transactional | |||
| // fun assignNewLotLine(stockOutLine: StockOutLine, request: UpdateStockOutLineRequest): List<StockOutLine?> { | |||
| // val newInventoryLotLine = inventoryLotLineRepository.findById(request.inventoryLotLineId).orElseThrow() | |||
| // return listOf(stockOutLine.apply { | |||
| // this.inventoryLotLine = newInventoryLotLine | |||
| // }) | |||
| // } | |||
| private fun checkIsStockOutLineCompleted(pickOrderLineId: Long) { | |||
| val unfinishedLine = stockOutLineRepository | |||
| .findAllByPickOrderLineIdAndDeletedFalse(pickOrderLineId) | |||
| .filter { | |||
| it.status != StockOutLineStatus.COMPLETE.status | |||
| || it.status != StockOutLineStatus.REJECTED.status | |||
| } | |||
| if (unfinishedLine.isEmpty()) { | |||
| // set pick order line status to complete | |||
| val pol = pickOrderLineRepository.findById(pickOrderLineId).orElseThrow() | |||
| pickOrderLineRepository.save( | |||
| pol.apply { | |||
| this.status = PickOrderLineStatus.COMPLETED | |||
| } | |||
| ) | |||
| } else { | |||
| // return unfinished ids to frontend | |||
| } | |||
| } | |||
| @Transactional | |||
| open fun update(request: UpdateStockOutLineRequest): MessageResponse { | |||
| val stockOutLine = stockOutLineRepository.findById(request.id).orElseThrow() | |||
| var stockOutLineEntries: List<StockOutLine?> = listOf() | |||
| // pick flow step 2 | |||
| if (request.status == StockOutLineStatus.COMPLETE.status) { // doing qc | |||
| println("doing sth") | |||
| if (request.qty <= 0) { | |||
| return MessageResponse( | |||
| id = null, | |||
| code = null, | |||
| name = null, | |||
| type = "qty == 0", | |||
| message = "qty cannot be 0", | |||
| errorPosition = "request.qty", | |||
| ) | |||
| } | |||
| stockOutLineEntries = handleQc(stockOutLine, request) | |||
| } | |||
| if (request.status == StockOutLineStatus.LOT_CHANGE_APPROVAL.status) { | |||
| if (request.qty < 0) { | |||
| return MessageResponse( | |||
| id = null, | |||
| code = null, | |||
| name = null, | |||
| type = "qty < 0", | |||
| message = "qty cannot be smaller than 0", | |||
| errorPosition = "request.qty", | |||
| ) | |||
| } | |||
| stockOutLineEntries = handleLotChangeApprovalOrReject(stockOutLine, request) | |||
| } | |||
| checkIsStockOutLineCompleted(pickOrderLineId = stockOutLine.pickOrderLine!!.id!!) | |||
| val savedEntries = stockOutLineRepository.saveAllAndFlush(stockOutLineEntries.filterNotNull()) | |||
| val lineInfoList = stockOutLineRepository.findAllByIdIn(savedEntries.map { it.id!! }) | |||
| return MessageResponse( | |||
| id = 0, | |||
| name = "id: ${lineInfoList.map { it.id }.joinToString(separator = ",")}", | |||
| code = stockOutLine.stockOut!!.consoPickOrderCode, | |||
| type = lineInfoList.joinToString(separator = ",") { it.status }, | |||
| message = "success", | |||
| errorPosition = null, | |||
| entity = lineInfoList, | |||
| ) | |||
| } | |||
| } | |||
| @@ -44,7 +44,7 @@ open class StockOutService( | |||
| var sum = 0.0 | |||
| when (status) { | |||
| StockOutLineStatus.PENDING.status -> sum += it.qty ?: 0.0; | |||
| StockOutLineStatus.PICKED.status -> sum -= it.qty ?: 0.0; | |||
| // StockOutLineStatus.PICKED.status -> sum -= it.qty ?: 0.0; | |||
| } | |||
| sum == 0.0 | |||
| } | |||
| @@ -62,7 +62,7 @@ open class StockOutService( | |||
| val baseLines = allLines.filter { stockOutLine -> stockOutLine.status === StockOutLineStatus.PENDING.status} | |||
| // update pick record to complete | |||
| val pickLines = allLines | |||
| .filter { stockOutLine -> stockOutLine.status === StockOutLineStatus.PICKED.status} | |||
| // .filter { stockOutLine -> stockOutLine.status === StockOutLineStatus.PICKED.status} | |||
| .map { stockOutLine -> | |||
| stockOutLine.apply { | |||
| status = StockOutLineStatus.COMPLETE.status | |||
| @@ -1,16 +1,15 @@ | |||
| package com.ffii.fpsms.modules.stock.web | |||
| import com.ffii.fpsms.modules.master.web.models.MessageResponse | |||
| import com.ffii.fpsms.modules.purchaseOrder.entity.PurchaseOrderLine | |||
| import com.ffii.fpsms.modules.stock.entity.StockOutLine | |||
| import com.ffii.fpsms.modules.stock.entity.projection.StockOutLineInfo | |||
| import com.ffii.fpsms.modules.stock.service.StockInLineService | |||
| import com.ffii.fpsms.modules.stock.service.StockOutLineService | |||
| import com.ffii.fpsms.modules.stock.web.model.CreateStockOutLineRequest | |||
| import com.ffii.fpsms.modules.stock.web.model.UpdateStockOutLineRequest | |||
| import jakarta.validation.Valid | |||
| 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 | |||
| import org.springframework.web.bind.annotation.* | |||
| @RestController | |||
| @RequestMapping("/stockOutLine") | |||
| @@ -22,4 +21,13 @@ class StockOutLineController( | |||
| return stockOutLineService.getAllStockOutLineByPickOrderLineId(pickOrderLineId) | |||
| } | |||
| @PostMapping("/create") | |||
| fun create(@Valid @RequestBody request: CreateStockOutLineRequest): MessageResponse { | |||
| return stockOutLineService.create(request) | |||
| } | |||
| @PostMapping("/update") | |||
| fun update(@Valid @RequestBody request: UpdateStockOutLineRequest): MessageResponse { | |||
| println("triggering") | |||
| return stockOutLineService.update(request) | |||
| } | |||
| } | |||
| @@ -10,7 +10,6 @@ enum class StockInStatus(val status: String) { | |||
| COMPLETE("completed"), | |||
| // CANCELLED("cancelled") | |||
| } | |||
| class GameScore(val grade: String) | |||
| enum class StockInLineStatus(val status: String) { | |||
| PENDING("pending"), | |||
| QC("qc"), | |||
| @@ -1,8 +1,9 @@ | |||
| package com.ffii.fpsms.modules.stock.web.model | |||
| import jakarta.validation.constraints.NotBlank | |||
| import jakarta.validation.constraints.NotNull | |||
| import java.time.LocalDate | |||
| import java.time.LocalDateTime | |||
| enum class StockOutStatus(val status: String) { | |||
| PENDING("pending"), | |||
| COMPLETE("completed"), | |||
| @@ -10,11 +11,12 @@ enum class StockOutStatus(val status: String) { | |||
| } | |||
| enum class StockOutLineStatus(val status: String) { | |||
| PENDING("pending"), | |||
| PICKED("picked"), | |||
| COMPLETE("completed"), | |||
| // CANCELLED("cancelled") | |||
| DETERMINE1("determine1"), // qc failed qty? | |||
| LOT_CHANGE_APPROVAL("lot-change"), // just a flag for frontend | |||
| REJECTED("rejected"), | |||
| COMPLETE("completed"), // == picked | |||
| } | |||
| data class SaveStockOutRequest( | |||
| data class SaveStockOutRequest( // not usable | |||
| val id: Long?, | |||
| val type: String, // delivery || pick || etc | |||
| val deliveryOrderCode: String?, | |||
| @@ -25,17 +27,22 @@ data class SaveStockOutRequest( | |||
| val handler: Long?, | |||
| val targetOutletId: Long?, | |||
| val remarks: String?, | |||
| val stockOutLine: List<SaveStockOutLineRequest> | |||
| val stockOutLine: List<UpdateStockOutLineRequest> | |||
| ) | |||
| data class SaveStockOutLineRequest( | |||
| val id: Long?, | |||
| data class CreateStockOutLineRequest( | |||
| val consoCode: String, | |||
| val pickOrderLineId: Long, | |||
| val inventoryLotLineId: Long, | |||
| val qty: Double, | |||
| ) | |||
| data class UpdateStockOutLineRequest( | |||
| val id: Long, | |||
| val itemId: Long, | |||
| val qty: Double, | |||
| val pickOrderLineId: Long, | |||
| val inventoryLotLineId: Long, | |||
| val status: StockOutLineStatus?, | |||
| val status: String, | |||
| val inventoryLotLineId: Long?, | |||
| val pickTime: LocalDateTime?, | |||
| val pickerId: Long? | |||
| // val pickerId: Long? | |||
| ) | |||