Recalculate margin

TL;DR

An order's margin is wrong or stale after a rule change. How to re-queue it and what will not refresh.

Symptom: one order's margin looks wrong

Diagnose first. Compute it by hand from the order's impacts and compare. A difference in the total with matching impacts is an aggregation problem; a difference in the impacts is a rule problem, which belongs in calculation rules.

Repair: re-queue that order by setting its processed flag back to zero on its row in the per-order impact table. That queue is not the batches table.

Verify: the flag returns to processed and the figures move.

Symptom: margin did not change after a rule edit

Which edit you made decides what was re-queued. A basic edit deactivates the rule and deletes its impacts; activation queues the days in the submitted window; a filter or impact edit does not perform the same re-queue.

Repair: re-activate over the window you want recalculated, and check that the days were queued.

Verify: pick one order inside the window and one outside it, and confirm which moved.

Symptom: expected margin did not change

Expected margin has its own queue, guarded on the order amount. An order whose total has not changed keeps its processed state, so a rule or cost edit alone may leave its expected figure stale.

Repair: force that order's expected figure to recompute.

Verify: compare the expected figure against the rule you changed.

Shrinking a window

Saving activation does two things: it queues one calculate batch per day in the window you submitted, and it then deletes every impact the rule has ever booked, across every impact table and regardless of date.

So the rule's own impacts are gone everywhere the moment you save. The days inside the new window are rebuilt as their batches run. The days that fell out of the window are not rebuilt, and do not need to be: the rule should no longer apply to them.

What is left behind is the per-order total, not the impact. cm_impact_salesorders is recalculated only for the orders the calculate jobs touched, which is the new window. An order dated in the dropped range has had the rule's impact deleted from the per-group table, and still carries the old total, because nothing re-queued it.

Repair: set processed back to 0 on those orders in cm_impact_salesorders, and calculate/impactsalesorders.php re-aggregates them from what the per-group tables now hold.

Verify: pick an order dated in the dropped range, check that the rule's impact is gone from /contributionmargin/impact, and check that the order's total on the sales order screen moved by the same amount.

Re-aggregate a known order

Use these statements in an authorised database session, replacing the synthetic id 123456 with an order you have inspected. They target CCC's MySQL database, never verkoopdump. Check that a queue row exists first:

SELECT salesorder_id, processed, revenues, cogs, voc, vmc, cm2
FROM cm_impact_salesorders
WHERE salesorder_id = 123456;

UPDATE cm_impact_salesorders SET processed = 0
WHERE salesorder_id = 123456;

The realised worker is app/jobs/calculate/impactsalesorders.php. It sums existing impacts; it does not rerun rules. A missing queue row needs the normal upstream calculate job to queue the order; the UPDATE above affects no rows in that case.

Expected has a different row and worker:

SELECT salesorder_id, processed, amount_eur_invat, cm2
FROM cm_expectedimpact_salesorders
WHERE salesorder_id = 123456;

UPDATE cm_expectedimpact_salesorders SET processed = 0
WHERE salesorder_id = 123456;

Its worker is app/jobs/calculate/expectedimpactsalesorders.php. It recomputes the synthetic transactions with the active rules. Run one worker per type or let the schedule drain it; do not start a concurrent loop. Confirm processed changes and compare the resulting signed components.

Capture the affected orders before changing a rule

Before saving Basic or Activation settings, record its old type, dates and the distinct salesorder_id values from its existing per-group impacts. Deletion removes the evidence needed to find orders outside the new range. The relevant date is the impacted transaction's date, which may be later than its sales order's date.

After saving, let queued calculation days finish before re-aggregating the captured orders. Include old and new transaction types when a basic edit changes type. If the impacts have already been deleted, reconstruct the affected orders from the old transaction type and window before applying a scoped repair. Do not substitute an order-creation-date filter for that reconstruction.

Evidence

Per-order queue from app/jobs/calculate/impactsalesorders.php. Edit lifecycle from app/http/contributionmargin/calculationrules.php. Expected queue guard from app/models/batches.php. The window-shrink behaviour from the re-queue loop in app/http/contributionmargin/calculationrules.php, which walks the saved window only. Basis: code-checked against the current implementation.