| @@ -1,5 +1,6 @@ | |||
| package com.ffii.fpsms.modules.deliveryOrder.service | |||
| import com.ffii.fpsms.modules.jobOrder.service.JoWorkbenchPickConstants | |||
| import com.ffii.fpsms.modules.settings.entity.SettingsRepository | |||
| import org.springframework.stereotype.Service | |||
| import java.util.Locale | |||
| @@ -12,9 +13,22 @@ open class DoFloorSupplierSettingsService( | |||
| companion object { | |||
| private const val SETTING_DO_FLOOR_SUPPLIERS_2F = "DO.floor.suppliers.2F" | |||
| private const val SETTING_DO_FLOOR_SUPPLIERS_4F = "DO.floor.suppliers.4F" | |||
| const val SETTING_DO_PICK_EXCLUDE_WAREHOUSES = "DO.pick.excludeWarehouses" | |||
| const val SETTING_JO_PICK_EXCLUDE_WAREHOUSES = "JO.pick.excludeWarehouses" | |||
| const val SETTING_DO_PICK_EXCLUDE_PRINT_MODE = "DO.pick.excludePrintMode" | |||
| const val SETTING_JO_PICK_EXCLUDE_PRINT_MODE = "JO.pick.excludePrintMode" | |||
| const val PRINT_MODE_HIDE_LIST = "hideList" | |||
| const val PRINT_MODE_HIDE_QR = "hideQr" | |||
| /** Saved when the list is cleared. Settings update rejects a blank value. */ | |||
| const val EMPTY_CODE_LIST_MARKER = "-" | |||
| private val DEFAULT_SUPPLIERS_2F = listOf("P07", "P06D", "P06Y") | |||
| private val DEFAULT_SUPPLIERS_4F = listOf("P06B") | |||
| private val DEFAULT_DO_PICK_EXCLUDE_WAREHOUSES = listOf( | |||
| "2F-W202-01-00", | |||
| "2F-W200-#A-00", | |||
| ) | |||
| } | |||
| open fun supplierCodesFromSetting(settingName: String, defaultList: List<String>): List<String> { | |||
| @@ -32,6 +46,54 @@ open class DoFloorSupplierSettingsService( | |||
| return suppliers2F to suppliers4F | |||
| } | |||
| /** DO pick / workbench default: lots in these warehouses are not suggested. */ | |||
| open fun loadDoPickExcludeWarehouseCodes(): Set<String> = | |||
| warehouseCodesFromSettingOrDefault( | |||
| SETTING_DO_PICK_EXCLUDE_WAREHOUSES, | |||
| DEFAULT_DO_PICK_EXCLUDE_WAREHOUSES, | |||
| ) | |||
| /** JO workbench (and consumable user 246): lots in these warehouses are not suggested. */ | |||
| open fun loadJoPickExcludeWarehouseCodes(): Set<String> = | |||
| warehouseCodesFromSettingOrDefault( | |||
| SETTING_JO_PICK_EXCLUDE_WAREHOUSES, | |||
| JoWorkbenchPickConstants.DEFAULT_EXCLUDE_WAREHOUSE_CODES.toList(), | |||
| ) | |||
| /** | |||
| * hideList: excluded warehouses are omitted from the print list, and scanning their physical QR does not pass. | |||
| * hideQr: they stay on the list without a QR, and scanning the physical QR still passes. | |||
| * Missing row is hideList. | |||
| */ | |||
| open fun loadDoPickExcludePrintMode(): String = printModeFromSetting(SETTING_DO_PICK_EXCLUDE_PRINT_MODE) | |||
| open fun loadJoPickExcludePrintMode(): String = printModeFromSetting(SETTING_JO_PICK_EXCLUDE_PRINT_MODE) | |||
| open fun rejectsScanOfExcludedWarehouse(printMode: String): Boolean = | |||
| printMode != PRINT_MODE_HIDE_QR | |||
| private fun printModeFromSetting(settingName: String): String { | |||
| val raw = settingsRepository.findByName(settingName).map { it.value }.orElse(null)?.trim().orEmpty() | |||
| return if (raw == PRINT_MODE_HIDE_QR) PRINT_MODE_HIDE_QR else PRINT_MODE_HIDE_LIST | |||
| } | |||
| /** | |||
| * Missing row uses [defaultList]. | |||
| * A saved blank value or [EMPTY_CODE_LIST_MARKER] means exclude nothing. | |||
| */ | |||
| open fun warehouseCodesFromSettingOrDefault(settingName: String, defaultList: List<String>): Set<String> { | |||
| val row = settingsRepository.findByName(settingName) | |||
| if (row.isEmpty) { | |||
| return defaultList.map { it.trim().uppercase(Locale.ROOT) }.filter { it.isNotEmpty() }.toSet() | |||
| } | |||
| val raw = row.get().value?.trim().orEmpty() | |||
| if (raw.isEmpty() || raw == EMPTY_CODE_LIST_MARKER) return emptySet() | |||
| return raw.split(",") | |||
| .map { it.trim().uppercase(Locale.ROOT) } | |||
| .filter { it.isNotEmpty() && it != EMPTY_CODE_LIST_MARKER } | |||
| .toSet() | |||
| } | |||
| open fun allowedSupplierCodesForFloor(floor: String?): List<String> { | |||
| val f = floor?.trim()?.uppercase(Locale.ROOT).orEmpty() | |||
| val (codes2F, codes4F) = loadDoFloorSupplierLists() | |||
| @@ -63,7 +63,6 @@ import com.ffii.fpsms.modules.deliveryOrder.web.models.ReleasedDoPickOrderListIt | |||
| import com.ffii.fpsms.modules.deliveryOrder.web.models.WorkbenchTicketReleaseTableResponse | |||
| import com.ffii.fpsms.modules.user.service.UserService | |||
| import com.ffii.fpsms.modules.jobOrder.entity.JoPickOrderRepository | |||
| import com.ffii.fpsms.modules.jobOrder.service.JoWorkbenchPickConstants | |||
| import com.ffii.fpsms.modules.pickOrder.entity.TruckRepository | |||
| import kotlin.system.measureTimeMillis | |||
| import org.slf4j.LoggerFactory | |||
| @@ -246,9 +245,9 @@ open class DoWorkbenchMainService( | |||
| /** | |||
| * FP-MTMS Version Checklist | Functions Ref. No. 38 | v1.0.2 | 2026-08-13 | |||
| * Warehouse exclude list for workbench **re-suggest** after scan-pick (shortfall / lot split). | |||
| * - JO: same list as assign ([JoWorkbenchPickConstants.DEFAULT_EXCLUDE_WAREHOUSE_CODES]). | |||
| * - JO: setting `JO.pick.excludeWarehouses` (fallback is JoWorkbenchPickConstants when the row is missing). | |||
| * - Consumable: hardcoded user [ConsumableWorkbenchPickConstants.HARDCODED_EXCLUDE_USER_ID] → JO list; else empty (no limit). | |||
| * - DO / other: pass through request (`null` → service default excludes). | |||
| * - DO / other: pass through request (`null` → `DO.pick.excludeWarehouses`). | |||
| */ | |||
| private fun workbenchResuggestExcludeWarehouseCodes( | |||
| pickOrderId: Long?, | |||
| @@ -259,14 +258,49 @@ open class DoWorkbenchMainService( | |||
| val resolvedType = poType | |||
| ?: pickOrderId?.let { pickOrderRepository.findById(it).orElse(null)?.type } | |||
| if (resolvedType == PickOrderType.JOB_ORDER) { | |||
| return JoWorkbenchPickConstants.DEFAULT_EXCLUDE_WAREHOUSE_CODES.toList() | |||
| return doFloorSupplierSettingsService.loadJoPickExcludeWarehouseCodes().toList() | |||
| } | |||
| if (resolvedType == PickOrderType.Consumable && userId != null) { | |||
| return ConsumableWorkbenchPickConstants.resolveExcludeWarehouseCodes(userId) | |||
| return ConsumableWorkbenchPickConstants.resolveExcludeWarehouseCodes( | |||
| userId, | |||
| doFloorSupplierSettingsService.loadJoPickExcludeWarehouseCodes(), | |||
| ) | |||
| } | |||
| return requestExcludeWarehouseCodes | |||
| } | |||
| /** | |||
| * hideList: a lot in an excluded warehouse is not on the print list, and scanning its physical QR does not pass. | |||
| * hideQr: the lot stays on the list without a printed QR, and scanning the physical QR still passes. | |||
| */ | |||
| private fun rejectScanWhenWarehouseHiddenFromList( | |||
| pickOrderType: PickOrderType?, | |||
| userId: Long?, | |||
| warehouseCode: String?, | |||
| ): String? { | |||
| val code = warehouseCode?.trim()?.uppercase(Locale.ROOT).orEmpty() | |||
| if (code.isEmpty()) return null | |||
| val joCodes = doFloorSupplierSettingsService.loadJoPickExcludeWarehouseCodes() | |||
| val (codes, printMode) = when (pickOrderType) { | |||
| PickOrderType.JOB_ORDER -> joCodes to doFloorSupplierSettingsService.loadJoPickExcludePrintMode() | |||
| PickOrderType.Consumable -> { | |||
| val limited = if (userId == null) { | |||
| emptySet() | |||
| } else { | |||
| ConsumableWorkbenchPickConstants.resolveExcludeWarehouseCodes(userId, joCodes) | |||
| .map { it.uppercase(Locale.ROOT) } | |||
| .toSet() | |||
| } | |||
| limited to doFloorSupplierSettingsService.loadJoPickExcludePrintMode() | |||
| } | |||
| else -> doFloorSupplierSettingsService.loadDoPickExcludeWarehouseCodes() to | |||
| doFloorSupplierSettingsService.loadDoPickExcludePrintMode() | |||
| } | |||
| if (code !in codes) return null | |||
| if (!doFloorSupplierSettingsService.rejectsScanOfExcludedWarehouse(printMode)) return null | |||
| return "此儲位不在揀貨清單,掃到實物 QR 也不可以揀。" | |||
| } | |||
| /** | |||
| * FP-MTMS Version Checklist | Functions Ref. No. 38 | v1.0.2 | 2026-08-13 | |||
| * Workbench scan-pick (DO FG): | |||
| @@ -450,6 +484,18 @@ open class DoWorkbenchMainService( | |||
| println("Scanned lot does not match pick line item") | |||
| } | |||
| val hiddenWarehouseReject = rejectScanWhenWarehouseHiddenFromList( | |||
| pickOrderType = pol.pickOrder?.type, | |||
| userId = request.userId, | |||
| warehouseCode = scannedIll.warehouse?.code, | |||
| ) | |||
| if (hiddenWarehouseReject != null) { | |||
| return MessageResponse( | |||
| id = sol.id, name = "scan_pick", code = "INVALID", type = "workbench_scan_pick", | |||
| message = hiddenWarehouseReject, errorPosition = null, entity = null, | |||
| ) | |||
| } | |||
| sol.inventoryLotLine = scannedIll | |||
| val required = pol.qty ?: BigDecimal.ZERO | |||
| @@ -1,5 +1,6 @@ | |||
| package com.ffii.fpsms.modules.jobOrder.service | |||
| import com.ffii.fpsms.modules.deliveryOrder.service.DoFloorSupplierSettingsService | |||
| import com.ffii.fpsms.modules.jobOrder.entity.JoPickOrderRepository | |||
| import com.ffii.fpsms.modules.jobOrder.entity.JobOrderRepository | |||
| import com.ffii.fpsms.modules.jobOrder.enums.JobOrderStatus | |||
| @@ -50,9 +51,8 @@ open class JoWorkbenchMainService( | |||
| private val stockOutRepository: StockOutRepository, | |||
| private val suggestedPickLotWorkbenchService: SuggestedPickLotWorkbenchService, | |||
| private val stockOutLineWorkbenchService: StockOutLineWorkbenchService, | |||
| private val doFloorSupplierSettingsService: DoFloorSupplierSettingsService, | |||
| ) { | |||
| private val workbenchDefaultExcludeWarehouseCodes: Set<String> = | |||
| JoWorkbenchPickConstants.DEFAULT_EXCLUDE_WAREHOUSE_CODES | |||
| private fun debugPrintSuggestionNullReasons(pickOrderId: Long) { | |||
| val pickOrder = pickOrderRepository.findById(pickOrderId).orElse(null) ?: return | |||
| @@ -80,7 +80,7 @@ open class JoWorkbenchMainService( | |||
| val notExpired = availableStatus.filter { it.inventoryLot?.expiryDate?.isBefore(today) != true } | |||
| val notExcluded = notExpired.filter { lot -> | |||
| val code = lot.warehouse?.code?.trim()?.uppercase(Locale.ROOT) | |||
| code.isNullOrEmpty() || code !in workbenchDefaultExcludeWarehouseCodes | |||
| code.isNullOrEmpty() || code !in doFloorSupplierSettingsService.loadJoPickExcludeWarehouseCodes() | |||
| } | |||
| val positiveQty = notExcluded.filter { lot -> | |||
| val inQ = lot.inQty ?: BigDecimal.ZERO | |||
| @@ -539,7 +539,7 @@ open class JoWorkbenchMainService( | |||
| suggestedPickLotWorkbenchService.primeNextSingleLotSuggestionsForPickOrder( | |||
| pickOrderId = pickOrderId, | |||
| storeId = null, | |||
| excludeWarehouseCodes = workbenchDefaultExcludeWarehouseCodes.toList() | |||
| excludeWarehouseCodes = doFloorSupplierSettingsService.loadJoPickExcludeWarehouseCodes().toList() | |||
| ) | |||
| stockOutLineWorkbenchService.ensureStockOutLinesForPickOrderNoHold(pickOrderId, userId) | |||
| val splAfter = loadSplDebugRowsByPol(pickOrderId) | |||
| @@ -3,8 +3,8 @@ package com.ffii.fpsms.modules.jobOrder.service | |||
| /** | |||
| * JO workbench pick constants. | |||
| * | |||
| * [DEFAULT_EXCLUDE_WAREHOUSE_CODES] applies on **assign / first prime** ([JoWorkbenchMainService]) | |||
| * and on **scan-pick re-suggest** ([com.ffii.fpsms.modules.deliveryOrder.service.DoWorkbenchMainService]). | |||
| * [DEFAULT_EXCLUDE_WAREHOUSE_CODES] is the fallback when setting `JO.pick.excludeWarehouses` is missing. | |||
| * Assign / first prime and scan-pick re-suggest read the setting at runtime. | |||
| * JO re-suggest keeps [storeId] null (same as assign); it does not use DO floor store resolution. | |||
| */ | |||
| object JoWorkbenchPickConstants { | |||
| @@ -1,21 +1,19 @@ | |||
| package com.ffii.fpsms.modules.pickOrder.service | |||
| import com.ffii.fpsms.modules.jobOrder.service.JoWorkbenchPickConstants | |||
| /** | |||
| * Temporary consumable workbench exclude list until per-user DB config (Scheme A) lands. | |||
| * | |||
| * - [HARDCODED_EXCLUDE_USER_ID] → same warehouses as JO ([JoWorkbenchPickConstants]). | |||
| * - [HARDCODED_EXCLUDE_USER_ID] → the JO exclude list passed in (setting `JO.pick.excludeWarehouses`). | |||
| * - All other users → empty list (no warehouse limit). Do not return null: | |||
| * [SuggestedPickLotWorkbenchService] treats null as DO default 2F excludes. | |||
| * [com.ffii.fpsms.modules.stock.service.SuggestedPickLotWorkbenchService] treats null as the DO exclude setting. | |||
| * Consumable re-suggest keeps storeId null (same as first prime / JO); it does not use DO floor store resolution. | |||
| */ | |||
| object ConsumableWorkbenchPickConstants { | |||
| const val HARDCODED_EXCLUDE_USER_ID: Long = 246L | |||
| /** FP-MTMS Version Checklist | Functions Ref. No. 66 | v1.0.0 | 2026-08-13 */ | |||
| fun resolveExcludeWarehouseCodes(userId: Long): List<String> { | |||
| fun resolveExcludeWarehouseCodes(userId: Long, joExcludeWarehouseCodes: Collection<String>): List<String> { | |||
| if (userId != HARDCODED_EXCLUDE_USER_ID) return emptyList() | |||
| return JoWorkbenchPickConstants.DEFAULT_EXCLUDE_WAREHOUSE_CODES.toList() | |||
| return joExcludeWarehouseCodes.map { it.trim() }.filter { it.isNotEmpty() } | |||
| } | |||
| } | |||
| @@ -1,6 +1,7 @@ | |||
| package com.ffii.fpsms.modules.pickOrder.service | |||
| import com.ffii.core.support.JdbcDao | |||
| import com.ffii.fpsms.modules.deliveryOrder.service.DoFloorSupplierSettingsService | |||
| import com.ffii.fpsms.modules.master.web.models.MessageResponse | |||
| import com.ffii.fpsms.modules.pickOrder.entity.PickOrderLineRepository | |||
| import com.ffii.fpsms.modules.pickOrder.entity.PickOrderRepository | |||
| @@ -23,6 +24,7 @@ open class PickOrderWorkbenchService( | |||
| private val userService: UserService, | |||
| private val suggestedPickLotWorkbenchService: SuggestedPickLotWorkbenchService, | |||
| private val stockOutLineWorkbenchService: StockOutLineWorkbenchService, | |||
| private val doFloorSupplierSettingsService: DoFloorSupplierSettingsService, | |||
| ) { | |||
| private fun toBigDecimal(v: Any?): BigDecimal? = when (v) { | |||
| null -> null | |||
| @@ -447,7 +449,10 @@ open class PickOrderWorkbenchService( | |||
| val excludeWarehouseCodes = | |||
| if (pickOrder.type == PickOrderType.Consumable) { | |||
| ConsumableWorkbenchPickConstants.resolveExcludeWarehouseCodes(userId) | |||
| ConsumableWorkbenchPickConstants.resolveExcludeWarehouseCodes( | |||
| userId, | |||
| doFloorSupplierSettingsService.loadJoPickExcludeWarehouseCodes(), | |||
| ) | |||
| } else { | |||
| null | |||
| } | |||
| @@ -106,10 +106,7 @@ open class SuggestedPickLotService( | |||
| val suggestedList: MutableList<SuggestedPickLot> = mutableListOf() | |||
| val holdQtyMap: MutableMap<Long?, BigDecimal?> = request.holdQtyMap | |||
| val excludedWarehouseCodes = setOf<String>( | |||
| "2F-W202-01-00", | |||
| "2F-W200-#A-00", | |||
| ) | |||
| val excludedWarehouseCodes = doFloorSupplierSettingsService.loadDoPickExcludeWarehouseCodes() | |||
| val availableInventoryLotLines = inventoryLotLineService | |||
| .allInventoryLotLinesByItemIdIn(itemIds) | |||
| .filter { it.status == InventoryLotLineStatus.AVAILABLE.value } | |||
| @@ -173,7 +170,7 @@ open class SuggestedPickLotService( | |||
| val warehouseStoreId=inventoryLotLine?.warehouse?.store_id | |||
| // 3F filter only applies to delivery order release; no limit for job order, AssignAndRelease, etc. | |||
| val isDeliveryOrderPick = line.pickOrder?.type?.value == "do" | |||
| val warehouseCodeForExclusion = inventoryLotLine?.warehouse?.code | |||
| val warehouseCodeForExclusion = inventoryLotLine?.warehouse?.code?.trim()?.uppercase(java.util.Locale.ROOT) | |||
| if (isDeliveryOrderPick && | |||
| warehouseCodeForExclusion != null && | |||
| excludedWarehouseCodes.contains(warehouseCodeForExclusion) | |||
| @@ -1,5 +1,6 @@ | |||
| package com.ffii.fpsms.modules.stock.service | |||
| import com.ffii.fpsms.modules.deliveryOrder.service.DoFloorSupplierSettingsService | |||
| import com.ffii.fpsms.modules.pickOrder.entity.PickOrderLine | |||
| import com.ffii.fpsms.modules.pickOrder.entity.PickOrderLineRepository | |||
| import com.ffii.fpsms.modules.stock.entity.InventoryLotLine | |||
| @@ -29,6 +30,7 @@ open class SuggestedPickLotWorkbenchService( | |||
| private val inventoryLotLineRepository: InventoryLotLineRepository, | |||
| private val stockOutLIneRepository: StockOutLIneRepository, | |||
| private val suggestPickLotRepository: SuggestPickLotRepository, | |||
| private val doFloorSupplierSettingsService: DoFloorSupplierSettingsService, | |||
| ) { | |||
| private val log = LoggerFactory.getLogger(SuggestedPickLotWorkbenchService::class.java) | |||
| @@ -126,16 +128,9 @@ open class SuggestedPickLotWorkbenchService( | |||
| } | |||
| /** | |||
| * Backend-default avoid warehouse list for workbench suggestions. | |||
| * - Case-insensitive match against `warehouse.code`. | |||
| * - When request.excludeWarehouseCodes is provided, it overrides this default. | |||
| * | |||
| * Keep it empty if you don't need any default excludes. | |||
| * DO workbench default exclude list comes from settings (`DO.pick.excludeWarehouses`). | |||
| * When request.excludeWarehouseCodes is provided, it overrides that list. | |||
| */ | |||
| private val defaultExcludeWarehouseCodes: Set<String> = setOf( | |||
| "2F-W202-01-00", | |||
| "2F-W200-#A-00", | |||
| ) | |||
| private fun normalizeStoreId(raw: String?): String? { | |||
| val s = raw?.trim()?.uppercase() ?: return null | |||
| if (s.isEmpty()) return null | |||
| @@ -149,7 +144,7 @@ open class SuggestedPickLotWorkbenchService( | |||
| } | |||
| private fun effectiveExcludeWarehouseCodes(raw: List<String>?): Set<String> { | |||
| if (raw == null) return defaultExcludeWarehouseCodes | |||
| if (raw == null) return doFloorSupplierSettingsService.loadDoPickExcludeWarehouseCodes() | |||
| val normalized = raw | |||
| .map { it.trim() } | |||
| .filter { it.isNotEmpty() } | |||
| @@ -0,0 +1,79 @@ | |||
| --liquibase formatted sql | |||
| --changeset fpsms:20260922_auto_putaway | |||
| -- QC accept auto put-away. Warehouse 2F-W200-#A-00 is warehouse id 1141. | |||
| -- Item keyword FA matches item codes that contain FA. | |||
| -- JO also auto put-away when BOM description is WIP or FG. | |||
| -- "-" means that list is empty (condition off, or no fallback warehouse). | |||
| INSERT INTO `settings` (`name`, `value`, `category`, `type`) | |||
| SELECT v.name, v.value, v.category, v.type | |||
| FROM ( | |||
| SELECT 'PO.autoPutAway.warehouse' AS name, | |||
| '2F-W200-#A-00' AS value, | |||
| 'PICK_RULES' AS category, | |||
| 'string' AS type | |||
| ) v | |||
| WHERE NOT EXISTS ( | |||
| SELECT 1 FROM `settings` s WHERE s.name = 'PO.autoPutAway.warehouse' | |||
| ); | |||
| INSERT INTO `settings` (`name`, `value`, `category`, `type`) | |||
| SELECT v.name, v.value, v.category, v.type | |||
| FROM ( | |||
| SELECT 'PO.autoPutAway.itemKeywords' AS name, | |||
| 'FA' AS value, | |||
| 'PICK_RULES' AS category, | |||
| 'string' AS type | |||
| ) v | |||
| WHERE NOT EXISTS ( | |||
| SELECT 1 FROM `settings` s WHERE s.name = 'PO.autoPutAway.itemKeywords' | |||
| ); | |||
| INSERT INTO `settings` (`name`, `value`, `category`, `type`) | |||
| SELECT v.name, v.value, v.category, v.type | |||
| FROM ( | |||
| SELECT 'PO.autoPutAway.bomTypes' AS name, | |||
| '-' AS value, | |||
| 'PICK_RULES' AS category, | |||
| 'string' AS type | |||
| ) v | |||
| WHERE NOT EXISTS ( | |||
| SELECT 1 FROM `settings` s WHERE s.name = 'PO.autoPutAway.bomTypes' | |||
| ); | |||
| INSERT INTO `settings` (`name`, `value`, `category`, `type`) | |||
| SELECT v.name, v.value, v.category, v.type | |||
| FROM ( | |||
| SELECT 'JO.autoPutAway.warehouse' AS name, | |||
| '2F-W200-#A-00' AS value, | |||
| 'PICK_RULES' AS category, | |||
| 'string' AS type | |||
| ) v | |||
| WHERE NOT EXISTS ( | |||
| SELECT 1 FROM `settings` s WHERE s.name = 'JO.autoPutAway.warehouse' | |||
| ); | |||
| INSERT INTO `settings` (`name`, `value`, `category`, `type`) | |||
| SELECT v.name, v.value, v.category, v.type | |||
| FROM ( | |||
| SELECT 'JO.autoPutAway.itemKeywords' AS name, | |||
| 'FA' AS value, | |||
| 'PICK_RULES' AS category, | |||
| 'string' AS type | |||
| ) v | |||
| WHERE NOT EXISTS ( | |||
| SELECT 1 FROM `settings` s WHERE s.name = 'JO.autoPutAway.itemKeywords' | |||
| ); | |||
| INSERT INTO `settings` (`name`, `value`, `category`, `type`) | |||
| SELECT v.name, v.value, v.category, v.type | |||
| FROM ( | |||
| SELECT 'JO.autoPutAway.bomTypes' AS name, | |||
| 'WIP,FG' AS value, | |||
| 'PICK_RULES' AS category, | |||
| 'string' AS type | |||
| ) v | |||
| WHERE NOT EXISTS ( | |||
| SELECT 1 FROM `settings` s WHERE s.name = 'JO.autoPutAway.bomTypes' | |||
| ); | |||
| @@ -0,0 +1,30 @@ | |||
| --liquibase formatted sql | |||
| --changeset fpsms:20260922_auto_putaway_rules | |||
| -- Multiple auto put-away rules. Empty itemKeywords means every item. | |||
| -- locationMode itemLocation uses the item location code. warehouse uses warehouseCode. | |||
| -- First matching rule wins. whitelist puts away. blacklist does not. | |||
| INSERT INTO `settings` (`name`, `value`, `category`, `type`) | |||
| SELECT v.name, v.value, v.category, v.type | |||
| FROM ( | |||
| SELECT 'PO.autoPutAway.rules' AS name, | |||
| '[{"itemKeywords":"FA","bomTypes":"","locationMode":"warehouse","warehouseCode":"2F-W200-#A-00","listMode":"whitelist"}]' AS value, | |||
| 'PICK_RULES' AS category, | |||
| 'string' AS type | |||
| ) v | |||
| WHERE NOT EXISTS ( | |||
| SELECT 1 FROM `settings` s WHERE s.name = 'PO.autoPutAway.rules' | |||
| ); | |||
| INSERT INTO `settings` (`name`, `value`, `category`, `type`) | |||
| SELECT v.name, v.value, v.category, v.type | |||
| FROM ( | |||
| SELECT 'JO.autoPutAway.rules' AS name, | |||
| '[{"itemKeywords":"","bomTypes":"FG","locationMode":"itemLocation","warehouseCode":"2F-W200-#A-00","listMode":"whitelist"},{"itemKeywords":"","bomTypes":"WIP","locationMode":"warehouse","warehouseCode":"2F-W200-#A-00","listMode":"whitelist"}]' AS value, | |||
| 'PICK_RULES' AS category, | |||
| 'string' AS type | |||
| ) v | |||
| WHERE NOT EXISTS ( | |||
| SELECT 1 FROM `settings` s WHERE s.name = 'JO.autoPutAway.rules' | |||
| ); | |||
| @@ -0,0 +1,8 @@ | |||
| --liquibase formatted sql | |||
| --changeset fpsms:20260922_auto_putaway_rules_drop_jo_fa | |||
| -- Job order auto put-away does not include an item-code FA rule. | |||
| UPDATE `settings` | |||
| SET `value` = '[{"itemKeywords":"","bomTypes":"FG","locationMode":"itemLocation","warehouseCode":"2F-W200-#A-00","listMode":"whitelist"},{"itemKeywords":"","bomTypes":"WIP","locationMode":"warehouse","warehouseCode":"2F-W200-#A-00","listMode":"whitelist"}]' | |||
| WHERE `name` = 'JO.autoPutAway.rules' | |||
| AND `value` = '[{"itemKeywords":"","bomTypes":"FG","locationMode":"itemLocation","warehouseCode":"2F-W200-#A-00","listMode":"whitelist"},{"itemKeywords":"","bomTypes":"WIP","locationMode":"warehouse","warehouseCode":"2F-W200-#A-00","listMode":"whitelist"},{"itemKeywords":"FA","bomTypes":"","locationMode":"warehouse","warehouseCode":"2F-W200-#A-00","listMode":"whitelist"}]'; | |||
| @@ -0,0 +1,28 @@ | |||
| --liquibase formatted sql | |||
| --changeset fpsms:20260922_pick_exclude_print_mode | |||
| -- hideList: print modal omits these warehouses. hideQr: list them but hide QR. | |||
| INSERT INTO `settings` (`name`, `value`, `category`, `type`) | |||
| SELECT v.name, v.value, v.category, v.type | |||
| FROM ( | |||
| SELECT 'DO.pick.excludePrintMode' AS name, | |||
| 'hideList' AS value, | |||
| 'PICK_RULES' AS category, | |||
| 'string' AS type | |||
| ) v | |||
| WHERE NOT EXISTS ( | |||
| SELECT 1 FROM `settings` s WHERE s.name = 'DO.pick.excludePrintMode' | |||
| ); | |||
| INSERT INTO `settings` (`name`, `value`, `category`, `type`) | |||
| SELECT v.name, v.value, v.category, v.type | |||
| FROM ( | |||
| SELECT 'JO.pick.excludePrintMode' AS name, | |||
| 'hideList' AS value, | |||
| 'PICK_RULES' AS category, | |||
| 'string' AS type | |||
| ) v | |||
| WHERE NOT EXISTS ( | |||
| SELECT 1 FROM `settings` s WHERE s.name = 'JO.pick.excludePrintMode' | |||
| ); | |||
| @@ -0,0 +1,29 @@ | |||
| --liquibase formatted sql | |||
| --changeset fpsms:20260922_pick_exclude_warehouses | |||
| -- DO / JO pick exclude warehouse codes (comma-separated). "-" means exclude nothing. | |||
| -- Missing row falls back to the same defaults in DoFloorSupplierSettingsService. | |||
| INSERT INTO `settings` (`name`, `value`, `category`, `type`) | |||
| SELECT v.name, v.value, v.category, v.type | |||
| FROM ( | |||
| SELECT 'DO.pick.excludeWarehouses' AS name, | |||
| '2F-W202-01-00,2F-W200-#A-00' AS value, | |||
| 'PICK_RULES' AS category, | |||
| 'string' AS type | |||
| ) v | |||
| WHERE NOT EXISTS ( | |||
| SELECT 1 FROM `settings` s WHERE s.name = 'DO.pick.excludeWarehouses' | |||
| ); | |||
| INSERT INTO `settings` (`name`, `value`, `category`, `type`) | |||
| SELECT v.name, v.value, v.category, v.type | |||
| FROM ( | |||
| SELECT 'JO.pick.excludeWarehouses' AS name, | |||
| '4F-W402-01-00,4F-W402-02-00,4F-W402-03-00,4F-W402-04-00,4F-W402-05-00,4F-W402-#A-00,4F-W402-#B-00,4F-W402-#C-00,4F-W402-#D-00,4F-W402-#E-00,4F-W402-#F-00,4F-W402-#G-00,4F-W402-#H-00,4F-W402-#I-00,4F-W402-#J-00,4F-W402-#K-00,4F-W402-#L-00,4F-W402-#M-00,4F-W402-#N-00,4F-W402-#O-00,4F-W402-#P-00,4F-W402-#Q-00,4F-W402-#R-00,4F-W402-#S-00' AS value, | |||
| 'PICK_RULES' AS category, | |||
| 'string' AS type | |||
| ) v | |||
| WHERE NOT EXISTS ( | |||
| SELECT 1 FROM `settings` s WHERE s.name = 'JO.pick.excludeWarehouses' | |||
| ); | |||