diff --git a/FS25_ADSmartPickup/adCropPlan.lua b/FS25_ADSmartPickup/adCropPlan.lua
new file mode 100644
index 0000000..fc81616
--- /dev/null
+++ b/FS25_ADSmartPickup/adCropPlan.lua
@@ -0,0 +1,76 @@
+-- AD Crop Plan (ren logik)
+-- Mangel pr. afgrøde over 12 måneder (lager + forventet høst mod kædens behov) og valg af marker der skal skifte
+-- målafgrøde: "fri til kæden" -> auto, ellers forslag. Brugerens egen målafgrøde ændres aldrig automatisk.
+
+ADCropPlan = {}
+ADCropPlan.HORIZON_MONTHS = 12
+ADCropPlan.MIN_SHARE = 0.10
+ADCropPlan.MIN_HA = 0.5
+
+function ADCropPlan.plannedFruit(field)
+ if field.targetFruit ~= nil and field.targetFruit ~= "" then return field.targetFruit end
+ return field.fruit
+end
+
+function ADCropPlan.shortages(demand, stock, fields, yields)
+ local list = {}
+ for fruit, perMonth in pairs(demand or {}) do
+ local perHa = (yields or {})[fruit] or 0
+ if perHa > 0 and (perMonth or 0) > 0 then
+ local need = perMonth * ADCropPlan.HORIZON_MONTHS
+ local supply = (stock or {})[fruit] or 0
+ for _, field in ipairs(fields or {}) do
+ if ADCropPlan.plannedFruit(field) == fruit then supply = supply + (field.areaHa or 0) * perHa end
+ end
+ local missing = need - supply
+ local missingHa = missing / perHa
+ if missing > need * ADCropPlan.MIN_SHARE and missingHa > ADCropPlan.MIN_HA then
+ table.insert(list, {fruit = fruit, needLiters = need, supplyLiters = supply, missingLiters = missing, missingHa = missingHa})
+ end
+ end
+ end
+ table.sort(list, function(a, b)
+ if a.missingHa ~= b.missingHa then return a.missingHa > b.missingHa end
+ return a.fruit < b.fruit
+ end)
+ return list
+end
+
+local function isCandidate(field, used, dismissedFruit, fruit)
+ if field.permanent or field.excluded then return false end
+ local planned = ADCropPlan.plannedFruit(field)
+ if planned ~= nil and planned ~= "" and used[planned] then return false end
+ return dismissedFruit ~= fruit
+end
+
+function ADCropPlan.choose(shortages, fields, usedFruits, yields, prices, dismissed)
+ local actions, taken = {}, {}
+ local used = usedFruits or {}
+ for _, shortage in ipairs(shortages or {}) do
+ local remaining = shortage.missingHa
+ local candidates = {}
+ for _, field in ipairs(fields or {}) do
+ if not taken[field.id] and isCandidate(field, used, (dismissed or {})[field.id], shortage.fruit) then
+ local planned = ADCropPlan.plannedFruit(field) or ""
+ local value = ((yields or {})[planned] or 0) * ((prices or {})[planned] or 0)
+ table.insert(candidates, {field = field, value = value})
+ end
+ end
+ table.sort(candidates, function(a, b)
+ if a.value ~= b.value then return a.value < b.value end
+ local da, db = math.abs((a.field.areaHa or 0) - remaining), math.abs((b.field.areaHa or 0) - remaining)
+ if da ~= db then return da < db end
+ return a.field.id < b.field.id
+ end)
+ for _, candidate in ipairs(candidates) do
+ if remaining <= 0 then break end
+ local field = candidate.field
+ local auto = field.chainFree == true and field.targetSource ~= "user"
+ table.insert(actions, {fieldId = field.id, from = ADCropPlan.plannedFruit(field) or "", to = shortage.fruit,
+ hectares = field.areaHa or 0, reason = shortage.fruit, mode = auto and "auto" or "suggest"})
+ taken[field.id] = true
+ remaining = remaining - (field.areaHa or 0)
+ end
+ end
+ return actions
+end
diff --git a/FS25_ADSmartPickup/modDesc.xml b/FS25_ADSmartPickup/modDesc.xml
index 8a16652..664eb7a 100644
--- a/FS25_ADSmartPickup/modDesc.xml
+++ b/FS25_ADSmartPickup/modDesc.xml
@@ -56,6 +56,7 @@
+