diff --git a/FS25_ADSmartPickup/adLoadSwap.lua b/FS25_ADSmartPickup/adLoadSwap.lua
index 19411a4..d53424c 100644
--- a/FS25_ADSmartPickup/adLoadSwap.lua
+++ b/FS25_ADSmartPickup/adLoadSwap.lua
@@ -139,6 +139,30 @@ function ADLoadSwap.findDumpMarker(adEnv, vehicle, fillType, rest, homeWayPointI
return nil, string.format("%d silo(er) tager varen, men AutoDrive finder ingen rute fra traktoren til dem", withSpace)
end
+-- Tvunget lagermål (flowets "Lager til"): kun de valgte aflæsningsmarkører, i rækkefølge. Første der
+-- tager varen, har plads til læsset og kan nås, vinder. Returnerer marker — eller nil + forklaring.
+function ADLoadSwap.findForcedDumpMarker(adEnv, vehicle, fillType, liters, markerIds, isReachable)
+ local farmId = ADSmartPickup.getFarmId(vehicle)
+ local reasons = {}
+ for _, wayPointId in ipairs(markerIds or {}) do
+ local marker = adEnv.ADGraphManager:getMapMarkerByWayPointId(wayPointId)
+ local station = marker ~= nil and ADSmartPickup.getUnloadStationAtWayPointId(adEnv, wayPointId, farmId) or nil
+ local name = marker ~= nil and tostring(marker.name) or tostring(wayPointId)
+ if station == nil then
+ table.insert(reasons, name .. ": ingen aflæsning ved markøren")
+ elseif station.getIsFillTypeSupported ~= nil and not station:getIsFillTypeSupported(fillType) then
+ table.insert(reasons, name .. ": tager ikke varen")
+ elseif not acceptsRest(station, fillType, liters, farmId) then
+ table.insert(reasons, name .. ": fuld")
+ elseif not isReachable(wayPointId) then
+ table.insert(reasons, name .. ": ingen vej")
+ else
+ return marker
+ end
+ end
+ return nil, "valgt lager: " .. (#reasons > 0 and table.concat(reasons, "; ") or "ingen markør")
+end
+
-- Kaldes fra Wait-logikken hvert tick; redirect(task, id) og release(vehicle) er dens egne.
-- true = traktoren er sendt til en silo (Wait-punktet er frigivet).
function ADLoadSwap.tryFromWait(adEnv, task, wait, dt, redirect, release, isReachable)
diff --git a/FS25_ADSmartPickup/adOutbound.lua b/FS25_ADSmartPickup/adOutbound.lua
index cdb88df..a2f864c 100644
--- a/FS25_ADSmartPickup/adOutbound.lua
+++ b/FS25_ADSmartPickup/adOutbound.lua
@@ -179,7 +179,7 @@ local function findSellQuote(adEnv, vehicle, product, sellMarkers, sellAtPercent
return quotes[1], nil
end
-local function findStoreMarker(adEnv, vehicle, product, capacity, sources, litersOverride)
+local function findStoreMarker(adEnv, vehicle, product, capacity, sources, litersOverride, run)
local isReachable = function(markerId)
return ADWaitPool.hasRouteTo(adEnv, vehicle, markerId)
end
@@ -200,6 +200,16 @@ local function findStoreMarker(adEnv, vehicle, product, capacity, sources, liter
end
end
local liters = math.max(1, math.min(available, capacity or available))
+ -- "Lager til" valgt i flowet: KUN de bygninger (fx gylle -> BGA, aldrig AIO-fabrikken)
+ local forced = run ~= nil and ADRunsController ~= nil and ADRunsController.getStoreMarkers ~= nil and ADRunsController.getStoreMarkers(adEnv, run) or {}
+ if #forced > 0 and ADLoadSwap.findForcedDumpMarker ~= nil then
+ local chosen, why = ADLoadSwap.findForcedDumpMarker(adEnv, vehicle, product.fillType, liters, forced, isReachable)
+ if chosen == nil then
+ warnOnce(adEnv, vehicle, "forced:" .. tostring(product.name) .. ":" .. tostring(why), string.format("'%s': %s kan ikke læsses af — %s", tostring(ADRuns.vehicleLabel(vehicle)), tostring(product.name), tostring(why)))
+ return nil
+ end
+ return {wayPointId = chosen.id, name = tostring(chosen.name), free = liters}
+ end
local avoid = ADRunsController ~= nil and ADRunsController.getPickupMarkers ~= nil and ADRunsController.getPickupMarkers() or nil
local marker = ADLoadSwap.findDumpMarker(adEnv, vehicle, product.fillType, liters, nil, isReachable, ADSources.getSourcePlaceables(sources), avoid, true)
if marker == nil then
@@ -293,7 +303,7 @@ function ADOutbound.buildPlanInput(adEnv, run, vehicle)
end
end
if wantsStore(product.policy) then
- product.store = findStoreMarker(adEnv, vehicle, product, capacity, sources)
+ product.store = findStoreMarker(adEnv, vehicle, product, capacity, sources, nil, run)
end
end
local input = {
@@ -331,7 +341,7 @@ function ADOutbound.planCargoTarget(adEnv, run, vehicle, fillType, liters)
end
end
if wantsStore(policy) then
- local store = findStoreMarker(adEnv, vehicle, product, capacity, sources, liters or capacity)
+ local store = findStoreMarker(adEnv, vehicle, product, capacity, sources, liters or capacity, run)
if store ~= nil then
return store.wayPointId
end
diff --git a/FS25_ADSmartPickup/adRuns.lua b/FS25_ADSmartPickup/adRuns.lua
index 3310505..0007b24 100644
--- a/FS25_ADSmartPickup/adRuns.lua
+++ b/FS25_ADSmartPickup/adRuns.lua
@@ -47,6 +47,7 @@ local function copyRun(run)
sourceWayPointIds = copyList(run.sourceWayPointIds),
sourceBuildingIds = copyList(run.sourceBuildingIds),
targetBuildingIds = copyList(run.targetBuildingIds),
+ storeBuildingIds = copyList(run.storeBuildingIds),
products = copyProducts(run.products),
sellAtPercent = run.sellAtPercent,
storeAbovePercent = run.storeAbovePercent,
@@ -93,6 +94,7 @@ function ADRuns.createRun(store, name, kind)
sourceWayPointIds = {},
sourceBuildingIds = {},
targetBuildingIds = {},
+ storeBuildingIds = {},
products = {},
sellAtPercent = ADRuns.DEFAULT_SELL_AT_PERCENT,
storeAbovePercent = ADRuns.DEFAULT_STORE_ABOVE_PERCENT,
@@ -158,6 +160,7 @@ function ADRuns.updateRun(store, runId, changes)
if changes.sourceWayPointIds ~= nil then run.sourceWayPointIds = copyList(changes.sourceWayPointIds) end
if changes.sourceBuildingIds ~= nil then run.sourceBuildingIds = copyList(changes.sourceBuildingIds) end
if changes.targetBuildingIds ~= nil then run.targetBuildingIds = copyList(changes.targetBuildingIds) end
+ if changes.storeBuildingIds ~= nil then run.storeBuildingIds = copyList(changes.storeBuildingIds) end
if changes.products ~= nil then run.products = copyProducts(changes.products) end
if changes.sellAtPercent ~= nil then run.sellAtPercent = clampRange(changes.sellAtPercent, ADRuns.DEFAULT_SELL_AT_PERCENT, ADRuns.MIN_SELL_AT_PERCENT, ADRuns.MAX_SELL_AT_PERCENT) end
if changes.storeAbovePercent ~= nil then run.storeAbovePercent = clampRange(changes.storeAbovePercent, ADRuns.DEFAULT_STORE_ABOVE_PERCENT, ADRuns.MIN_STORE_ABOVE_PERCENT, ADRuns.MAX_STORE_ABOVE_PERCENT) end
@@ -182,6 +185,13 @@ function ADRuns.toggleSourceBuilding(store, runId, buildingId)
return ADRuns.updateRun(store, runId, {sourceBuildingIds = toggleInList(run.sourceBuildingIds, buildingId)})
end
+-- Tvunget lagermål for udkørsel: lagerture kører KUN til disse bygninger (tom = moden vælger selv).
+function ADRuns.toggleStoreBuilding(store, runId, buildingId)
+ local run = ADRuns.getRun(store, runId)
+ if run == nil then return copyStore(store) end
+ return ADRuns.updateRun(store, runId, {storeBuildingIds = toggleInList(run.storeBuildingIds, buildingId)})
+end
+
function ADRuns.toggleTargetBuilding(store, runId, buildingId)
local run = ADRuns.getRun(store, runId)
if run == nil then return copyStore(store) end
@@ -350,6 +360,7 @@ function ADRuns.toRecords(store)
sources = table.concat(run.sourceWayPointIds or {}, " "),
sourceBuildings = table.concat(run.sourceBuildingIds or {}, " "),
targetBuildings = table.concat(run.targetBuildingIds or {}, " "),
+ storeBuildings = table.concat(run.storeBuildingIds or {}, " "),
products = ADRuns.serializeProducts(run.products),
sellAt = run.sellAtPercent,
storeAbove = run.storeAbovePercent,
@@ -392,6 +403,7 @@ function ADRuns.fromRecords(records)
sourceWayPointIds = sources,
sourceBuildingIds = parseIds(record.sourceBuildings),
targetBuildingIds = parseIds(record.targetBuildings),
+ storeBuildingIds = parseIds(record.storeBuildings),
products = ADRuns.parseProducts(record.products),
sellAtPercent = clampRange(record.sellAt, ADRuns.DEFAULT_SELL_AT_PERCENT, ADRuns.MIN_SELL_AT_PERCENT, ADRuns.MAX_SELL_AT_PERCENT),
storeAbovePercent = clampRange(record.storeAbove, ADRuns.DEFAULT_STORE_ABOVE_PERCENT, ADRuns.MIN_STORE_ABOVE_PERCENT, ADRuns.MAX_STORE_ABOVE_PERCENT),
diff --git a/FS25_ADSmartPickup/adRunsController.lua b/FS25_ADSmartPickup/adRunsController.lua
index bf99e82..4f01827 100644
--- a/FS25_ADSmartPickup/adRunsController.lua
+++ b/FS25_ADSmartPickup/adRunsController.lua
@@ -147,6 +147,20 @@ function ADRunsController.getPickupMarkers()
return result
end
+-- "Lager til": aflæsningsmarkører for flowets valgte lagerbygninger, i valgt rækkefølge (tom = ingen tvang).
+function ADRunsController.getStoreMarkers(adEnv, run)
+ local result = {}
+ if ADBuildings == nil or run == nil then return result end
+ local farmId = g_currentMission ~= nil and g_currentMission.getFarmId ~= nil and g_currentMission:getFarmId() or 1
+ for _, buildingId in ipairs(run.storeBuildingIds or {}) do
+ local building = ADBuildings.find(adEnv, farmId, buildingId)
+ if building ~= nil and building.unloadMarker ~= nil then
+ table.insert(result, building.unloadMarker.id)
+ end
+ end
+ return result
+end
+
-- ---- køretøjsnavne ----
ADRunsController.NAME_INTERVAL_MS = 2000
diff --git a/FS25_ADSmartPickup/adRunsStorage.lua b/FS25_ADSmartPickup/adRunsStorage.lua
index 4b72c64..3242057 100644
--- a/FS25_ADSmartPickup/adRunsStorage.lua
+++ b/FS25_ADSmartPickup/adRunsStorage.lua
@@ -42,6 +42,7 @@ function ADRunsStorage.writeRecords(xmlFile, records)
-- v3: bygninger (flow-tavlen)
xmlFile:setString(key .. "#sourceBuildings", record.sourceBuildings or "")
xmlFile:setString(key .. "#targetBuildings", record.targetBuildings or "")
+ xmlFile:setString(key .. "#storeBuildings", record.storeBuildings or "")
xmlFile:setString(key .. "#products", record.products or "")
xmlFile:setInt(key .. "#sellAt", record.sellAt or ADRuns.DEFAULT_SELL_AT_PERCENT)
xmlFile:setInt(key .. "#storeAbove", record.storeAbove or ADRuns.DEFAULT_STORE_ABOVE_PERCENT)
@@ -69,6 +70,7 @@ function ADRunsStorage.readRecords(xmlFile)
sources = xmlFile:getString(key .. "#sources"),
sourceBuildings = xmlFile:getString(key .. "#sourceBuildings"),
targetBuildings = xmlFile:getString(key .. "#targetBuildings"),
+ storeBuildings = xmlFile:getString(key .. "#storeBuildings"),
products = xmlFile:getString(key .. "#products"),
sellAt = xmlFile:getInt(key .. "#sellAt"),
storeAbove = xmlFile:getInt(key .. "#storeAbove"),
diff --git a/FS25_ADSmartPickup/gui/SmartPickupFlowTab.lua b/FS25_ADSmartPickup/gui/SmartPickupFlowTab.lua
index 935ca66..ec35f84 100644
--- a/FS25_ADSmartPickup/gui/SmartPickupFlowTab.lua
+++ b/FS25_ADSmartPickup/gui/SmartPickupFlowTab.lua
@@ -122,22 +122,25 @@ function SmartPickupFrame:fillBuildingRows(adEnv, run, role)
return
end
local chosen = {}
- local ids = role == "load" and run.sourceBuildingIds or run.targetBuildingIds
+ local ids = role == "load" and run.sourceBuildingIds or role == "store" and run.storeBuildingIds or run.targetBuildingIds
+ local rowKind = role == "load" and "sourceBuilding" or role == "store" and "storeBuilding" or "targetBuilding"
for _, id in ipairs(ids or {}) do chosen[id] = true end
local count = 0
for _, building in ipairs(ADBuildings.list(adEnv, g_currentMission:getFarmId())) do
+ -- "Lager til": siloer og gårdens fabrikker (fx BGA) — markøren er bygningens aflæsningsmarkør
local fits = (role == "load" and building.canBeSource) or (role == "unload" and building.canBeTarget)
+ or (role == "store" and (building.kind == "silo" or building.kind == "factory"))
if fits then
count = count + 1
- local row = self:addRow(self.ingredientPrefab, {kind = role == "load" and "sourceBuilding" or "targetBuilding", buildingId = building.id})
- row:getDescendantByName("title"):setText(self:describeBuilding(building, role))
+ local row = self:addRow(self.ingredientPrefab, {kind = rowKind, buildingId = building.id})
+ row:getDescendantByName("title"):setText(self:describeBuilding(building, role == "store" and "unload" or role))
row:getDescendantByName("option"):setState(chosen[building.id] and SmartPickupFrame.STATE_ON or SmartPickupFrame.STATE_OFF, false)
end
end
-- valgte bygninger der ikke længere findes (solgt)
for _, id in ipairs(ids or {}) do
if ADBuildings.find(adEnv, g_currentMission:getFarmId(), id) == nil then
- local row = self:addRow(self.ingredientPrefab, {kind = role == "load" and "sourceBuilding" or "targetBuilding", buildingId = id})
+ local row = self:addRow(self.ingredientPrefab, {kind = rowKind, buildingId = id})
row:getDescendantByName("title"):setText(string.format(" %s — %s", tostring(id), self.i18n:getText("spu_buildingGone")))
row:getDescendantByName("option"):setState(SmartPickupFrame.STATE_ON, false)
end
@@ -162,6 +165,8 @@ function SmartPickupFrame:rebuildFlowDetail(adEnv, run)
self.sellAtOption:setState(SmartPickupFrame.getStepIndex(self.sellAtSteps, run.sellAtPercent, ADRuns.DEFAULT_SELL_AT_PERCENT), false)
self.storeAboveOption:setState(SmartPickupFrame.getStepIndex(self.storeAboveSteps, run.storeAbovePercent, ADRuns.DEFAULT_STORE_ABOVE_PERCENT), false)
self.collectRoundRowOption = self:addToggleRow("collectRound", self.i18n:getText("spu_collectRound"), {self.i18n:getText("ui_no"), self.i18n:getText("ui_yes")}, run.collectRound == true)
+ self:addTextRow(self.i18n:getText("spu_section_store"))
+ self:fillBuildingRows(adEnv, run, "store")
self:fillOutboundStatusRows(adEnv, run)
else
self:addTextRow(self.i18n:getText("spu_section_to"))
@@ -193,6 +198,12 @@ function SmartPickupFrame:onClickBuilding(state, element)
if run == nil or data == nil then return end
if data.kind == "sourceBuilding" then
ADRunsController.store = ADRuns.toggleSourceBuilding(ADRunsController.store, run.id, data.buildingId)
+ elseif data.kind == "storeBuilding" then
+ ADRunsController.store = ADRuns.toggleStoreBuilding(ADRunsController.store, run.id, data.buildingId)
+ local updatedStore = ADRuns.getRun(ADRunsController.store, run.id)
+ Logging.info(SmartPickupFrame.LOG_PREFIX .. "run '%s': storeBuildings=%s", tostring(run.name), table.concat(updatedStore.storeBuildingIds or {}, " "))
+ frame:requestRebuild()
+ return
else
ADRunsController.store = ADRuns.toggleTargetBuilding(ADRunsController.store, run.id, data.buildingId)
end
diff --git a/FS25_ADSmartPickup/gui/SmartPickupFrame.lua b/FS25_ADSmartPickup/gui/SmartPickupFrame.lua
index c48fb69..4cfb039 100644
--- a/FS25_ADSmartPickup/gui/SmartPickupFrame.lua
+++ b/FS25_ADSmartPickup/gui/SmartPickupFrame.lua
@@ -678,7 +678,7 @@ function SmartPickupFrame:onClickIngredient(state, element)
return self:onClickCollectRound(state)
elseif data ~= nil and data.kind == "direction" then
return self:onClickDirection(state)
- elseif data ~= nil and (data.kind == "sourceBuilding" or data.kind == "targetBuilding") then
+ elseif data ~= nil and (data.kind == "sourceBuilding" or data.kind == "targetBuilding" or data.kind == "storeBuilding") then
return self:onClickBuilding(state, element)
end
self:safe(function(frame)
diff --git a/FS25_ADSmartPickup/l10n/l10n_da.xml b/FS25_ADSmartPickup/l10n/l10n_da.xml
index 2b37676..637fcb7 100644
--- a/FS25_ADSmartPickup/l10n/l10n_da.xml
+++ b/FS25_ADSmartPickup/l10n/l10n_da.xml
@@ -134,6 +134,7 @@
+
diff --git a/FS25_ADSmartPickup/l10n/l10n_en.xml b/FS25_ADSmartPickup/l10n/l10n_en.xml
index 2327125..fa93ab3 100644
--- a/FS25_ADSmartPickup/l10n/l10n_en.xml
+++ b/FS25_ADSmartPickup/l10n/l10n_en.xml
@@ -134,6 +134,7 @@
+
diff --git a/FS25_ADSmartPickup/modDesc.xml b/FS25_ADSmartPickup/modDesc.xml
index 5d9d242..cf71c4f 100644
--- a/FS25_ADSmartPickup/modDesc.xml
+++ b/FS25_ADSmartPickup/modDesc.xml
@@ -1,7 +1,7 @@
masterdraco
- 1.17.7.0
+ 1.18.0.0
AD Smart Pickup
diff --git a/docs/BRUGERMANUAL.md b/docs/BRUGERMANUAL.md
index b617131..e4fdcf2 100644
--- a/docs/BRUGERMANUAL.md
+++ b/docs/BRUGERMANUAL.md
@@ -1,6 +1,6 @@
# AD Smart Pickup — brugermanual
-Version 1.17.7 · Farming Simulator 25 · tillæg til AutoDrive
+Version 1.18.0 · Farming Simulator 25 · tillæg til AutoDrive
*English version: [USER_MANUAL.md](USER_MANUAL.md)*
@@ -298,6 +298,15 @@ Bulkvarer kræver en tipvogn eller tank. Pallevarer kræver en vogn med Universa
| **Kør til lager over** | Lager-varer køres væk, når kilden er fyldt over denne andel | 50–95 %, standard 80 % |
| **Saml op på én tur** | Opsamlingsrunde, se nedenfor | Ja/Nej |
+### Lager til: tvunget aflæsningssted
+
+Under reglerne står afsnittet **Lager til** med gårdens siloer og egne fabrikker. Vælger du én eller
+flere, kører lagerture kun dertil. Den første i listen, der tager varen, har plads og kan nås, får
+læsset. Et eksempel er gylle, der kun må til biogasanlægget og aldrig til AIO-fabrikken. Kan ingen af
+de valgte tage læsset, venter traktoren ved Wait, og loggen skriver hvorfor, fx *valgt lager: BGA500kw:
+fuld*. Vælger du ingen, finder moden selv den nærmeste silo eller egne fabrik, der bruger varen.
+Paller følger stadig reglen om nærmeste palleskur.
+
### Sådan vælges hver tur
Før hver pålæsning, og hvert femte sekund mens traktoren holder, vurderer moden:
diff --git a/docs/USER_MANUAL.md b/docs/USER_MANUAL.md
index 3929968..cba1d69 100644
--- a/docs/USER_MANUAL.md
+++ b/docs/USER_MANUAL.md
@@ -1,6 +1,6 @@
# AD Smart Pickup — user manual
-Version 1.17.7 · Farming Simulator 25 · add-on for AutoDrive
+Version 1.18.0 · Farming Simulator 25 · add-on for AutoDrive
*Dansk version: [BRUGERMANUAL.md](BRUGERMANUAL.md)*
@@ -302,6 +302,15 @@ Bulk goods need a tipper or tank. Pallet goods need a trailer with Universal Aut
| **Drive to storage above** | Store goods are moved when the source is filled above this share | 50–95 %, default 80 % |
| **Collect in one trip** | Collection round, see below | Yes/No |
+### Store to: forced drop-off
+
+Below the rules there is a **Store to** section listing the farm's silos and own factories. If you
+pick one or more, storage trips only go there. The first one in the list that accepts the good, has
+room and can be reached gets the load. An example is slurry that must only go to the biogas plant and
+never to the AIO factory. If none of the chosen buildings can take the load, the tractor waits at
+Wait, and the log says why, such as *valgt lager: BGA500kw: fuld*. If you pick none, the mod finds the
+nearest silo or own factory that uses the good by itself. Pallets still follow the nearest pallet shed rule.
+
### How each trip is chosen
Before each loading, and every five seconds while the tractor waits, the mod checks:
diff --git a/tests/test_adLoadSwap.lua b/tests/test_adLoadSwap.lua
index 23420d9..ee25b0f 100644
--- a/tests/test_adLoadSwap.lua
+++ b/tests/test_adLoadSwap.lua
@@ -44,5 +44,30 @@ check("LS2 fremmed anlæg aldrig", L.isStoreTarget({owningPlaceable = foreignBio
check("LS2 hjemmet selv aldrig", L.isStoreTarget({owningPlaceable = biogas}, SLURRY, {owningPlaceable = biogas}, true), false)
g_currentMission = nil
+-- LS3: tvunget lagermål — kun de valgte markører, i rækkefølge; første der tager varen, har plads og kan nås
+do
+ local stations = {
+ [101] = {accepts = {[SLURRY] = true}, free = 0}, -- BGA fuld
+ [102] = {accepts = {[SLURRY] = true}, free = 900000}, -- gylletank
+ [103] = {accepts = {[MILK] = true}, free = 900000}, -- tager ikke gylle
+ }
+ for _, st in pairs(stations) do
+ st.getIsFillTypeSupported = function(self, ft) return self.accepts[ft] == true end
+ st.getFreeCapacity = function(self) return self.free end
+ end
+ ADSmartPickup = {getFarmId = function() return 1 end, getUnloadStationAtWayPointId = function(_, id) return stations[id] end}
+ local adEnv = {ADGraphManager = {getMapMarkerByWayPointId = function(_, id) return {id = id, name = "m" .. id} end}}
+ local vehicle = {}
+ local all = function() return true end
+ check("LS3 fuld BGA springes over -> tank", L.findForcedDumpMarker(adEnv, vehicle, SLURRY, 20000, {101, 102}, all).id, 102)
+ stations[101].free = 500000
+ check("LS3 BGA med plads vælges først", L.findForcedDumpMarker(adEnv, vehicle, SLURRY, 20000, {101, 102}, all).id, 101)
+ local none, why = L.findForcedDumpMarker(adEnv, vehicle, SLURRY, 20000, {103}, all)
+ check("LS3 intet valgt mål tager varen -> nil", none, nil)
+ check("LS3 forklaring", why ~= nil and why:find("valgt", 1, true) ~= nil, true)
+ check("LS3 ikke nåelig -> næste", L.findForcedDumpMarker(adEnv, vehicle, SLURRY, 20000, {101, 102}, function(id) return id ~= 101 end).id, 102)
+ ADSmartPickup = nil
+end
+
print(failures == 0 and "\nALLE TESTS OK" or ("\n" .. failures .. " FEJL"))
os.exit(failures == 0 and 0 or 1)
diff --git a/tests/test_adOutbound.lua b/tests/test_adOutbound.lua
index 1af7729..53a14ba 100644
--- a/tests/test_adOutbound.lua
+++ b/tests/test_adOutbound.lua
@@ -582,5 +582,22 @@ do
sources[31], sources[32], products[31], products[32] = nil, nil, nil, nil
end
+-- O-forced: "Lager til" valgt -> lagerturen går til det valgte mål, ikke moden egen silo-valg
+do
+ sources[41] = {kind = "silo", wayPointId = 41, station = {name = "gylleSilo"}, name = "Gyllesilo"}
+ products[41] = {{fillType = SUGAR, name = "SUGAR", title = "Sukker", available = 11000000, level = 11000000, capacity = 12000000, isPallet = false}}
+ local vF = newVehicle("Gyllevogn", 0, 0, 20000)
+ runs[vF] = outboundRun(79, {41}, {{fillType = "SUGAR", policy = "store"}})
+ runs[vF].forcedMarkers = {31}
+ ADRunsController.getStoreMarkers = function(_, run) return run.forcedMarkers or {} end
+ local forcedCalls = 0
+ ADLoadSwap.findForcedDumpMarker = function(_, _, _, _, ids) forcedCalls = forcedCalls + 1 return {id = ids[1], name = "BGA", markerIndex = 99} end
+ dumpMarker = mStore
+ ADOutbound.choosePickup(adEnv, vF)
+ check("O-forced valgt mål brugt", forcedCalls > 0, true)
+ check("O-forced tur-mål = BGA", ADOutbound.getTripFor(vF) and ADOutbound.getTripFor(vF).targetWayPointId, 31)
+ ADRunsController.getStoreMarkers, ADLoadSwap.findForcedDumpMarker = nil, nil
+end
+
print(failures == 0 and "\nALLE TESTS OK" or ("\n" .. failures .. " FEJL"))
os.exit(failures == 0 and 0 or 1)
diff --git a/tests/test_adRuns.lua b/tests/test_adRuns.lua
index b7652ee..c2db6a5 100644
--- a/tests/test_adRuns.lua
+++ b/tests/test_adRuns.lua
@@ -307,5 +307,20 @@ do
check("RN ukendt køretøj", names.v9, nil)
end
+-- RS: tvunget lagermål — storeBuildingIds (toggle, kopi, records roundtrip)
+do
+ local store, run = ADRuns.createRun(ADRuns.newStore(), "Slurry", "outbound")
+ check("RS tom som standard", #run.storeBuildingIds, 0)
+ store = ADRuns.toggleStoreBuilding(store, run.id, "BGA500kw")
+ store = ADRuns.toggleStoreBuilding(store, run.id, "tank2")
+ check("RS to valgt", table.concat(ADRuns.getRun(store, run.id).storeBuildingIds, ","), "BGA500kw,tank2")
+ store = ADRuns.toggleStoreBuilding(store, run.id, "tank2")
+ check("RS fravalgt igen", table.concat(ADRuns.getRun(store, run.id).storeBuildingIds, ","), "BGA500kw")
+ local records = ADRuns.toRecords(store)
+ check("RS record", records[1].storeBuildings, "BGA500kw")
+ local back = ADRuns.fromRecords(records)
+ check("RS roundtrip", table.concat(ADRuns.getRun(back, run.id).storeBuildingIds, ","), "BGA500kw")
+end
+
print(failures == 0 and "\nALLE TESTS OK" or ("\n" .. failures .. " FEJL"))
os.exit(failures == 0 and 0 or 1)
diff --git a/tests/test_adRunsController.lua b/tests/test_adRunsController.lua
index da20080..b05a6e7 100644
--- a/tests/test_adRunsController.lua
+++ b/tests/test_adRunsController.lua
@@ -700,6 +700,14 @@ local rowIn = ADRunsController.getFlowRow(adEnv, ADRuns.getRun(ADRunsController.
check("CF5 forsyning fra = alle siloer", rowIn.fromAllSilos, true)
check("CF5 forsyning til", table.concat(rowIn.toNames, ","), "Kostald 1,Sukkerfabrik")
check("CF5 forsyning auto-varer", rowIn.productAuto, true)
+-- SB: "Lager til" — valgte bygninger -> deres aflæsningsmarkører (i valgt rækkefølge; ukendte udelades)
+do
+ local sbRun = ADRunsController.createRun("Gylle", "outbound")
+ ADRunsController.updateRun(sbRun.id, {storeBuildingIds = {"mill", "findes-ikke", "barn1"}})
+ check("SB markører", table.concat(ADRunsController.getStoreMarkers(adEnv, ADRuns.getRun(ADRunsController.store, sbRun.id)), ","), "77,94")
+ ADRunsController.updateRun(sbRun.id, {storeBuildingIds = {}})
+ check("SB tom = ingen tvang", #ADRunsController.getStoreMarkers(adEnv, ADRuns.getRun(ADRunsController.store, sbRun.id)), 0)
+end
ADBuildings = nil; ADSources = nil; ADOutbound = nil
-- RC-navne: køretøjer får AD-førernavn efter forløbet; ude af forløb -> modelnavnet igen