feat: v1.12.1.0 — prisregel via sæsonkurven, reserve til gårdens eget forbrug, paller til palleskur, fanen Lagre (idéer fra Distribution Redux)

This commit is contained in:
2026-09-23 10:34:41 +02:00
parent 43483bda76
commit 2d6e6dd7bf
16 changed files with 595 additions and 43 deletions
+29 -2
View File
@@ -8,7 +8,8 @@
-- products = { { fillType, name, policy = "store"|"sell"|"sellElseStore", isPallet,
-- sources = { {wayPointId, available, level, capacity} ... }, -- kilder med varen
-- quote = {percent, price, wayPointId, stationName} | nil, -- bedste NÅELIGE salgssted
-- store = {wayPointId, name, free} | nil } ... } } -- silo med plads, nåelig
-- store = {wayPointId, name, free} | nil, -- silo/palleskur med plads, nåelig
-- reserve = liter | nil, reservedFor = {navne} | nil } ... } } -- gårdens eget behov (sælges ikke)
-- plan(input) -> trip | nil, reasons
-- trip = {kind = "sell"|"store", fillTypes, names, sourceIds, targetWayPointId, targetName, percent}
-- reasons = én linje pr. vare der ikke gav en tur ("SUGAR 78 % af top (kræver 90), lager 41 %")
@@ -43,8 +44,11 @@ end
function ADOutboundPlanner.evaluate(product, input)
local available, storagePercent = sumSources(product)
local percent = product.quote ~= nil and product.quote.percent or nil
local reserve = math.max(0, product.reserve or 0)
return {
available = available,
reserve = reserve,
sellable = math.max(0, available - reserve),
fullLoad = input.capacity ~= nil and input.capacity > 0 and available >= input.capacity,
storagePercent = storagePercent,
storageHigh = storagePercent >= (input.storeAbovePercent or 80),
@@ -53,10 +57,33 @@ function ADOutboundPlanner.evaluate(product, input)
}
end
local function formatLiters(liters)
if liters >= 1000000 then
return string.format("%.1fM", liters / 1000000)
elseif liters >= 1000 then
return string.format("%.1fk", liters / 1000)
end
return string.format("%d", liters)
end
-- Salg må ikke røre reserven: enten er intet reserveret, eller et helt læs kan tages uden om den
-- (AutoDrive læsser altid vognen fuld, så en delvis mængde kan ikke styres).
local function canSellWithoutReserve(e, input)
if e.reserve <= 0 then
return e.sellable >= ADOutboundPlanner.MIN_AVAILABLE_LITERS
end
local capacity = input.capacity or 0
return e.sellable >= ADOutboundPlanner.MIN_AVAILABLE_LITERS and (capacity <= 0 or e.sellable >= capacity)
end
local function reasonFor(product, e, input)
if e.available < ADOutboundPlanner.MIN_AVAILABLE_LITERS then
return product.name .. ": intet på lager"
end
if wantsSell(product.policy) and e.priceOk and product.quote ~= nil and not canSellWithoutReserve(e, input) then
local names = table.concat(product.reservedFor or {}, ", ")
return string.format("%s: %s reserveret til %s", product.name, formatLiters(e.reserve), names ~= "" and names or "gårdens eget forbrug")
end
local canStore = wantsStore(product.policy) and product.store ~= nil
if wantsSell(product.policy) and product.quote == nil and not canStore then
return product.name .. ": intet nåeligt salgssted"
@@ -79,7 +106,7 @@ local function collectCandidates(input)
local e = ADOutboundPlanner.evaluate(product, input)
local added = false
if e.available >= ADOutboundPlanner.MIN_AVAILABLE_LITERS then
if wantsSell(product.policy) and e.priceOk and product.quote ~= nil then
if wantsSell(product.policy) and e.priceOk and product.quote ~= nil and canSellWithoutReserve(e, input) then
table.insert(sell, {product = product, e = e, kind = "sell", targetWayPointId = product.quote.wayPointId, targetName = product.quote.stationName, percent = e.percent})
added = true
elseif wantsStore(product.policy) and (e.fullLoad or e.storageHigh) and product.store ~= nil then