CANCERYS\kw093 2 тижднів тому
джерело
коміт
f6016887ab
18 змінених файлів з 609 додано та 42 видалено
  1. +2
    -0
      src/main/java/com/ffii/fpsms/modules/jobOrder/entity/JobOrder.kt
  2. +1
    -0
      src/main/java/com/ffii/fpsms/modules/jobOrder/entity/JobOrderTypeRepository.kt
  3. +25
    -1
      src/main/java/com/ffii/fpsms/modules/jobOrder/entity/projections/JobOrderInfo.kt
  4. +324
    -1
      src/main/java/com/ffii/fpsms/modules/jobOrder/service/JoPickOrderService.kt
  5. +58
    -2
      src/main/java/com/ffii/fpsms/modules/jobOrder/service/JobOrderService.kt
  6. +68
    -3
      src/main/java/com/ffii/fpsms/modules/jobOrder/web/JobOrderController.kt
  7. +0
    -24
      src/main/java/com/ffii/fpsms/modules/jobOrder/web/JobOrderProcessController.kt
  8. +34
    -0
      src/main/java/com/ffii/fpsms/modules/jobOrder/web/model/CreateJobOrderRequest.kt
  9. +1
    -0
      src/main/java/com/ffii/fpsms/modules/jobOrder/web/model/SearchJobOrderInfoRequest.kt
  10. +1
    -1
      src/main/java/com/ffii/fpsms/modules/master/entity/EquipmentDetailRepository.kt
  11. +3
    -0
      src/main/java/com/ffii/fpsms/modules/pickOrder/entity/PickOrderRepository.kt
  12. +2
    -0
      src/main/java/com/ffii/fpsms/modules/productProcess/entity/ProductProcessLine.kt
  13. +1
    -0
      src/main/java/com/ffii/fpsms/modules/productProcess/entity/projections/ProductProcessInfo.kt
  14. +58
    -2
      src/main/java/com/ffii/fpsms/modules/productProcess/service/ProductProcessService.kt
  15. +6
    -2
      src/main/java/com/ffii/fpsms/modules/productProcess/web/model/SaveProductProcessRequest.kt
  16. +17
    -6
      src/main/java/com/ffii/fpsms/modules/user/entity/User.java
  17. +2
    -0
      src/main/java/com/ffii/fpsms/modules/user/entity/UserRepository.java
  18. +6
    -0
      src/main/resources/db/changelog/changes/20251130_01_enson/01_altertable_enson.sql

+ 2
- 0
src/main/java/com/ffii/fpsms/modules/jobOrder/entity/JobOrder.kt Переглянути файл

@@ -80,4 +80,6 @@ open class JobOrder : BaseEntity<Long>() {

@Column(name = "jobTypeId")
open var jobTypeId: Long? = null


}

+ 1
- 0
src/main/java/com/ffii/fpsms/modules/jobOrder/entity/JobOrderTypeRepository.kt Переглянути файл

@@ -16,4 +16,5 @@ import java.util.Optional
interface JobTypeRepository : JpaRepository<JobType, Long> {
//fun findByName(name: String): Optional<JobType>
//fun findByIdAndDeletedIsFalse(id: Long): Optional<JobType>

}

+ 25
- 1
src/main/java/com/ffii/fpsms/modules/jobOrder/entity/projections/JobOrderInfo.kt Переглянути файл

@@ -38,8 +38,32 @@ interface JobOrderInfo {

@get:Value("#{target.status.value}")
val status: String;
}

@get:Value("#{target.jobTypeId}")
val jobTypeId: Long?;
}
data class JobTypeResponse(
val id: Long?,
val name: String?
)
data class JobOrderInfoWithTypeName(
val id: Long,
val code: String,
val itemCode: String,
val itemName: String,
val name: String,
val reqQty: BigDecimal,
val item: JobOrderItemInfo,
val stockInLineId: Long?,
val stockInLineStatus: String?,
val silHandlerId: Long?,
val planStart: LocalDateTime?,
val status: String,
val jobTypeId: Long?,
val jobTypeName: String?
)
// Job Order
interface JobOrderDetailWithJsonString {
val id: Long?;


+ 324
- 1
src/main/java/com/ffii/fpsms/modules/jobOrder/service/JoPickOrderService.kt Переглянути файл

@@ -14,6 +14,7 @@ import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional
import java.time.LocalDate
import com.ffii.fpsms.modules.stock.entity.enum.InventoryLotLineStatus
import com.ffii.fpsms.modules.pickOrder.enums.PickOrderLineStatus
import java.time.LocalDateTime
import com.ffii.core.support.JdbcDao
import com.ffii.fpsms.modules.jobOrder.web.model.SecondScanSubmitRequest
@@ -29,6 +30,10 @@ import com.ffii.fpsms.modules.stock.entity.SuggestPickLotRepository
import com.ffii.fpsms.modules.stock.entity.InventoryLotLineRepository
import com.ffii.fpsms.modules.stock.entity.InventoryLotRepository
import com.ffii.fpsms.modules.stock.entity.StockOutLIneRepository
import com.ffii.fpsms.modules.jobOrder.web.model.AllJoPickOrderResponse
import com.ffii.fpsms.modules.jobOrder.entity.JobOrderRepository
import com.ffii.fpsms.modules.jobOrder.entity.JobTypeRepository
import com.ffii.fpsms.modules.master.entity.ItemsRepository
@Service
open class JoPickOrderService(
private val joPickOrderRepository: JoPickOrderRepository,
@@ -42,7 +47,11 @@ open class JoPickOrderService(
private val suggestPickLotRepository: SuggestPickLotRepository,
private val inventoryLotLineRepository: InventoryLotLineRepository,
private val inventoryLotRepository: InventoryLotRepository,
private val stockOutLineRepository: StockOutLIneRepository
private val stockOutLineRepository: StockOutLIneRepository,
private val jobOrderRepository: JobOrderRepository,
private val jobTypeRepository: JobTypeRepository,
private val itemsRepository: ItemsRepository

) {
open fun save(record: JoPickOrder): JoPickOrder {
@@ -1629,4 +1638,318 @@ open fun getCompletedJobOrderPickOrderLotDetailsForCompletedPick(pickOrderId: Lo
emptyList()
}
}


open fun getAllJoPickOrders(): List<AllJoPickOrderResponse> {
println("=== getAllJoPickOrders ===")
return try {
val releasedPickOrders = pickOrderRepository.findAllByStatusAndDeletedFalse(
PickOrderStatus.RELEASED
).filter { pickOrder ->
pickOrder.jobOrder != null
}

println("Found ${releasedPickOrders.size} released job order pick orders")

val jobOrderPickOrders = releasedPickOrders.mapNotNull { pickOrder ->
println("Processing pick order: ${pickOrder.id}, code: ${pickOrder.code}")
val jobOrder = pickOrder.jobOrder
if (jobOrder == null) {
println("❌ Pick order ${pickOrder.id} has no job order")
return@mapNotNull null
}
println("Job order found: ${jobOrder.id}, code: ${jobOrder.code}")
val bom = jobOrder.bom

println("BOM found: ${bom?.id}")
val item = bom?.item
if (item == null) {
println("❌ BOM ${bom?.id} has no item")
return@mapNotNull null
}
println("Item found: ${item.id}, name: ${item.name}")
val uom = bom.outputQtyUom
if (uom == null) {
println("❌ BOM ${bom.id} has no uom")
return@mapNotNull null
}
// println("UOM found: ${uom.id}, code: ${uom.code}")
val pickOrderLines = pickOrderLineRepository.findAllByPickOrderId(pickOrder.id ?: return@mapNotNull null)
val finishedLines = pickOrderLines.count { it.status == PickOrderLineStatus.COMPLETED }
val jobOrderType = jobOrder.jobTypeId?.let { jobTypeRepository.findById(it).orElse(null) }

println("✅ Building response for pick order ${pickOrder.id}")

AllJoPickOrderResponse(
id = pickOrder.id ?: 0L,
pickOrderId = pickOrder.id,
pickOrderCode = pickOrder.code,
jobOrderId = jobOrder.id,
jobOrderCode = jobOrder.code,
jobOrderTypeId = jobOrder.jobTypeId,
jobOrderType = jobOrderType?.name,
itemId = item.id ?: 0L,
itemName = item.name ?: "",
reqQty = jobOrder.reqQty ?: BigDecimal.ZERO,
//uomId = bom.outputQtyUom?.id : 0L,
uomId = 0,
uomName = bom?.outputQtyUom?: "",
jobOrderStatus = jobOrder.status?.value ?: "",
finishedPickOLineCount = finishedLines
)
}

println("Returning ${jobOrderPickOrders.size} released job order pick orders")
jobOrderPickOrders
} catch (e: Exception) {
println("❌ Error in getAllJoPickOrders: ${e.message}")
e.printStackTrace()
emptyList()
}
}
open fun getJobOrderLotsHierarchicalByPickOrderId(pickOrderId: Long): Map<String, Any?> {
println("=== getJobOrderLotsHierarchicalByPickOrderId ===")
println("pickOrderId: $pickOrderId")
return try {
val pickOrder = pickOrderRepository.findById(pickOrderId).orElse(null)
if (pickOrder == null || pickOrder.deleted == true) {
println("❌ Pick order $pickOrderId not found or deleted")
return mapOf(
"pickOrder" to null as Any?,
"pickOrderLines" to emptyList<Map<String, Any>>() as Any?
)
}
val jobOrder = pickOrder.jobOrder ?: return mapOf(
"pickOrder" to null as Any?,
"pickOrderLines" to emptyList<Map<String, Any>>() as Any?
)
// 获取 pick order lines
val pickOrderLines = pickOrderLineRepository.findAllByPickOrderId(pickOrder.id!!)
.filter { it.deleted == false }
// 获取所有 pick order line IDs
val pickOrderLineIds = pickOrderLines.map { it.id!! }
// 获取 suggested pick lots
val suggestedPickLots = if (pickOrderLineIds.isNotEmpty()) {
suggestPickLotRepository.findAllByPickOrderLineIdIn(pickOrderLineIds)
.filter { it.deleted == false }
} else {
emptyList()
}
// 获取所有 inventory lot line IDs
val inventoryLotLineIds = suggestedPickLots.mapNotNull { it.suggestedLotLine?.id }
// 获取 inventory lot lines
val inventoryLotLines = if (inventoryLotLineIds.isNotEmpty()) {
inventoryLotLineRepository.findAllByIdIn(inventoryLotLineIds)
.filter { it.deleted == false }
} else {
emptyList()
}
// 获取 inventory lots
val inventoryLotIds = inventoryLotLines.mapNotNull { it.inventoryLot?.id }.distinct()
val inventoryLots = if (inventoryLotIds.isNotEmpty()) {
inventoryLotRepository.findAllByIdIn(inventoryLotIds)
.filter { it.deleted == false }
} else {
emptyList()
}
// 获取 stock out lines
val stockOutLines = if (pickOrderLineIds.isNotEmpty() && inventoryLotLineIds.isNotEmpty()) {
pickOrderLineIds.flatMap { polId ->
inventoryLotLineIds.flatMap { illId ->
stockOutLineRepository.findByPickOrderLineIdAndInventoryLotLineIdAndDeletedFalse(polId, illId)
}
}
} else {
emptyList()
}
// 取得所有 stock out line(含無 lot 情況)
val stockOutLinesByPickOrderLine = pickOrderLineIds.associateWith { polId ->
stockOutLineRepository.findAllByPickOrderLineIdAndDeletedFalse(polId)
}
// 获取 jo_pick_order 记录
val joPickOrders = joPickOrderRepository.findByPickOrderId(pickOrder.id!!)
// 构建 pick order info
val pickOrderInfo = mapOf(
"id" to pickOrder.id,
"code" to pickOrder.code,
"consoCode" to pickOrder.consoCode,
"targetDate" to pickOrder.targetDate?.let {
"${it.year}-${String.format("%02d", it.monthValue)}-${String.format("%02d", it.dayOfMonth)}"
},
"type" to pickOrder.type?.value,
"status" to pickOrder.status?.value,
"assignTo" to pickOrder.assignTo?.id,
"jobOrder" to mapOf(
"id" to jobOrder.id,
"code" to jobOrder.code,
"name" to "Job Order ${jobOrder.code}"
)
)
// 构建 pick order lines with lots
val pickOrderLinesResult = pickOrderLines.map { pol ->
val item = pol.item
val uom = pol.uom
val lineId = pol.id!!
val suggestions = suggestedPickLots.filter { it.pickOrderLine?.id == lineId }
val stockoutsForLine = stockOutLinesByPickOrderLine[lineId].orEmpty()
// 获取该 line 的 suggested pick lots
val lineSuggestedLots = suggestedPickLots.filter { it.pickOrderLine?.id == pol.id }
// 构建 lots 数据
val lots = lineSuggestedLots.mapNotNull { spl ->
val ill = spl.suggestedLotLine
if (ill == null || ill.deleted == true) return@mapNotNull null
val il = ill.inventoryLot
if (il == null || il.deleted == true) return@mapNotNull null
val warehouse = ill.warehouse
// 获取对应的 stock out line
val sol = stockOutLines.firstOrNull {
it.pickOrderLine?.id == pol.id && it.inventoryLotLine?.id == ill.id
}
// 获取对应的 jo_pick_order
val jpo = joPickOrders.firstOrNull { it.itemId == item?.id }
// 计算 available quantity
val availableQty = if (sol?.status == "rejected") {
null
} else {
(ill.inQty ?: BigDecimal.ZERO) - (ill.outQty ?: BigDecimal.ZERO) - (ill.holdQty ?: BigDecimal.ZERO)
}
// 计算 total picked by all pick orders
val totalPickedByAllPickOrders = stockOutLines
.filter { it.inventoryLotLine?.id == ill.id && it.deleted == false }
.filter { it.status in listOf("pending", "checked", "partially_completed", "completed") }
.sumOf { it.qty?.toBigDecimal() ?: BigDecimal.ZERO }
// 计算 lot availability
val lotAvailability = when {
il.expiryDate != null && il.expiryDate!!.isBefore(LocalDate.now()) -> "expired"
sol?.status == "rejected" -> "rejected"
availableQty != null && availableQty <= BigDecimal.ZERO -> "insufficient_stock"
ill.status == InventoryLotLineStatus.UNAVAILABLE -> "status_unavailable"
else -> "available"
}
// 计算 processing status
val processingStatus = when (sol?.status) {
"completed" -> "completed"
"rejected" -> "rejected"
"created" -> "pending"
else -> "pending"
}
mapOf(
"lotId" to ill.id,
"lotNo" to il.lotNo,
"expiryDate" to il.expiryDate?.let {
"${it.year}-${String.format("%02d", it.monthValue)}-${String.format("%02d", it.dayOfMonth)}"
},
"location" to warehouse?.name,
"availableQty" to availableQty?.toDouble(),
"requiredQty" to (spl.qty?.toDouble() ?: 0.0),
"actualPickQty" to (sol?.qty ?: 0.0),
"processingStatus" to processingStatus,
"lotAvailability" to lotAvailability,
"pickOrderId" to pickOrder.id,
"pickOrderCode" to pickOrder.code,
"pickOrderConsoCode" to pickOrder.consoCode,
"pickOrderLineId" to pol.id,
"stockOutLineId" to sol?.id,
"suggestedPickLotId" to spl.id,
"stockOutLineQty" to (sol?.qty ?: 0.0),
"stockOutLineStatus" to sol?.status,
"routerIndex" to warehouse?.order,
"routerArea" to warehouse?.code,
"routerRoute" to warehouse?.code,
"uomShortDesc" to uom?.udfShortDesc,
"matchStatus" to jpo?.matchStatus?.value,
"matchBy" to jpo?.matchBy,
"matchQty" to jpo?.matchQty
)
}
mapOf(
"id" to pol.id,
"itemId" to item?.id,
"itemCode" to item?.code,
"itemName" to item?.name,
"requiredQty" to pol.qty?.toDouble(),
"uomCode" to uom?.code,
"uomDesc" to uom?.udfudesc,
"lots" to lots
)
}
return mapOf(
"pickOrder" to pickOrderInfo as Any?,
"pickOrderLines" to pickOrderLinesResult as Any?
)
} catch (e: Exception) {
println("❌ Error in getJobOrderLotsHierarchicalByPickOrderId: ${e.message}")
e.printStackTrace()
return mapOf(
"pickOrder" to null as Any?,
"pickOrderLines" to emptyList<Map<String, Any>>() as Any?
)
}
}

open fun updateHandledByForItem(pickOrderId: Long, itemId: Long, userId: Long): JoPickOrder? {
val joPickOrderOpt = joPickOrderRepository.findByPickOrderIdAndItemId(pickOrderId, itemId)
if (joPickOrderOpt.isEmpty) {
println("⚠️ JoPickOrder not found for pickOrderId: $pickOrderId, itemId: $itemId")
return null
}
val joPickOrder = joPickOrderOpt.get()
joPickOrder.handledBy = userId
// Don't update other fields - only handledBy
return joPickOrderRepository.save(joPickOrder)
}

open fun updateRecordHandledByForItem(pickOrderId: Long, itemId: Long, userId: Long): JoPickOrderRecord? {
val joPickOrderRecordOpt = joPickOrderRecordRepository.findByPickOrderIdAndItemId(pickOrderId, itemId)
if (joPickOrderRecordOpt.isEmpty) {
println("⚠️ JoPickOrderRecord not found for pickOrderId: $pickOrderId, itemId: $itemId")
return null
}
val joPickOrderRecord = joPickOrderRecordOpt.get()
joPickOrderRecord.handledBy = userId
// Don't update other fields - only handledBy
return joPickOrderRecordRepository.save(joPickOrderRecord)
}

}

+ 58
- 2
src/main/java/com/ffii/fpsms/modules/jobOrder/service/JobOrderService.kt Переглянути файл

@@ -54,6 +54,8 @@ import org.springframework.core.io.ClassPathResource
import java.io.File
import java.io.FileNotFoundException
import java.io.IOException
import com.ffii.fpsms.modules.jobOrder.entity.projections.JobOrderInfoWithTypeName
import com.ffii.fpsms.modules.jobOrder.entity.projections.JobTypeResponse


@Service
@@ -82,7 +84,7 @@ open class JobOrderService(
code = request.code ?: "",
bomName = request.itemName ?: "",
pageable = pageable
)
)

val planStartFrom = request.planStart
val planStartTo = request.planStartTo
@@ -97,7 +99,59 @@ open class JobOrderService(
val total = response.totalElements
return RecordsRes<JobOrderInfo>(records, total.toInt());
}
open fun allJobOrdersByPageWithTypeName(request: SearchJobOrderInfoRequest): RecordsRes<JobOrderInfoWithTypeName> {
val pageable = PageRequest.of(request.pageNum ?: 0, request.pageSize ?: 10);
println("allJobOrdersByPage")
println(request)
val response = jobOrderRepository.findJobOrderInfoByCodeContainsAndBomNameContainsAndDeletedIsFalseOrderByIdDesc(
code = request.code ?: "",
bomName = request.itemName ?: "",
pageable = pageable
)
val jobTypeIds = response.content.mapNotNull { it.jobTypeId }.distinct()
val jobTypes = if (jobTypeIds.isNotEmpty()) {
jobTypeRepository.findAllById(jobTypeIds).associateBy { it.id }
} else {
emptyMap()
}
val planStartFrom = request.planStart
val planStartTo = request.planStartTo
val records = response.content
.filter {
(planStartFrom == null || (it.planStart != null && (planStartFrom.isEqual(it.planStart) || planStartFrom.isBefore(it.planStart)))) &&
(planStartTo == null || (it.planStart != null && (planStartTo.isEqual(it.planStart) || planStartTo.isAfter(it.planStart))))
}
.map { info ->
JobOrderInfoWithTypeName(
id = info.id,
code = info.code,
itemCode = info.itemCode,
itemName = info.itemName,
name = info.name,
reqQty = info.reqQty,
item = info.item,
stockInLineId = info.stockInLineId,
stockInLineStatus = info.stockInLineStatus,
silHandlerId = info.silHandlerId,
planStart = info.planStart,
status = info.status,
jobTypeId = info.jobTypeId,
jobTypeName = info.jobTypeId?.let { jobTypes[it]?.name }
)
}
.filter { info ->
// Filter by jobTypeName if provided
request.jobTypeName == null ||
request.jobTypeName.isBlank() ||
info.jobTypeName?.equals(request.jobTypeName, ignoreCase = true) == true ||
info.jobTypeName?.contains(request.jobTypeName, ignoreCase = true) == true
}

val total = response.totalElements
return RecordsRes<JobOrderInfoWithTypeName>(records, total.toInt());
}
open fun jobOrderDetail(id: Long): JobOrderDetail {
val sqlResult = jobOrderRepository.findJobOrderDetailById(id) ?: throw NoSuchElementException();

@@ -594,6 +648,8 @@ open class JobOrderService(
//tempPdfFile.delete
}
}

open fun getAllJobTypes(): List<JobTypeResponse> {
return jobTypeRepository.findAll().map { JobTypeResponse(it.id, it.name) }
}

}

+ 68
- 3
src/main/java/com/ffii/fpsms/modules/jobOrder/web/JobOrderController.kt Переглянути файл

@@ -2,7 +2,7 @@ package com.ffii.fpsms.modules.jobOrder.web

import com.ffii.core.response.RecordsRes
import com.ffii.fpsms.modules.jobOrder.entity.projections.JobOrderDetail
import com.ffii.fpsms.modules.jobOrder.entity.projections.JobOrderInfo
import com.ffii.fpsms.modules.jobOrder.service.JobOrderBomMaterialService
import com.ffii.fpsms.modules.jobOrder.service.JobOrderProcessService
import com.ffii.fpsms.modules.jobOrder.service.JobOrderService
@@ -10,6 +10,7 @@ import com.ffii.fpsms.modules.jobOrder.web.model.CreateJobOrderRequest
import com.ffii.fpsms.modules.jobOrder.web.model.JobOrderCommonActionRequest
import com.ffii.fpsms.modules.jobOrder.web.model.SearchJobOrderInfoRequest
import com.ffii.fpsms.modules.master.web.models.MessageResponse
import com.ffii.fpsms.modules.jobOrder.entity.projections.JobTypeResponse
import jakarta.validation.Valid
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.ModelAttribute
@@ -35,6 +36,9 @@ import java.io.UnsupportedEncodingException
import java.text.ParseException
import org.springframework.web.bind.annotation.*
import org.springframework.web.bind.annotation.RequestParam
import com.ffii.fpsms.modules.jobOrder.web.model.UpdateJoPickOrderHandledByRequest
import com.ffii.fpsms.modules.jobOrder.entity.projections.JobOrderInfo
import com.ffii.fpsms.modules.jobOrder.entity.projections.JobOrderInfoWithTypeName
@RestController
@RequestMapping("/jo")
class JobOrderController(
@@ -46,10 +50,11 @@ class JobOrderController(
) {

@GetMapping("/getRecordByPage")
fun allJobOrdersByPage(@ModelAttribute request: SearchJobOrderInfoRequest): RecordsRes<JobOrderInfo> {
fun allJobOrdersByPage(@ModelAttribute request: SearchJobOrderInfoRequest): RecordsRes<JobOrderInfoWithTypeName> {
println("getRecordByPage")
println(request)
return jobOrderService.allJobOrdersByPage(request);
//return jobOrderService.allJobOrdersByPage(request);
return jobOrderService.allJobOrdersByPageWithTypeName(request);
}

@GetMapping("/detail/{id}")
@@ -224,4 +229,64 @@ fun recordSecondScanIssue(
fun getCompletedJobOrderPickOrderLotDetailsForCompletedPick(@PathVariable pickOrderId: Long): List<Map<String, Any?>> {
return joPickOrderService.getCompletedJobOrderPickOrderLotDetailsForCompletedPick(pickOrderId)
}
@GetMapping("/AllJoPickOrder")
fun getAllJoPickOrder(): List<AllJoPickOrderResponse> {
return joPickOrderService.getAllJoPickOrders()
}

@GetMapping("/all-lots-hierarchical-by-pick-order/{pickOrderId}")
fun getJobOrderLotsHierarchicalByPickOrderId(@PathVariable pickOrderId: Long): Map<String, Any?> {
return joPickOrderService.getJobOrderLotsHierarchicalByPickOrderId(pickOrderId)
}
@PostMapping("/update-jo-pick-order-handled-by")
fun updateJoPickOrderHandledBy(@Valid @RequestBody request: UpdateJoPickOrderHandledByRequest): MessageResponse {
try {
val joPickOrder = joPickOrderService.updateHandledByForItem(
request.pickOrderId,
request.itemId,
request.userId
)
val joPickOrderRecord = joPickOrderService.updateRecordHandledByForItem(
request.pickOrderId,
request.itemId,
request.userId
)
if (joPickOrder == null) {
return MessageResponse(
id = null,
code = "NOT_FOUND",
name = null,
type = null,
message = "JoPickOrder not found for pickOrderId: ${request.pickOrderId}, itemId: ${request.itemId}",
errorPosition = null
)
}
return MessageResponse(
id = joPickOrder.id,
code = "SUCCESS",
name = null,
type = null,
message = "JoPickOrder handledBy updated successfully for item ${request.itemId}",
errorPosition = null
)
} catch (e: Exception) {
println("Error updating JoPickOrder handledBy: ${e.message}")
e.printStackTrace()
return MessageResponse(
id = null,
code = "ERROR",
name = null,
type = null,
message = "Failed to update JoPickOrder handledBy: ${e.message}",
errorPosition = null
)
}
}
@GetMapping("/jobTypes")
fun getAllJobTypes(): List<JobTypeResponse> {
return jobOrderService.getAllJobTypes()
}
}

+ 0
- 24
src/main/java/com/ffii/fpsms/modules/jobOrder/web/JobOrderProcessController.kt Переглянути файл

@@ -1,24 +0,0 @@
package com.ffii.fpsms.modules.jobOrder.web

import OperatorRequest
import com.ffii.core.response.RecordsRes
import com.ffii.core.utils.CriteriaArgsBuilder
import com.ffii.fpsms.modules.jobOrder.service.JobOrderProcessService
import com.ffii.fpsms.modules.master.web.models.MessageResponse
import com.ffii.fpsms.modules.purchaseOrder.entity.projections.PurchaseOrderDataClass
import com.ffii.fpsms.modules.purchaseOrder.service.PurchaseOrderService
import com.ffii.fpsms.modules.purchaseOrder.web.model.PagingRequest
import jakarta.servlet.http.HttpServletRequest
import org.springframework.web.bind.annotation.*

@RestController
@RequestMapping("/jop")
class JobOrderProcessController(
private val jobOrderProcessService: JobOrderProcessService
) {
@PostMapping("/isOperatorExist")
fun checkPolAndCompletePo(@RequestBody request: OperatorRequest): MessageResponse {
println(request)
return jobOrderProcessService.isOperatorExist(request)
}
}

+ 34
- 0
src/main/java/com/ffii/fpsms/modules/jobOrder/web/model/CreateJobOrderRequest.kt Переглянути файл

@@ -31,4 +31,38 @@ data class CreateJobOrderProcessRequest (
val seqNo: Long?,
val remarks: String? = null,
val status: String = "pending",
)


data class AllJoPickOrderResponse(
val id: Long,
val pickOrderId: Long?,
val pickOrderCode: String?,
val jobOrderId: Long?,
val jobOrderCode: String?,
val jobOrderTypeId: Long?,
val jobOrderType: String?,
val itemId: Long,
val itemName: String,
val reqQty: BigDecimal,
val uomId: Long,
val uomName: String,
val jobOrderStatus: String,
val finishedPickOLineCount: Int,

)
data class JobOrderLotsHierarchicalResponse(
val id: Long,
val lotId: Long?,
val lotCode: String?,
val lotName: String?,
val lotQty: BigDecimal?,
val lotUomId: Long?,
val lotUomName: String?,
)

data class UpdateJoPickOrderHandledByRequest(
val pickOrderId: Long,
val itemId: Long,
val userId: Long
)

+ 1
- 0
src/main/java/com/ffii/fpsms/modules/jobOrder/web/model/SearchJobOrderInfoRequest.kt Переглянути файл

@@ -9,4 +9,5 @@ data class SearchJobOrderInfoRequest(
val planStartTo: LocalDateTime?,
val pageSize: Int?,
val pageNum: Int?,
val jobTypeName: String?,
)

+ 1
- 1
src/main/java/com/ffii/fpsms/modules/master/entity/EquipmentDetailRepository.kt Переглянути файл

@@ -11,6 +11,6 @@ interface EquipmentDetailRepository : AbstractRepository<EquipmentDetail, Long>
fun findByNameAndDeletedIsFalse(name: String): EquipmentDetail?;

fun findByDescriptionAndDeletedIsFalse(description: String): EquipmentDetail?;
fun findByEquipmentTypeIdAndDeletedFalse(equipmentTypeId: Long): List<EquipmentDetail>;

}

+ 3
- 0
src/main/java/com/ffii/fpsms/modules/pickOrder/entity/PickOrderRepository.kt Переглянути файл

@@ -78,4 +78,7 @@ interface PickOrderRepository : AbstractRepository<PickOrder, Long> {

// 在 PickOrderRepository 中添加:
fun findAllByStatusAndAssignToIsNullAndDeletedFalse(status: PickOrderStatus): List<PickOrder>
//fun findAllByJoid(jobOrderId: Long): List<PickOrder>
fun findAllByJobOrder_Id(jobOrderId: Long): List<PickOrder>
fun findTopByJobOrder_IdOrderByCreatedDesc(jobOrderId: Long): PickOrder?
}

+ 2
- 0
src/main/java/com/ffii/fpsms/modules/productProcess/entity/ProductProcessLine.kt Переглянути файл

@@ -26,6 +26,8 @@ open class ProductProcessLine : BaseEntity<Long>() {

@Column(name = "equipmentDetailId")
open var equipmentDetailId: Long? = null

@Size(max = 100)
@Column(name = "name", length = 100)
open var name: String? = null


+ 1
- 0
src/main/java/com/ffii/fpsms/modules/productProcess/entity/projections/ProductProcessInfo.kt Переглянути файл

@@ -52,6 +52,7 @@ data class ProductProcessLineInfo(
val name: String?,
val description: String?,
val equipment_name: String?,
val equipmentDetailCode: String?,
val status: String?,
val byproductId: Long?,
val byproductName: String?,


+ 58
- 2
src/main/java/com/ffii/fpsms/modules/productProcess/service/ProductProcessService.kt Переглянути файл

@@ -7,6 +7,7 @@ import com.ffii.fpsms.modules.productProcess.enums.ProductProcessStatus
import com.ffii.fpsms.modules.productProcess.web.model.*
import org.springframework.data.domain.Page
import org.springframework.data.domain.Pageable
import com.ffii.fpsms.modules.master.entity.EquipmentDetailRepository
import com.ffii.fpsms.modules.productProcess.entity.projections.jobOrderLineInfo
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional
@@ -38,7 +39,7 @@ import com.ffii.fpsms.modules.stock.service.StockInLineService
import com.ffii.fpsms.modules.stock.web.model.SaveStockInLineRequest
import com.ffii.fpsms.modules.master.entity.BomProcessMaterialRepository
import com.ffii.fpsms.modules.master.entity.BomMaterialRepository
import com.ffii.fpsms.modules.master.entity.EquipmentDetailRepository
import com.ffii.fpsms.modules.jobOrder.entity.JobTypeRepository
@Service
@Transactional
@@ -482,6 +483,7 @@ open class ProductProcessService(
// get bom materials
val bomMaterials = jobOrderBomMaterialRepository.findAllByJobOrderId(jobOrder?.id?:0)
val bomProcessIds = bomProcess.mapNotNull { it.id }
val itemIds = bomMaterials.mapNotNull { it.item?.id }
// calculate each item's available stock
@@ -559,6 +561,8 @@ open class ProductProcessService(
return productProcesses.map { process ->
val jobType = jobTypeRepository.findById(process.jobOrder?.jobTypeId?:0L).orElse(null)
println("jobType id ${process.jobOrder?.jobTypeId}")
ProductProcessInfo(
id = process.id?:0,
bomId = process.bom?.id?:0,
@@ -586,6 +590,10 @@ open class ProductProcessService(
insufficientStockQty = insufficientStockQty,
sufficientStockQty = sufficientStockQty,
productProcessLines = productProcessLineRepository.findByProductProcess_Id(process.id?:0).map { line ->
val equipmentDetail =equipmentDetailRepository.findById(line.equipmentDetailId?:0L).orElse(null)
println("equipmentDetail ${equipmentDetail?.code}")
println("equipmentDetail id${line.equipmentDetailId}")
println("line id${line.id}")
ProductProcessLineInfo(
id = line.id?:0,
bomprocessId = line.bomProcess?.id?:0,
@@ -597,6 +605,7 @@ open class ProductProcessService(
name = line.name?:"",
description = line.description?:"",
equipment_name = line.equipmentType?:"",
equipmentDetailCode = equipmentDetail?.code?:"",
status = line.status?:"",
durationInMinutes = line.bomProcess?.durationInMinute?:0,
prepTimeInMinutes = line.bomProcess?.prepTimeInMinute?:0,
@@ -693,7 +702,7 @@ open class ProductProcessService(
this.seqNo = bomProcess.seqNo?:0
this.name = process?.name?:""
this.description = bomProcess.description?:""
this.equipmentType = equipment?.name?:""
this.equipmentType = equipment?.code?:""
this.status = "Pending"
}
productProcessLineRepository.save(productProcessLine)
@@ -710,6 +719,7 @@ open class ProductProcessService(
}
open fun UpdateProductProcessLineOperatorIdOrEquipmentId(request: UpdateProductProcessLineOperatorIdOrEquipmentIdRequest): MessageResponse {
val productProcessLine = productProcessLineRepository.findById(request.productProcessLineId).orElse(null)
/*
val equipmentId = request.equipmentId
val user = userRepository.findById(request.operatorId?:0L).orElse(null)
val bomProcess= bomProcessRepository.findById(productProcessLine?.bomProcess?.id?:0L).orElse(null)
@@ -753,6 +763,52 @@ open class ProductProcessService(
productProcessLineRepository.save(productProcessLine)
}

*/
val equipmentTypeSubTypeEquipmentNo = request.equipmentTypeSubTypeEquipmentNo
println("equipmentTypeSubTypeEquipmentNo ${equipmentTypeSubTypeEquipmentNo}")
val staffNo = request.staffNo
println("staffNo ${staffNo}")
val user = userRepository.findByStaffNo(staffNo?:"").orElse(null)
println("user ${user?.id}")
val equipmentDetail = equipmentDetailRepository.findByCode(equipmentTypeSubTypeEquipmentNo?:"")
println("equipmentDetail ${equipmentDetail?.id}")
val equipmentId = equipmentDetail?.equipmentTypeId
println("equipmentId ${equipmentId}")
val bomProcess= bomProcessRepository.findById(productProcessLine?.bomProcess?.id?:0L).orElse(null)
val bomProcessEquipment=bomProcess?.equipment
println("bomProcessEquipment ${bomProcessEquipment?.id}")
if (equipmentId != null && user != null) {
// 检查 equipmentId 是否与 bomProcessEquipment 匹配
if (equipmentId != bomProcessEquipment?.id) {
println("productProcessLine id${request.productProcessLineId}")

println("user ${user?.id}")
println("bomProcess ${bomProcess?.id}")
println("not match equipment id${equipmentId} and ${bomProcessEquipment?.id}")
// 返回错误响应
return MessageResponse(
id = request.productProcessLineId,
code = "400",
name = "Equipment Validation Failed",
type = "error",
message = "Input Equipment ID($equipmentId) and BOM Process Equipment ID(${bomProcessEquipment?.id}) not match",
errorPosition = "equipmentId"
)
}
}
if(equipmentId != null &&( equipmentId==bomProcessEquipment?.id)) {
productProcessLine?.equipment = bomProcessEquipment
productProcessLine?.equipmentDetailId = equipmentDetail?.id
productProcessLineRepository.save(productProcessLine)
}
if(user != null) {
productProcessLine.operator = user
productProcessLineRepository.save(productProcessLine)
}

return MessageResponse(
id = null,


+ 6
- 2
src/main/java/com/ffii/fpsms/modules/productProcess/web/model/SaveProductProcessRequest.kt Переглянути файл

@@ -2,14 +2,18 @@ package com.ffii.fpsms.modules.productProcess.web.model

import java.time.LocalDate
import java.time.LocalDateTime
import com.fasterxml.jackson.annotation.JsonProperty
data class SaveProductProcessRequest(
val bomId: Long,
val jobOrderId: Long?,
)
data class UpdateProductProcessLineOperatorIdOrEquipmentIdRequest(
val productProcessLineId: Long,
val operatorId: Long?,
val equipmentId: Long?
//val operatorId: Long?,
//val equipmentId: Long?,
@JsonProperty("EquipmentType-SubType-EquipmentNo")
val equipmentTypeSubTypeEquipmentNo: String?,
val staffNo: String?,
)
data class UpdateProductProcessLineHandlerIdRequest(
val productProcessLineId: Long,


+ 17
- 6
src/main/java/com/ffii/fpsms/modules/user/entity/User.java Переглянути файл

@@ -3,18 +3,18 @@ package com.ffii.fpsms.modules.user.entity;
import java.time.LocalDate;
import java.util.Collection;

import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.Table;
import jakarta.persistence.Transient;
import jakarta.validation.constraints.NotBlank;

import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.ffii.core.entity.BaseEntity;

import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.Table;
import jakarta.persistence.Transient;
import jakarta.validation.constraints.NotBlank;

/** @author Terence */
@Entity
@Table(name = "user")
@@ -77,6 +77,17 @@ public class User extends BaseEntity<Long> implements UserDetails {
@Column
private boolean lotusNotesUser = false;

@Column
private String staffNo;

public String getStaffNo() {
return staffNo;
}

public void setStaffNo(String staffNo) {
this.staffNo = staffNo;
}

public boolean isLocked() {
return this.locked == null ? false : this.locked;
}


+ 2
- 0
src/main/java/com/ffii/fpsms/modules/user/entity/UserRepository.java Переглянути файл

@@ -15,4 +15,6 @@ public interface UserRepository extends AbstractRepository<User, Long> {
Optional<User> findByUsernameAndDeletedFalse(String username);

List<UserCombo> findUserComboByTitleNotNullAndDepartmentNotNullAndNameNotNullAndDeletedFalse();

Optional<User> findByStaffNo(@Param("staffNo") String staffNo);
}

+ 6
- 0
src/main/resources/db/changelog/changes/20251130_01_enson/01_altertable_enson.sql Переглянути файл

@@ -0,0 +1,6 @@
-- liquibase formatted sql
-- changeset enson:altertable_enson


ALTER TABLE `fpsmsdb`.`equipment_detail`
MODIFY COLUMN `equipmentTypeID` INT NULL;

Завантаження…
Відмінити
Зберегти