瀏覽代碼

adding PS settings

reset-do-picking-order
Fai Luk 1 周之前
父節點
當前提交
2d062470b4
共有 4 個檔案被更改,包括 192 行新增4 行删除
  1. +126
    -0
      src/main/java/com/ffii/fpsms/modules/jobOrder/service/PSService.kt
  2. +50
    -1
      src/main/java/com/ffii/fpsms/modules/jobOrder/web/PSController.kt
  3. +6
    -3
      src/main/java/com/ffii/fpsms/modules/master/service/ProductionScheduleService.kt
  4. +10
    -0
      src/main/resources/db/changelog/changes/20260314_01_item_daily_out/01_create_item_daily_out.sql

+ 126
- 0
src/main/java/com/ffii/fpsms/modules/jobOrder/service/PSService.kt 查看文件

@@ -4,6 +4,7 @@ import com.ffii.fpsms.modules.jobOrder.entity.JobOrderRepository
import com.ffii.fpsms.modules.jobOrder.web.model.PrintRequest
import com.ffii.fpsms.modules.jobOrder.web.model.LaserRequest
import org.springframework.stereotype.Service
import java.time.LocalDate

import com.ffii.core.support.JdbcDao

@@ -12,6 +13,131 @@ open class PSService(
private val jdbcDao: JdbcDao,
) {
/** Default: past 30 days including today. */
fun getItemDailyOut(fromDate: LocalDate? = null, toDate: LocalDate? = null): List<Map<String, Any>> {
val to = toDate ?: LocalDate.now()
val from = fromDate ?: to.minusDays(29)
val args = mapOf(
"fromDate" to from.toString(),
"toDate" to to.toString(),
)
val sql = """
SELECT
(SELECT dailyQty FROM item_daily_out WHERE itemCode = items.code) AS dailyQty,
(SELECT
IFNULL(ROUND(AVG(d.dailyQty)), 0)
FROM
(SELECT
SUM(dol.qty) AS dailyQty
FROM
delivery_order_line dol
LEFT JOIN delivery_order do ON dol.deliveryOrderId = do.id
WHERE
do.deleted = 0
AND dol.itemId = items.id
AND do.estimatedArrivalDate >= :fromDate AND do.estimatedArrivalDate <= :toDate
GROUP BY do.estimatedArrivalDate) AS d) AS avgQtyLastMonth,
(SELECT SUM(reqQty) FROM job_order WHERE bomId = bom.id AND status != 'completed') AS pendingJobQty,
(SELECT COUNT(1) FROM coffee_or_tea WHERE systemType = 'coffee' AND itemCode = items.code AND deleted = 0) AS isCoffee,
(SELECT COUNT(1) FROM coffee_or_tea WHERE systemType = 'tea' AND itemCode = items.code AND deleted = 0) AS isTea,
(SELECT COUNT(1) FROM coffee_or_tea WHERE systemType = 'lemon' AND itemCode = items.code AND deleted = 0) AS isLemon,
CASE WHEN item_fake_onhand.onHandQty IS NOT NULL THEN item_fake_onhand.onHandQty
ELSE inventory.onHandQty - 500 END AS stockQty,
bom.baseScore,
bom.outputQty,
bom.outputQtyUom,
(SELECT udfudesc
FROM delivery_order_line
LEFT JOIN uom_conversion ON delivery_order_line.uomId = uom_conversion.id
WHERE delivery_order_line.itemId = bom.itemId
LIMIT 1) AS doUom,
items.code AS itemCode,
items.name AS itemName,
uc_stock.udfudesc AS unit,
bom.description,
inventory.onHandQty,
item_fake_onhand.onHandQty AS fakeOnHandQty,
bom.itemId,
bom.id AS bomId,
CASE WHEN bom.isDark = 5 THEN 11 WHEN bom.isDark = 3 THEN 6 WHEN bom.isDark = 1 THEN 2 ELSE 0 END AS markDark,
CASE WHEN bom.isFloat = 5 THEN 11 WHEN bom.isFloat = 3 THEN 6 WHEN bom.isFloat = 1 THEN 2 ELSE 0 END AS markFloat,
CASE WHEN bom.isDense = 5 THEN 11 WHEN bom.isDense = 3 THEN 6 WHEN bom.isDense = 1 THEN 2 ELSE 0 END AS markDense,
bom.timeSequence AS markTimeSequence,
bom.complexity AS markComplexity,
CASE WHEN bom.allergicSubstances = 5 THEN 11 ELSE 0 END AS markAS,
inventory.id AS inventoryId
FROM
bom
LEFT JOIN items ON bom.itemId = items.id
LEFT JOIN inventory ON items.id = inventory.itemId
LEFT JOIN item_fake_onhand ON items.code = item_fake_onhand.itemCode
LEFT JOIN item_uom iu ON iu.itemId = items.id AND iu.stockUnit = 1
LEFT JOIN uom_conversion uc_stock ON uc_stock.id = iu.uomId
WHERE 1
""".trimIndent()
return jdbcDao.queryForList(sql, args)
}

/** Update or insert dailyQty for itemCode. */
fun setDailyQtyOut(itemCode: String, dailyQty: Number) {
val args = mapOf("itemCode" to itemCode, "dailyQty" to dailyQty)
val updated = jdbcDao.executeUpdate(
"UPDATE item_daily_out SET dailyQty = :dailyQty WHERE itemCode = :itemCode",
args
)
if (updated == 0) {
jdbcDao.executeUpdate(
"INSERT INTO item_daily_out (itemCode, dailyQty) VALUES (:itemCode, :dailyQty)",
args
)
}
}

/** Remove dailyQty override for itemCode (delete row from item_daily_out). */
fun clearDailyQtyOut(itemCode: String) {
jdbcDao.executeUpdate(
"DELETE FROM item_daily_out WHERE itemCode = :itemCode",
mapOf("itemCode" to itemCode)
)
}

/** Set or clear item_fake_onhand for itemCode. If onHandQty is null, delete the row; else update or insert. */
fun setFakeOnHand(itemCode: String, onHandQty: Number?) {
if (onHandQty == null) {
jdbcDao.executeUpdate(
"DELETE FROM item_fake_onhand WHERE itemCode = :itemCode",
mapOf("itemCode" to itemCode)
)
return
}
val args = mapOf("itemCode" to itemCode, "onHandQty" to onHandQty)
val updated = jdbcDao.executeUpdate(
"UPDATE item_fake_onhand SET onHandQty = :onHandQty WHERE itemCode = :itemCode",
args
)
if (updated == 0) {
jdbcDao.executeUpdate(
"INSERT INTO item_fake_onhand (itemCode, onHandQty) VALUES (:itemCode, :onHandQty)",
args
)
}
}

/** Set or clear coffee_or_tea for itemCode + systemType (coffee / tea / lemon). */
fun setCoffeeOrTea(itemCode: String, systemType: String, enabled: Boolean) {
val args = mapOf("itemCode" to itemCode, "systemType" to systemType)
jdbcDao.executeUpdate(
"DELETE FROM coffee_or_tea WHERE itemCode = :itemCode AND systemType = :systemType",
args
)
if (enabled) {
jdbcDao.executeUpdate(
"INSERT INTO coffee_or_tea (itemCode, systemType) VALUES (:itemCode, :systemType)",
args
)
}
}

fun searchProductionSchedules(produceAt: String): List<Map<String, Any>> {
val args = mapOf(
"produceAt" to produceAt,


+ 50
- 1
src/main/java/com/ffii/fpsms/modules/jobOrder/web/PSController.kt 查看文件

@@ -28,6 +28,55 @@ class PSController(
val results = psService.getProductionScheduleLines(psId)
return ResponseEntity.ok(results)
}

/** 每日平均出貨量: itemCode, itemName, avgQtyLastMonth, dailyQty, isCoffee, isTea, isLemon. Default: past 30 days. */
@GetMapping("/itemDailyOut.json")
fun itemDailyOut(
@RequestParam(required = false) fromDate: String?,
@RequestParam(required = false) toDate: String?,
): ResponseEntity<List<Map<String, Any>>> {
val to = toDate?.let { LocalDate.parse(it) } ?: LocalDate.now()
val from = fromDate?.let { LocalDate.parse(it) } ?: to.minusDays(29)
val results = psService.getItemDailyOut(from, to)
return ResponseEntity.ok(results)
}

/** Set daily out qty for an item (update or insert). */
@PostMapping("/setDailyQtyOut")
fun setDailyQtyOut(@RequestBody body: Map<String, Any>): ResponseEntity<Map<String, Any>> {
val itemCode = body["itemCode"]?.toString() ?: return ResponseEntity.badRequest().body(mapOf("error" to "itemCode required"))
val dailyQty = (body["dailyQty"] as? Number) ?: (body["dailyQty"]?.toString()?.toDoubleOrNull() ?: 0)
psService.setDailyQtyOut(itemCode, dailyQty)
return ResponseEntity.ok(mapOf("ok" to true, "itemCode" to itemCode, "dailyQty" to dailyQty))
}

/** Clear daily out qty for an item (delete from item_daily_out). */
@PostMapping("/clearDailyQtyOut")
fun clearDailyQtyOut(@RequestBody body: Map<String, Any>): ResponseEntity<Map<String, Any>> {
val itemCode = body["itemCode"]?.toString() ?: return ResponseEntity.badRequest().body(mapOf("error" to "itemCode required"))
psService.clearDailyQtyOut(itemCode)
return ResponseEntity.ok(mapOf("ok" to true, "itemCode" to itemCode))
}

/** Set or clear fake onhand for an item. onHandQty: number to set, or omit/null to delete. */
@PostMapping("/setFakeOnHand")
fun setFakeOnHand(@RequestBody body: Map<String, Any>): ResponseEntity<Map<String, Any>> {
val itemCode = body["itemCode"]?.toString() ?: return ResponseEntity.badRequest().body(mapOf("error" to "itemCode required"))
val onHandQty = body["onHandQty"]?.let { (it as? Number) ?: (it.toString().toDoubleOrNull()) }
psService.setFakeOnHand(itemCode, onHandQty)
return ResponseEntity.ok(mapOf("ok" to true, "itemCode" to itemCode, "onHandQty" to (onHandQty ?: "deleted")))
}

/** Set or clear coffee/tea/lemon for an item. systemType: coffee | tea | lemon, enabled: boolean. */
@PostMapping("/setCoffeeOrTea")
fun setCoffeeOrTea(@RequestBody body: Map<String, Any>): ResponseEntity<Map<String, Any>> {
val itemCode = body["itemCode"]?.toString() ?: return ResponseEntity.badRequest().body(mapOf("error" to "itemCode required"))
val systemType = body["systemType"]?.toString() ?: return ResponseEntity.badRequest().body(mapOf("error" to "systemType required"))
if (systemType !in listOf("coffee", "tea", "lemon")) {
return ResponseEntity.badRequest().body(mapOf("error" to "systemType must be coffee, tea, or lemon"))
}
val enabled = body["enabled"] == true
psService.setCoffeeOrTea(itemCode, systemType, enabled)
return ResponseEntity.ok(mapOf("ok" to true, "itemCode" to itemCode, "systemType" to systemType, "enabled" to enabled))
}
}

+ 6
- 3
src/main/java/com/ffii/fpsms/modules/master/service/ProductionScheduleService.kt 查看文件

@@ -665,8 +665,10 @@ open class ProductionScheduleService(
i.*
FROM
(SELECT
(SELECT
ROUND(AVG(d.dailyQty) * 1.5)
COALESCE(
(SELECT dailyQty FROM item_daily_out WHERE itemCode = items.code),
(SELECT
ROUND(AVG(d.dailyQty))
FROM
(SELECT
SUM(dol.qty) AS dailyQty
@@ -677,7 +679,8 @@ open class ProductionScheduleService(
do.deleted = 0 and
dol.itemId = items.id
AND do.estimatedArrivalDate >= :fromDate AND do.estimatedArrivalDate <= :toDate
GROUP BY do.estimatedArrivalDate) AS d) AS avgQtyLastMonth,
GROUP BY do.estimatedArrivalDate) AS d)
) AS avgQtyLastMonth,

(select sum(reqQty) from job_order where bomId = bom.id and status != 'completed') AS pendingJobQty,



+ 10
- 0
src/main/resources/db/changelog/changes/20260314_01_item_daily_out/01_create_item_daily_out.sql 查看文件

@@ -0,0 +1,10 @@
--liquibase formatted sql

--changeset ps:create item_daily_out table
--precondition onFail:MARK_RAN
--precondition-sql-check expectedResult:0 SELECT COUNT(*) FROM information_schema.TABLES WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'item_daily_out'
CREATE TABLE `item_daily_out` (
`itemCode` VARCHAR(100) NOT NULL,
`dailyQty` DECIMAL(14,2) NULL DEFAULT 0,
PRIMARY KEY (`itemCode`)
);

Loading…
取消
儲存