Stock Level Monitoring Applet
Watch the quantity on hand at each location against an optimal and a maximum level you set per item category, and e-mail a list of people when anything falls below or rises above them. It is pure alerting: it reads stock balances and writes nothing back, creates no document and posts no journal — the warning half of Stock Replenishment, which acts. Nothing displays the result: the breach list lives on the run record and in the e-mail, and no screen shows what a run found, so the e-mail is the result.
Your job, and the screen for it
| The job | Screen or tab | Read |
|---|---|---|
| Set what to watch, where, and who is told — an item never received at a location is not checked there | Level Monitoring Template: Category List, Locations, Entities | Applet settings; Troubleshooting |
| Send an alert now or at a later time — only saving a new event schedules a run, not changing a saved one’s date; Recurring is never read | Level Monitoring Events › create, with the template picked first | Lifecycle and effects |
| Re-run a check, or correct one run’s text, recipients or levels | Level Monitoring Runs › the run view; SAVE, then RUN | Lifecycle and effects |
| See what a run found — only the e-mail shows it; a run with no breaches sends none, so silence proves nothing | The alert e-mail; the run’s Modified Date shows it ran | Lifecycle and effects |
| Find why an alert never came or reported nothing | The run’s Locations and Emails tabs; the template’s Category List | Troubleshooting |
| Set up categories, locations and recipients with e-mail addresses | Item Maintenance and Organisation applets; the template’s Entities tab | Before you can use it |
Overview
Think of it as the warning half of Stock Replenishment. The two applets share the same template → event → run shape and the same Rules Logic / Negation Logic controls (this applet’s own source says the controls were ported from there). Replenishment acts — it raises purchase orders and transfers. Monitoring only tells you.
Where it fits
| Upstream | This applet | Downstream |
|---|---|---|
| Item categories (inventory labels) and the items carrying them, from Item Maintenance | Template — which categories to watch, at which optimal and maximum levels, and the e-mail subject and body to send | — |
| Locations from the Organisation Applet | Template and run scope — the run checks stock at its locations only. Companies can be picked on the template, event and run too, and are copied onto the run, but the check never reads them: a company narrows nothing | — |
| Entities with e-mail addresses (customers, suppliers, contacts) | The recipient list | The alert e-mail |
| The per-location stock balance the Stock Balance Applet reports on | Run — reads the ledger quantity per item per location and records breaches | Alert e-mail, and the run’s own record of what it found |
| — | Event — a start date and, optionally, a template | One queued run |
Screens and menus
| Menu | Route | What it lists | Actions |
|---|---|---|---|
| Level Monitoring Runs | stock-level-monitoring | Run Name, Description, Created Date, Updated Date, Status | + (create a run by hand) and search on the listing; RUN (evaluate this run now), SAVE and Delete on the run view |
| Level Monitoring Events | stock-level-monitoring-events | Event Name, Event Code, Created Date, Updated Date, Status | Create, open, delete |
| Level Monitoring Template | stock-level-monitoring-template | Template Code, Template Name, Created Date, Updated Date, Status | Create, open, delete |
| Settings | settings/field-settings, settings/default-selection, settings/release-notes, plus the shared Webhook, Feature Visibility and permission screens | See Configuration | — |
| Personalization | personalization/personal-default-selection, personalization/sidebar | Per-user defaults, sidebar | — |
The Template and Run screens each open into a view with a Details tab and four more: Locations, Companies, Category List, and Entities (template) or Emails (run). The Event view has Locations, Companies, Entities and Category List. The tabs appear in whatever order was last saved under Settings → Default Selection (see Applet settings); the order above is the one they ship in. Category List is the tab that carries numbers — one row per item category, with an editable Optimal Stock Value and Maximum Stock Value.
Both grids label those two columns "(MYR)". They are not money: they are compared with the stock balance’s ledger quantity. Read them as units.
Listings load a fixed window and extend it with a More action rather than paging.
Screenshots needed
Screenshots for this page are still to be captured. A capture session should take: the Template listing; a Template view showing the Category List grid with Optimal and Maximum values and the Negation Logic control; the Default Optimal / Maximum Stock Value pair with the inline warning callout; an Event view with Recurring ticked and the recurrence editor open; the Run view with the RUN button; and the run’s Emails section. A scratch tenant is required — the Emails section shows real entity e-mail addresses.
Configuration
Before you can use it
| Prerequisite | Where | Why |
|---|---|---|
| Item categories with items attached | Item Maintenance | A run resolves each category label to its item set; a category with no items contributes nothing. |
| Locations on the template | Organisation Applet | A run with no locations checks nothing and reports nothing, and nothing fails. |
| At least one optimal or maximum value | The template’s Categories grid | A category link with both values null is skipped. |
| Entities with an e-mail address linked to the run | The run’s Emails section, copied from the template’s entities when the run starts with none | An entity whose record has no e-mail address is dropped; one whose e-mail box holds several addresses separated by commas or semicolons gets the alert at each of them. With no address left the alert is skipped and nobody is told, not even in the run. |
| Server-side permissions | Settings → Permission Set / Role Permission | See below. |
Applet settings
Settings are applet-local, and only one screen changes anything.
- Settings → Field Settings is the unbound eight-toggle stub — Unit Discount, SST/VAT/GST, WHT, Blanket Order, Segment, G/L Dimension, Profit Center, Project — with no form group, no
formControlNameand a SAVE button with no click handler. The labels belong to a sales-document settings screen and have nothing to do with stock monitoring. No exposed control found. - Settings → Default Selection offers Default Branch and Default Location, and does save them — but nothing in the applet reads them, so nothing changes when you set them. The same screen also carries three Details Tab Ordering lists — Run View, Event View, Template View — which you drag into order and SAVE; each view then shows its tabs in that order for every user, and a tab added in a later build is appended to a saved order. There is no RESET button on this screen.
- Personalization → Default Selection shows the same Default Branch / Default Location pair, but saves nothing: the first change errors (the screen is opened without the settings it needs) and SAVE goes nowhere (read from the code, not tried).
- Settings → Release Notes shows the applet’s release notes.
Everything that actually changes behaviour lives on the template, the event and the run, not in Settings. That is the part to configure:
| Where | Setting | What it controls | Default |
|---|---|---|---|
| Template → Category List | Optimal Stock Value per category | A location whose ledger quantity is below this is reported as understock | empty (no understock check for that category) |
| Template → Category List | Maximum Stock Value per category | A location whose ledger quantity is above this is reported as overstock | empty (no overstock check) |
| Template → Category List | Default Optimal Stock Value, Default Maximum Stock Value | Applied to every category not listed, and only when Negation Logic is ENABLED | empty |
| Template / Event / Run → Companies | Company picks | Nothing. Copied from the template onto the run and never read; only the Locations tab decides where stock is checked | empty |
| Template / Event / Run | Negation Logic | DISABLED = monitor the categories you picked. ENABLED = monitor every other active category in the same lists, using the two default values above | DISABLED |
| Template / Event / Run | Rules Logic (AND / OR) | Nothing. The value is carried from template to event to run and logged, but every picked category is always evaluated independently — OR semantics. AND is not implemented. | AND (and treated as OR) |
| Template | Email Subject, Email Body | Copied onto the run when the run is created, so editing the template later does not rewrite an alert that already went out. What arrives is described under The alert e-mail | empty (subject becomes Stock Level Alert: N breach(es) detected) |
| Event | Cycle Start Date | Now or in the past: the run happens inside the save, before the success message appears — and the message says created even when that run fails (see Lifecycle). In the future: it waits for the platform’s background sweep, which picks it up once the time has passed | now |
| Run | RUN | Evaluates this run again inside the button press, with its own locations, categories, recipients, subject and body — save the run first so the button sees your edits | — |
| Settings → Default Selection | Details Tab Ordering (Run / Event / Template view) | The order of the tabs on each view, for every user | the shipped order |
Who can change what
The applet has no client-side permissions and checks no HIDE_* / SHOW_* keys, so the Feature Visibility screen has nothing to gate. Access is entirely server-side:
| Screen | Permission |
|---|---|
| Level Monitoring Template | API_STOCK_LEVEL_MONITORING_TEMPLATE_HDR_READ / _CREATE / _UPDATE / _DELETE |
| Level Monitoring Events | API_STOCK_LEVEL_MONITORING_EVENT_HDR_READ / _CREATE / _UPDATE / _DELETE |
| Level Monitoring Runs | API_STOCK_LEVEL_MONITORING_RUN_READ / _CREATE / _UPDATE / _DELETE |
Whoever may create an event or press RUN can send an e-mail to every recipient on the template, as often as they like. Decide who that is the way you decide who may send customer statements, and review the list when someone changes role.
Fields
Template → Create asks for a Template Code and a Template Name (both required), a Description, the Email Subject and Email Body, and a Status that starts as ACTIVE. The template view adds Creation Date, Created By, Modified Date and Modified By as read-only fields, plus the Locations, Companies, Entities and Categories sections.
What happens to those fields later is not on the form:
- Neither the code nor the name is checked against other templates. Two templates can share a code. Search the listing before you create one.
- The name becomes every run’s name. A run is called Run for template: ‘’, and that is the Run Name in the Runs listing and in the first line of the e-mail. Renaming the template later leaves older runs with the old name. Give each template a name that says what it watches and for whom (Flagship smartphones — Klang Valley — understock), because the Runs listing has nothing else to tell one run from another.
- The Description becomes the run’s Description when the run is created, and changes to it do not reach earlier runs either.
- Email Body is sent as written, with no merge fields. Nothing in it is replaced by an item, location or quantity; the product adds those itself, as a table after your text. The body is placed into the e-mail as HTML, so the line breaks you type in the box are not kept and several paragraphs arrive as one. Use
<p>or<br>if you need them. That is read from the code, not tried: before the template goes live, run it once with yourself as the only recipient and read what arrives.
Event → Create asks for an Event Code and an Event Name (both required, neither checked against other events), a Description and a Status that starts as ACTIVE, plus the fields below.
| Field | What the form does not tell you |
|---|---|
| Level Monitoring Template | The form lets you save without one, but the server refuses an event with no template, and the error message is gone in about a second. Pick the template first. |
| Cycle Start Date | Empty or earlier than now: the check runs and the e-mail goes out while you wait for the save, so a large template makes the save slow. If that run fails, the save still says created: the failed job is kept with one retry spent and the background sweep tries it again later, so a missing e-mail after an immediate event is not proof that nothing will come. Later than now: the event is saved at once and the run happens when the background sweep next passes after that moment. Only saving a new event does this; changing the date on a saved event schedules nothing. |
| Cycle End Date | Copied onto the run as its Current Run End Date and nothing more. It does not stop a run or limit which stock movements are counted, because the check is always of the balance at the moment it runs. |
| Recurring + recurrence rule | Stored on the event and never read — see Lifecycle. |
Run → view opens on a Details tab: the Level Monitoring Template, a Level Monitoring Event Name box, Run Name, Status, the current and previous run’s start and end dates, Description, Email Subject and Email Body, and who created and last changed it. The other tabs are Locations, Companies, Emails (Entity Name, Email, Updated Date, Actions) and Category List, in the order saved under Settings → Default Selection.
- The Event Name box is read-only and nothing fills it, so a run does not show which event produced it (read from the code, not tried). The start date is the nearest clue, since an event-made run takes its dates from the event.
- Subject, body, recipients, locations and the per-category levels are all editable on the run, and saving them changes what the next RUN on that run evaluates and sends. This is where to correct a run whose template text or scope was wrong — the template every other run depends on is untouched.
Lifecycle and effects
This applet writes only its own templates, events and runs. No stock movement, no journal, no document.
How a run is triggered. There are exactly two paths, and both start the same monitoring job:
- Creating an event. Saving a new event runs the job inside the save if the event’s start date is now or in the past, and leaves it waiting if the date is in the future. A waiting job is picked up by the platform’s background sweep, the same one that fires every Scheduler clock, once its time has passed. It needs no Scheduler entry of its own. Updating an event does not queue anything. When the inside-the-save run fails, the save still answers created: the job is rolled back with one of its five tries spent, the error goes to a table no screen shows, and the sweep tries it again later.
- Pressing RUN on a run, which runs the job for that run inside the button press.
A job that fails is tried again by the sweep, up to five times in all. That, and the whole path above, is read from the code, not tried. Before you rely on it, create one event dated a few minutes ahead with yourself as the only recipient, and confirm the e-mail arrives after that time.
What the job does. It depends on what started it:
- an event — loads the event; if the event has no template it logs an error and stops. Otherwise it creates a run from the template and the event, then processes it.
- a run with a template — processes that run.
- a run with no template — logs “Monitoring run without a template is not supported” and does nothing. The Template box on the run’s Create screen is optional, so a run created by hand without one can never produce anything.
Processing a run:
- Snapshot the template’s locations, companies and entities onto the run, but only where the run has none yet — so a run that has been edited keeps its own scope. (The companies are copied and then never read; only the locations decide where stock is checked.)
- Snapshot the template’s category links onto the run the same way. The event’s own category links are deliberately not used: they hold category groups, not categories with stock values.
- Resolve Negation Logic from the run, falling back to the template.
- When negation is ENABLED, replace the picked categories with every other active category in the same label lists, each carrying the template’s default optimal and maximum values.
- Detect breaches — for each category link with at least one of the two values set, for each item in that category, for each of the run’s locations, read the ledger quantity on that item’s stock balance record at that location and compare. Below optimal is understock; above maximum is overstock. Understock wins when both would apply, and each item, location and breach type is reported once. A balance record with an empty quantity counts as zero, but an item that has no balance record at a location is not checked there at all, so an item a branch has never received never shows as understock at that branch. That is read from the code, not tried; see Troubleshooting for the check.
- Record the breach count, the time and the full list of breaches on the run.
- Send the alert e-mail.
The alert e-mail. Recipients are the entities linked to the run that have an e-mail address. A run that finds nothing sends nothing, so silence can mean “all stock is within its levels” or “the run did not happen”, and the e-mail cannot tell you which. The alert comes from BigLedger’s no-reply address, not from your company, so a recipient’s mail filter may need telling about it and nobody should reply to it. The subject is the run’s, or Stock Level Alert: N breach(es) detected when that is empty. The body is your text from the template, followed by one sentence naming the run and the number of breaches, and a table with one row per breach: the item code (not its name), the location as code and name, the breach type, the current quantity, and the optimal and maximum levels it was compared with. The subject and body were copied from the template when the run was created, so changing the template does not rewrite an alert already sent. A sent alert is recorded in the run’s e-mail history, which no screen in the applet shows. A failed send does not stop the run and is not recorded anywhere a user can see; the run still completes.
Nothing displays the result. The breach list exists on the run record and in the e-mail, and no screen in the applet shows what a run found. The e-mail is the result, so the people on the recipient list hold the only readable record. Agree who acts on each alert (for GadgetSphere, the branch’s stock controller for understock and the category buyer for overstock) and have them keep the e-mails in a shared folder, which is also how you show later that a warning was given. The run’s Modified Date moves when a run finishes, which is the only on-screen sign that it ran.
Related applets
- Stock Replenishment Applet — the same template / event / run shape, and the same Rules Logic and Negation Logic controls, but it acts on the shortfall by creating purchase orders and transfers instead of e-mailing.
- Stock Balance Applet — reports on the same ledger quantities this applet compares against.
- Stock Availability Applet — the interactive way to answer “how much is at this location right now”.
- Item Maintenance — where the item categories this applet monitors are defined.
- Organisation Applet — the companies and locations a template can scope to.
Troubleshooting
| Symptom | Cause | Fix |
|---|---|---|
| A run completes and reports zero breaches although stock is clearly low | Most often the run has no locations: a run with none checks nothing, without failing. | Check the run’s Locations section, and the template’s, and re-run. |
| An item that is out of stock at a branch never appears in the alert | The item has never had stock at that location, so there is no balance record there to check (read from the code, not tried). | The branch’s stock controller compares the category’s item list with the branch’s range once a month in Stock Availability. To test the rule itself, put one category on a template with a single location where one of its items has never been received, set Optimal to 1, and run it with yourself as the recipient. |
| The alert’s text runs together in one paragraph | The Email Body is placed into the e-mail as HTML, which ignores the line breaks typed into the box. | Mark paragraphs with <p> or <br> in the template’s body, and on any run already created. |
| Negation Logic is on and every run reports nothing | Neither Default Optimal Stock Value nor Default Maximum Stock Value is set, so every synthetic category link is skipped. The applet shows an inline warning and the backend logs one. | Set at least one of the two defaults on the template. |
| A category is listed on the template but never breaches | Its Optimal and Maximum values are both empty, so the link is skipped. | Enter at least one value on that row. |
| Breaches are detected but nobody gets an e-mail | No entity linked to the run has an e-mail address; the alert is skipped with a warning in the log. | Add entities with e-mail addresses to the run’s Emails section. |
| Editing the template’s Email Subject or Body does not change the alert | Subject and body are snapshotted onto the run when the run is created. | Edit them on the run. Or create a new run from the Runs listing’s Create with the template picked — picking it copies the template’s current subject and body — save, and press RUN. A run saved with no template picked produces nothing. |
| Switching Rules Logic from OR to AND changes nothing | AND is not implemented; the value is propagated and logged, and every category is evaluated independently. | Narrow the category list instead. |
| An event was set to Recurring and only ran once | The recurrence is stored on the event and nothing reads it. The only run is queued once, when the event is created. | Whoever owns the alert creates the next event on a calendar reminder (for a weekly check, every Monday morning before the buyers’ meeting), or presses RUN on the last run. They know it worked when the e-mail arrives. |
| An event was saved with the right dates but never ran | Only creating an event queues a run; updating one does not. | Delete and re-create the event, or run the run manually. |
| An event dated now was saved with created but no e-mail came | The run inside the save failed; the save does not say so. The job keeps its remaining tries and the background sweep retries it, so the e-mail may still arrive later. | Wait for one sweep. If nothing comes, press RUN on the run the event made (if one exists) and check the Emails tab has recipients with addresses; otherwise re-create the event. |
| A future-dated event never produced a run | No Scheduler entry is involved: the waiting job is picked up by the platform’s background sweep once its time passes. If the run is still missing an hour after that, the sweep has not reached it or the job failed five times. | Press RUN on an earlier run from the same template to get the alert now, and send BigLedger support the event’s code and start time (what to send). |
| Picking a company on the template or run changes nothing | Companies are copied onto the run and never read; the check runs over the run’s locations only. | Scope by Locations. To watch one company, pick that company’s locations. |
| There is nowhere to see what the last run found | By design at this commit — the breach list exists only on the run record and in the alert e-mail. | Read the e-mail; for the raw list, ask support to read the run. |
Related documentation
- Inventory module
- Best practices §6 — why a new branch’s alert covers only what it has already stocked.