| @@ -18,7 +18,7 @@ interface JobOrderRepository : AbstractRepository<JobOrder, Long> { | |||||
| fun findLatestCodeByPrefix(prefix: String): String? | fun findLatestCodeByPrefix(prefix: String): String? | ||||
| fun findJobOrderInfoByCodeContainsAndBomNameContainsAndDeletedIsFalseOrderByIdDesc(code: String, bomName: String, pageable: Pageable): Page<JobOrderInfo> | fun findJobOrderInfoByCodeContainsAndBomNameContainsAndDeletedIsFalseOrderByIdDesc(code: String, bomName: String, pageable: Pageable): Page<JobOrderInfo> | ||||
| fun findByBom_Id(bomId: Long): List<JobOrder> | |||||
| @Query( | @Query( | ||||
| nativeQuery = true, | nativeQuery = true, | ||||
| value = """ | value = """ | ||||
| @@ -1757,7 +1757,7 @@ private fun normalizeFloor(raw: String): String { | |||||
| val num = cleaned.replace(Regex("[^0-9]"), "") | val num = cleaned.replace(Regex("[^0-9]"), "") | ||||
| return if (num.isNotEmpty()) "${num}F" else cleaned | return if (num.isNotEmpty()) "${num}F" else cleaned | ||||
| } | } | ||||
| open fun getAllJoPickOrders(): List<AllJoPickOrderResponse> { | |||||
| open fun getAllJoPickOrders(isDrink: Boolean?): List<AllJoPickOrderResponse> { | |||||
| println("=== getAllJoPickOrders ===") | println("=== getAllJoPickOrders ===") | ||||
| return try { | return try { | ||||
| @@ -1808,7 +1808,8 @@ open fun getAllJoPickOrders(): List<AllJoPickOrderResponse> { | |||||
| val bom = jobOrder.bom | val bom = jobOrder.bom | ||||
| // 按 isDrink 过滤:null 表示不过滤(全部) | |||||
| if (isDrink != null && bom?.isDrink != isDrink) return@mapNotNull null | |||||
| println("BOM found: ${bom?.id}") | println("BOM found: ${bom?.id}") | ||||
| val item = bom?.item | val item = bom?.item | ||||
| @@ -1860,7 +1861,7 @@ open fun getAllJoPickOrders(): List<AllJoPickOrderResponse> { | |||||
| } | } | ||||
| println("Returning ${jobOrderPickOrders.size} released job order pick orders") | println("Returning ${jobOrderPickOrders.size} released job order pick orders") | ||||
| jobOrderPickOrders | |||||
| jobOrderPickOrders.sortedByDescending { it.id } | |||||
| } catch (e: Exception) { | } catch (e: Exception) { | ||||
| println("❌ Error in getAllJoPickOrders: ${e.message}") | println("❌ Error in getAllJoPickOrders: ${e.message}") | ||||
| e.printStackTrace() | e.printStackTrace() | ||||
| @@ -22,6 +22,7 @@ import com.ffii.fpsms.modules.pickOrder.service.PickOrderService | |||||
| import com.ffii.fpsms.modules.pickOrder.web.models.SavePickOrderLineRequest | import com.ffii.fpsms.modules.pickOrder.web.models.SavePickOrderLineRequest | ||||
| import com.ffii.fpsms.modules.pickOrder.web.models.SavePickOrderRequest | import com.ffii.fpsms.modules.pickOrder.web.models.SavePickOrderRequest | ||||
| import com.ffii.fpsms.modules.user.service.UserService | import com.ffii.fpsms.modules.user.service.UserService | ||||
| import com.google.gson.reflect.TypeToken | import com.google.gson.reflect.TypeToken | ||||
| import org.springframework.data.domain.PageRequest | import org.springframework.data.domain.PageRequest | ||||
| import org.springframework.stereotype.Service | import org.springframework.stereotype.Service | ||||
| @@ -69,6 +70,8 @@ import java.time.LocalDateTime | |||||
| import com.ffii.fpsms.modules.master.entity.BomMaterialRepository | import com.ffii.fpsms.modules.master.entity.BomMaterialRepository | ||||
| import com.ffii.fpsms.modules.master.service.ItemUomService | import com.ffii.fpsms.modules.master.service.ItemUomService | ||||
| import com.ffii.fpsms.modules.master.web.models.ConvertUomByItemRequest | import com.ffii.fpsms.modules.master.web.models.ConvertUomByItemRequest | ||||
| import com.ffii.fpsms.modules.stock.service.StockInLineService | |||||
| import com.ffii.fpsms.modules.stock.web.model.SaveStockInLineRequest | |||||
| @Service | @Service | ||||
| open class JobOrderService( | open class JobOrderService( | ||||
| val jobOrderRepository: JobOrderRepository, | val jobOrderRepository: JobOrderRepository, | ||||
| @@ -87,6 +90,7 @@ open class JobOrderService( | |||||
| val jobTypeRepository: JobTypeRepository, | val jobTypeRepository: JobTypeRepository, | ||||
| val inventoryRepository: InventoryRepository, | val inventoryRepository: InventoryRepository, | ||||
| val stockInLineRepository: StockInLineRepository, | val stockInLineRepository: StockInLineRepository, | ||||
| val stockInLineService: StockInLineService, | |||||
| val productProcessRepository: ProductProcessRepository, | val productProcessRepository: ProductProcessRepository, | ||||
| val jobOrderBomMaterialRepository: JobOrderBomMaterialRepository, | val jobOrderBomMaterialRepository: JobOrderBomMaterialRepository, | ||||
| val bomMaterialRepository: BomMaterialRepository, | val bomMaterialRepository: BomMaterialRepository, | ||||
| @@ -437,7 +441,21 @@ open class JobOrderService( | |||||
| } | } | ||||
| val savedJo = jobOrderRepository.saveAndFlush(jo); | val savedJo = jobOrderRepository.saveAndFlush(jo); | ||||
| savedJo.bom?.item?.id?.let { fgItemId -> | |||||
| stockInLineService.create( | |||||
| SaveStockInLineRequest( | |||||
| itemId = fgItemId, | |||||
| acceptedQty = BigDecimal.ZERO, | |||||
| acceptQty = null, | |||||
| jobOrderId = savedJo.id, | |||||
| status = "pending", | |||||
| expiryDate = null, | |||||
| productLotNo = null, | |||||
| productionDate = null, | |||||
| receiptDate = null, | |||||
| ) | |||||
| ) | |||||
| } | |||||
| return MessageResponse( | return MessageResponse( | ||||
| id = savedJo.id, | id = savedJo.id, | ||||
| name = null, | name = null, | ||||
| @@ -243,8 +243,8 @@ fun recordSecondScanIssue( | |||||
| return joPickOrderService.getCompletedJobOrderPickOrderLotDetailsForCompletedPick(pickOrderId) | return joPickOrderService.getCompletedJobOrderPickOrderLotDetailsForCompletedPick(pickOrderId) | ||||
| } | } | ||||
| @GetMapping("/AllJoPickOrder") | @GetMapping("/AllJoPickOrder") | ||||
| fun getAllJoPickOrder(): List<AllJoPickOrderResponse> { | |||||
| return joPickOrderService.getAllJoPickOrders() | |||||
| fun getAllJoPickOrder(@RequestParam(required = false) isDrink: Boolean?): List<AllJoPickOrderResponse> { | |||||
| return joPickOrderService.getAllJoPickOrders(isDrink) | |||||
| } | } | ||||
| @GetMapping("/all-lots-hierarchical-by-pick-order/{pickOrderId}") | @GetMapping("/all-lots-hierarchical-by-pick-order/{pickOrderId}") | ||||
| @@ -22,6 +22,8 @@ open class Bom : BaseEntity<Long>() { | |||||
| @Column | @Column | ||||
| open var isDense: Int? = null | open var isDense: Int? = null | ||||
| @Column | @Column | ||||
| open var isDrink: Boolean? = false | |||||
| @Column | |||||
| open var scrapRate: Int? = null | open var scrapRate: Int? = null | ||||
| @Column | @Column | ||||
| open var allergicSubstances: Int? = null | open var allergicSubstances: Int? = null | ||||
| @@ -31,7 +33,7 @@ open class Bom : BaseEntity<Long>() { | |||||
| @Column | @Column | ||||
| open var complexity: Int? = null | open var complexity: Int? = null | ||||
| @JsonBackReference | @JsonBackReference | ||||
| @OneToOne | |||||
| @ManyToOne | |||||
| @JoinColumn(name = "itemId") | @JoinColumn(name = "itemId") | ||||
| open var item: Items? = null | open var item: Items? = null | ||||
| @@ -5,5 +5,6 @@ import org.springframework.stereotype.Repository | |||||
| @Repository | @Repository | ||||
| interface BomProcessMaterialRepository : AbstractRepository<BomProcessMaterial, Long> { | interface BomProcessMaterialRepository : AbstractRepository<BomProcessMaterial, Long> { | ||||
| fun findByBomProcessIdAndBomMaterialId(bomProcessId: Long, bomMaterialId: Long): BomProcessMaterial?; | |||||
| fun findByBomProcessIdAndBomMaterialId(bomProcessId: Long, bomMaterialId: Long): BomProcessMaterial? | |||||
| fun findByBomProcess_IdIn(bomProcessIds: List<Long>): List<BomProcessMaterial> | |||||
| } | } | ||||
| @@ -16,6 +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 findAllByItemIdAndDeletedIsFalse(itemId: Long): List<Bom> | |||||
| fun findByCodeAndDeletedIsFalse(code: String): Bom? | fun findByCodeAndDeletedIsFalse(code: String): Bom? | ||||
| } | } | ||||
| @@ -76,8 +76,8 @@ open class Items : BaseEntity<Long>() { | |||||
| open var inventories: MutableList<Inventory> = mutableListOf() | open var inventories: MutableList<Inventory> = mutableListOf() | ||||
| @JsonManagedReference | @JsonManagedReference | ||||
| @OneToOne(mappedBy = "item", cascade = [CascadeType.ALL], orphanRemoval = true) | |||||
| open var bom: Bom? = null | |||||
| @OneToMany(mappedBy = "item", cascade = [CascadeType.ALL], orphanRemoval = true) | |||||
| open var boms: MutableList<Bom> = mutableListOf() | |||||
| @ManyToOne | @ManyToOne | ||||
| @JoinColumn(name = "categoryId") | @JoinColumn(name = "categoryId") | ||||
| @@ -11,15 +11,64 @@ import org.springframework.http.ResponseEntity | |||||
| import org.springframework.web.bind.annotation.GetMapping | import org.springframework.web.bind.annotation.GetMapping | ||||
| import org.springframework.web.bind.annotation.PathVariable | import org.springframework.web.bind.annotation.PathVariable | ||||
| import org.springframework.web.bind.annotation.PostMapping | import org.springframework.web.bind.annotation.PostMapping | ||||
| import org.springframework.web.bind.annotation.RequestBody | |||||
| import org.springframework.web.bind.annotation.RequestMapping | import org.springframework.web.bind.annotation.RequestMapping | ||||
| import org.springframework.web.bind.annotation.RequestParam | |||||
| import org.springframework.web.bind.annotation.RestController | import org.springframework.web.bind.annotation.RestController | ||||
| import org.springframework.web.multipart.MultipartFile | |||||
| import org.springframework.web.multipart.MultipartHttpServletRequest | |||||
| import org.slf4j.LoggerFactory | |||||
| import java.time.LocalDate | import java.time.LocalDate | ||||
| import jakarta.servlet.http.HttpServletRequest | |||||
| import jakarta.servlet.http.Part | |||||
| import com.ffii.fpsms.modules.master.web.models.BomFormatCheckResponse | |||||
| import com.ffii.fpsms.modules.master.web.models.BomUploadResponse | |||||
| import com.ffii.fpsms.modules.master.web.models.BomFormatCheckRequest | |||||
| import com.ffii.fpsms.modules.master.web.models.ImportBomRequestPayload | |||||
| import java.util.logging.Logger | |||||
| @RestController | @RestController | ||||
| @RequestMapping("/bom") | @RequestMapping("/bom") | ||||
| class BomController ( | class BomController ( | ||||
| val bomService: BomService, private val bomRepository: BomRepository, | val bomService: BomService, private val bomRepository: BomRepository, | ||||
| ) { | ) { | ||||
| private val log = LoggerFactory.getLogger(BomController::class.java) | |||||
| @PostMapping("/import-bom/upload") | |||||
| fun uploadBomFiles(request: HttpServletRequest): BomUploadResponse { | |||||
| val multipartRequest = request as? MultipartHttpServletRequest | |||||
| if (multipartRequest != null) { | |||||
| val files = multipartRequest.getFiles("files").toList().filterNot { it.isEmpty } | |||||
| if (files.isNotEmpty()) { | |||||
| log.info("import-bom/upload: using getFiles(\"files\"), count={}", files.size) | |||||
| return bomService.uploadBomFiles(files) | |||||
| } | |||||
| // 先嘗試 Spring 包裝的 MultipartHttpServletRequest(與其他 Controller 一致) | |||||
| log.info("import-bom/upload: received files count={}", files.size) | |||||
| files.forEachIndexed { i, f -> | |||||
| log.info("import-bom/upload: file[{}] originalFilename={}", i, f.originalFilename) | |||||
| } | |||||
| } | |||||
| // 否則用 Servlet getParts()(Postman 等可能不會被包裝成 MultipartHttpServletRequest) | |||||
| val allParts = try { | |||||
| request.parts | |||||
| } catch (e: Exception) { | |||||
| log.info("import-bom/upload: request.parts failed, content-type may not be multipart: {}", e.message) | |||||
| return BomUploadResponse(batchId = "", fileNames = emptyList()) | |||||
| } | |||||
| val parts = allParts.filter { it.name == "files" } | |||||
| if (parts.isEmpty()) { | |||||
| log.info("import-bom/upload: no parts with name \"files\". All part names: {}", allParts.map { it.name }) | |||||
| return BomUploadResponse(batchId = "", fileNames = emptyList()) | |||||
| } | |||||
| log.info("import-bom/upload: using getParts(), count={}", parts.size) | |||||
| return bomService.uploadBomFilesFromParts(parts) | |||||
| } | |||||
| @PostMapping("/import-bom/format-check") | |||||
| fun checkBomFormat(@RequestBody request: BomFormatCheckRequest): BomFormatCheckResponse { | |||||
| return bomService.checkBomExcelFormat(request.batchId) | |||||
| } | |||||
| @GetMapping | @GetMapping | ||||
| fun getBoms(): List<Bom> { | fun getBoms(): List<Bom> { | ||||
| return bomService.findAll() | return bomService.findAll() | ||||
| @@ -31,8 +80,8 @@ class BomController ( | |||||
| } | } | ||||
| @PostMapping("/import-bom") | @PostMapping("/import-bom") | ||||
| fun importBom(): ResponseEntity<Resource> { | |||||
| val reportResult = bomService.importBOM() | |||||
| fun importBom(@RequestBody payload: ImportBomRequestPayload): ResponseEntity<Resource> { | |||||
| val reportResult = bomService.importBOM(payload.batchId, payload.items) | |||||
| val filename = "bom_excel_issue_log_${LocalDate.now()}.xlsx" | val filename = "bom_excel_issue_log_${LocalDate.now()}.xlsx" | ||||
| return ResponseEntity.ok() | return ResponseEntity.ok() | ||||
| @@ -27,4 +27,43 @@ data class ItemUomRequest( | |||||
| val ratioD: BigDecimal?, | val ratioD: BigDecimal?, | ||||
| val ratioN: BigDecimal?, | val ratioN: BigDecimal?, | ||||
| val deleted: Boolean?, | val deleted: Boolean?, | ||||
| ) | |||||
| data class BomFormatIssue( | |||||
| val fileName: String, // 檔名 | |||||
| val problem: String // 問題描述(用來 group 的 key) | |||||
| ) | |||||
| // 回傳給前端用:問題 → 檔名列表 | |||||
| data class BomFormatProblemGroup( | |||||
| val problem: String, | |||||
| val fileNames: List<String> | |||||
| ) | |||||
| data class BomFormatFileGroup( | |||||
| val fileName: String, | |||||
| val problems: List<String> | |||||
| ) | |||||
| data class BomUploadResponse( | |||||
| val batchId: String, | |||||
| val fileNames: List<String> | |||||
| ) | |||||
| data class BomFormatCheckRequest( | |||||
| val batchId: String | |||||
| ) | |||||
| /** Format-check 回傳:正確檔名列表 + 失敗列表(檔名 + 問題) */ | |||||
| data class BomFormatCheckResponse( | |||||
| val correctFileNames: List<String>, | |||||
| val failList: List<BomFormatFileGroup> | |||||
| ) | |||||
| data class ImportBomItemRequest( | |||||
| val fileName: String, | |||||
| val isAlsoWip: Boolean = false | |||||
| ) | |||||
| data class ImportBomRequestPayload( | |||||
| val batchId: String, | |||||
| val items: List<ImportBomItemRequest> | |||||
| ) | ) | ||||
| @@ -12,5 +12,5 @@ interface ProductProcessRepository : JpaRepository<ProductProcess, Long>, JpaSpe | |||||
| fun findByProductProcessCodeStartingWith(prefix: String): List<ProductProcess> | fun findByProductProcessCodeStartingWith(prefix: String): List<ProductProcess> | ||||
| fun findByIdAndDeletedIsFalse(id: Long): Optional<ProductProcess> | fun findByIdAndDeletedIsFalse(id: Long): Optional<ProductProcess> | ||||
| fun findAllByDeletedIsFalse(): List<ProductProcess> | fun findAllByDeletedIsFalse(): List<ProductProcess> | ||||
| fun findByBom_Id(bomId: Long): List<ProductProcess> | |||||
| } | } | ||||
| @@ -52,7 +52,7 @@ import com.ffii.fpsms.modules.master.web.models.* | |||||
| import java.math.RoundingMode | import java.math.RoundingMode | ||||
| import java.time.LocalDate | import java.time.LocalDate | ||||
| import java.time.format.DateTimeFormatter | import java.time.format.DateTimeFormatter | ||||
| import com.ffii.fpsms.modules.stock.entity.StockInLineRepository | |||||
| @Service | @Service | ||||
| @Transactional | @Transactional | ||||
| open class ProductProcessService( | open class ProductProcessService( | ||||
| @@ -78,7 +78,8 @@ open class ProductProcessService( | |||||
| private val joPickOrderRepository: JoPickOrderRepository, | private val joPickOrderRepository: JoPickOrderRepository, | ||||
| private val itemUomRepository: ItemUomRespository, | private val itemUomRepository: ItemUomRespository, | ||||
| private val uomConversionRepository: UomConversionRepository, | private val uomConversionRepository: UomConversionRepository, | ||||
| private val itemUomService: ItemUomService | |||||
| private val itemUomService: ItemUomService, | |||||
| private val stockInLineRepository: StockInLineRepository | |||||
| ) { | ) { | ||||
| open fun findAll(pageable: Pageable): Page<ProductProcess> { | open fun findAll(pageable: Pageable): Page<ProductProcess> { | ||||
| @@ -1446,10 +1447,13 @@ open class ProductProcessService( | |||||
| val jobOrder = jobOrderRepository.findById(response.jobOrderId ?: 0L).orElse(null) | val jobOrder = jobOrderRepository.findById(response.jobOrderId ?: 0L).orElse(null) | ||||
| val stockInLineStatus = jobOrder?.stockInLines?.firstOrNull()?.status | val stockInLineStatus = jobOrder?.stockInLines?.firstOrNull()?.status | ||||
| stockInLineStatus != "completed" | stockInLineStatus != "completed" | ||||
| && stockInLineStatus != "escalated" | |||||
| // && stockInLineStatus != "escalated" // 你已經註解掉,會把 escalated 顯示出來 | |||||
| && stockInLineStatus != "rejected" | && stockInLineStatus != "rejected" | ||||
| && jobOrder?.status != JobOrderStatus.PLANNING | && jobOrder?.status != JobOrderStatus.PLANNING | ||||
| } | |||||
| }.sortedWith( | |||||
| compareByDescending<AllJoborderProductProcessInfoResponse> { it.date ?: LocalDate.MIN } | |||||
| .thenBy { it.productionPriority ?: Int.MAX_VALUE } | |||||
| ) | |||||
| } | } | ||||
| open fun updateProductProcessLineStartTime(productProcessLineId: Long): MessageResponse { | open fun updateProductProcessLineStartTime(productProcessLineId: Long): MessageResponse { | ||||
| @@ -1567,20 +1571,36 @@ open class ProductProcessService( | |||||
| jobOrder.status = JobOrderStatus.STORING | jobOrder.status = JobOrderStatus.STORING | ||||
| jobOrderRepository.save(jobOrder) | jobOrderRepository.save(jobOrder) | ||||
| stockInLineService.create( | |||||
| SaveStockInLineRequest( | |||||
| itemId = productProcess?.item?.id ?: 0L, | |||||
| acceptedQty = jobOrder?.reqQty ?: BigDecimal.ZERO, | |||||
| productLotNo = jobOrder?.code, | |||||
| productionDate = LocalDate.now(), | |||||
| jobOrderId = jobOrder.id, | |||||
| acceptQty = jobOrder?.reqQty ?: BigDecimal.ZERO, | |||||
| //acceptQty = null, | |||||
| expiryDate = null, | |||||
| status = "pending", | |||||
| val existingSil = jobOrder.id?.let { stockInLineRepository.findFirstByJobOrder_IdAndDeletedFalse(it) } | |||||
| if (existingSil != null) { | |||||
| stockInLineService.update( | |||||
| SaveStockInLineRequest( | |||||
| id = existingSil.id, | |||||
| itemId = existingSil.item?.id ?: productProcess?.item?.id ?: 0L, | |||||
| acceptedQty = jobOrder.reqQty ?: BigDecimal.ZERO, | |||||
| acceptQty = jobOrder.reqQty ?: BigDecimal.ZERO, | |||||
| productLotNo = jobOrder.code, | |||||
| productionDate = LocalDate.now(), | |||||
| jobOrderId = jobOrder.id, | |||||
| expiryDate = null, | |||||
| status = "pending", | |||||
| receiptDate = null, | |||||
| ) | |||||
| ) | ) | ||||
| ) | |||||
| } else { | |||||
| stockInLineService.create( | |||||
| SaveStockInLineRequest( | |||||
| itemId = productProcess?.item?.id ?: 0L, | |||||
| acceptedQty = jobOrder?.reqQty ?: BigDecimal.ZERO, | |||||
| productLotNo = jobOrder?.code, | |||||
| productionDate = LocalDate.now(), | |||||
| jobOrderId = jobOrder.id, | |||||
| acceptQty = jobOrder?.reqQty ?: BigDecimal.ZERO, | |||||
| expiryDate = null, | |||||
| status = "pending", | |||||
| ) | |||||
| ) | |||||
| } | |||||
| } | } | ||||
| } | } | ||||
| return MessageResponse( | return MessageResponse( | ||||
| @@ -1609,19 +1629,38 @@ open class ProductProcessService( | |||||
| if (jobOrder != null) { | if (jobOrder != null) { | ||||
| jobOrder.status = JobOrderStatus.STORING | jobOrder.status = JobOrderStatus.STORING | ||||
| jobOrderRepository.save(jobOrder) | jobOrderRepository.save(jobOrder) | ||||
| stockInLineService.create( | |||||
| SaveStockInLineRequest( | |||||
| itemId = productProcess?.item?.id ?: 0L, | |||||
| acceptedQty = jobOrder?.reqQty ?: BigDecimal.ZERO, | |||||
| productLotNo = jobOrder?.code, | |||||
| productionDate = LocalDate.now(), | |||||
| jobOrderId = jobOrder.id, | |||||
| acceptQty = jobOrder?.reqQty ?: BigDecimal.ZERO, | |||||
| expiryDate = null, | |||||
| status = "", | |||||
| val existingSil = jobOrder.id?.let { stockInLineRepository.findFirstByJobOrder_IdAndDeletedFalse(it) } | |||||
| if (existingSil != null) { | |||||
| stockInLineService.update( | |||||
| SaveStockInLineRequest( | |||||
| id = existingSil.id, | |||||
| itemId = existingSil.item?.id ?: productProcess?.item?.id ?: 0L, | |||||
| acceptedQty = jobOrder.reqQty ?: BigDecimal.ZERO, | |||||
| acceptQty = jobOrder.reqQty ?: BigDecimal.ZERO, | |||||
| productLotNo = jobOrder.code, | |||||
| productionDate = LocalDate.now(), | |||||
| jobOrderId = jobOrder.id, | |||||
| expiryDate = null, | |||||
| status = "pending", | |||||
| receiptDate = null, | |||||
| ) | |||||
| ) | ) | ||||
| ) | |||||
| } else { | |||||
| stockInLineService.create( | |||||
| SaveStockInLineRequest( | |||||
| itemId = productProcess?.item?.id ?: 0L, | |||||
| acceptedQty = jobOrder?.reqQty ?: BigDecimal.ZERO, | |||||
| productLotNo = jobOrder?.code, | |||||
| productionDate = LocalDate.now(), | |||||
| jobOrderId = jobOrder.id, | |||||
| acceptQty = jobOrder?.reqQty ?: BigDecimal.ZERO, | |||||
| expiryDate = null, | |||||
| status = "", | |||||
| ) | |||||
| ) | |||||
| } | |||||
| } | } | ||||
| } | } | ||||
| @@ -12,7 +12,10 @@ interface EscalationLogInfo { | |||||
| @get:Value("#{target.stockInLine?.purchaseOrderLine?.id}") | @get:Value("#{target.stockInLine?.purchaseOrderLine?.id}") | ||||
| val polId: Long? | val polId: Long? | ||||
| @get:Value("#{target.stockInLine?.jobOrder?.id}") | |||||
| val jobOrderId: Long? | |||||
| @get:Value("#{target.stockInLine?.jobOrder?.code}") | |||||
| val jobOrderCode: String? | |||||
| @get:Value("#{target.stockInLine?.stockIn?.code}") | @get:Value("#{target.stockInLine?.stockIn?.code}") | ||||
| val poCode: String? | val poCode: String? | ||||
| @@ -55,4 +55,5 @@ fun searchStockInLines( | |||||
| //AND sil.type IS NOT NULL | //AND sil.type IS NOT NULL | ||||
| @Query("SELECT sil FROM StockInLine sil WHERE sil.item.id IN :itemIds AND sil.deleted = false") | @Query("SELECT sil FROM StockInLine sil WHERE sil.item.id IN :itemIds AND sil.deleted = false") | ||||
| fun findAllByItemIdInAndDeletedFalse(itemIds: List<Long>): List<StockInLine> | fun findAllByItemIdInAndDeletedFalse(itemIds: List<Long>): List<StockInLine> | ||||
| fun findFirstByJobOrder_IdAndDeletedFalse(jobOrderId: Long): StockInLine? | |||||
| } | } | ||||
| @@ -210,6 +210,9 @@ open class StockInLineService( | |||||
| this.type = "Nor" | this.type = "Nor" | ||||
| status = StockInLineStatus.PENDING.status | status = StockInLineStatus.PENDING.status | ||||
| } | } | ||||
| if (jo != null) { | |||||
| stockInLine.lotNo = assignLotNo() | |||||
| } | |||||
| val savedSIL = saveAndFlush(stockInLine) | val savedSIL = saveAndFlush(stockInLine) | ||||
| if (pol != null) { | if (pol != null) { | ||||
| //logger.info("[create] Stock-in line created with PO, running PO/GRN update for stockInLine id=${savedSIL.id}") | //logger.info("[create] Stock-in line created with PO, running PO/GRN update for stockInLine id=${savedSIL.id}") | ||||
| @@ -238,7 +241,7 @@ open class StockInLineService( | |||||
| ) | ) | ||||
| val inventoryCount = jdbcDao.queryForInt(INVENTORY_COUNT.toString(), args) | val inventoryCount = jdbcDao.queryForInt(INVENTORY_COUNT.toString(), args) | ||||
| // val newLotNo = CodeGenerator.generateCode(prefix = "MPO", itemId = stockInLine.item!!.id!!, count = inventoryCount) | // val newLotNo = CodeGenerator.generateCode(prefix = "MPO", itemId = stockInLine.item!!.id!!, count = inventoryCount) | ||||
| val newLotNo = assignLotNo() | |||||
| val newLotNo = stockInLine.lotNo ?: assignLotNo() | |||||
| inventoryLot.apply { | inventoryLot.apply { | ||||
| this.item = stockInLine.item | this.item = stockInLine.item | ||||
| this.stockInLine = stockInLine | this.stockInLine = stockInLine | ||||
| @@ -27,6 +27,10 @@ spring: | |||||
| logging: | logging: | ||||
| config: 'classpath:log4j2.yml' | config: 'classpath:log4j2.yml' | ||||
| bom: | |||||
| import: | |||||
| temp-dir: ${java.io.tmpdir}/fpsms-bom-import | |||||
| m18: | m18: | ||||
| config: | config: | ||||
| grant-type: password | grant-type: password | ||||
| @@ -0,0 +1,10 @@ | |||||
| -- liquibase formatted sql | |||||
| -- changeset Enson:add_isDrink_to_bom | |||||
| ALTER TABLE `fpsmsdb`.`bom` | |||||
| ADD COLUMN `isDrink` TINYINT(1) NULL DEFAULT false AFTER `isDense`; | |||||