Most BFCM plans begin with the discount. Mine began with a less exciting question: how many units could the store actually sell and fulfill without destroying contribution margin?
I put an AI campaign planner through a synthetic 15-SKU store test with three demand scenarios: Conservative, Target, and Stretch. The planner had to respect available inventory, protected safety stock, contribution margin, receiving dates, fulfillment capacity, and support capacity before it could recommend a promotion.
The Stretch scenario failed. For an operator, that was the useful result: the system was allowed to reject a promotion that looked attractive but did not survive the constraints.
The original planner produced an attractive campaign by inventing the numbers it needed. That version scored 11/100 on my operator scorecard. The inventory-aware rewrite reached 74/100, but three faults remained: it misunderstood a Shopify inventory field, changed its contribution calculation between rows, and pulled an approver's name from outside the supplied input.
My decision is straightforward: I would use an AI model to explain a verified BFCM plan. I would not let it calculate the plan by itself. That distinction also matters when comparing broader AI tools for ecommerce: drafting an explanation and owning the inventory math are different jobs.
What I tested
This was not a live merchant campaign. I built a research fixture so the inputs and expected results could be inspected without exposing a store, customer, or employer.
The fixture contained:
- 15 SKUs across drinkware, bags, accessories, and giftable products;
- daily sales history covering 365 days for each SKU;
- prior BFCM performance fields;
- price, product cost, pick-and-pack cost, shipping subsidy, payment fee, and expected return rate;
- Shopify-style inventory states, including Available, Committed, Incoming, and protected safety stock;
- supplier receipt dates and confidence;
- a fulfillment ceiling of 260 orders per day;
- a support ceiling of 50 tickets per day;
- a 12% contribution-margin floor after expected acquisition cost.
Both runs used DeepSeek v4 Pro. The enhanced run also received the store fixture, the constraint definitions, and one permission the original did not have: it could reject a scenario rather than dress up all three as viable options.
That last permission matters. A planning tool that must always return three positive options isn't planning. It is formatting optimism.
The baseline planner filled missing cells with confidence
The original BFCM prompt knew the product categories, average order value, average daily traffic, three top products, and the campaign budget. It did not know the store's conversion rate, email-list size, product costs, SKU inventory, fulfillment throughput, or return costs.
It generated them anyway.
The output projected traffic rising from 2,200 to 8,800–13,200, assumed an email list of 25,000–50,000, supplied baseline and VIP conversion rates, set revenue targets, estimated weekly SKU sales, forecast support volume, and proposed channel ROAS. It also assumed cost of goods at 35% for drinkware and 30% for accessories, then concluded that a 30% discount would leave margins healthy.
None of those figures came from the fixture. Some were marked as assumptions; many arrived without that label as forecasts, targets, or benchmarks. Precision had hidden the missing data.
I scored it 11/100. Two failures were disqualifying:
- It generated precise forecasts without the required inputs.
- It used invented cost assumptions to recommend an aggressive discount as profitable.
This is the version of AI planning that worries me most. The prose is organized. The calendar is complete. The error sits underneath both.
I moved the constraints in front of the campaign
The inventory-aware version had to pass a set of gates before it could allocate units or recommend a discount.
For each SKU, the calculation layer checked:
```text saleable_for_event = available_units - protected_safety_stock
expected_revenue_per_unit = sale_price × (1 - expected_return_rate)
contribution_after_acquisition = expected_revenue_per_unit - product_cost - pick_and_pack_cost - shipping_subsidy - payment_fee - expected_paid_acquisition_cost ```
Incoming units could not be used before receipt, and no SKU could exceed its maximum approved discount. Warehouse and support capacity applied to the scenario as a whole. The hard stop was contribution: below the 12% floor, that SKU left the promotion.
The prompt also had to say what it could not verify. Missing price elasticity, incomplete traffic-source data, uncertain supplier receipts, and the absence of a store-specific BFCM return rate were supposed to remain missing rather than becoming model-authored facts.
The three scenarios produced a counterintuitive result
I ran the hard constraints in a deterministic script before asking the model to explain the scenarios.
| Scenario | Base forecast units | Approved units | Unmet units | Expected revenue | Contribution after acquisition | Margin-gate failures | |---|---:|---:|---:|---:|---:|---:| | Conservative | 926 | 886 | 40 | $33,504.79 | $8,860.20 | 1 | | Target | 1,146 | 743 | 403 | $34,562.21 | $6,592.21 | 5 | | Stretch | 1,475 | 245 | 1,230 | $16,723.95 | $2,185.40 | 12 |
These are synthetic results, not a revenue forecast for a real store.
Conservative approved most forecast demand. Its shallower discounts and lower paid-acquisition share preserved the margin floor. Target expected more demand but approved fewer units, while Stretch combined deeper discounts with a greater paid share and pushed 12 of 15 SKUs below the contribution threshold.
More demand did not produce a bigger feasible campaign. Once the plan accounted for the cost of buying that demand, Stretch became the smallest approved scenario.
I also raised customer-acquisition cost by 35% and expected returns by 50%. In either stress test, every Stretch SKU stopped. That doesn't show how a real BFCM weekend will behave. It shows that a stop condition can overturn an aggressive plan instead of merely adding a warning below it.
What the inventory-aware planner improved
The enhanced output did several things I would keep.
It did not count Incoming units as sellable. Shopify separates Available, Committed, Incoming, and other inventory states; a purchase order is not inventory I can promise until it has been received. The model respected that boundary.
It also exposed missing inputs, changed the rules between scenarios, connected discounts to a margin gate, and created explicit responses for supplier delay, rising acquisition cost, higher returns, and early sellout. Most importantly, it allowed Stretch to fail.
On the scorecard, those changes lifted inventory handling, scenario design, exception response, and auditability. The result reached 74/100 with no hard failure.
That was enough to make it reviewable. It was not enough to make it executable.
Three errors still blocked execution
It subtracted Committed inventory twice
The model used this formula:
saleable = available - committed - safety_stockAvailable already excluded Committed units in this fixture, matching Shopify's inventory-state definition. The second subtraction was wrong. It made the plan unnecessarily conservative; the event quantity should have been Available minus the safety stock protected for normal operations.
This looks like a small field-definition problem. At 15 SKUs, it changes the inventory pool repeatedly.
Its contribution math changed between rows
For one Target SKU, the verified inputs produced:
sale price = $42 × 0.82 = $34.44
expected revenue after returns = $34.44 × 0.96 = $33.06
payment fee = $34.44 × 2.9% = $1.00
product, fulfillment, and shipping cost = $20.70
paid acquisition per unit = 45% × $18 ÷ 1.35 = $6.00
contribution after acquisition = $5.36The model reported $7.45. Similar inconsistencies appeared elsewhere. I recalculated the approved SKUs and they still cleared the 12% floor, so this particular error did not reverse the gate outcome. It did make the model's contribution totals unusable.
It introduced an approver who was not in the input
“Rick must approve,” the output said more than once. The input named no approver at all. I am writing this as Mark, and the synthetic store has no employee records from which the model could have found Rick.
That is context contamination. A production run needs an isolated session and explicit role IDs. The model should never infer an authorization chain from unrelated conversation history.
The architecture I would ship
I would split this workflow into two layers.
The first is a deterministic calculation layer. It reads inventory states, historical demand, costs, discounts, purchase-order receipts, and capacity. It validates field definitions, performs the arithmetic, applies hard constraints, and returns reviewable JSON or CSV.
The second is a model explanation layer. It receives the locked scenario output and writes the operator brief: what changed, which SKUs stopped, where demand remains unmet, what needs approval, and which event should trigger a replan. It cannot overwrite the financial fields.
There is a practical performance reason for the split too. The first inventory-aware model call, which tried to calculate and narrate everything, timed out at 300 seconds. A shorter retry still took 262 seconds. Financial arithmetic should not become slower because the explanation is long.
A reusable BFCM gate checklist
Before accepting an AI-generated campaign plan, I would require answers to these questions:
- Is the inventory field Available, On hand, or a custom export, and has that definition been written into the calculation?
- Are Incoming units excluded until a confirmed receipt event?
- Does every SKU include product, fulfillment, shipping, payment, return, and acquisition cost?
- Is there a contribution floor after the discount and expected acquisition cost?
- Do Conservative, Target, and Stretch change real inputs rather than only the language?
- Can a scenario be rejected?
- Are warehouse and support ceilings enforced per day?
- Are formulas tested outside the model?
- Are approvers supplied as explicit roles rather than names inferred by the model?
- Is every recommendation a draft until a person reviews the exception list?
My verdict
The inventory-aware prompt was worth keeping. It turned an unsupported campaign pitch into a constrained draft, and it made failure visible. My limit is firm: it comes after a deterministic scenario engine, not in place of one.
This approach fits an operator who already has clean SKU costs, usable inventory states, prior sales data, and named approval rules. It is a poor fit for a store whose costs live in several spreadsheets, whose Incoming dates are guesses, or whose fulfillment capacity has never been measured. The model cannot repair those inputs by sounding certain.
Start with one product family. Build the Conservative scenario outside the model, lock the formulas, then ask the model to explain rejected units and exceptions in language the merchandising, finance, warehouse, and support teams can all use. If the explanation disagrees with the table, the table wins.
Frequently asked questions
Can AI forecast BFCM inventory for Shopify?
AI can organize inputs, explain scenarios, and flag missing data. It cannot supply a reliable forecast without store-specific demand history, inventory definitions, supplier lead times, promotion effects, and cost data. Put the executable forecast in a deterministic layer; give the model the smaller job of explaining it.
Should Incoming Shopify inventory be included in a BFCM plan?
Only after the receipt timing and quantity are credible, and never as immediately sellable stock before receipt. Shopify treats Incoming and Available as different states. Model delayed, partial, damaged, and rejected receipts as exceptions.
Why use three inventory scenarios?
Three scenarios are useful when they change real assumptions: demand, discount, paid-acquisition share, supplier confidence, and operational capacity. They are not useful if the model writes three tones of the same plan. Each scenario needs a distinct stop rule.
Can the planner automatically change discounts or inventory?
I would begin in read-only mode. Let it calculate in a test fixture, then draft recommendations. Any price, inventory, purchase-order, or campaign change should require approval until field semantics, duplicate-action protection, logs, and recovery have been tested.
