| @@ -123,6 +123,41 @@ open class PSService( | |||||
| } | } | ||||
| } | } | ||||
| /** | |||||
| * Bulk import fake on-hand: clear table, then insert rows from Excel. | |||||
| * rows: list of maps with keys like "itemCode" and "onHandQty". | |||||
| */ | |||||
| fun importFakeOnHand(rows: List<Map<String, Any?>>) { | |||||
| // 1. Clear existing overrides | |||||
| jdbcDao.executeUpdate("DELETE FROM item_fake_onhand", emptyMap<String, Any>()) | |||||
| // 2. Normalise and de-duplicate by itemCode (last one wins) | |||||
| val byItemCode = LinkedHashMap<String, Number>() | |||||
| rows.forEach { row -> | |||||
| val itemCode = row["itemCode"]?.toString()?.trim() | |||||
| if (itemCode.isNullOrBlank()) return@forEach | |||||
| val rawQty = row["onHandQty"] | |||||
| val qty: Number? = when (rawQty) { | |||||
| null -> null | |||||
| is Number -> rawQty | |||||
| else -> rawQty.toString().toDoubleOrNull() | |||||
| } | |||||
| if (qty == null) return@forEach | |||||
| byItemCode[itemCode] = qty | |||||
| } | |||||
| // 3. Insert one row per itemCode | |||||
| byItemCode.forEach { (itemCode, qty) -> | |||||
| jdbcDao.executeUpdate( | |||||
| "INSERT INTO item_fake_onhand (itemCode, onHandQty) VALUES (:itemCode, :onHandQty)", | |||||
| mapOf("itemCode" to itemCode, "onHandQty" to qty) | |||||
| ) | |||||
| } | |||||
| } | |||||
| /** Set or clear coffee_or_tea for itemCode + systemType (coffee / tea / lemon). */ | /** Set or clear coffee_or_tea for itemCode + systemType (coffee / tea / lemon). */ | ||||
| fun setCoffeeOrTea(itemCode: String, systemType: String, enabled: Boolean) { | fun setCoffeeOrTea(itemCode: String, systemType: String, enabled: Boolean) { | ||||
| val args = mapOf("itemCode" to itemCode, "systemType" to systemType) | val args = mapOf("itemCode" to itemCode, "systemType" to systemType) | ||||
| @@ -15,6 +15,17 @@ import org.springframework.http.ResponseEntity | |||||
| class PSController( | class PSController( | ||||
| private val psService: PSService, | private val psService: PSService, | ||||
| ) { | ) { | ||||
| /** | |||||
| * Bulk import fake on-hand overrides from Excel. | |||||
| * Replaces the entire item_fake_onhand table with the provided rows. | |||||
| * Expected body: [{ "itemCode": "...", "onHandQty": 123.45 }, ...] | |||||
| */ | |||||
| @PostMapping("/importFakeOnHand") | |||||
| fun importFakeOnHand(@RequestBody rows: List<Map<String, Any?>>): ResponseEntity<Map<String, Any>> { | |||||
| psService.importFakeOnHand(rows) | |||||
| return ResponseEntity.ok(mapOf("ok" to true, "count" to rows.size)) | |||||
| } | |||||
| @GetMapping("/search-ps") | @GetMapping("/search-ps") | ||||
| fun searchPs(@RequestParam produceAt: String): ResponseEntity<List<Map<String, Any>>> { | fun searchPs(@RequestParam produceAt: String): ResponseEntity<List<Map<String, Any>>> { | ||||
| // Returns fields: id, produceAt, totalEstProdCount, totalFGType | // Returns fields: id, produceAt, totalEstProdCount, totalFGType | ||||
| @@ -0,0 +1,9 @@ | |||||
| -- liquibase formatted sql | |||||
| -- changeset ai:add_indexes_for_ps | |||||
| --precondition-sql-check expectedResult:0 SELECT COUNT(*) FROM information_schema.STATISTICS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'production_schedule' AND INDEX_NAME = 'idx_production_schedule_produceAt' | |||||
| CREATE INDEX idx_production_schedule_produceAt ON production_schedule (produceAt); | |||||
| --precondition-sql-check expectedResult:0 SELECT COUNT(*) FROM information_schema.STATISTICS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'production_schedule_line' AND INDEX_NAME = 'idx_production_schedule_line_prodScheduleId' | |||||
| CREATE INDEX idx_production_schedule_line_prodScheduleId ON production_schedule_line (prodScheduleId); | |||||