diff --git a/src/main/java/com/ffii/fpsms/modules/common/CodeGenerator.kt b/src/main/java/com/ffii/fpsms/modules/common/CodeGenerator.kt index cc0a6eb..d4c633c 100644 --- a/src/main/java/com/ffii/fpsms/modules/common/CodeGenerator.kt +++ b/src/main/java/com/ffii/fpsms/modules/common/CodeGenerator.kt @@ -4,6 +4,7 @@ import java.time.LocalDate import java.time.format.DateTimeFormatter object CodeGenerator { + // Default Value for Lot No private var dateFormat = DateTimeFormatter.ofPattern("yyMMdd") fun generateCode(prefix: String, itemId: Long, count: Int): String { // prefix = "ITEM" || "LOT" @@ -13,4 +14,33 @@ object CodeGenerator { val countStr = String.format("%04d", count) return "$prefix-$todayStr$itemStr$countStr" } + + // Default Value for Order No + val DEFAULT_SUFFIX_FORMAT = "%03d"; + val DEFAULT_PATTERN = "yyyyMMdd"; + val DEFAULT_FORMATTER = DateTimeFormatter.ofPattern(DEFAULT_PATTERN) + val DEFAULT_MIDFIX = LocalDate.now().format(DEFAULT_FORMATTER) + fun generateOrderNo( + suffixFormat: String? = null, + pattern: String? = null, + prefix: String, + midfix: String? = null, + latestCode: String? + ): String { + val formatter = pattern?.let { DateTimeFormatter.ofPattern(it) } ?: DEFAULT_FORMATTER + val finalSuffixFormat = suffixFormat ?: DEFAULT_SUFFIX_FORMAT + + val midfix = midfix ?: LocalDate.now().format(formatter) + val suffix = String.format(finalSuffixFormat, 1) + + if (latestCode != null) { + val splitLatestCode = latestCode.split("-") + if (splitLatestCode.size > 2) { + val latestNo = splitLatestCode[2].toInt() + return listOf(prefix, midfix, String.format(finalSuffixFormat, latestNo + 1)).joinToString("-") + } + } + + return listOf(prefix, midfix, suffix).joinToString("-") + } } \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/common/mail/service/MailTemplateService.kt b/src/main/java/com/ffii/fpsms/modules/common/mail/service/MailTemplateService.kt index 3261d7a..569b4e6 100644 --- a/src/main/java/com/ffii/fpsms/modules/common/mail/service/MailTemplateService.kt +++ b/src/main/java/com/ffii/fpsms/modules/common/mail/service/MailTemplateService.kt @@ -93,7 +93,7 @@ open class MailTemplateService( val supplierEmail: String = "", val supplierName: String = "", val dnNo: String = "", - val dnDate: String = "", + val receiptDate: String = "", val poNo: String = "", val supplierId: String = "", val itemNo: String = "", @@ -115,7 +115,7 @@ open class MailTemplateService( val args = mapOf( "\${supplierName}" to supplierName, "\${dnNo}" to dnNo, - "\${dnDate}" to dnDate, + "\${receiptDate}" to receiptDate, "\${poNo}" to poNo, "\${supplierId}" to supplierId, "\${itemNo}" to itemNo, @@ -149,7 +149,7 @@ open class MailTemplateService( val supplierName = po?.supplier?.name ?: "N/A" val supplierEmail = (if((po?.supplier?.contactEmail).isNullOrEmpty()) "N/A" else po?.supplier?.contactEmail) ?: "N/A" val dnNo = stockInLine.dnNo ?: "N/A" - val dnDate = formatter.format(stockInLine.dnDate) ?: "N/A" + val receiptDate = formatter.format(stockInLine.receiptDate) ?: "N/A" val poNo = po?.code ?: "N/A" val supplierId = po?.supplier?.code ?: "N/A" // Id? val itemNo = item?.code ?: "N/A" @@ -193,7 +193,7 @@ open class MailTemplateService( supplierEmail = supplierEmail, supplierName = supplierName, dnNo = dnNo, - dnDate = dnDate, + receiptDate = receiptDate, poNo = poNo, supplierId = supplierId, itemNo = itemNo, diff --git a/src/main/java/com/ffii/fpsms/modules/deliveryOrder/entity/DoPickOrder.kt b/src/main/java/com/ffii/fpsms/modules/deliveryOrder/entity/DoPickOrder.kt index 7db8d11..4e0f860 100644 --- a/src/main/java/com/ffii/fpsms/modules/deliveryOrder/entity/DoPickOrder.kt +++ b/src/main/java/com/ffii/fpsms/modules/deliveryOrder/entity/DoPickOrder.kt @@ -62,6 +62,9 @@ class DoPickOrder { @Column(name = "deleted") var deleted: Boolean = false + + @Column(name = "hide", nullable = false) + var hide: Boolean = false // Default constructor for Hibernate constructor() diff --git a/src/main/java/com/ffii/fpsms/modules/jobOrder/entity/JobOrder.kt b/src/main/java/com/ffii/fpsms/modules/jobOrder/entity/JobOrder.kt index 9f1907b..5511fed 100644 --- a/src/main/java/com/ffii/fpsms/modules/jobOrder/entity/JobOrder.kt +++ b/src/main/java/com/ffii/fpsms/modules/jobOrder/entity/JobOrder.kt @@ -2,6 +2,8 @@ package com.ffii.fpsms.modules.jobOrder.entity import com.fasterxml.jackson.annotation.JsonManagedReference import com.ffii.core.entity.BaseEntity +import com.ffii.fpsms.modules.jobOrder.enums.JobOrderStatus +import com.ffii.fpsms.modules.jobOrder.enums.JobOrderStatusConverter import com.ffii.fpsms.modules.master.entity.Bom import com.ffii.fpsms.modules.master.entity.ProductionScheduleLine import com.ffii.fpsms.modules.user.entity.User @@ -42,10 +44,11 @@ open class JobOrder : BaseEntity() { @Column(name = "actualQty", precision = 14, scale = 2) open var actualQty: BigDecimal? = null - @Size(max = 100) +// @Size(max = 100) @NotNull @Column(name = "status", nullable = false, length = 100) - open var status: String? = null + @Convert(converter = JobOrderStatusConverter::class) + open var status: JobOrderStatus? = null @Size(max = 500) @Column(name = "remarks", length = 500) diff --git a/src/main/java/com/ffii/fpsms/modules/jobOrder/entity/JobOrderBomMaterialRepository.kt b/src/main/java/com/ffii/fpsms/modules/jobOrder/entity/JobOrderBomMaterialRepository.kt index 825a1f2..8395771 100644 --- a/src/main/java/com/ffii/fpsms/modules/jobOrder/entity/JobOrderBomMaterialRepository.kt +++ b/src/main/java/com/ffii/fpsms/modules/jobOrder/entity/JobOrderBomMaterialRepository.kt @@ -5,4 +5,5 @@ import org.springframework.stereotype.Repository @Repository interface JobOrderBomMaterialRepository : AbstractRepository { + fun findByJobOrderIdAndItemId(jobOrderId: Long, itemId: Long): JobOrderBomMaterial?; } \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/jobOrder/entity/JobOrderRepository.kt b/src/main/java/com/ffii/fpsms/modules/jobOrder/entity/JobOrderRepository.kt index a9b5c5d..4b700e3 100644 --- a/src/main/java/com/ffii/fpsms/modules/jobOrder/entity/JobOrderRepository.kt +++ b/src/main/java/com/ffii/fpsms/modules/jobOrder/entity/JobOrderRepository.kt @@ -20,6 +20,24 @@ interface JobOrderRepository : AbstractRepository { @Query( nativeQuery = true, value = """ + with picked_lot_no as ( + select + pol.itemId, + po.joId, + json_arrayagg( + json_object( + 'lotNo', il.lotNo, + 'qty', sol.qty + ) + ) as pickedLotNo + from pick_order po + left join pick_order_line pol on pol.poId = po.id + left join stock_out_line sol on sol.pickOrderLineId = pol.id and sol.status = 'completed' + left join inventory_lot_line ill on ill.id = sol.inventoryLotLineId + left join inventory_lot il on il.id = ill.inventoryLotId + where po.joId = :id and il.lotNo is not null + group by pol.itemId, po.joId + ) select jo.id, jo.code, @@ -32,7 +50,7 @@ interface JobOrderRepository : AbstractRepository { 'id', jobm.id, 'code', i.code, 'name', i.name, - 'lotNo', il.lotNo, + 'pickedLotNo', pln.pickedLotNo, 'reqQty', jobm.reqQty, 'uom', uc.udfudesc, 'status', jobm.status @@ -46,15 +64,14 @@ interface JobOrderRepository : AbstractRepository { left join job_order_bom_material jobm on jo.id = jobm.jobOrderId left join items i on i.id = jobm.itemId left join uom_conversion uc on uc.id = jobm.uomId - left join stock_out_line sol on sol.id = jobm.stockOutLineId - left join inventory_lot_line ill on ill.id = sol.inventoryLotLineId - left join inventory_lot il on il.id = ill.inventoryLotId + left join picked_lot_no pln on pln.itemId = jobm.itemId and pln.joId = jo.id where jo.id = :id group by jo.id, uc2.udfudesc limit 1 """ ) fun findJobOrderDetailById(id: Long): JobOrderDetailWithJsonString?; + @Query( nativeQuery = true, value = """ diff --git a/src/main/java/com/ffii/fpsms/modules/jobOrder/entity/projections/JobOrderInfo.kt b/src/main/java/com/ffii/fpsms/modules/jobOrder/entity/projections/JobOrderInfo.kt index 143e5f4..d4e3ab3 100644 --- a/src/main/java/com/ffii/fpsms/modules/jobOrder/entity/projections/JobOrderInfo.kt +++ b/src/main/java/com/ffii/fpsms/modules/jobOrder/entity/projections/JobOrderInfo.kt @@ -14,6 +14,8 @@ interface JobOrderInfo { @get:Value("#{target.bom.item.itemUoms.^[salesUnit == true && deleted == false]?.uom.udfudesc}") val uom: String; + +@get:Value("#{target.status.value}") val status: String; } @@ -42,8 +44,13 @@ data class JobOrderDetailPickLine( val id: Long?, val code: String?, val name: String?, - val lotNo: String?, + val pickedLotNo: List?, val reqQty: BigDecimal?, val uom: String?, val status: String? +) + +data class JobOrderDetailPickedLotNo( + val lotNo: String?, + val qty: BigDecimal?, ) \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/jobOrder/enums/JobOrderEnum.kt b/src/main/java/com/ffii/fpsms/modules/jobOrder/enums/JobOrderEnum.kt new file mode 100644 index 0000000..e90c8af --- /dev/null +++ b/src/main/java/com/ffii/fpsms/modules/jobOrder/enums/JobOrderEnum.kt @@ -0,0 +1,10 @@ +package com.ffii.fpsms.modules.jobOrder.enums + +enum class JobOrderStatus(val value: String) { + PLANNING("planning"), + PENDING("pending"), + PROCESSING("processing"), + PACKAGING("packaging"), + STORING("storing"), + COMPLETED("completed") +} \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/jobOrder/enums/JobOrderEnumConverter.kt b/src/main/java/com/ffii/fpsms/modules/jobOrder/enums/JobOrderEnumConverter.kt new file mode 100644 index 0000000..7b2e5de --- /dev/null +++ b/src/main/java/com/ffii/fpsms/modules/jobOrder/enums/JobOrderEnumConverter.kt @@ -0,0 +1,18 @@ +package com.ffii.fpsms.modules.jobOrder.enums + +import jakarta.persistence.AttributeConverter +import jakarta.persistence.Converter + +// Job Order Status +@Converter(autoApply = true) +class JobOrderStatusConverter : AttributeConverter { + override fun convertToDatabaseColumn(status: JobOrderStatus?): String? { + return status?.value + } + + override fun convertToEntityAttribute(value: String?): JobOrderStatus? { + return value?.let { v -> + JobOrderStatus.entries.find { it.value == v } + } + } +} \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/jobOrder/service/JobOrderBomMaterialService.kt b/src/main/java/com/ffii/fpsms/modules/jobOrder/service/JobOrderBomMaterialService.kt index ed1294b..178a2f3 100644 --- a/src/main/java/com/ffii/fpsms/modules/jobOrder/service/JobOrderBomMaterialService.kt +++ b/src/main/java/com/ffii/fpsms/modules/jobOrder/service/JobOrderBomMaterialService.kt @@ -24,7 +24,7 @@ open class JobOrderBomMaterialService( open fun createJobOrderBomMaterialRequests(joId: Long): List { val zero = BigDecimal.ZERO val jo = jobOrderRepository.findById(joId).getOrNull() ?: throw NoSuchElementException() - val proportion = BigDecimal.ONE //(jo.reqQty ?: zero).divide(jo.bom?.outputQty ?: BigDecimal.ONE, 5, RoundingMode.HALF_UP) + val proportion = (jo.reqQty ?: zero).divide(jo.bom?.outputQty ?: BigDecimal.ONE, 5, RoundingMode.HALF_UP) val jobmRequests = jo.bom?.bomMaterials?.map { bm -> val salesUnit = bm.item?.id?.let { itemUomService.findSalesUnitByItemId(it) } @@ -35,7 +35,7 @@ open class JobOrderBomMaterialService( reqQty = bm.qty?.times(proportion) ?: zero, uomId = salesUnit?.uom?.id ) - } ?: listOf() + } ?: listOf() return jobmRequests } diff --git a/src/main/java/com/ffii/fpsms/modules/jobOrder/service/JobOrderService.kt b/src/main/java/com/ffii/fpsms/modules/jobOrder/service/JobOrderService.kt index 0b99164..0aff8c8 100644 --- a/src/main/java/com/ffii/fpsms/modules/jobOrder/service/JobOrderService.kt +++ b/src/main/java/com/ffii/fpsms/modules/jobOrder/service/JobOrderService.kt @@ -8,6 +8,7 @@ import com.ffii.fpsms.modules.jobOrder.entity.JobOrderRepository import com.ffii.fpsms.modules.jobOrder.entity.projections.JobOrderDetail import com.ffii.fpsms.modules.jobOrder.entity.projections.JobOrderDetailPickLine import com.ffii.fpsms.modules.jobOrder.entity.projections.JobOrderInfo +import com.ffii.fpsms.modules.jobOrder.enums.JobOrderStatus import com.ffii.fpsms.modules.jobOrder.web.model.CreateJobOrderRequest import com.ffii.fpsms.modules.jobOrder.web.model.JobOrderReleaseRequest import com.ffii.fpsms.modules.jobOrder.web.model.SearchJobOrderInfoRequest @@ -111,6 +112,7 @@ open class JobOrderService( val approver = request.approverId?.let { userService.find(it).getOrNull() } ?: SecurityUtils.getUser().getOrNull() val prodScheduleLine = request.prodScheduleLineId?.let { productionScheduleLineRepository.findById(it).getOrNull() } val code = assignJobNo() + val status = JobOrderStatus.entries.find { it.value == request.status } jo.apply { this.code = code @@ -119,7 +121,7 @@ open class JobOrderService( planStart = request.planStart ?: LocalDateTime.now() planEnd = request.planEnd ?: LocalDateTime.now() reqQty = request.reqQty - status = request.status + this.status = status type = request.type this.approver = approver this.prodScheduleLine = prodScheduleLine @@ -141,7 +143,7 @@ open class JobOrderService( open fun releaseJobOrder(request: JobOrderReleaseRequest): MessageResponse { val jo = request.id.let { jobOrderRepository.findById(it).getOrNull() } ?: throw NoSuchElementException() jo.apply { - status = "pending" + status = JobOrderStatus.PENDING } jobOrderRepository.save(jo) @@ -168,7 +170,7 @@ open class JobOrderService( type = null, message = null, errorPosition = null, - entity = mapOf("status" to jo.status) + entity = mapOf("status" to jo.status?.value) ) } } \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/master/entity/BomMaterialRepository.kt b/src/main/java/com/ffii/fpsms/modules/master/entity/BomMaterialRepository.kt index d0ed3f5..c6f2903 100644 --- a/src/main/java/com/ffii/fpsms/modules/master/entity/BomMaterialRepository.kt +++ b/src/main/java/com/ffii/fpsms/modules/master/entity/BomMaterialRepository.kt @@ -13,4 +13,6 @@ interface BomMaterialRepository : AbstractRepository { fun findAllByBomItemIdAndDeletedIsFalse(itemId: Long): List fun findAllByBomIdAndDeletedIsFalse(bomId: Long): List + + fun findByBomIdAndItemId(bomId: Long, itemId: Long): BomMaterial? } \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/master/entity/BomProcessMaterialRepository.kt b/src/main/java/com/ffii/fpsms/modules/master/entity/BomProcessMaterialRepository.kt index 3fbf890..0fd0cf6 100644 --- a/src/main/java/com/ffii/fpsms/modules/master/entity/BomProcessMaterialRepository.kt +++ b/src/main/java/com/ffii/fpsms/modules/master/entity/BomProcessMaterialRepository.kt @@ -5,4 +5,5 @@ import org.springframework.stereotype.Repository @Repository interface BomProcessMaterialRepository : AbstractRepository { + fun findByBomProcessIdAndBomMaterialId(bomProcessId: Long, bomMaterialId: Long): BomProcessMaterial?; } \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/master/entity/BomRepository.kt b/src/main/java/com/ffii/fpsms/modules/master/entity/BomRepository.kt index 1d3a0ef..5b48070 100644 --- a/src/main/java/com/ffii/fpsms/modules/master/entity/BomRepository.kt +++ b/src/main/java/com/ffii/fpsms/modules/master/entity/BomRepository.kt @@ -16,4 +16,6 @@ interface BomRepository : AbstractRepository { fun findByItemIdAndDeletedIsFalse(itemId: Serializable): Bom? fun findBomComboByDeletedIsFalse(): List + + fun findByCodeAndDeletedIsFalse(code: String): Bom? } \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/master/entity/WarehouseRepository.kt b/src/main/java/com/ffii/fpsms/modules/master/entity/WarehouseRepository.kt index 94bf93d..4707a3a 100644 --- a/src/main/java/com/ffii/fpsms/modules/master/entity/WarehouseRepository.kt +++ b/src/main/java/com/ffii/fpsms/modules/master/entity/WarehouseRepository.kt @@ -3,8 +3,13 @@ package com.ffii.fpsms.modules.master.entity import com.ffii.core.support.AbstractRepository import com.ffii.fpsms.modules.master.entity.projections.WarehouseCombo import org.springframework.stereotype.Repository +import java.io.Serializable @Repository interface WarehouseRepository : AbstractRepository { fun findWarehouseComboByDeletedFalse(): List; + + fun findByIdAndDeletedIsFalse(id: Serializable): Warehouse?; + + fun findByCodeAndDeletedIsFalse(code: String): Warehouse?; } \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/master/service/BomService.kt b/src/main/java/com/ffii/fpsms/modules/master/service/BomService.kt index 65eb645..d5cb103 100644 --- a/src/main/java/com/ffii/fpsms/modules/master/service/BomService.kt +++ b/src/main/java/com/ffii/fpsms/modules/master/service/BomService.kt @@ -90,7 +90,8 @@ open class BomService( private fun saveBomEntity(req: ImportBomRequest): Bom { val item = itemsRepository.findByCodeAndDeletedFalse(req.code) ?: itemsRepository.findByNameAndDeletedFalse(req.name) val uom = if (req.uomId != null) uomConversionRepository.findById(req.uomId!!).orElseThrow() else null - val bom = Bom().apply { + val bom = bomRepository.findByCodeAndDeletedIsFalse(req.code) ?: Bom() + bom.apply { this.isDark = req.isDark this.isFloat = req.isFloat this.isDense = req.isDense @@ -111,10 +112,11 @@ open class BomService( private fun saveBomMaterial(req: ImportBomMatRequest): BomMaterial { // val uom = uomConversionRepository.findByCodeAndDeletedFalse() - println("printing") - println(req) - println(req.item) - val bomMaterial = BomMaterial().apply { +// println("printing") +// println(req) +// println(req.item) + val bomMaterial = req.bom?.id?.let { bId -> req.item?.id?.let { iId -> bomMaterialRepository.findByBomIdAndItemId(bId, iId) } } ?: BomMaterial() + bomMaterial.apply { this.item = req.item this.itemName = req.item!!.name this.isConsumable = req.isConsumable @@ -124,12 +126,12 @@ open class BomService( this.uom = req.uom this.uomName = req.uomName this.bom = req.bom - } return bomMaterialRepository.saveAndFlush(bomMaterial) } private fun saveBomProcess(req: ImportBomProcessRequest): BomProcess { - val bomProcess = BomProcess().apply { + val bomProcess = req.bom!!.id?.let { id -> req.seqNo?.let { seqNo -> bomProcessRepository.findBySeqNoAndBomIdAndDeletedIsFalse(seqNo.toInt(), id) } } ?: BomProcess() + bomProcess.apply { this.process = req.process this.equipment = req.equipment this.description = req.description @@ -142,7 +144,8 @@ open class BomService( return bomProcessRepository.saveAndFlush(bomProcess) } fun saveBomProcessMaterial(req: ImportBomProcessMaterialRequest): BomProcessMaterial { - val bomProcessMaterial = BomProcessMaterial().apply { + val bomProcessMaterial = req.bomProcess?.id?.let { pid -> req.bomMaterial?.id?.let { mid -> bomProcessMaterialRepository.findByBomProcessIdAndBomMaterialId(pid, mid)} } ?: BomProcessMaterial() + bomProcessMaterial.apply { this.bomProcess = req.bomProcess this.bomMaterial = req.bomMaterial } @@ -489,7 +492,7 @@ open class BomService( // val folder = File(folderPath) val resolver = PathMatchingResourcePatternResolver() // val excels = resolver.getResources("bomImport/*.xlsx") - val excels = resolver.getResources("file:C:/Users/2Fi/Desktop/test folder/*.xlsx") + val excels = resolver.getResources("file:C:/Users/ffii_/Downloads/bom/new/*.xlsx") // val excels = resolver.getResources("file:C:/Users/2Fi/Desktop/Third Wave of BOM Excel/*.xlsx") println("size: ${excels.size}") val logExcel = ClassPathResource("excelTemplate/bom_excel_issue_log.xlsx") diff --git a/src/main/java/com/ffii/fpsms/modules/master/service/ItemsService.kt b/src/main/java/com/ffii/fpsms/modules/master/service/ItemsService.kt index 04d8bfc..26e5e73 100644 --- a/src/main/java/com/ffii/fpsms/modules/master/service/ItemsService.kt +++ b/src/main/java/com/ffii/fpsms/modules/master/service/ItemsService.kt @@ -210,6 +210,10 @@ open class ItemsService( return itemsRepository.findByIdAndDeletedFalse(id); } + open fun findByCode(code: String): Items? { + return itemsRepository.findByCodeAndDeletedFalse(code); + } + open fun findByM18Id(m18Id: Long): Items? { return itemsRepository.findByM18IdAndDeletedIsFalse(m18Id) } diff --git a/src/main/java/com/ffii/fpsms/modules/master/service/WarehouseService.kt b/src/main/java/com/ffii/fpsms/modules/master/service/WarehouseService.kt index af3bfab..2f91488 100644 --- a/src/main/java/com/ffii/fpsms/modules/master/service/WarehouseService.kt +++ b/src/main/java/com/ffii/fpsms/modules/master/service/WarehouseService.kt @@ -6,6 +6,7 @@ import com.ffii.fpsms.modules.master.entity.ItemsRepository import com.ffii.fpsms.modules.master.entity.Warehouse import com.ffii.fpsms.modules.master.entity.WarehouseRepository import com.ffii.fpsms.modules.master.entity.projections.WarehouseCombo +import com.ffii.fpsms.modules.master.web.models.SaveWarehouseRequest import com.ffii.fpsms.modules.purchaseOrder.entity.PurchaseOrderLineRepository import com.ffii.fpsms.modules.stock.entity.InventoryLotRepository import com.ffii.fpsms.modules.stock.entity.StockInLine @@ -14,6 +15,7 @@ import com.ffii.fpsms.modules.stock.entity.StockInRepository import com.ffii.fpsms.modules.stock.service.InventoryLotService import com.ffii.fpsms.modules.stock.service.StockInService import org.springframework.stereotype.Service +import kotlin.jvm.optionals.getOrNull @Service open class WarehouseService( @@ -28,4 +30,25 @@ open class WarehouseService( open fun findCombo(): List { return warehouseRepository.findWarehouseComboByDeletedFalse(); } + + open fun findById(id: Long): Warehouse? { + return warehouseRepository.findByIdAndDeletedIsFalse(id); + } + + open fun findByCode(code: String): Warehouse? { + return warehouseRepository.findByCodeAndDeletedIsFalse(code); + } + + open fun saveWarehouse(request: SaveWarehouseRequest): Warehouse { + val warehouse = request.id?.let { warehouseRepository.findById(it).getOrNull() } ?: Warehouse(); + + warehouse.apply { + code = request.code + name = request.name + description = request.description + capacity = request.capacity + }; + + return warehouseRepository.save(warehouse); + } } \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/master/web/models/SaveWarehouseRequest.kt b/src/main/java/com/ffii/fpsms/modules/master/web/models/SaveWarehouseRequest.kt new file mode 100644 index 0000000..317ad93 --- /dev/null +++ b/src/main/java/com/ffii/fpsms/modules/master/web/models/SaveWarehouseRequest.kt @@ -0,0 +1,11 @@ +package com.ffii.fpsms.modules.master.web.models + +import java.math.BigDecimal + +data class SaveWarehouseRequest( + val id: Long? = null, + val code: String, + val name: String, + val description: String, + val capacity: BigDecimal, +) 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 a45383f..d62a548 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 @@ -80,7 +80,9 @@ open class PickOrderService( private val doPickOrderService: DoPickOrderService, private val routerRepository: RouterRepository, private val doPickOrderRecordRepository: DoPickOrderRecordRepository, - private val doPickOrderRepository: DoPickOrderRepository + private val doPickOrderRepository: DoPickOrderRepository, + private val userRepository: UserRepository + ) : AbstractBaseEntityService(jdbcDao, pickOrderRepository) { @@ -2765,19 +2767,22 @@ if (existingRecords.isNotEmpty()) { println("❌ Pick order not found with ID: $pickOrderId") return emptyList() } - + if (doPickOrderRepository.findByPickOrderId(pickOrderId).firstOrNull()?.hide == true) { + println("🔍 Pick order $pickOrderId is hidden, returning empty list") + return emptyList() + } println("🔍 Found pick order: ${pickOrder.code}, type: ${pickOrder.type?.value}, status: ${pickOrder.status?.value}") if (pickOrder.type?.value != "do") { println("❌ Pick order is not of type 'do': ${pickOrder.type?.value}") return emptyList() } - - if (pickOrder.status?.value !in listOf("assigned", "released", "picking", "completed")) { + val allowedstatuses= listOf("assigned", "released", "picking", "completed") + if (pickOrder.status?.value !in allowedstatuses) { println("❌ Pick order status is not in allowed states: ${pickOrder.status?.value}") return emptyList() } - + val deliveryOrder = pickOrder.deliveryOrder val shop = deliveryOrder?.shop val supplier = deliveryOrder?.supplier @@ -3164,17 +3169,21 @@ open fun getAllPickOrderLotsWithDetailsHierarchical(userId: Long): Map + val doPickOrders = doPickOrderRepository.findByPickOrderId(pickOrder.id!!) + doPickOrders.none { it.hide } // 只显示 hide = false 的订单 + } // ✅ NEW LOGIC: Filter based on assignment and status - val filteredPickOrders = if (allAssignedPickOrders.isNotEmpty()) { + val filteredPickOrders = if (visiblePickOrders.isNotEmpty()) { // Check if there are any RELEASED orders assigned to this user (active work) val assignedReleasedOrders = allAssignedPickOrders.filter { it.status == PickOrderStatus.RELEASED && it.assignTo?.id == userId @@ -3341,11 +3350,11 @@ open fun getAllPickOrderLotsWithDetailsHierarchical(userId: Long): Map - val availableQty = row["availableQty"] - availableQty != null - } - + // val filteredResults = results.filter { row -> + //val availableQty = row["availableQty"] + // availableQty != null + // } + val filteredResults = results println("✅ Filtered result count: ${filteredResults.size}") // ✅ Transform flat results into hierarchical structure @@ -3432,6 +3441,7 @@ open fun getAllPickOrderLotsWithDetailsHierarchical(userId: Long): Map { println("=== Debug: getPickOrdersByDateAndStore ===") @@ -3663,4 +3673,132 @@ open fun confirmLotSubstitution(req: LotSubstitutionConfirmRequest): MessageResp errorPosition = null ) } -} \ No newline at end of file +open fun updateDoPickOrderHideStatus(pickOrderId: Long, hide: Boolean): MessageResponse { + return try { + // ✅ 修复:根据 pickOrderId 查找 do_pick_order 记录 + val doPickOrders = doPickOrderRepository.findByPickOrderId(pickOrderId) + if (doPickOrders.isEmpty()) { + MessageResponse( + id = null, + name = "Pick order not found", + code = "ERROR", + type = "pickorder", + message = "No do_pick_order found for pickOrderId $pickOrderId", + errorPosition = "pickOrderId" + ) + } else { + // ✅ 更新所有相关的 do_pick_order 记录 + doPickOrders.forEach { doPickOrder -> + doPickOrder.hide = hide + doPickOrderRepository.save(doPickOrder) + } + + MessageResponse( + id = null, + name = "Hide status updated", + code = "SUCCESS", + type = "pickorder", + message = "Updated hide status for ${doPickOrders.size} do_pick_order records to $hide", + errorPosition = null + ) + } + } catch (e: Exception) { + println("❌ Error in updateDoPickOrderHideStatus: ${e.message}") + e.printStackTrace() + MessageResponse( + id = null, + name = "Failed to update hide status", + code = "ERROR", + type = "pickorder", + message = "Failed to update hide status: ${e.message}", + errorPosition = null + ) + } +} +open fun getCompletedDoPickOrders( + userId: Long, + pickOrderCode: String?, + shopName: String?, + deliveryNo: String?, + ticketNo: String? +): List> { + return try { + println("=== getCompletedDoPickOrders ===") + println("userId: $userId") + println("pickOrderCode: $pickOrderCode") + println("shopName: $shopName") + println("deliveryNo: $deliveryNo") + println("ticketNo: $ticketNo") + + // ✅ 修复:使用正确的方法获取已完成的 pick orders + val user = userRepository.findById(userId).orElse(null) + if (user == null) { + println("User not found: $userId") + return emptyList() + } + + // ✅ 修复:使用正确的方法获取已完成的 pick orders + val completedPickOrders = pickOrderRepository.findAllByAssignToIdAndStatusIn( + userId, + listOf(PickOrderStatus.COMPLETED) + ) + + println("Found ${completedPickOrders.size} completed pick orders for user $userId") + + val result = mutableListOf>() + + for (pickOrder in completedPickOrders) { + // 获取该 pick order 的 FG pick orders + val fgPickOrders = getFgPickOrdersByPickOrderId(pickOrder.id!!) + + if (fgPickOrders.isNotEmpty()) { + val firstFgOrder = fgPickOrders[0] as Map // ✅ 修复:转换为 Map + + // ✅ 修复:使用正确的属性名进行搜索过滤,并处理空值 + val matchesSearch = when { + pickOrderCode != null && !(pickOrder.code?.contains(pickOrderCode, ignoreCase = true) ?: false) -> false + shopName != null && !((firstFgOrder["shopName"] as? String)?.contains(shopName, ignoreCase = true) ?: false) -> false + deliveryNo != null && !((firstFgOrder["deliveryNo"] as? String)?.contains(deliveryNo, ignoreCase = true) ?: false) -> false + ticketNo != null && !((firstFgOrder["ticketNo"] as? String)?.contains(ticketNo, ignoreCase = true) ?: false) -> false + else -> true + } + + if (matchesSearch) { + result.add(mapOf( + "id" to pickOrder.id, + "pickOrderId" to pickOrder.id, + "pickOrderCode" to pickOrder.code, + "pickOrderConsoCode" to pickOrder.consoCode, + "pickOrderStatus" to pickOrder.status.toString(), + "deliveryOrderId" to firstFgOrder["deliveryOrderId"], + "deliveryNo" to firstFgOrder["deliveryNo"], + "deliveryDate" to firstFgOrder["deliveryDate"], + "shopId" to firstFgOrder["shopId"], + "shopCode" to firstFgOrder["shopCode"], + "shopName" to firstFgOrder["shopName"], + "shopAddress" to firstFgOrder["shopAddress"], + "ticketNo" to firstFgOrder["ticketNo"], + "shopPoNo" to firstFgOrder["shopPoNo"], + "numberOfCartons" to firstFgOrder["numberOfCartons"], + "truckNo" to firstFgOrder["truckNo"], + "storeId" to firstFgOrder["storeId"], + "completedDate" to (pickOrder.completeDate?.toString() ?: pickOrder.modified?.toString()), + "fgPickOrders" to fgPickOrders + )) + } + } + } + + // 按完成时间倒序排列 + result.sortByDescending { it["completedDate"] as String? } + + println("Returning ${result.size} completed DO pick orders") + result + + } catch (e: Exception) { + println("❌ Error in getCompletedDoPickOrders: ${e.message}") + e.printStackTrace() + emptyList() + } +} +} 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 962a4ae..fc7624d 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 @@ -287,4 +287,21 @@ fun getPickOrdersByStore(@PathVariable storeId: String): Map { fun confirmLotSubstitution(@RequestBody req: LotSubstitutionConfirmRequest): MessageResponse { return pickOrderService.confirmLotSubstitution(req) } +@PostMapping("/update-hide-status/{pickOrderId}") +fun updatePickOrderHideStatus( + @PathVariable pickOrderId: Long, + @RequestParam hide: Boolean +): MessageResponse { + return pickOrderService.updateDoPickOrderHideStatus(pickOrderId, hide) +} +@GetMapping("/completed-do-pick-orders/{userId}") +fun getCompletedDoPickOrders( + @PathVariable userId: Long, + @RequestParam(required = false) pickOrderCode: String?, + @RequestParam(required = false) shopName: String?, + @RequestParam(required = false) deliveryNo: String?, + @RequestParam(required = false) ticketNo: String? +): List> { + return pickOrderService.getCompletedDoPickOrders(userId, pickOrderCode, shopName, deliveryNo, ticketNo) +} } \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/purchaseOrder/service/PurchaseOrderService.kt b/src/main/java/com/ffii/fpsms/modules/purchaseOrder/service/PurchaseOrderService.kt index 0139da3..309cf79 100644 --- a/src/main/java/com/ffii/fpsms/modules/purchaseOrder/service/PurchaseOrderService.kt +++ b/src/main/java/com/ffii/fpsms/modules/purchaseOrder/service/PurchaseOrderService.kt @@ -209,7 +209,7 @@ open class PurchaseOrderService( } val mappedPoLine = pol.map { thisPol -> val inLine = stockInLine.filter { it.purchaseOrderLineId == thisPol.id } - .filter { it.dnDate != null } + .filter { it.receiptDate != null } val categoryCode = thisPol.item?.qcCategory?.code val qcItems = thisPol.item?.qcCategory?.qcItemCategory?.map { QcForPoLine( diff --git a/src/main/java/com/ffii/fpsms/modules/stock/entity/EscalationLogInfo.kt b/src/main/java/com/ffii/fpsms/modules/stock/entity/EscalationLogInfo.kt index 90dc59f..05d85f5 100644 --- a/src/main/java/com/ffii/fpsms/modules/stock/entity/EscalationLogInfo.kt +++ b/src/main/java/com/ffii/fpsms/modules/stock/entity/EscalationLogInfo.kt @@ -25,8 +25,8 @@ interface EscalationLogInfo { @get:Value("#{target.stockInLine?.dnNo}") val dnNo: String? - @get:Value("#{target.stockInLine?.dnDate}") - val dnDate: LocalDateTime? +// @get:Value("#{target.stockInLine?.dnDate}") +// val dnDate: LocalDateTime? @get:Value("#{target.stockInLine?.item?.code} - #{target.stockInLine?.item?.name}") val item: String? 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 965291d..1379127 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 @@ -13,6 +13,9 @@ import org.springframework.data.repository.query.Param @Repository interface InventoryLotLineRepository : AbstractRepository { + + fun findByIdAndDeletedIsFalse(id: Serializable): InventoryLotLine; + fun findInventoryLotLineInfoByInventoryLotItemIdIn(ids: List): List @Query("select ill from InventoryLotLine ill where :id is null or ill.inventoryLot.item.id = :id order by ill.id desc") @@ -35,6 +38,9 @@ interface InventoryLotLineRepository : AbstractRepository): List fun findAllByInventoryLotId(id: Serializable): List + + fun findByInventoryLotStockInLineIdAndWarehouseId(inventoryLotStockInLineId: Long, warehouseId: Long): InventoryLotLine? + fun findAllByInventoryLotItemIdAndStatus(itemId: Long, status: String): List fun findAllByInventoryLotItemIdAndStatus(itemId: Long, status: InventoryLotLineStatus): List } \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/stock/entity/StockIn.kt b/src/main/java/com/ffii/fpsms/modules/stock/entity/StockIn.kt index b37cacc..4a65a0e 100644 --- a/src/main/java/com/ffii/fpsms/modules/stock/entity/StockIn.kt +++ b/src/main/java/com/ffii/fpsms/modules/stock/entity/StockIn.kt @@ -40,6 +40,10 @@ open class StockIn : BaseEntity() { @JoinColumn(name = "stockOutId") open var stockOutId: StockOut? = null + @ManyToOne + @JoinColumn(name = "stockTakeId") + open var stockTake: StockTake? = null + @Column(name = "orderDate") open var orderDate: LocalDateTime? = null 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 566ec9f..f70d3be 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 @@ -37,6 +37,10 @@ open class StockInLine : BaseEntity() { @JoinColumn(name = "purchaseOrderLineId") open var purchaseOrderLine: PurchaseOrderLine? = null + @ManyToOne + @JoinColumn(name = "stockTakeLineId") + open var stockTakeLine: StockTakeLine? = null + @NotNull @ManyToOne @JoinColumn(name = "stockInId", nullable = false) diff --git a/src/main/java/com/ffii/fpsms/modules/stock/entity/StockInRepository.kt b/src/main/java/com/ffii/fpsms/modules/stock/entity/StockInRepository.kt index 7cfabfd..6ed6d2d 100644 --- a/src/main/java/com/ffii/fpsms/modules/stock/entity/StockInRepository.kt +++ b/src/main/java/com/ffii/fpsms/modules/stock/entity/StockInRepository.kt @@ -1,9 +1,19 @@ package com.ffii.fpsms.modules.stock.entity import com.ffii.core.support.AbstractRepository +import org.springframework.data.jpa.repository.Query import org.springframework.stereotype.Repository +import java.io.Serializable @Repository interface StockInRepository : AbstractRepository { + fun findByIdAndDeletedFalse(id: Long): StockIn? fun findByPurchaseOrderIdAndDeletedFalse(purchaseOrderId: Long): StockIn? + +// @Query(""" +// select si from StockIn si where si.stockTake.id = :stockTakeId and si.deleted = false +// """) + fun findByStockTakeIdAndDeletedFalse(stockTakeId: Long): StockIn? + + fun findByIdAndDeletedIsFalse(id: Serializable): StockIn? } \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/stock/entity/StockTake.kt b/src/main/java/com/ffii/fpsms/modules/stock/entity/StockTake.kt index f0e5d70..a255704 100644 --- a/src/main/java/com/ffii/fpsms/modules/stock/entity/StockTake.kt +++ b/src/main/java/com/ffii/fpsms/modules/stock/entity/StockTake.kt @@ -1,6 +1,8 @@ package com.ffii.fpsms.modules.stock.entity import com.ffii.core.entity.BaseEntity +import com.ffii.fpsms.modules.stock.enums.StockTakeStatus +import com.ffii.fpsms.modules.stock.enums.StockTakeStatusConverter import jakarta.persistence.* import jakarta.validation.constraints.NotNull import jakarta.validation.constraints.Size @@ -28,10 +30,11 @@ open class StockTake: BaseEntity() { @Column(name = "actualEnd") open var actualEnd: LocalDateTime? = null - @Size(max = 20) +// @Size(max = 20) @NotNull @Column(name = "status", nullable = false, length = 20) - open var status: String? = null + @Convert(converter = StockTakeStatusConverter::class) + open var status: StockTakeStatus? = null @Size(max = 500) @Column(name = "remarks", length = 500) diff --git a/src/main/java/com/ffii/fpsms/modules/stock/entity/StockTakeLine.kt b/src/main/java/com/ffii/fpsms/modules/stock/entity/StockTakeLine.kt index 9bbc03a..f60d4d5 100644 --- a/src/main/java/com/ffii/fpsms/modules/stock/entity/StockTakeLine.kt +++ b/src/main/java/com/ffii/fpsms/modules/stock/entity/StockTakeLine.kt @@ -2,6 +2,8 @@ package com.ffii.fpsms.modules.stock.entity import com.ffii.core.entity.BaseEntity import com.ffii.fpsms.modules.master.entity.UomConversion +import com.ffii.fpsms.modules.stock.enums.StockTakeLineStatus +import com.ffii.fpsms.modules.stock.enums.StockTakeLineStatusConverter import jakarta.persistence.* import jakarta.validation.constraints.NotNull import jakarta.validation.constraints.Size @@ -16,7 +18,6 @@ open class StockTakeLine : BaseEntity() { @JoinColumn(name = "stockTakeId", nullable = false) open var stockTake: StockTake? = null - @NotNull @ManyToOne @JoinColumn(name = "inventoryLotLineId", nullable = false) open var inventoryLotLine: InventoryLotLine? = null @@ -34,10 +35,11 @@ open class StockTakeLine : BaseEntity() { @Column(name = "completeDate") open var completeDate: LocalDateTime? = null - @Size(max = 20) +// @Size(max = 20) @NotNull @Column(name = "status", nullable = false, length = 20) - open var status: String? = null + @Convert(converter = StockTakeLineStatusConverter::class) + open var status: StockTakeLineStatus? = null @Size(max = 500) @Column(name = "remarks", length = 500) diff --git a/src/main/java/com/ffii/fpsms/modules/stock/entity/StockTakeLineRepository.kt b/src/main/java/com/ffii/fpsms/modules/stock/entity/StockTakeLineRepository.kt index 72bc64f..1a78615 100644 --- a/src/main/java/com/ffii/fpsms/modules/stock/entity/StockTakeLineRepository.kt +++ b/src/main/java/com/ffii/fpsms/modules/stock/entity/StockTakeLineRepository.kt @@ -2,7 +2,9 @@ package com.ffii.fpsms.modules.stock.entity import com.ffii.core.support.AbstractRepository import org.springframework.stereotype.Repository +import java.io.Serializable @Repository interface StockTakeLineRepository : AbstractRepository { + fun findByIdAndDeletedIsFalse(id: Serializable): StockTakeLine?; } \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/stock/entity/StockTakeRepository.kt b/src/main/java/com/ffii/fpsms/modules/stock/entity/StockTakeRepository.kt index 0636461..4ce0b1c 100644 --- a/src/main/java/com/ffii/fpsms/modules/stock/entity/StockTakeRepository.kt +++ b/src/main/java/com/ffii/fpsms/modules/stock/entity/StockTakeRepository.kt @@ -1,8 +1,16 @@ package com.ffii.fpsms.modules.stock.entity import com.ffii.core.support.AbstractRepository +import org.springframework.data.jpa.repository.Query import org.springframework.stereotype.Repository +import java.io.Serializable @Repository interface StockTakeRepository : AbstractRepository { + fun findByIdAndDeletedIsFalse(id: Serializable): StockTake; + + @Query(""" + select st.code from StockTake st where st.code like :prefix% order by st.code desc limit 1 + """) + fun findLatestCodeByPrefix(prefix: String): String? } \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/stock/entity/projection/StockInLineInfo.kt b/src/main/java/com/ffii/fpsms/modules/stock/entity/projection/StockInLineInfo.kt index 40c6205..a45bc51 100644 --- a/src/main/java/com/ffii/fpsms/modules/stock/entity/projection/StockInLineInfo.kt +++ b/src/main/java/com/ffii/fpsms/modules/stock/entity/projection/StockInLineInfo.kt @@ -43,7 +43,7 @@ interface StockInLineInfo { @get:Value("#{target.item?.type}") val itemType: String val dnNo: String - val dnDate: LocalDateTime? +// val dnDate: LocalDateTime? // val qcDecision: LocalDateTime? @get:Value("#{target.escalationLog.^[status.value == 'pending']?.handler?.id}") val handlerId: Long? diff --git a/src/main/java/com/ffii/fpsms/modules/stock/enums/StockTakeEnum.kt b/src/main/java/com/ffii/fpsms/modules/stock/enums/StockTakeEnum.kt new file mode 100644 index 0000000..54d8326 --- /dev/null +++ b/src/main/java/com/ffii/fpsms/modules/stock/enums/StockTakeEnum.kt @@ -0,0 +1,7 @@ +package com.ffii.fpsms.modules.stock.enums + +enum class StockTakeStatus(val value: String) { + PENDING("pending"), + STOCKTAKING("stockTaking"), + COMPLETED("completed"), +} \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/stock/enums/StockTakeEnumConverter.kt b/src/main/java/com/ffii/fpsms/modules/stock/enums/StockTakeEnumConverter.kt new file mode 100644 index 0000000..dd19836 --- /dev/null +++ b/src/main/java/com/ffii/fpsms/modules/stock/enums/StockTakeEnumConverter.kt @@ -0,0 +1,18 @@ +package com.ffii.fpsms.modules.stock.enums + +import jakarta.persistence.AttributeConverter +import jakarta.persistence.Converter + +// Stock Take Status +@Converter(autoApply = true) +class StockTakeStatusConverter : AttributeConverter{ + override fun convertToDatabaseColumn(status: StockTakeStatus?): String? { + return status?.value + } + + override fun convertToEntityAttribute(value: String?): StockTakeStatus? { + return value?.let { v -> + StockTakeStatus.entries.find { it.value == v } + } + } +} \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/stock/enums/StockTakeLineEnum.kt b/src/main/java/com/ffii/fpsms/modules/stock/enums/StockTakeLineEnum.kt new file mode 100644 index 0000000..2a8b9de --- /dev/null +++ b/src/main/java/com/ffii/fpsms/modules/stock/enums/StockTakeLineEnum.kt @@ -0,0 +1,6 @@ +package com.ffii.fpsms.modules.stock.enums + +enum class StockTakeLineStatus(val value: String) { + PENDING("pending"), + COMPLETED("completed"), +} \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/stock/enums/StockTakeLineEnumConverter.kt b/src/main/java/com/ffii/fpsms/modules/stock/enums/StockTakeLineEnumConverter.kt new file mode 100644 index 0000000..f12d636 --- /dev/null +++ b/src/main/java/com/ffii/fpsms/modules/stock/enums/StockTakeLineEnumConverter.kt @@ -0,0 +1,18 @@ +package com.ffii.fpsms.modules.stock.enums + +import jakarta.persistence.AttributeConverter +import jakarta.persistence.Converter + +// Stock Take Line Status +@Converter(autoApply = true) +class StockTakeLineStatusConverter : AttributeConverter { + override fun convertToDatabaseColumn(status: StockTakeLineStatus?): String? { + return status?.value + } + + override fun convertToEntityAttribute(value: String?): StockTakeLineStatus? { + return value?.let { v -> + StockTakeLineStatus.entries.find { it.value == v } + } + } +} \ 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 dd5f8a1..f40f1d7 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 @@ -26,6 +26,8 @@ import com.ffii.core.utils.ZebraPrinterUtil import com.ffii.fpsms.modules.master.entity.ItemUomRespository import com.ffii.fpsms.modules.master.entity.WarehouseRepository import com.ffii.fpsms.modules.master.service.PrinterService +import com.ffii.fpsms.modules.purchaseOrder.entity.PurchaseOrder +import com.ffii.fpsms.modules.purchaseOrder.entity.PurchaseOrderLine import com.ffii.fpsms.modules.purchaseOrder.entity.PurchaseOrderRepository import com.ffii.fpsms.modules.purchaseOrder.enums.PurchaseOrderLineStatus import com.ffii.fpsms.modules.purchaseOrder.enums.PurchaseOrderStatus @@ -50,7 +52,7 @@ data class QrContent(val itemId: Long, val stockInLineId: Long) @Service open class StockInLineService( private val jdbcDao: JdbcDao, - private val purchaseOrderRepository: PurchaseOrderRepository, + private val poRepository: PurchaseOrderRepository, private val polRepository: PurchaseOrderLineRepository, private val qcItemsRepository: QcItemRepository, private val qcResultRepository: QcResultRepository, @@ -65,6 +67,7 @@ open class StockInLineService( private val warehouseRepository: WarehouseRepository, private val itemUomRespository: ItemUomRespository, private val printerService: PrinterService, + private val stockTakeLineRepository: StockTakeLineRepository, ): AbstractBaseEntityService(jdbcDao, stockInLineRepository) { open fun getStockInLineInfo(stockInLineId: Long): StockInLineInfo { @@ -77,44 +80,74 @@ open class StockInLineService( @Transactional open fun create(request: SaveStockInLineRequest): MessageResponse { val stockInLine = StockInLine() - val item = itemRepository.findById(request.itemId).orElseThrow() - val purchaseOrderLine = polRepository.findById(request.purchaseOrderLineId).orElseThrow() - var stockIn = stockInRepository.findByPurchaseOrderIdAndDeletedFalse(request.purchaseOrderId) - purchaseOrderLine.apply { - status = PurchaseOrderLineStatus.RECEIVING - } - val pol = polRepository.saveAndFlush(purchaseOrderLine) + val pol = if (request.purchaseOrderLineId != null) + request.purchaseOrderLineId?.let { polRepository.findById(it).orElseThrow() } + else null + val stl = if (request.stockTakeId != null) + request.stockTakeId?.let { stockTakeLineRepository.findById(it).getOrNull() } + else null + + var stockIn = if (request.stockInId != null) request.stockInId?.let { stockInRepository.findByIdAndDeletedFalse(it) } + else if (pol != null) pol.purchaseOrder?.id?.let { stockInRepository.findByPurchaseOrderIdAndDeletedFalse(it) } + else if (request.stockTakeId != null) request.stockTakeId?.let { stockInRepository.findByStockTakeIdAndDeletedFalse(it) } + else null + if (stockIn == null) { - stockIn = stockInService.create(SaveStockInRequest(purchaseOrderId = request.purchaseOrderId)).entity as StockIn + stockIn = stockInService.create(SaveStockInRequest(purchaseOrderId = pol?.purchaseOrder?.id)).entity as StockIn +// stockIn = stockInService.create(SaveStockInRequest(purchaseOrderId = request.purchaseOrderId)).entity as StockIn +// var stockIn = stockInRepository.findByPurchaseOrderIdAndDeletedFalse(request.purchaseOrderId) + } + val item = itemRepository.findById(request.itemId).orElseThrow() + // If request contains valid POL + if (pol != null) { + val po = pol.purchaseOrder; + pol.apply { + status = PurchaseOrderLineStatus.RECEIVING + } + val savedPol = polRepository.saveAndFlush(pol) + stockInLine.apply { + this.purchaseOrderLine = savedPol + } + // update po status to receiving - val po = purchaseOrderRepository.findById(request.purchaseOrderId).orElseThrow() - po.apply { - status = PurchaseOrderStatus.RECEIVING + if (po != null) { + po.apply { + status = PurchaseOrderStatus.RECEIVING + } + val savedPo = poRepository.save(po) + stockInLine.apply { + this.purchaseOrder = savedPo + } } - purchaseOrderRepository.save(po) } - val allStockInLine = stockInLineRepository.findAllStockInLineInfoByStockInIdAndDeletedFalse(stockIn.id!!) - if (pol.qty!! < request.acceptedQty) { - throw BadRequestException() + + // If request contains valid stock take id + if (stl != null) { + stockInLine.apply { + this.stockTakeLine = stl + } } + +// val allStockInLine = stockInLineRepository.findAllStockInLineInfoByStockInIdAndDeletedFalse(stockIn.id!!) +// if (pol.qty!! < request.acceptedQty) { +// throw BadRequestException() +// } stockInLine.apply { this.item = item itemNo = item.code - this.purchaseOrder = purchaseOrderLine.purchaseOrder - this.purchaseOrderLine = purchaseOrderLine this.stockIn = stockIn acceptedQty = request.acceptedQty dnNo = request.dnNo - dnDate = request.dnDate?.atStartOfDay() + receiptDate = request.receiptDate?.atStartOfDay() ?: LocalDateTime.now() productLotNo = request.productLotNo status = StockInLineStatus.PENDING.status } - val savedInLine = saveAndFlush(stockInLine) - val lineInfo = stockInLineRepository.findStockInLineInfoByIdAndDeletedFalse(savedInLine.id!!) + val savedSIL = saveAndFlush(stockInLine) + val lineInfo = stockInLineRepository.findStockInLineInfoByIdAndDeletedFalse(savedSIL.id!!) return MessageResponse( - id = savedInLine.id, - code = savedInLine.itemNo, - name = savedInLine.item!!.name, + id = savedSIL.id, + code = savedSIL.itemNo, + name = savedSIL.item!!.name, type = "stock in line created: status = pending", message = "save success", errorPosition = null, @@ -156,7 +189,7 @@ open class StockInLineService( itemId = request.itemId ) val purchaseItemUom = itemUomRespository.findByItemIdAndPurchaseUnitIsTrueAndDeletedIsFalse(request.itemId) - val convertedBaseQty = if (stockItemUom != null && purchaseItemUom != null) { + val convertedBaseQty = if (request.stockTakeLineId == null && stockItemUom != null && purchaseItemUom != null) { (line.qty) * (purchaseItemUom.ratioN!! / purchaseItemUom.ratioD!!) / (stockItemUom.ratioN!! / stockItemUom.ratioD!!) } else { (line.qty) @@ -267,66 +300,50 @@ open class StockInLineService( @Throws(IOException::class) @Transactional - open fun updatePurchaseOrderStatus(request: SaveStockInLineRequest) { - if (request.status == StockInLineStatus.COMPLETE.status) { - val unfinishedLines = polRepository - .findAllByPurchaseOrderIdAndStatusNotAndDeletedIsFalse(purchaseOrderId = request.purchaseOrderId, status = PurchaseOrderLineStatus.COMPLETED) - if (unfinishedLines.isEmpty()) { - val po = purchaseOrderRepository.findById(request.purchaseOrderId).orElseThrow() - po.apply { - status = PurchaseOrderStatus.COMPLETED - } - purchaseOrderRepository.saveAndFlush(po) - } else { - + open fun updatePurchaseOrderStatus(po : PurchaseOrder) { + val unfinishedPol = polRepository + .findAllByPurchaseOrderIdAndStatusNotAndDeletedIsFalse(po.id!!, + PurchaseOrderLineStatus.COMPLETED) + // If all POL is completed + if (unfinishedPol.isEmpty()) { + po.apply { + status = PurchaseOrderStatus.COMPLETED } + poRepository.saveAndFlush(po) + } else { + } } @Throws(IOException::class) @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 + fun updatePurchaseOrderLineStatus(pol: PurchaseOrderLine) { + val stockInLines = stockInLineRepository.findStockInLineInfoByPurchaseOrderLineIdAndDeletedFalse(pol.id!!) + val qcLines = stockInLines.filter { it.status == StockInLineStatus.PENDING.status + || it.status == StockInLineStatus.ESCALATED.status } + val receivingLines = stockInLines.filter { it.status == StockInLineStatus.RECEIVED.status + || it.status == StockInLineStatus.RECEIVING.status } + // TODO: also check the qty + if (stockInLines.isEmpty()) { // No Stock In Line + pol.apply { status = PurchaseOrderLineStatus.PENDING } + } else { + if (qcLines.isEmpty()) { // No pending QC lines + if (receivingLines.isEmpty()) { // No receiving lines + pol.apply { + status = PurchaseOrderLineStatus.COMPLETED + } + } else { // Only remain receiving lines + pol.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) - .filter { - it.status != StockInLineStatus.COMPLETE.status - && it.status != StockInLineStatus.REJECT.status - } - println("unfinishedLines") - println(unfinishedLines) - if (unfinishedLines.isEmpty()) { - // all stock in lines finished - // change status of purchase order line - val purchaseOrderLine = polRepository.findById(request.purchaseOrderLineId).orElseThrow() - purchaseOrderLine.apply { - status = PurchaseOrderLineStatus.COMPLETED + // still have pending QC lines + pol.apply { + status = PurchaseOrderLineStatus.RECEIVING } - polRepository.saveAndFlush(purchaseOrderLine) - } else { - // still have unfinished lines } } + polRepository.saveAndFlush(pol) } @Throws(IOException::class) @Transactional @@ -353,7 +370,7 @@ open class StockInLineService( this.productionDate = request.productionDate?.atStartOfDay() ?: this.productionDate// maybe need to change the request to LocalDateTime this.productLotNo = request.productLotNo ?: this.productLotNo this.dnNo = request.dnNo ?: this.dnNo - this.dnDate = request.dnDate?.atStartOfDay() ?: this.dnDate +// this.dnDate = request.dnDate?.atStartOfDay() ?: this.dnDate this.acceptedQty = request.acceptedQty this.demandQty = request.acceptQty this.invoiceNo = request.invoiceNo @@ -372,7 +389,7 @@ open class StockInLineService( val purchaseItemUom = itemUomRespository.findByItemIdAndPurchaseUnitIsTrueAndDeletedIsFalse(request.itemId) val stockItemUom = itemUomRespository.findByItemIdAndStockUnitIsTrueAndDeletedIsFalse(request.itemId) - val ratio = if (stockItemUom != null && purchaseItemUom != null) { + val ratio = if (request.stockTakeLineId == null && stockItemUom != null && purchaseItemUom != null) { (purchaseItemUom.ratioN!! / purchaseItemUom.ratioD!!) / (stockItemUom.ratioN!! / stockItemUom.ratioD!!) } else { BigDecimal.ONE @@ -416,76 +433,6 @@ open class StockInLineService( errorPosition = "request.acceptedQty", ) } - // if (request.acceptedQty.compareTo(stockInLine.acceptedQty) != 0) { - // // Partial Accept - // if (request.acceptedQty <= BigDecimal(0)) { - // return MessageResponse( - // id = null, - // code = null, - // name = null, - // type = "acceptedQty == 0", - // message = "acceptedQty cannot be 0", - // errorPosition = "request.acceptedQty", - // ) - // } - // val newStockInLine = StockInLine() - // newStockInLine.apply { - // this.item = stockInLine.item - // this.itemNo = stockInLine.itemNo - // this.purchaseOrder = stockInLine.purchaseOrderLine?.purchaseOrder - // this.purchaseOrderLine = stockInLine.purchaseOrderLine - // this.productLotNo = stockInLine.productLotNo - // this.dnNo = stockInLine.dnNo - // this.invoiceNo = stockInLine.invoiceNo - // this.remarks = stockInLine.remarks ?: request.remarks - // this.receiptDate = stockInLine.receiptDate - // this.stockIn = stockInLine.stockIn - // this.demandQty = stockInLine.demandQty - // this.acceptedQty = stockInLine.acceptedQty!!.minus(request.acceptedQty) - // this.price = stockInLine.price - // this.priceUnit = stockInLine.priceUnit - // this.inventoryLot = stockInLine.inventoryLot - // this.lotNo = stockInLine.lotNo - // this.productionDate = stockInLine.productionDate - // this.expiryDate = stockInLine.expiryDate - // this.status = StockInLineStatus.RECEIVING.status// stockInLine.status // this does update status - // this.user = stockInLine.user - // } - // saveQcResultWhenStockIn(request, stockInLine) - // var savedInventoryLot: InventoryLot? = null - // var savedInventoryLotLine: InventoryLotLine? = null // maybe remove this later - // if (request.status == StockInLineStatus.RECEIVED.status) { - // savedInventoryLot = saveInventoryLotWhenStockIn(request = request, stockInLine = stockInLine) - // } - // if (request.status == StockInLineStatus.COMPLETE.status) { - // savedInventoryLotLine = - // saveInventoryLotLineWhenStockIn(request = request, stockInLine = stockInLine) - // } - // stockInLine.apply { - // this.acceptedQty = request.acceptedQty - // this.status = StockInLineStatus.RECEIVED.status//request.status - // this.inventoryLot = savedInventoryLot ?: stockInLine.inventoryLot - // this.inventoryLotLine = savedInventoryLotLine - // this.lotNo = savedInventoryLot?.lotNo ?: stockInLine.lotNo - // } - // - // val stockInLineEntries = listOf(stockInLine, newStockInLine) - // val savedEntries = stockInLineRepository.saveAllAndFlush(stockInLineEntries) - // val ids = savedEntries.map { it.id!! } - // val lineInfoList = stockInLineRepository.findStockInLineInfoByIdInAndDeletedFalse(ids) - // // check if all line completed - // updatePurchaseOrderLineStatus(request) - // - // return MessageResponse( - // id = stockInLine.id, - // code = null, - // name = null, - // type = "Save success", - // message = "created 2 stock in line", - // errorPosition = null, - // entity = lineInfoList - // ) - // } } else if (request.qcAccept == false) { if (request.escalationLog != null) { // Escalated @@ -505,7 +452,14 @@ open class StockInLineService( } val savedStockInLine = saveAndFlush(stockInLine) // check if all line completed - updatePurchaseOrderLineStatus(request) + if (savedStockInLine.purchaseOrderLine != null) { + val pol = savedStockInLine.purchaseOrderLine + if (pol != null) { + updatePurchaseOrderLineStatus(pol) + updatePurchaseOrderStatus(pol.purchaseOrder!!) + } + } + val lineInfo = stockInLineRepository.findStockInLineInfoByIdAndDeletedFalse(savedStockInLine.id!!) return MessageResponse( diff --git a/src/main/java/com/ffii/fpsms/modules/stock/service/StockInService.kt b/src/main/java/com/ffii/fpsms/modules/stock/service/StockInService.kt index 3fc36ca..083e632 100644 --- a/src/main/java/com/ffii/fpsms/modules/stock/service/StockInService.kt +++ b/src/main/java/com/ffii/fpsms/modules/stock/service/StockInService.kt @@ -3,48 +3,55 @@ package com.ffii.fpsms.modules.stock.service import com.ffii.core.support.AbstractBaseEntityService import com.ffii.core.support.JdbcDao import com.ffii.fpsms.modules.master.web.models.MessageResponse +import com.ffii.fpsms.modules.purchaseOrder.entity.PurchaseOrder import com.ffii.fpsms.modules.purchaseOrder.entity.PurchaseOrderRepository -import com.ffii.fpsms.modules.stock.entity.StockIn -import com.ffii.fpsms.modules.stock.entity.StockInLine -import com.ffii.fpsms.modules.stock.entity.StockInLineRepository -import com.ffii.fpsms.modules.stock.entity.StockInRepository +import com.ffii.fpsms.modules.stock.entity.* import com.ffii.fpsms.modules.stock.web.model.SaveStockInRequest import com.ffii.fpsms.modules.stock.web.model.StockInStatus import org.springframework.stereotype.Service import org.springframework.transaction.annotation.Transactional import java.io.IOException +import kotlin.jvm.optionals.getOrNull +import java.time.LocalDate +import java.time.LocalDateTime +import java.util.* @Service open class StockInService( private val jdbcDao: JdbcDao, private val stockInRepository: StockInRepository, private val purchaseOrderRepository: PurchaseOrderRepository, - ): AbstractBaseEntityService(jdbcDao, stockInRepository) { + private val stockTakeRepository: StockTakeRepository, +): AbstractBaseEntityService(jdbcDao, stockInRepository) { @Throws(IOException::class) @Transactional open fun create(request: SaveStockInRequest): MessageResponse { val stockIn = StockIn() - val purchaseOrder = if (request.purchaseOrderId != null) - purchaseOrderRepository.findByIdAndDeletedFalse(request.purchaseOrderId).orElseThrow() - else return MessageResponse( - id = null, - code = null, - name = null, - type = "Found Null", - message = "request.purchaseOrderId is null", - errorPosition = "Stock In" - ) stockIn.apply { - code = purchaseOrder.code - supplier = purchaseOrder.supplier - this.purchaseOrder = purchaseOrder -// shop = purchaseOrder.shop - orderDate = purchaseOrder.orderDate - estimatedCompleteDate = purchaseOrder.estimatedArrivalDate?.toLocalDate() - completeDate = purchaseOrder.completeDate status = StockInStatus.PENDING.status + orderDate = LocalDateTime.now() + code = LocalDateTime.now().toString().take(19) + } + if (request.purchaseOrderId != null) { + val purchaseOrder : PurchaseOrder = purchaseOrderRepository.findByIdAndDeletedFalse(request.purchaseOrderId).orElseThrow(); + stockIn.apply { + code = purchaseOrder.code + supplier = purchaseOrder.supplier + this.purchaseOrder = purchaseOrder + // shop = purchaseOrder.shop + orderDate = purchaseOrder.orderDate + estimatedCompleteDate = purchaseOrder.estimatedArrivalDate?.toLocalDate() + completeDate = purchaseOrder.completeDate + } } + if (request.stockTakeId != null) { + val stockTake = request.stockTakeId.let { stockTakeRepository.findByIdAndDeletedIsFalse(it) } + stockIn.apply { + this.stockTake = stockTake + } + } + val savedStockIn = saveAndFlush(stockIn) return MessageResponse( id = savedStockIn.id, diff --git a/src/main/java/com/ffii/fpsms/modules/stock/service/StockTakeLineService.kt b/src/main/java/com/ffii/fpsms/modules/stock/service/StockTakeLineService.kt new file mode 100644 index 0000000..c73b496 --- /dev/null +++ b/src/main/java/com/ffii/fpsms/modules/stock/service/StockTakeLineService.kt @@ -0,0 +1,40 @@ +package com.ffii.fpsms.modules.stock.service + +import com.ffii.fpsms.modules.master.entity.UomConversionRepository +import com.ffii.fpsms.modules.stock.entity.InventoryLotLineRepository +import com.ffii.fpsms.modules.stock.entity.StockTakeLine +import com.ffii.fpsms.modules.stock.entity.StockTakeLineRepository +import com.ffii.fpsms.modules.stock.entity.StockTakeRepository +import com.ffii.fpsms.modules.stock.enums.StockTakeLineStatus +import com.ffii.fpsms.modules.stock.web.model.SaveStockTakeLineRequest +import org.springframework.stereotype.Service + +@Service +open class StockTakeLineService( + val stockTakeLineRepository: StockTakeLineRepository, + val stockTakeRepository: StockTakeRepository, + val inventoryLotLineRepository: InventoryLotLineRepository, + val uomConversionRepository: UomConversionRepository, + val inventoryLotLineService: InventoryLotLineService, +) { + fun saveStockTakeLine(request: SaveStockTakeLineRequest): StockTakeLine { + val stockTake = request.stockTakeId?.let { stockTakeRepository.findByIdAndDeletedIsFalse(it) } ?: throw Exception("Cant Find Stock Take"); + val inventoryLotLine = request.inventoryLotLineId?.let { inventoryLotLineRepository.findByIdAndDeletedIsFalse(it) }; + val uom = request.uomId?.let { uomConversionRepository.findByIdAndDeletedFalse(it) }; + val status = request.status?.let { _status -> StockTakeLineStatus.entries.find { it.value == _status} } + + val stockTakeLine = request.id?.let { stockTakeLineRepository.findByIdAndDeletedIsFalse(it) } ?: StockTakeLine(); + + // will skip null + stockTake.let { stockTakeLine.stockTake = it } + inventoryLotLine.let { stockTakeLine.inventoryLotLine = it } + uom.let { stockTakeLine.uom = it } + request.initialQty?.let { stockTakeLine.initialQty = it } + request.finalQty?.let { stockTakeLine.finalQty = it } + request.completeDate?.let { stockTakeLine.completeDate = it } + status?.let { stockTakeLine.status = it } + request.remarks?.let { stockTakeLine.remarks = it } + + return stockTakeLineRepository.save(stockTakeLine); + } +} \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/stock/service/StockTakeService.kt b/src/main/java/com/ffii/fpsms/modules/stock/service/StockTakeService.kt new file mode 100644 index 0000000..1a6c47e --- /dev/null +++ b/src/main/java/com/ffii/fpsms/modules/stock/service/StockTakeService.kt @@ -0,0 +1,233 @@ +package com.ffii.fpsms.modules.stock.service + +import com.ffii.core.utils.JwtTokenUtil +import com.ffii.fpsms.modules.common.CodeGenerator +import com.ffii.fpsms.modules.master.entity.WarehouseRepository +import com.ffii.fpsms.modules.master.service.ItemUomService +import com.ffii.fpsms.modules.master.service.ItemsService +import com.ffii.fpsms.modules.master.service.WarehouseService +import com.ffii.fpsms.modules.master.web.models.SaveWarehouseRequest +import com.ffii.fpsms.modules.stock.entity.InventoryLotLineRepository +import com.ffii.fpsms.modules.stock.entity.StockTake +import com.ffii.fpsms.modules.stock.entity.StockTakeRepository +import com.ffii.fpsms.modules.stock.enums.StockTakeLineStatus +import com.ffii.fpsms.modules.stock.enums.StockTakeStatus +import com.ffii.fpsms.modules.stock.web.model.* +import org.apache.poi.ss.usermodel.Cell +import org.apache.poi.ss.usermodel.CellType +import org.apache.poi.ss.usermodel.Sheet +import org.apache.poi.ss.usermodel.Workbook +import org.slf4j.Logger +import org.slf4j.LoggerFactory +import org.springframework.stereotype.Service +import java.math.BigDecimal +import java.time.LocalDate +import java.time.LocalDateTime + +@Service +class StockTakeService( + val stockTakeRepository: StockTakeRepository, + val warehouseRepository: WarehouseRepository, + val warehouseService: WarehouseService, + val stockInService: StockInService, + val stockInLineService: StockInLineService, + val itemsService: ItemsService, + val itemUomService: ItemUomService, + val stockTakeLineService: StockTakeLineService, + val inventoryLotLineRepository: InventoryLotLineRepository, +) { + val logger: Logger = LoggerFactory.getLogger(JwtTokenUtil::class.java) + + fun assignStockTakeNo(): String { + val prefix = "ST" + val midfix = CodeGenerator.DEFAULT_MIDFIX + val latestCode = stockTakeRepository.findLatestCodeByPrefix("${prefix}-${midfix}") + return CodeGenerator.generateOrderNo(prefix = prefix, latestCode = latestCode) + } + + fun saveStockTake(request: SaveStockTakeRequest): StockTake { + val stockTake = request.id?.let { stockTakeRepository.findByIdAndDeletedIsFalse(it) } ?: StockTake(); + val status = request.status?.let { _status -> StockTakeStatus.entries.find { it.value == _status } } + + request.code?.let { stockTake.code = it } + if (stockTake.code == null) { + stockTake.code = assignStockTakeNo() + } + request.planStart?.let { stockTake.planStart = it } + request.planEnd?.let { stockTake.planEnd = it } + request.actualStart?.let { stockTake.actualStart = it } + request.actualEnd?.let { stockTake.actualEnd = it } + status?.let { stockTake.status = it } + request.remarks?.let { stockTake.remarks = it } + + return stockTakeRepository.save(stockTake); + } + + // ---------------------------------------------- Import Excel ---------------------------------------------- // + fun getCellStringValue(cell: Cell): String { + return cell.let { + when (it.cellType) { + CellType.STRING -> it.stringCellValue + CellType.NUMERIC -> it.numericCellValue + else -> "" + } + }.toString() + } + fun importExcel(workbook: Workbook?): String { + logger.info("--------- Start - Import Stock Take Excel -------"); + + if (workbook == null) { + logger.error("No Excel Import"); + return "Import Excel failure"; + } + + val sheet: Sheet = workbook.getSheetAt(0); + + // Columns + val COLUMN_ITEM_CODE_INDEX = 6; + val COLUMN_FLOOR_INDEX = 11; + val COLUMN_PLACE_INDEX = 12; + val COLUMN_WAREHOSE_INDEX = 13; + val COLUMN_ZONE_INDEX = 14; + val COLUMN_SLOT_INDEX = 15; + val COLUMN_QTY_INDEX = 18; + + val START_ROW_INDEX = 2; + + // Start Import + val startTime = LocalDateTime.now(); + val stCode = assignStockTakeNo() + val saveStockTakeReq = SaveStockTakeRequest( + code = stCode, + planStart = startTime, + planEnd = startTime, + actualStart = startTime, + actualEnd = null, + status = StockTakeStatus.PENDING.value, + remarks = null + ) + val savedStockTake = saveStockTake(saveStockTakeReq); + + val saveStockInReq = SaveStockInRequest( + code = stCode, + stockTakeId = savedStockTake.id + ) + val savedStockIn = stockInService.create(saveStockInReq) + + for (i in START_ROW_INDEX ..< sheet.lastRowNum) { + val row = sheet.getRow(i) + + // Warehouse + val warehouse = try { + val floor = getCellStringValue(row.getCell(COLUMN_FLOOR_INDEX)) + val place = getCellStringValue(row.getCell(COLUMN_PLACE_INDEX)) + val code = getCellStringValue(row.getCell(COLUMN_WAREHOSE_INDEX)) + val zone = getCellStringValue(row.getCell(COLUMN_ZONE_INDEX)) + val slot = getCellStringValue(row.getCell(COLUMN_SLOT_INDEX)) +// logger.info("Warehouse code - zone - slot: ${row.getCell(COLUMN_WAREHOSE_INDEX).cellType} - ${row.getCell(COLUMN_ZONE_INDEX).cellType} - ${row.getCell(COLUMN_SLOT_INDEX).cellType}") + + val defaultCapacity = BigDecimal(10000) + val warehouseCode = "$code-$zone-$slot" + val warehouseName = "$floor-$place" + val existingWarehouse = warehouseService.findByCode(warehouseCode) + + if (existingWarehouse != null) { + existingWarehouse + } else { + val warehouseRequest = SaveWarehouseRequest( + code = warehouseCode, + name = warehouseName, + description = warehouseName, + capacity = defaultCapacity + ) + warehouseService.saveWarehouse(warehouseRequest) + } + } catch (e: Exception) { + logger.error("Import Error (Warehouse Error): ${e.message}") + null + } ?: continue + + // Item + val item = try { +// logger.info("Item Type: ${row.getCell(COLUMN_ITEM_CODE_INDEX).cellType}") + val itemCode = row.getCell(COLUMN_ITEM_CODE_INDEX).stringCellValue; + itemsService.findByCode(itemCode) + } catch (e: Exception) { + logger.error("Import Error (Item Code Error): ${e.message}") + null + } ?: continue + + // Stock Take Line (First Create) + val qty = try { +// logger.info("Qty Type: ${row.getCell(COLUMN_QTY_INDEX).cellType}") + row.getCell(COLUMN_QTY_INDEX).numericCellValue.toBigDecimal() + } catch (e: Exception) { + logger.error("Import Error (Qty Error): ${e.message}") + null + } ?: continue + + val uom = itemUomService.findStockUnitByItemId(item.id!!)?.uom + val saveStockTakeLineReq = SaveStockTakeLineRequest( + stockTakeId = savedStockTake.id, + initialQty = qty, + finalQty = qty, + uomId = uom?.id, + status = StockTakeLineStatus.PENDING.value, + remarks = null, + ) + val savedStockTakeLine = stockTakeLineService.saveStockTakeLine(saveStockTakeLineReq) + + // Stock In Line + val expiryDate = LocalDateTime.now().plusMonths(6) // TODO: Add expiry date from excel + val saveStockInLineReq = SaveStockInLineRequest( + stockInId = savedStockIn.id, + itemId = item.id!!, + acceptedQty = qty, + acceptQty = qty, + expiryDate = expiryDate.toLocalDate(), + warehouseId = warehouse.id, + stockTakeLineId = savedStockTakeLine.id, + qcAccept = true, + status = StockInLineStatus.PENDING.status + ) + + val savedStockInLine = stockInLineService.create(saveStockInLineReq) + saveStockInLineReq.apply { + id = savedStockInLine.id + } + stockInLineService.update(saveStockInLineReq) + + val inventoryLotLines = SaveInventoryLotLineForSil( + qty = qty, + warehouseId = warehouse.id + ) + saveStockInLineReq.apply { + status = StockInLineStatus.RECEIVED.status + this.inventoryLotLines = if (qty > BigDecimal.ZERO) mutableListOf(inventoryLotLines) else null + } + val finalStockInLine = stockInLineService.update(saveStockInLineReq) + + // Stock Take Line (Second Completed) + val inventoryLotLine = inventoryLotLineRepository.findByInventoryLotStockInLineIdAndWarehouseId(inventoryLotStockInLineId = finalStockInLine.id!!, warehouseId = warehouse.id!!) + saveStockTakeLineReq.apply { + id = savedStockTakeLine.id + status = StockTakeLineStatus.COMPLETED.value + completeDate = LocalDateTime.now() + inventoryLotLineId = inventoryLotLine?.id + } + stockTakeLineService.saveStockTakeLine(saveStockTakeLineReq) + logger.info("[Stock Take]: Saved item '${item.name}' to warehouse '${warehouse.code}'") + } + + // End of Import + val endTime = LocalDateTime.now(); + saveStockTakeReq.apply { + id = savedStockTake.id + actualEnd = endTime + status = StockTakeStatus.COMPLETED.value + } + saveStockTake(saveStockTakeReq) + logger.info("--------- End - Import Stock Take Excel -------") + return "Import Excel success"; + } +} \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/stock/web/StockTakeController.kt b/src/main/java/com/ffii/fpsms/modules/stock/web/StockTakeController.kt new file mode 100644 index 0000000..3aef8aa --- /dev/null +++ b/src/main/java/com/ffii/fpsms/modules/stock/web/StockTakeController.kt @@ -0,0 +1,43 @@ +package com.ffii.fpsms.modules.stock.web + +import com.ffii.fpsms.modules.stock.service.StockTakeService +import jakarta.servlet.http.HttpServletRequest +import org.apache.poi.ss.usermodel.Workbook +import org.apache.poi.util.IOUtils +import org.apache.poi.xssf.streaming.SXSSFWorkbook +import org.apache.poi.xssf.usermodel.XSSFWorkbook +import org.springframework.http.ResponseEntity +import org.springframework.web.bind.ServletRequestBindingException +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.multipart.MultipartHttpServletRequest + +@RequestMapping("/stockTake") +@RestController +class StockTakeController( + val stockTakeService: StockTakeService, +) { + @PostMapping("/import") + @Throws(ServletRequestBindingException::class) + fun importExcel(request: HttpServletRequest): ResponseEntity<*> { + var workbook: Workbook? = null; + + try { + println("Start") + val multipartFile = (request as MultipartHttpServletRequest).getFile("multipartFileList") + workbook = XSSFWorkbook(multipartFile?.inputStream) + + // Use SXSSFWorkbook instead of XSSFWorkbook +// IOUtils.setByteArrayMaxOverride(300_000_000) +// workbook = SXSSFWorkbook(XSSFWorkbook(multipartFile?.inputStream)) + // Optional: Configure SXSSF to limit memory usage +// workbook.setCompressTempFiles(true) + } catch (e: Exception) { + println("Excel Wrong") + println(e) + } + + return ResponseEntity.ok(stockTakeService.importExcel(workbook)) + } +} \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/stock/web/model/SaveStockInRequest.kt b/src/main/java/com/ffii/fpsms/modules/stock/web/model/SaveStockInRequest.kt index 4575a82..4ba6bd4 100644 --- a/src/main/java/com/ffii/fpsms/modules/stock/web/model/SaveStockInRequest.kt +++ b/src/main/java/com/ffii/fpsms/modules/stock/web/model/SaveStockInRequest.kt @@ -33,32 +33,36 @@ data class SaveStockInRequest( val completeDate: LocalDateTime? = null, val status: String? = null, val stockOutId: Long? = null, + val stockTakeId: Long? = null, // val m18 ) data class SaveStockInLineRequest( - var id: Long?, - var purchaseOrderId: Long, - var purchaseOrderLineId: Long, + var id: Long? = null, + var stockInId: Long? = null, + var stockTakeId: Long? = null, + var stockTakeLineId: Long? = null, var itemId: Long, + var purchaseOrderId: Long? = null, + var purchaseOrderLineId: Long? = null, var acceptedQty: BigDecimal, var acceptQty: BigDecimal?, - var acceptedWeight: BigDecimal?, + var acceptedWeight: BigDecimal? = null, var status: String?, var expiryDate: LocalDate?, - var productLotNo: String?, - var dnNo: String?, - var invoiceNo: String?, - var remarks: String?, - var dnDate: LocalDate?, - var receiptDate: LocalDate?, - var productionDate: LocalDate?, - var qcAccept: Boolean?, - var qcResult: List?, - var escalationLog: SaveEscalationLogRequest?, - var warehouseId: Long?, - var rejectQty: BigDecimal?, - var inventoryLotLines: List? + var productLotNo: String? = null, + var dnNo: String? = null, + var invoiceNo: String? = null, + var remarks: String? = null, +// var dnDate: LocalDate? = null, + var receiptDate: LocalDate? = null, + var productionDate: LocalDate? = null, + var qcAccept: Boolean? = null, + var qcResult: List? = null, + var escalationLog: SaveEscalationLogRequest? = null, + var warehouseId: Long? = null, + var rejectQty: BigDecimal? = null, + var inventoryLotLines: List? = null ) data class SaveInventoryLotLineForSil ( diff --git a/src/main/java/com/ffii/fpsms/modules/stock/web/model/SaveStockTakeLineRequest.kt b/src/main/java/com/ffii/fpsms/modules/stock/web/model/SaveStockTakeLineRequest.kt new file mode 100644 index 0000000..24530ba --- /dev/null +++ b/src/main/java/com/ffii/fpsms/modules/stock/web/model/SaveStockTakeLineRequest.kt @@ -0,0 +1,16 @@ +package com.ffii.fpsms.modules.stock.web.model + +import java.math.BigDecimal +import java.time.LocalDateTime + +data class SaveStockTakeLineRequest( + var id: Long? = null, + val stockTakeId: Long?, + var inventoryLotLineId: Long? = null, + val initialQty: BigDecimal?, + val finalQty: BigDecimal?, + val uomId: Long?, + var completeDate: LocalDateTime? = null, + var status: String?, + val remarks: String?, +) diff --git a/src/main/java/com/ffii/fpsms/modules/stock/web/model/SaveStockTakeRequest.kt b/src/main/java/com/ffii/fpsms/modules/stock/web/model/SaveStockTakeRequest.kt new file mode 100644 index 0000000..6ce4798 --- /dev/null +++ b/src/main/java/com/ffii/fpsms/modules/stock/web/model/SaveStockTakeRequest.kt @@ -0,0 +1,14 @@ +package com.ffii.fpsms.modules.stock.web.model + +import java.time.LocalDateTime + +data class SaveStockTakeRequest( + var id: Long? = null, + val code: String?, + val planStart: LocalDateTime?, + val planEnd: LocalDateTime?, + val actualStart: LocalDateTime?, + var actualEnd: LocalDateTime?, + var status: String?, + val remarks: String?, +) diff --git a/src/main/resources/db/changelog/changes/20250918_01_cyril/01_update_stock_in_line.sql b/src/main/resources/db/changelog/changes/20250918_01_cyril/01_update_stock_in_line.sql new file mode 100644 index 0000000..5dea155 --- /dev/null +++ b/src/main/resources/db/changelog/changes/20250918_01_cyril/01_update_stock_in_line.sql @@ -0,0 +1,24 @@ +-- liquibase formatted sql +-- changeset cyril:update_stock_in + +ALTER TABLE `stock_in` + ADD COLUMN `stockTakeId` INT NULL DEFAULT NULL AFTER `stockOutId`, + ADD INDEX `FK_STOCK_IN_ON_STOCKTAKEID` (`stockTakeId` ASC) VISIBLE; +; +ALTER TABLE `stock_in` + ADD CONSTRAINT `FK_STOCK_IN_ON_STOCKTAKEID` + FOREIGN KEY (`stockTakeId`) + REFERENCES `stock_take` (`id`) + ON DELETE RESTRICT + ON UPDATE RESTRICT; + +ALTER TABLE `stock_in_line` + ADD COLUMN `stockTakeLineId` INT NULL DEFAULT NULL AFTER `purchaseOrderLineId`, + ADD INDEX `FK_STOCK_IN_LINE_ON_STOCKTAKELINEID` (`stockTakeLineId` ASC) VISIBLE; +; +ALTER TABLE `stock_in_line` + ADD CONSTRAINT `FK_STOCK_IN_LINE_ON_STOCKTAKELINEID` + FOREIGN KEY (`stockTakeLineId`) + REFERENCES `stock_take_line` (`id`) + ON DELETE RESTRICT + ON UPDATE RESTRICT; diff --git a/src/main/resources/db/changelog/changes/20250918_01_cyril/02_update_stock_take_line.sql b/src/main/resources/db/changelog/changes/20250918_01_cyril/02_update_stock_take_line.sql new file mode 100644 index 0000000..8b43b04 --- /dev/null +++ b/src/main/resources/db/changelog/changes/20250918_01_cyril/02_update_stock_take_line.sql @@ -0,0 +1,11 @@ +-- liquibase formatted sql +-- changeset cyril:update_stock_take_line + +ALTER TABLE `stock_take_line` + DROP FOREIGN KEY `FK_STOCK_TAKE_LINE_ON_INVENTORYLOTLINEID`; +ALTER TABLE `stock_take_line` + CHANGE COLUMN `inventoryLotLineId` `inventoryLotLineId` INT NULL ; +ALTER TABLE `stock_take_line` + ADD CONSTRAINT `FK_STOCK_TAKE_LINE_ON_INVENTORYLOTLINEID` + FOREIGN KEY (`inventoryLotLineId`) + REFERENCES `inventory_lot_line` (`id`); diff --git a/src/main/resources/db/changelog/changes/202510926_01_enson/01_altertable_enson.sql b/src/main/resources/db/changelog/changes/202510926_01_enson/01_altertable_enson.sql new file mode 100644 index 0000000..73d5693 --- /dev/null +++ b/src/main/resources/db/changelog/changes/202510926_01_enson/01_altertable_enson.sql @@ -0,0 +1,5 @@ +-- liquibase formatted sql +-- changeset enson:altertable_enson + + ALTER TABLE `fpsmsdb`.`do_pick_order` + ADD COLUMN `hide` TINYINT NULL DEFAULT '0' AFTER `deleted`;