Reason codes
Every correction must carry a reason code, and the code must be one the tenant has configured. This is the only configuration Stock Corrections has, and it is what turns a pile of adjustments into something a business can act on.
Get it wrong and corrections that use the code are rejected: see When a code cannot be used.
Where reason codes live
Reason codes are held in Customer Controlled Configuration (CCC), under the kind:
rco.reason-codes.v1
They are read per business unit, so different stores can offer different reasons — although most tenants configure one list at tenant level and let every store inherit it.
The same kind serves several parts of Hii Retail. One tenant-wide list of codes covers refunds, price
overrides, goods received, cash management and stock corrections, and each code declares which of
those it belongs to through its groups.
⚠ A code can only be used for stock corrections if its groups contains StockCorrection. This
is the single most common configuration mistake: a code is created and looks right, but never appears
in the app and is rejected over REST — because it was never put in the group.
What a reason code looks like
{
"id": "DMG",
"name": "Damaged goods",
"description": "Items broken in store or in the stockroom",
"status": "Activated",
"groups": ["StockCorrection"],
"isNegativeQuantityByDefault": true,
"isCommentRequired": false
}
| Field | Required | What it does |
|---|---|---|
id | yes | The value sent as reasonCode on the correction, and carried on the event. Maximum 20 characters. Keep it stable — it is what your reporting will group by. |
name | yes | What staff see in the picker. 3–40 characters. |
status | yes | Activated or Deactivated. A deactivated code is not offered to staff, and a correction that sends one is rejected. |
groups | — | Which parts of Hii Retail may use this code. Must contain StockCorrection to appear here. |
description | no | Longer explanation, shown alongside the name. 3–255 characters. |
isNegativeQuantityByDefault | no | Sets which way the correction points by default. true preselects Remove in the app, false or unset preselects Add. Staff can still change it. |
isCommentRequired | no | When true, the app will not let staff submit without a comment. |
⚠ Only the fields above affect stock corrections. The kind also defines
isManualEntryRequired, isReferenceNumberRequired, minimumAmount, maximumAmount and
uniqueItems, because it is shared with other domains. They have no effect on a stock
correction — setting a maximumAmount will not cap a correction's quantity.
isNegativeQuantityByDefault is a default, not a rule
It decides where the toggle starts, not what the code means. A code marked negative by default can still be used to add stock, and nothing rejects it. If you are analysing corrections, read the sign of the quantity on the event — never infer it from the reason code's configuration.
Designing the list
A few things that are worth deciding before the first store goes live, because changing them later means your history stops being comparable:
- Enough codes to be useful, few enough to be picked correctly. A staff member standing at a shelf with 30 options will pick the first plausible one. Somewhere around 6–12 is usual.
- Separate what you will act on differently. Damage and theft belong apart because they lead to different responses. Damaged in transit and damaged in store only belong apart if somebody will actually chase the carrier.
- Require a comment where the code alone is not enough.
isCommentRequiredon a catch-all Other code is what stops it becoming the default answer to everything. - Retire codes by deactivating them, not deleting them. The
idstays on every correction already published; if you delete the code, old events reference something that no longer exists and your reporting loses its labels. Deactivating takes effect at once — the app stops offering the code and a correction that sends it is rejected — so change any integration that sends it at the same time. Corrections already published under it are unaffected.
When a code cannot be used
A correction is rejected with 400, and nothing is published, when its reason code:
- does not exist in the business unit's configuration — a misspelling, or a difference in case;
- exists, but its
groupsdoes not containStockCorrection; - is deactivated.
It is also rejected when the business unit has no reason codes configured at all. The error message says which of these applies and names the code, so whoever maintains the configuration knows where to look. See The API for the responses.
In the app this can only happen when a code changes between the moment a correction is captured and the moment it is sent — a correction captured offline under a code that has since been deactivated, for example. The app shows that correction as failed and does not retry it automatically.
The check is made once, when the correction arrives. A correction accepted under a code that is later deactivated stays valid.
What to check when something is wrong
| Symptom | Check first |
|---|---|
| A new code does not appear in the app | groups contains StockCorrection, and status is Activated |
| A code appears in one store but not another | the configuration is set per business unit — compare the two |
A correction is rejected with 400 naming its reason code | the message says which check failed: the id (matched exactly, including case), the StockCorrection group, or the status |
| Staff must always enter a comment | isCommentRequired on the code they are picking |
| The app defaults to Remove when it should Add | isNegativeQuantityByDefault on that code |
| Corrections are accepted but stock never moves | not a reason-code problem — see How it works |
A maximumAmount is being ignored | expected — that field does not apply to stock corrections |