Explorar el Código

Dashboard: Goods Receipt Status

master
B.E.N.S.O.N hace 1 semana
padre
commit
43529caa25
Se han modificado 5 ficheros con 191 adiciones y 0 borrados
  1. +111
    -0
      src/main/java/com/ffii/fpsms/modules/dashboard/service/GoodsReceiptStatusService.kt
  2. +26
    -0
      src/main/java/com/ffii/fpsms/modules/dashboard/web/DashboardController.kt
  3. +12
    -0
      src/main/java/com/ffii/fpsms/modules/dashboard/web/models/GoodsReceiptStatusResponse.kt
  4. +15
    -0
      src/main/java/com/ffii/fpsms/modules/purchaseOrder/entity/PurchaseOrderRepository.kt
  5. +27
    -0
      src/main/java/com/ffii/fpsms/modules/qc/entity/QcResultRepository.kt

+ 111
- 0
src/main/java/com/ffii/fpsms/modules/dashboard/service/GoodsReceiptStatusService.kt Ver fichero

@@ -0,0 +1,111 @@
package com.ffii.fpsms.modules.dashboard.service

import com.ffii.fpsms.modules.dashboard.web.models.GoodsReceiptStatusResponse
import com.ffii.fpsms.modules.purchaseOrder.entity.PurchaseOrderRepository
import com.ffii.fpsms.modules.qc.entity.QcResultRepository
import com.ffii.fpsms.modules.stock.entity.StockInLineRepository
import org.springframework.stereotype.Service
import java.time.LocalDate

@Service
class GoodsReceiptStatusService(
private val purchaseOrderRepository: PurchaseOrderRepository,
private val stockInLineRepository: StockInLineRepository,
private val qcResultRepository: QcResultRepository
) {
private data class Agg(
val supplierId: Long?,
val supplierName: String,
var expectedNoOfDelivery: Int = 0,
val ordersReceivedAtDock: MutableSet<Long> = mutableSetOf(),
var itemsInspected: Int = 0,
var itemsWithIqcIssue: Int = 0,
var itemsCompletedPutAway: Int = 0
)

fun getGoodsReceiptStatus(date: LocalDate): List<GoodsReceiptStatusResponse> {
val from = date.atStartOfDay()
val to = date.plusDays(1).atStartOfDay()

val purchaseOrders = purchaseOrderRepository.findAllByEstimatedArrivalDateRange(from, to)
val stockInLines = stockInLineRepository.findByReceiptDateAndDeletedFalse(date)

val stockInLineIds = stockInLines.mapNotNull { it.id }
val inspectedLineIdSet = if (stockInLineIds.isEmpty()) {
emptySet()
} else {
qcResultRepository
.findDistinctStockInLineIdsByStockInLineIdInAndDeletedFalse(stockInLineIds)
.toSet()
}
val failedLineIdSet = if (stockInLineIds.isEmpty()) {
emptySet()
} else {
qcResultRepository
.findDistinctFailedStockInLineIdsByStockInLineIdInAndDeletedFalse(stockInLineIds)
.toSet()
}

val bySupplier = mutableMapOf<Long?, Agg>()

fun upsertAgg(supplierId: Long?, supplierName: String): Agg {
return bySupplier.getOrPut(supplierId) {
Agg(supplierId = supplierId, supplierName = supplierName)
}
}

// Expected deliveries (by PO estimated arrival date)
purchaseOrders.forEach { po ->
val supplier = po.supplier
val supplierId = supplier?.id
val supplierName = supplier?.name ?: "N/A"
upsertAgg(supplierId, supplierName).expectedNoOfDelivery += 1
}

// Receiving / IQC / Put-away (by stock-in receipt date)
stockInLines.forEach { sil ->
val supplier = sil.purchaseOrder?.supplier
val supplierId = supplier?.id
val supplierName = supplier?.name ?: "N/A"
val agg = upsertAgg(supplierId, supplierName)

val silId = sil.id ?: return@forEach

// Orders received at dock: count orders with DN + (supplier) lot no entered
val poId = sil.purchaseOrder?.id
if (poId != null && !sil.dnNo.isNullOrBlank() && !sil.productLotNo.isNullOrBlank()) {
agg.ordersReceivedAtDock.add(poId)
}

// Items inspected: any IQC result recorded
if (inspectedLineIdSet.contains(silId)) {
agg.itemsInspected += 1
}

// Items with IQC issue: any failed IQC criteria
if (failedLineIdSet.contains(silId)) {
agg.itemsWithIqcIssue += 1
}

// Put-away completed at store: stock-in line completed
if (sil.status.equals("completed", ignoreCase = true)) {
agg.itemsCompletedPutAway += 1
}
}

return bySupplier.values
.map { agg ->
GoodsReceiptStatusResponse(
supplierId = agg.supplierId,
supplierName = agg.supplierName,
expectedNoOfDelivery = agg.expectedNoOfDelivery,
noOfOrdersReceivedAtDock = agg.ordersReceivedAtDock.size,
noOfItemsInspected = agg.itemsInspected,
noOfItemsWithIqcIssue = agg.itemsWithIqcIssue,
noOfItemsCompletedPutAwayAtStore = agg.itemsCompletedPutAway
)
}
.sortedBy { it.supplierName }
}
}


+ 26
- 0
src/main/java/com/ffii/fpsms/modules/dashboard/web/DashboardController.kt Ver fichero

@@ -0,0 +1,26 @@
package com.ffii.fpsms.modules.dashboard.web

import com.ffii.fpsms.modules.dashboard.service.GoodsReceiptStatusService
import com.ffii.fpsms.modules.dashboard.web.models.GoodsReceiptStatusResponse
import org.springframework.format.annotation.DateTimeFormat
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RequestParam
import org.springframework.web.bind.annotation.RestController
import java.time.LocalDate

@RestController
@RequestMapping("/dashboard")
class DashboardController(
private val goodsReceiptStatusService: GoodsReceiptStatusService
) {
@GetMapping("/goods-receipt-status")
fun getGoodsReceiptStatus(
@RequestParam(required = false)
@DateTimeFormat(iso = DateTimeFormat.ISO.DATE)
date: LocalDate?
): List<GoodsReceiptStatusResponse> {
return goodsReceiptStatusService.getGoodsReceiptStatus(date ?: LocalDate.now())
}
}


+ 12
- 0
src/main/java/com/ffii/fpsms/modules/dashboard/web/models/GoodsReceiptStatusResponse.kt Ver fichero

@@ -0,0 +1,12 @@
package com.ffii.fpsms.modules.dashboard.web.models

data class GoodsReceiptStatusResponse(
val supplierId: Long?,
val supplierName: String,
val expectedNoOfDelivery: Int,
val noOfOrdersReceivedAtDock: Int,
val noOfItemsInspected: Int,
val noOfItemsWithIqcIssue: Int,
val noOfItemsCompletedPutAwayAtStore: Int
)


+ 15
- 0
src/main/java/com/ffii/fpsms/modules/purchaseOrder/entity/PurchaseOrderRepository.kt Ver fichero

@@ -6,6 +6,7 @@ import com.ffii.fpsms.modules.purchaseOrder.entity.projections.PurchaseOrderInfo
import org.springframework.data.domain.Page
import org.springframework.stereotype.Repository
import java.io.Serializable
import java.time.LocalDateTime
import java.util.Optional
import org.springframework.data.domain.Pageable
import org.springframework.data.jpa.repository.Query
@@ -19,5 +20,19 @@ interface PurchaseOrderRepository : AbstractRepository<PurchaseOrder, Long> {

fun findByIdAndDeletedFalse(id: Long): Optional<PurchaseOrder>

@Query(
"""
SELECT po FROM PurchaseOrder po
WHERE po.deleted = false
AND po.estimatedArrivalDate IS NOT NULL
AND po.estimatedArrivalDate >= :from
AND po.estimatedArrivalDate < :to
"""
)
fun findAllByEstimatedArrivalDateRange(
@Param("from") from: LocalDateTime,
@Param("to") to: LocalDateTime
): List<PurchaseOrder>

override fun findAll(pageable: Pageable): Page<PurchaseOrder>
}

+ 27
- 0
src/main/java/com/ffii/fpsms/modules/qc/entity/QcResultRepository.kt Ver fichero

@@ -3,10 +3,37 @@ package com.ffii.fpsms.modules.qc.entity
import com.ffii.core.support.AbstractRepository
import com.ffii.fpsms.modules.qc.entity.projection.QcResultInfo
import com.ffii.fpsms.modules.stock.entity.StockInLine
import org.springframework.data.jpa.repository.Query
import org.springframework.data.repository.query.Param
import org.springframework.stereotype.Repository

@Repository
interface QcResultRepository: AbstractRepository<QcResult, Long> {
fun findQcResultInfoByStockInLineIdAndDeletedFalse(stockInLineId: Long): List<QcResultInfo>
fun findQcResultInfoByStockOutLineIdAndDeletedFalse(stockOutLineId: Long): List<QcResultInfo>

@Query(
"""
SELECT DISTINCT qr.stockInLine.id
FROM QcResult qr
WHERE qr.deleted = false
AND qr.stockInLine.id IN :stockInLineIds
"""
)
fun findDistinctStockInLineIdsByStockInLineIdInAndDeletedFalse(
@Param("stockInLineIds") stockInLineIds: List<Long>
): List<Long>

@Query(
"""
SELECT DISTINCT qr.stockInLine.id
FROM QcResult qr
WHERE qr.deleted = false
AND qr.stockInLine.id IN :stockInLineIds
AND (qr.qcPassed = false OR COALESCE(qr.failQty, 0) > 0)
"""
)
fun findDistinctFailedStockInLineIdsByStockInLineIdInAndDeletedFalse(
@Param("stockInLineIds") stockInLineIds: List<Long>
): List<Long>
}

Cargando…
Cancelar
Guardar