| @@ -4,6 +4,7 @@ import java.time.LocalDate | |||||
| import java.time.format.DateTimeFormatter | import java.time.format.DateTimeFormatter | ||||
| object CodeGenerator { | object CodeGenerator { | ||||
| // Default Value for Lot No | |||||
| private var dateFormat = DateTimeFormatter.ofPattern("yyMMdd") | private var dateFormat = DateTimeFormatter.ofPattern("yyMMdd") | ||||
| fun generateCode(prefix: String, itemId: Long, count: Int): String { | fun generateCode(prefix: String, itemId: Long, count: Int): String { | ||||
| // prefix = "ITEM" || "LOT" | // prefix = "ITEM" || "LOT" | ||||
| @@ -13,4 +14,33 @@ object CodeGenerator { | |||||
| val countStr = String.format("%04d", count) | val countStr = String.format("%04d", count) | ||||
| return "$prefix-$todayStr$itemStr$countStr" | 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<String>(prefix, midfix, String.format(finalSuffixFormat, latestNo + 1)).joinToString("-") | |||||
| } | |||||
| } | |||||
| return listOf<String>(prefix, midfix, suffix).joinToString("-") | |||||
| } | |||||
| } | } | ||||
| @@ -2,6 +2,8 @@ package com.ffii.fpsms.modules.jobOrder.entity | |||||
| import com.fasterxml.jackson.annotation.JsonManagedReference | import com.fasterxml.jackson.annotation.JsonManagedReference | ||||
| import com.ffii.core.entity.BaseEntity | 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.Bom | ||||
| import com.ffii.fpsms.modules.master.entity.ProductionScheduleLine | import com.ffii.fpsms.modules.master.entity.ProductionScheduleLine | ||||
| import com.ffii.fpsms.modules.user.entity.User | import com.ffii.fpsms.modules.user.entity.User | ||||
| @@ -42,10 +44,11 @@ open class JobOrder : BaseEntity<Long>() { | |||||
| @Column(name = "actualQty", precision = 14, scale = 2) | @Column(name = "actualQty", precision = 14, scale = 2) | ||||
| open var actualQty: BigDecimal? = null | open var actualQty: BigDecimal? = null | ||||
| @Size(max = 100) | |||||
| // @Size(max = 100) | |||||
| @NotNull | @NotNull | ||||
| @Column(name = "status", nullable = false, length = 100) | @Column(name = "status", nullable = false, length = 100) | ||||
| open var status: String? = null | |||||
| @Convert(converter = JobOrderStatusConverter::class) | |||||
| open var status: JobOrderStatus? = null | |||||
| @Size(max = 500) | @Size(max = 500) | ||||
| @Column(name = "remarks", length = 500) | @Column(name = "remarks", length = 500) | ||||
| @@ -5,4 +5,5 @@ import org.springframework.stereotype.Repository | |||||
| @Repository | @Repository | ||||
| interface JobOrderBomMaterialRepository : AbstractRepository<JobOrderBomMaterial, Long> { | interface JobOrderBomMaterialRepository : AbstractRepository<JobOrderBomMaterial, Long> { | ||||
| fun findByJobOrderIdAndItemId(jobOrderId: Long, itemId: Long): JobOrderBomMaterial?; | |||||
| } | } | ||||
| @@ -20,6 +20,24 @@ interface JobOrderRepository : AbstractRepository<JobOrder, Long> { | |||||
| @Query( | @Query( | ||||
| nativeQuery = true, | nativeQuery = true, | ||||
| value = """ | 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 | |||||
| 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 | select | ||||
| jo.id, | jo.id, | ||||
| jo.code, | jo.code, | ||||
| @@ -32,7 +50,7 @@ interface JobOrderRepository : AbstractRepository<JobOrder, Long> { | |||||
| 'id', jobm.id, | 'id', jobm.id, | ||||
| 'code', i.code, | 'code', i.code, | ||||
| 'name', i.name, | 'name', i.name, | ||||
| 'lotNo', il.lotNo, | |||||
| 'pickedLotNo', pln.pickedLotNo, | |||||
| 'reqQty', jobm.reqQty, | 'reqQty', jobm.reqQty, | ||||
| 'uom', uc.udfudesc, | 'uom', uc.udfudesc, | ||||
| 'status', jobm.status | 'status', jobm.status | ||||
| @@ -46,15 +64,14 @@ interface JobOrderRepository : AbstractRepository<JobOrder, Long> { | |||||
| left join job_order_bom_material jobm on jo.id = jobm.jobOrderId | left join job_order_bom_material jobm on jo.id = jobm.jobOrderId | ||||
| left join items i on i.id = jobm.itemId | left join items i on i.id = jobm.itemId | ||||
| left join uom_conversion uc on uc.id = jobm.uomId | 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 | where jo.id = :id | ||||
| group by jo.id, uc2.udfudesc | group by jo.id, uc2.udfudesc | ||||
| limit 1 | limit 1 | ||||
| """ | """ | ||||
| ) | ) | ||||
| fun findJobOrderDetailById(id: Long): JobOrderDetailWithJsonString?; | fun findJobOrderDetailById(id: Long): JobOrderDetailWithJsonString?; | ||||
| @Query( | @Query( | ||||
| nativeQuery = true, | nativeQuery = true, | ||||
| value = """ | value = """ | ||||
| @@ -14,6 +14,8 @@ interface JobOrderInfo { | |||||
| @get:Value("#{target.bom.item.itemUoms.^[salesUnit == true && deleted == false]?.uom.udfudesc}") | @get:Value("#{target.bom.item.itemUoms.^[salesUnit == true && deleted == false]?.uom.udfudesc}") | ||||
| val uom: String; | val uom: String; | ||||
| @get:Value("#{target.status.value}") | |||||
| val status: String; | val status: String; | ||||
| } | } | ||||
| @@ -42,8 +44,13 @@ data class JobOrderDetailPickLine( | |||||
| val id: Long?, | val id: Long?, | ||||
| val code: String?, | val code: String?, | ||||
| val name: String?, | val name: String?, | ||||
| val lotNo: String?, | |||||
| val pickedLotNo: List<JobOrderDetailPickedLotNo>?, | |||||
| val reqQty: BigDecimal?, | val reqQty: BigDecimal?, | ||||
| val uom: String?, | val uom: String?, | ||||
| val status: String? | val status: String? | ||||
| ) | |||||
| data class JobOrderDetailPickedLotNo( | |||||
| val lotNo: String?, | |||||
| val qty: BigDecimal?, | |||||
| ) | ) | ||||
| @@ -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") | |||||
| } | |||||
| @@ -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<JobOrderStatus, String> { | |||||
| 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 } | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -24,7 +24,7 @@ open class JobOrderBomMaterialService( | |||||
| open fun createJobOrderBomMaterialRequests(joId: Long): List<CreateJobOrderBomMaterialRequest> { | open fun createJobOrderBomMaterialRequests(joId: Long): List<CreateJobOrderBomMaterialRequest> { | ||||
| val zero = BigDecimal.ZERO | val zero = BigDecimal.ZERO | ||||
| val jo = jobOrderRepository.findById(joId).getOrNull() ?: throw NoSuchElementException() | 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 jobmRequests = jo.bom?.bomMaterials?.map { bm -> | ||||
| val salesUnit = bm.item?.id?.let { itemUomService.findSalesUnitByItemId(it) } | val salesUnit = bm.item?.id?.let { itemUomService.findSalesUnitByItemId(it) } | ||||
| @@ -35,7 +35,7 @@ open class JobOrderBomMaterialService( | |||||
| reqQty = bm.qty?.times(proportion) ?: zero, | reqQty = bm.qty?.times(proportion) ?: zero, | ||||
| uomId = salesUnit?.uom?.id | uomId = salesUnit?.uom?.id | ||||
| ) | ) | ||||
| } ?: listOf() | |||||
| } ?: listOf() | |||||
| return jobmRequests | return jobmRequests | ||||
| } | } | ||||
| @@ -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.JobOrderDetail | ||||
| import com.ffii.fpsms.modules.jobOrder.entity.projections.JobOrderDetailPickLine | import com.ffii.fpsms.modules.jobOrder.entity.projections.JobOrderDetailPickLine | ||||
| import com.ffii.fpsms.modules.jobOrder.entity.projections.JobOrderInfo | 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.CreateJobOrderRequest | ||||
| import com.ffii.fpsms.modules.jobOrder.web.model.JobOrderReleaseRequest | import com.ffii.fpsms.modules.jobOrder.web.model.JobOrderReleaseRequest | ||||
| import com.ffii.fpsms.modules.jobOrder.web.model.SearchJobOrderInfoRequest | 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 approver = request.approverId?.let { userService.find(it).getOrNull() } ?: SecurityUtils.getUser().getOrNull() | ||||
| val prodScheduleLine = request.prodScheduleLineId?.let { productionScheduleLineRepository.findById(it).getOrNull() } | val prodScheduleLine = request.prodScheduleLineId?.let { productionScheduleLineRepository.findById(it).getOrNull() } | ||||
| val code = assignJobNo() | val code = assignJobNo() | ||||
| val status = JobOrderStatus.entries.find { it.value == request.status } | |||||
| jo.apply { | jo.apply { | ||||
| this.code = code | this.code = code | ||||
| @@ -119,7 +121,7 @@ open class JobOrderService( | |||||
| planStart = request.planStart ?: LocalDateTime.now() | planStart = request.planStart ?: LocalDateTime.now() | ||||
| planEnd = request.planEnd ?: LocalDateTime.now() | planEnd = request.planEnd ?: LocalDateTime.now() | ||||
| reqQty = request.reqQty | reqQty = request.reqQty | ||||
| status = request.status | |||||
| this.status = status | |||||
| type = request.type | type = request.type | ||||
| this.approver = approver | this.approver = approver | ||||
| this.prodScheduleLine = prodScheduleLine | this.prodScheduleLine = prodScheduleLine | ||||
| @@ -141,7 +143,7 @@ open class JobOrderService( | |||||
| open fun releaseJobOrder(request: JobOrderReleaseRequest): MessageResponse { | open fun releaseJobOrder(request: JobOrderReleaseRequest): MessageResponse { | ||||
| val jo = request.id.let { jobOrderRepository.findById(it).getOrNull() } ?: throw NoSuchElementException() | val jo = request.id.let { jobOrderRepository.findById(it).getOrNull() } ?: throw NoSuchElementException() | ||||
| jo.apply { | jo.apply { | ||||
| status = "pending" | |||||
| status = JobOrderStatus.PENDING | |||||
| } | } | ||||
| jobOrderRepository.save(jo) | jobOrderRepository.save(jo) | ||||
| @@ -168,7 +170,7 @@ open class JobOrderService( | |||||
| type = null, | type = null, | ||||
| message = null, | message = null, | ||||
| errorPosition = null, | errorPosition = null, | ||||
| entity = mapOf("status" to jo.status) | |||||
| entity = mapOf("status" to jo.status?.value) | |||||
| ) | ) | ||||
| } | } | ||||
| } | } | ||||
| @@ -13,4 +13,6 @@ interface BomMaterialRepository : AbstractRepository<BomMaterial, Long> { | |||||
| fun findAllByBomItemIdAndDeletedIsFalse(itemId: Long): List<BomMaterial> | fun findAllByBomItemIdAndDeletedIsFalse(itemId: Long): List<BomMaterial> | ||||
| fun findAllByBomIdAndDeletedIsFalse(bomId: Long): List<BomMaterial> | fun findAllByBomIdAndDeletedIsFalse(bomId: Long): List<BomMaterial> | ||||
| fun findByBomIdAndItemId(bomId: Long, itemId: Long): BomMaterial? | |||||
| } | } | ||||
| @@ -5,4 +5,5 @@ import org.springframework.stereotype.Repository | |||||
| @Repository | @Repository | ||||
| interface BomProcessMaterialRepository : AbstractRepository<BomProcessMaterial, Long> { | interface BomProcessMaterialRepository : AbstractRepository<BomProcessMaterial, Long> { | ||||
| fun findByBomProcessIdAndBomMaterialId(bomProcessId: Long, bomMaterialId: Long): BomProcessMaterial?; | |||||
| } | } | ||||
| @@ -16,4 +16,6 @@ interface BomRepository : AbstractRepository<Bom, Long> { | |||||
| fun findByItemIdAndDeletedIsFalse(itemId: Serializable): Bom? | fun findByItemIdAndDeletedIsFalse(itemId: Serializable): Bom? | ||||
| fun findBomComboByDeletedIsFalse(): List<BomCombo> | fun findBomComboByDeletedIsFalse(): List<BomCombo> | ||||
| fun findByCodeAndDeletedIsFalse(code: String): Bom? | |||||
| } | } | ||||
| @@ -3,8 +3,13 @@ package com.ffii.fpsms.modules.master.entity | |||||
| import com.ffii.core.support.AbstractRepository | import com.ffii.core.support.AbstractRepository | ||||
| import com.ffii.fpsms.modules.master.entity.projections.WarehouseCombo | import com.ffii.fpsms.modules.master.entity.projections.WarehouseCombo | ||||
| import org.springframework.stereotype.Repository | import org.springframework.stereotype.Repository | ||||
| import java.io.Serializable | |||||
| @Repository | @Repository | ||||
| interface WarehouseRepository : AbstractRepository<Warehouse, Long> { | interface WarehouseRepository : AbstractRepository<Warehouse, Long> { | ||||
| fun findWarehouseComboByDeletedFalse(): List<WarehouseCombo>; | fun findWarehouseComboByDeletedFalse(): List<WarehouseCombo>; | ||||
| fun findByIdAndDeletedIsFalse(id: Serializable): Warehouse?; | |||||
| fun findByCodeAndDeletedIsFalse(code: String): Warehouse?; | |||||
| } | } | ||||
| @@ -90,7 +90,8 @@ open class BomService( | |||||
| private fun saveBomEntity(req: ImportBomRequest): Bom { | private fun saveBomEntity(req: ImportBomRequest): Bom { | ||||
| val item = itemsRepository.findByCodeAndDeletedFalse(req.code) ?: itemsRepository.findByNameAndDeletedFalse(req.name) | val item = itemsRepository.findByCodeAndDeletedFalse(req.code) ?: itemsRepository.findByNameAndDeletedFalse(req.name) | ||||
| val uom = if (req.uomId != null) uomConversionRepository.findById(req.uomId!!).orElseThrow() else null | 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.isDark = req.isDark | ||||
| this.isFloat = req.isFloat | this.isFloat = req.isFloat | ||||
| this.isDense = req.isDense | this.isDense = req.isDense | ||||
| @@ -111,10 +112,11 @@ open class BomService( | |||||
| private fun saveBomMaterial(req: ImportBomMatRequest): BomMaterial { | private fun saveBomMaterial(req: ImportBomMatRequest): BomMaterial { | ||||
| // val uom = uomConversionRepository.findByCodeAndDeletedFalse() | // 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.item = req.item | ||||
| this.itemName = req.item!!.name | this.itemName = req.item!!.name | ||||
| this.isConsumable = req.isConsumable | this.isConsumable = req.isConsumable | ||||
| @@ -124,12 +126,12 @@ open class BomService( | |||||
| this.uom = req.uom | this.uom = req.uom | ||||
| this.uomName = req.uomName | this.uomName = req.uomName | ||||
| this.bom = req.bom | this.bom = req.bom | ||||
| } | } | ||||
| return bomMaterialRepository.saveAndFlush(bomMaterial) | return bomMaterialRepository.saveAndFlush(bomMaterial) | ||||
| } | } | ||||
| private fun saveBomProcess(req: ImportBomProcessRequest): BomProcess { | 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.process = req.process | ||||
| this.equipment = req.equipment | this.equipment = req.equipment | ||||
| this.description = req.description | this.description = req.description | ||||
| @@ -142,7 +144,8 @@ open class BomService( | |||||
| return bomProcessRepository.saveAndFlush(bomProcess) | return bomProcessRepository.saveAndFlush(bomProcess) | ||||
| } | } | ||||
| fun saveBomProcessMaterial(req: ImportBomProcessMaterialRequest): BomProcessMaterial { | 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.bomProcess = req.bomProcess | ||||
| this.bomMaterial = req.bomMaterial | this.bomMaterial = req.bomMaterial | ||||
| } | } | ||||
| @@ -489,7 +492,7 @@ open class BomService( | |||||
| // val folder = File(folderPath) | // val folder = File(folderPath) | ||||
| val resolver = PathMatchingResourcePatternResolver() | val resolver = PathMatchingResourcePatternResolver() | ||||
| // val excels = resolver.getResources("bomImport/*.xlsx") | // 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") | // val excels = resolver.getResources("file:C:/Users/2Fi/Desktop/Third Wave of BOM Excel/*.xlsx") | ||||
| println("size: ${excels.size}") | println("size: ${excels.size}") | ||||
| val logExcel = ClassPathResource("excelTemplate/bom_excel_issue_log.xlsx") | val logExcel = ClassPathResource("excelTemplate/bom_excel_issue_log.xlsx") | ||||
| @@ -210,6 +210,10 @@ open class ItemsService( | |||||
| return itemsRepository.findByIdAndDeletedFalse(id); | return itemsRepository.findByIdAndDeletedFalse(id); | ||||
| } | } | ||||
| open fun findByCode(code: String): Items? { | |||||
| return itemsRepository.findByCodeAndDeletedFalse(code); | |||||
| } | |||||
| open fun findByM18Id(m18Id: Long): Items? { | open fun findByM18Id(m18Id: Long): Items? { | ||||
| return itemsRepository.findByM18IdAndDeletedIsFalse(m18Id) | return itemsRepository.findByM18IdAndDeletedIsFalse(m18Id) | ||||
| } | } | ||||
| @@ -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.Warehouse | ||||
| import com.ffii.fpsms.modules.master.entity.WarehouseRepository | import com.ffii.fpsms.modules.master.entity.WarehouseRepository | ||||
| import com.ffii.fpsms.modules.master.entity.projections.WarehouseCombo | 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.purchaseOrder.entity.PurchaseOrderLineRepository | ||||
| import com.ffii.fpsms.modules.stock.entity.InventoryLotRepository | import com.ffii.fpsms.modules.stock.entity.InventoryLotRepository | ||||
| import com.ffii.fpsms.modules.stock.entity.StockInLine | 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.InventoryLotService | ||||
| import com.ffii.fpsms.modules.stock.service.StockInService | import com.ffii.fpsms.modules.stock.service.StockInService | ||||
| import org.springframework.stereotype.Service | import org.springframework.stereotype.Service | ||||
| import kotlin.jvm.optionals.getOrNull | |||||
| @Service | @Service | ||||
| open class WarehouseService( | open class WarehouseService( | ||||
| @@ -28,4 +30,25 @@ open class WarehouseService( | |||||
| open fun findCombo(): List<WarehouseCombo> { | open fun findCombo(): List<WarehouseCombo> { | ||||
| return warehouseRepository.findWarehouseComboByDeletedFalse(); | 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); | |||||
| } | |||||
| } | } | ||||
| @@ -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, | |||||
| ) | |||||
| @@ -12,6 +12,9 @@ import com.ffii.fpsms.modules.stock.entity.enum.InventoryLotLineStatus | |||||
| @Repository | @Repository | ||||
| interface InventoryLotLineRepository : AbstractRepository<InventoryLotLine, Long> { | interface InventoryLotLineRepository : AbstractRepository<InventoryLotLine, Long> { | ||||
| fun findByIdAndDeletedIsFalse(id: Serializable): InventoryLotLine; | |||||
| fun findInventoryLotLineInfoByInventoryLotItemIdIn(ids: List<Serializable>): List<InventoryLotLineInfo> | fun findInventoryLotLineInfoByInventoryLotItemIdIn(ids: List<Serializable>): List<InventoryLotLineInfo> | ||||
| @Query("select ill from InventoryLotLine ill where :id is null or ill.inventoryLot.item.id = :id order by ill.id desc") | @Query("select ill from InventoryLotLine ill where :id is null or ill.inventoryLot.item.id = :id order by ill.id desc") | ||||
| @@ -34,6 +37,9 @@ interface InventoryLotLineRepository : AbstractRepository<InventoryLotLine, Long | |||||
| fun findAllByIdIn(ids: List<Serializable>): List<InventoryLotLine> | fun findAllByIdIn(ids: List<Serializable>): List<InventoryLotLine> | ||||
| fun findAllByInventoryLotId(id: Serializable): List<InventoryLotLine> | fun findAllByInventoryLotId(id: Serializable): List<InventoryLotLine> | ||||
| fun findByInventoryLotStockInLineIdAndWarehouseId(inventoryLotStockInLineId: Long, warehouseId: Long): InventoryLotLine? | |||||
| fun findAllByInventoryLotItemIdAndStatus(itemId: Long, status: String): List<InventoryLotLine> | fun findAllByInventoryLotItemIdAndStatus(itemId: Long, status: String): List<InventoryLotLine> | ||||
| fun findAllByInventoryLotItemIdAndStatus(itemId: Long, status: InventoryLotLineStatus): List<InventoryLotLine> | fun findAllByInventoryLotItemIdAndStatus(itemId: Long, status: InventoryLotLineStatus): List<InventoryLotLine> | ||||
| @@ -40,6 +40,10 @@ open class StockIn : BaseEntity<Long>() { | |||||
| @JoinColumn(name = "stockOutId") | @JoinColumn(name = "stockOutId") | ||||
| open var stockOutId: StockOut? = null | open var stockOutId: StockOut? = null | ||||
| @ManyToOne | |||||
| @JoinColumn(name = "stockTakeId") | |||||
| open var stockTake: StockTake? = null | |||||
| @Column(name = "orderDate") | @Column(name = "orderDate") | ||||
| open var orderDate: LocalDateTime? = null | open var orderDate: LocalDateTime? = null | ||||
| @@ -37,6 +37,10 @@ open class StockInLine : BaseEntity<Long>() { | |||||
| @JoinColumn(name = "purchaseOrderLineId") | @JoinColumn(name = "purchaseOrderLineId") | ||||
| open var purchaseOrderLine: PurchaseOrderLine? = null | open var purchaseOrderLine: PurchaseOrderLine? = null | ||||
| @ManyToOne | |||||
| @JoinColumn(name = "stockTakeLineId") | |||||
| open var stockTakeLine: StockTakeLine? = null | |||||
| @NotNull | @NotNull | ||||
| @ManyToOne | @ManyToOne | ||||
| @JoinColumn(name = "stockInId", nullable = false) | @JoinColumn(name = "stockInId", nullable = false) | ||||
| @@ -1,9 +1,18 @@ | |||||
| package com.ffii.fpsms.modules.stock.entity | package com.ffii.fpsms.modules.stock.entity | ||||
| import com.ffii.core.support.AbstractRepository | import com.ffii.core.support.AbstractRepository | ||||
| import org.springframework.data.jpa.repository.Query | |||||
| import org.springframework.stereotype.Repository | import org.springframework.stereotype.Repository | ||||
| import java.io.Serializable | |||||
| @Repository | @Repository | ||||
| interface StockInRepository : AbstractRepository<StockIn, Long> { | interface StockInRepository : AbstractRepository<StockIn, Long> { | ||||
| fun findByPurchaseOrderIdAndDeletedFalse(purchaseOrderId: 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? | |||||
| } | } | ||||
| @@ -1,6 +1,8 @@ | |||||
| package com.ffii.fpsms.modules.stock.entity | package com.ffii.fpsms.modules.stock.entity | ||||
| import com.ffii.core.entity.BaseEntity | 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.persistence.* | ||||
| import jakarta.validation.constraints.NotNull | import jakarta.validation.constraints.NotNull | ||||
| import jakarta.validation.constraints.Size | import jakarta.validation.constraints.Size | ||||
| @@ -28,10 +30,11 @@ open class StockTake: BaseEntity<Long>() { | |||||
| @Column(name = "actualEnd") | @Column(name = "actualEnd") | ||||
| open var actualEnd: LocalDateTime? = null | open var actualEnd: LocalDateTime? = null | ||||
| @Size(max = 20) | |||||
| // @Size(max = 20) | |||||
| @NotNull | @NotNull | ||||
| @Column(name = "status", nullable = false, length = 20) | @Column(name = "status", nullable = false, length = 20) | ||||
| open var status: String? = null | |||||
| @Convert(converter = StockTakeStatusConverter::class) | |||||
| open var status: StockTakeStatus? = null | |||||
| @Size(max = 500) | @Size(max = 500) | ||||
| @Column(name = "remarks", length = 500) | @Column(name = "remarks", length = 500) | ||||
| @@ -2,6 +2,8 @@ package com.ffii.fpsms.modules.stock.entity | |||||
| import com.ffii.core.entity.BaseEntity | import com.ffii.core.entity.BaseEntity | ||||
| import com.ffii.fpsms.modules.master.entity.UomConversion | 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.persistence.* | ||||
| import jakarta.validation.constraints.NotNull | import jakarta.validation.constraints.NotNull | ||||
| import jakarta.validation.constraints.Size | import jakarta.validation.constraints.Size | ||||
| @@ -16,7 +18,6 @@ open class StockTakeLine : BaseEntity<Long>() { | |||||
| @JoinColumn(name = "stockTakeId", nullable = false) | @JoinColumn(name = "stockTakeId", nullable = false) | ||||
| open var stockTake: StockTake? = null | open var stockTake: StockTake? = null | ||||
| @NotNull | |||||
| @ManyToOne | @ManyToOne | ||||
| @JoinColumn(name = "inventoryLotLineId", nullable = false) | @JoinColumn(name = "inventoryLotLineId", nullable = false) | ||||
| open var inventoryLotLine: InventoryLotLine? = null | open var inventoryLotLine: InventoryLotLine? = null | ||||
| @@ -34,10 +35,11 @@ open class StockTakeLine : BaseEntity<Long>() { | |||||
| @Column(name = "completeDate") | @Column(name = "completeDate") | ||||
| open var completeDate: LocalDateTime? = null | open var completeDate: LocalDateTime? = null | ||||
| @Size(max = 20) | |||||
| // @Size(max = 20) | |||||
| @NotNull | @NotNull | ||||
| @Column(name = "status", nullable = false, length = 20) | @Column(name = "status", nullable = false, length = 20) | ||||
| open var status: String? = null | |||||
| @Convert(converter = StockTakeLineStatusConverter::class) | |||||
| open var status: StockTakeLineStatus? = null | |||||
| @Size(max = 500) | @Size(max = 500) | ||||
| @Column(name = "remarks", length = 500) | @Column(name = "remarks", length = 500) | ||||
| @@ -2,7 +2,9 @@ package com.ffii.fpsms.modules.stock.entity | |||||
| import com.ffii.core.support.AbstractRepository | import com.ffii.core.support.AbstractRepository | ||||
| import org.springframework.stereotype.Repository | import org.springframework.stereotype.Repository | ||||
| import java.io.Serializable | |||||
| @Repository | @Repository | ||||
| interface StockTakeLineRepository : AbstractRepository<StockTakeLine, Long> { | interface StockTakeLineRepository : AbstractRepository<StockTakeLine, Long> { | ||||
| fun findByIdAndDeletedIsFalse(id: Serializable): StockTakeLine?; | |||||
| } | } | ||||
| @@ -1,8 +1,16 @@ | |||||
| package com.ffii.fpsms.modules.stock.entity | package com.ffii.fpsms.modules.stock.entity | ||||
| import com.ffii.core.support.AbstractRepository | import com.ffii.core.support.AbstractRepository | ||||
| import org.springframework.data.jpa.repository.Query | |||||
| import org.springframework.stereotype.Repository | import org.springframework.stereotype.Repository | ||||
| import java.io.Serializable | |||||
| @Repository | @Repository | ||||
| interface StockTakeRepository : AbstractRepository<StockTake, Long> { | interface StockTakeRepository : AbstractRepository<StockTake, Long> { | ||||
| 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? | |||||
| } | } | ||||
| @@ -0,0 +1,7 @@ | |||||
| package com.ffii.fpsms.modules.stock.enums | |||||
| enum class StockTakeStatus(val value: String) { | |||||
| PENDING("pending"), | |||||
| STOCKTAKING("stockTaking"), | |||||
| COMPLETED("completed"), | |||||
| } | |||||
| @@ -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<StockTakeStatus, String>{ | |||||
| 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 } | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,6 @@ | |||||
| package com.ffii.fpsms.modules.stock.enums | |||||
| enum class StockTakeLineStatus(val value: String) { | |||||
| PENDING("pending"), | |||||
| COMPLETED("completed"), | |||||
| } | |||||
| @@ -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<StockTakeLineStatus, String> { | |||||
| 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 } | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -65,6 +65,7 @@ open class StockInLineService( | |||||
| private val warehouseRepository: WarehouseRepository, | private val warehouseRepository: WarehouseRepository, | ||||
| private val itemUomRespository: ItemUomRespository, | private val itemUomRespository: ItemUomRespository, | ||||
| private val printerService: PrinterService, | private val printerService: PrinterService, | ||||
| private val stockTakeLineRepository: StockTakeLineRepository, | |||||
| ): AbstractBaseEntityService<StockInLine, Long, StockInLineRepository>(jdbcDao, stockInLineRepository) { | ): AbstractBaseEntityService<StockInLine, Long, StockInLineRepository>(jdbcDao, stockInLineRepository) { | ||||
| open fun getStockInLineInfo(stockInLineId: Long): StockInLineInfo { | open fun getStockInLineInfo(stockInLineId: Long): StockInLineInfo { | ||||
| @@ -78,30 +79,36 @@ open class StockInLineService( | |||||
| open fun create(request: SaveStockInLineRequest): MessageResponse { | open fun create(request: SaveStockInLineRequest): MessageResponse { | ||||
| val stockInLine = StockInLine() | val stockInLine = StockInLine() | ||||
| val item = itemRepository.findById(request.itemId).orElseThrow() | val item = itemRepository.findById(request.itemId).orElseThrow() | ||||
| val purchaseOrderLine = polRepository.findById(request.purchaseOrderLineId).orElseThrow() | |||||
| var stockIn = stockInRepository.findByPurchaseOrderIdAndDeletedFalse(request.purchaseOrderId) | |||||
| purchaseOrderLine.apply { | |||||
| val purchaseOrderLine = request.purchaseOrderLineId?.let { polRepository.findById(it).getOrNull() } | |||||
| val stockTakeLine = request.stockTakeLineId?.let { stockTakeLineRepository.findById(it).getOrNull() } | |||||
| var stockIn = request.stockInId?.let { stockInRepository.findByIdAndDeletedIsFalse(it) } | |||||
| ?: request.purchaseOrderId?.let { stockInRepository.findByPurchaseOrderIdAndDeletedFalse(it) } | |||||
| ?: request.stockTakeId?.let { stockInRepository.findByStockTakeIdAndDeletedFalse(it) } | |||||
| purchaseOrderLine?.apply { | |||||
| status = PurchaseOrderLineStatus.RECEIVING | status = PurchaseOrderLineStatus.RECEIVING | ||||
| } | } | ||||
| val pol = polRepository.saveAndFlush(purchaseOrderLine) | |||||
| val pol = purchaseOrderLine?.let { polRepository.saveAndFlush(it) } | |||||
| if (stockIn == null) { | if (stockIn == null) { | ||||
| stockIn = stockInService.create(SaveStockInRequest(purchaseOrderId = request.purchaseOrderId)).entity as StockIn | stockIn = stockInService.create(SaveStockInRequest(purchaseOrderId = request.purchaseOrderId)).entity as StockIn | ||||
| // update po status to receiving | // update po status to receiving | ||||
| val po = purchaseOrderRepository.findById(request.purchaseOrderId).orElseThrow() | |||||
| po.apply { | |||||
| status = PurchaseOrderStatus.RECEIVING | |||||
| val po = request.purchaseOrderId?.let { purchaseOrderRepository.findById(it).getOrNull() } | |||||
| if (po != null) { | |||||
| po.apply { | |||||
| status = PurchaseOrderStatus.RECEIVING | |||||
| } | |||||
| purchaseOrderRepository.save(po) | |||||
| } | } | ||||
| purchaseOrderRepository.save(po) | |||||
| } | } | ||||
| val allStockInLine = stockInLineRepository.findAllStockInLineInfoByStockInIdAndDeletedFalse(stockIn.id!!) | val allStockInLine = stockInLineRepository.findAllStockInLineInfoByStockInIdAndDeletedFalse(stockIn.id!!) | ||||
| if (pol.qty!! < request.acceptedQty) { | |||||
| if (pol != null && pol.qty!! < request.acceptedQty) { | |||||
| throw BadRequestException() | throw BadRequestException() | ||||
| } | } | ||||
| stockInLine.apply { | stockInLine.apply { | ||||
| this.item = item | this.item = item | ||||
| itemNo = item.code | itemNo = item.code | ||||
| this.purchaseOrder = purchaseOrderLine.purchaseOrder | |||||
| this.purchaseOrder = purchaseOrderLine?.purchaseOrder | |||||
| this.purchaseOrderLine = purchaseOrderLine | this.purchaseOrderLine = purchaseOrderLine | ||||
| this.stockTakeLine = stockTakeLine | |||||
| this.stockIn = stockIn | this.stockIn = stockIn | ||||
| acceptedQty = request.acceptedQty | acceptedQty = request.acceptedQty | ||||
| dnNo = request.dnNo | dnNo = request.dnNo | ||||
| @@ -156,7 +163,7 @@ open class StockInLineService( | |||||
| itemId = request.itemId | itemId = request.itemId | ||||
| ) | ) | ||||
| val purchaseItemUom = itemUomRespository.findByItemIdAndPurchaseUnitIsTrueAndDeletedIsFalse(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!!) | (line.qty) * (purchaseItemUom.ratioN!! / purchaseItemUom.ratioD!!) / (stockItemUom.ratioN!! / stockItemUom.ratioD!!) | ||||
| } else { | } else { | ||||
| (line.qty) | (line.qty) | ||||
| @@ -269,9 +276,9 @@ open class StockInLineService( | |||||
| @Transactional | @Transactional | ||||
| open fun updatePurchaseOrderStatus(request: SaveStockInLineRequest) { | open fun updatePurchaseOrderStatus(request: SaveStockInLineRequest) { | ||||
| if (request.status == StockInLineStatus.COMPLETE.status) { | if (request.status == StockInLineStatus.COMPLETE.status) { | ||||
| val unfinishedLines = polRepository | |||||
| .findAllByPurchaseOrderIdAndStatusNotAndDeletedIsFalse(purchaseOrderId = request.purchaseOrderId, status = PurchaseOrderLineStatus.COMPLETED) | |||||
| if (unfinishedLines.isEmpty()) { | |||||
| val unfinishedLines = request.purchaseOrderId?.let { polRepository | |||||
| .findAllByPurchaseOrderIdAndStatusNotAndDeletedIsFalse(purchaseOrderId = it, status = PurchaseOrderLineStatus.COMPLETED) } | |||||
| if (unfinishedLines != null && unfinishedLines.isEmpty()) { | |||||
| val po = purchaseOrderRepository.findById(request.purchaseOrderId).orElseThrow() | val po = purchaseOrderRepository.findById(request.purchaseOrderId).orElseThrow() | ||||
| po.apply { | po.apply { | ||||
| status = PurchaseOrderStatus.COMPLETED | status = PurchaseOrderStatus.COMPLETED | ||||
| @@ -287,42 +294,52 @@ open class StockInLineService( | |||||
| fun updatePurchaseOrderLineStatus(request: SaveStockInLineRequest) { | fun updatePurchaseOrderLineStatus(request: SaveStockInLineRequest) { | ||||
| println(request.status) | println(request.status) | ||||
| if (request.status == StockInLineStatus.RECEIVING.status) { | if (request.status == StockInLineStatus.RECEIVING.status) { | ||||
| val unQcedLines = stockInLineRepository.findStockInLineInfoByPurchaseOrderLineIdAndDeletedFalse(purchaseOrderLineId = request.purchaseOrderLineId) | |||||
| .filter { | |||||
| val unQcedLines = request.purchaseOrderLineId?.let { stockInLineRepository.findStockInLineInfoByPurchaseOrderLineIdAndDeletedFalse(purchaseOrderLineId = it) } | |||||
| ?.filter { | |||||
| it.status != StockInLineStatus.RECEIVING.status | it.status != StockInLineStatus.RECEIVING.status | ||||
| && it.status != StockInLineStatus.RECEIVED.status | && it.status != StockInLineStatus.RECEIVED.status | ||||
| && it.status != StockInLineStatus.COMPLETE.status | && it.status != StockInLineStatus.COMPLETE.status | ||||
| && it.status != StockInLineStatus.REJECT.status | && it.status != StockInLineStatus.REJECT.status | ||||
| } | } | ||||
| if (unQcedLines.isEmpty()) { | |||||
| if (unQcedLines != null && unQcedLines.isEmpty()) { | |||||
| // all stock in lines finished | // all stock in lines finished | ||||
| // change status of purchase order line | // change status of purchase order line | ||||
| val purchaseOrderLine = polRepository.findById(request.purchaseOrderLineId).orElseThrow() | |||||
| purchaseOrderLine.apply { | |||||
| status = PurchaseOrderLineStatus.RECEIVING | |||||
| val purchaseOrderLine = request.purchaseOrderLineId?.let { polRepository.findById(it).orElseThrow() } | |||||
| if (purchaseOrderLine != null) { | |||||
| purchaseOrderLine.apply { | |||||
| status = PurchaseOrderLineStatus.RECEIVING | |||||
| } | |||||
| polRepository.saveAndFlush(purchaseOrderLine) | |||||
| } | } | ||||
| polRepository.saveAndFlush(purchaseOrderLine) | |||||
| } else { | } else { | ||||
| // still have unQcedLines lines | // still have unQcedLines lines | ||||
| } | } | ||||
| } | } | ||||
| if (request.status == StockInLineStatus.COMPLETE.status || request.status == StockInLineStatus.REJECT.status) { | if (request.status == StockInLineStatus.COMPLETE.status || request.status == StockInLineStatus.REJECT.status) { | ||||
| // val unfinishedLines = stockInLineRepository.findStockInLineInfoByPurchaseOrderLineIdAndStatusNotAndDeletedFalse(purchaseOrderLineId = request.purchaseOrderLineId, status = request.status!!) | // val unfinishedLines = stockInLineRepository.findStockInLineInfoByPurchaseOrderLineIdAndStatusNotAndDeletedFalse(purchaseOrderLineId = request.purchaseOrderLineId, status = request.status!!) | ||||
| val unfinishedLines = stockInLineRepository.findStockInLineInfoByPurchaseOrderLineIdAndDeletedFalse(purchaseOrderLineId = request.purchaseOrderLineId) | |||||
| .filter { | |||||
| val unfinishedLines = request.purchaseOrderLineId?.let { | |||||
| stockInLineRepository.findStockInLineInfoByPurchaseOrderLineIdAndDeletedFalse( | |||||
| purchaseOrderLineId = it | |||||
| ) | |||||
| } | |||||
| ?.filter { | |||||
| it.status != StockInLineStatus.COMPLETE.status | it.status != StockInLineStatus.COMPLETE.status | ||||
| && it.status != StockInLineStatus.REJECT.status | && it.status != StockInLineStatus.REJECT.status | ||||
| } | } | ||||
| println("unfinishedLines") | println("unfinishedLines") | ||||
| println(unfinishedLines) | println(unfinishedLines) | ||||
| if (unfinishedLines.isEmpty()) { | |||||
| if (unfinishedLines != null && unfinishedLines.isEmpty()) { | |||||
| // all stock in lines finished | // all stock in lines finished | ||||
| // change status of purchase order line | // change status of purchase order line | ||||
| val purchaseOrderLine = polRepository.findById(request.purchaseOrderLineId).orElseThrow() | |||||
| purchaseOrderLine.apply { | |||||
| status = PurchaseOrderLineStatus.COMPLETED | |||||
| val purchaseOrderLine = request.purchaseOrderLineId?.let { | |||||
| polRepository.findById(it).orElseThrow() | |||||
| } | |||||
| if (purchaseOrderLine != null) { | |||||
| purchaseOrderLine.apply { | |||||
| status = PurchaseOrderLineStatus.COMPLETED | |||||
| } | |||||
| polRepository.saveAndFlush(purchaseOrderLine) | |||||
| } | } | ||||
| polRepository.saveAndFlush(purchaseOrderLine) | |||||
| } else { | } else { | ||||
| // still have unfinished lines | // still have unfinished lines | ||||
| } | } | ||||
| @@ -372,7 +389,7 @@ open class StockInLineService( | |||||
| val purchaseItemUom = itemUomRespository.findByItemIdAndPurchaseUnitIsTrueAndDeletedIsFalse(request.itemId) | val purchaseItemUom = itemUomRespository.findByItemIdAndPurchaseUnitIsTrueAndDeletedIsFalse(request.itemId) | ||||
| val stockItemUom = itemUomRespository.findByItemIdAndStockUnitIsTrueAndDeletedIsFalse(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!!) | (purchaseItemUom.ratioN!! / purchaseItemUom.ratioD!!) / (stockItemUom.ratioN!! / stockItemUom.ratioD!!) | ||||
| } else { | } else { | ||||
| BigDecimal.ONE | BigDecimal.ONE | ||||
| @@ -4,45 +4,47 @@ import com.ffii.core.support.AbstractBaseEntityService | |||||
| import com.ffii.core.support.JdbcDao | import com.ffii.core.support.JdbcDao | ||||
| import com.ffii.fpsms.modules.master.web.models.MessageResponse | import com.ffii.fpsms.modules.master.web.models.MessageResponse | ||||
| import com.ffii.fpsms.modules.purchaseOrder.entity.PurchaseOrderRepository | 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.SaveStockInRequest | ||||
| import com.ffii.fpsms.modules.stock.web.model.StockInStatus | import com.ffii.fpsms.modules.stock.web.model.StockInStatus | ||||
| import org.springframework.stereotype.Service | import org.springframework.stereotype.Service | ||||
| import org.springframework.transaction.annotation.Transactional | import org.springframework.transaction.annotation.Transactional | ||||
| import java.io.IOException | import java.io.IOException | ||||
| import kotlin.jvm.optionals.getOrNull | |||||
| @Service | @Service | ||||
| open class StockInService( | open class StockInService( | ||||
| private val jdbcDao: JdbcDao, | private val jdbcDao: JdbcDao, | ||||
| private val stockInRepository: StockInRepository, | private val stockInRepository: StockInRepository, | ||||
| private val purchaseOrderRepository: PurchaseOrderRepository, | private val purchaseOrderRepository: PurchaseOrderRepository, | ||||
| private val stockTakeRepository: StockTakeRepository, | |||||
| ): AbstractBaseEntityService<StockIn, Long, StockInRepository>(jdbcDao, stockInRepository) { | ): AbstractBaseEntityService<StockIn, Long, StockInRepository>(jdbcDao, stockInRepository) { | ||||
| @Throws(IOException::class) | @Throws(IOException::class) | ||||
| @Transactional | @Transactional | ||||
| open fun create(request: SaveStockInRequest): MessageResponse { | open fun create(request: SaveStockInRequest): MessageResponse { | ||||
| val stockIn = StockIn() | 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" | |||||
| ) | |||||
| // 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" | |||||
| // ) | |||||
| val purchaseOrder = request.purchaseOrderId?.let { purchaseOrderRepository.findByIdAndDeletedFalse(request.purchaseOrderId).getOrNull() } | |||||
| val stockTake = request.stockTakeId?.let { stockTakeRepository.findByIdAndDeletedIsFalse(it) } | |||||
| stockIn.apply { | stockIn.apply { | ||||
| code = purchaseOrder.code | |||||
| supplier = purchaseOrder.supplier | |||||
| code = request.code ?: purchaseOrder?.code | |||||
| supplier = purchaseOrder?.supplier | |||||
| this.purchaseOrder = purchaseOrder | this.purchaseOrder = purchaseOrder | ||||
| this.stockTake = stockTake | |||||
| // shop = purchaseOrder.shop | // shop = purchaseOrder.shop | ||||
| orderDate = purchaseOrder.orderDate | |||||
| estimatedCompleteDate = purchaseOrder.estimatedArrivalDate?.toLocalDate() | |||||
| completeDate = purchaseOrder.completeDate | |||||
| orderDate = purchaseOrder?.orderDate | |||||
| estimatedCompleteDate = purchaseOrder?.estimatedArrivalDate?.toLocalDate() | |||||
| completeDate = purchaseOrder?.completeDate | |||||
| status = StockInStatus.PENDING.status | status = StockInStatus.PENDING.status | ||||
| } | } | ||||
| val savedStockIn = saveAndFlush(stockIn) | val savedStockIn = saveAndFlush(stockIn) | ||||
| @@ -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); | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,218 @@ | |||||
| 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.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 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_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 code = row.getCell(COLUMN_WAREHOSE_INDEX).stringCellValue | |||||
| val zone = row.getCell(COLUMN_ZONE_INDEX).stringCellValue | |||||
| val slot = row.getCell(COLUMN_SLOT_INDEX).stringCellValue | |||||
| // 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 existingWarehouse = warehouseService.findByCode(warehouseCode) | |||||
| if (existingWarehouse != null) { | |||||
| existingWarehouse | |||||
| } else { | |||||
| val warehouseRequest = SaveWarehouseRequest( | |||||
| code = warehouseCode, | |||||
| name = warehouseCode, | |||||
| description = warehouseCode, | |||||
| 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"; | |||||
| } | |||||
| } | |||||
| @@ -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)) | |||||
| } | |||||
| } | |||||
| @@ -33,32 +33,36 @@ data class SaveStockInRequest( | |||||
| val completeDate: LocalDateTime? = null, | val completeDate: LocalDateTime? = null, | ||||
| val status: String? = null, | val status: String? = null, | ||||
| val stockOutId: Long? = null, | val stockOutId: Long? = null, | ||||
| val stockTakeId: Long? = null, | |||||
| // val m18 | // val m18 | ||||
| ) | ) | ||||
| data class SaveStockInLineRequest( | data class SaveStockInLineRequest( | ||||
| var id: Long?, | |||||
| var purchaseOrderId: Long, | |||||
| var purchaseOrderLineId: Long, | |||||
| var id: Long? = null, | |||||
| var purchaseOrderId: Long? = null, | |||||
| var purchaseOrderLineId: Long? = null, | |||||
| var itemId: Long, | var itemId: Long, | ||||
| var acceptedQty: BigDecimal, | var acceptedQty: BigDecimal, | ||||
| var acceptQty: BigDecimal?, | var acceptQty: BigDecimal?, | ||||
| var acceptedWeight: BigDecimal?, | |||||
| var acceptedWeight: BigDecimal? = null, | |||||
| var status: String?, | var status: String?, | ||||
| var expiryDate: LocalDate?, | 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<SaveQcResultRequest>?, | |||||
| var escalationLog: SaveEscalationLogRequest?, | |||||
| 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<SaveQcResultRequest>? = null, | |||||
| var escalationLog: SaveEscalationLogRequest? = null, | |||||
| var warehouseId: Long?, | var warehouseId: Long?, | ||||
| var rejectQty: BigDecimal?, | |||||
| var inventoryLotLines: List<SaveInventoryLotLineForSil>? | |||||
| var rejectQty: BigDecimal? = null, | |||||
| var inventoryLotLines: List<SaveInventoryLotLineForSil>? = null, | |||||
| var stockTakeLineId: Long? = null, | |||||
| var stockTakeId: Long? = null, | |||||
| var stockInId: Long? = null, | |||||
| ) | ) | ||||
| data class SaveInventoryLotLineForSil ( | data class SaveInventoryLotLineForSil ( | ||||
| @@ -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?, | |||||
| ) | |||||
| @@ -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?, | |||||
| ) | |||||
| @@ -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; | |||||
| @@ -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`); | |||||