# Business rules — magalies-pharma-heisenberg-print-server

Evidence date: 2026-10-09. Revision: `master` `07aa3df`.

No README or policy document states these rules. Unless a rule is marked **Documented**, it is **Inferred** from the JavaScript or ZPL at HEAD, or from a commit subject. Inferred rules describe what the code does. They are not a confirmed business policy.

## Who may print

**BR-001 — The server does not check a caller identity.**
`server.js` reads the JSON body and does not read a header, token, or role. **Inferred.**

**BR-002 — CORS is enabled with the library defaults.**
`app.use(cors())` passes no options. **Implemented.** Which origins are intended is **Unknown / Requires Confirmation.**

**BR-003 — The process listens on port 3000.**
`const port = 3000`. No environment override. **Implemented.**

## Which printer

**BR-004 — Barcode jobs always use the queue named `Barcode-Printer`.**
The name is not taken from the request. **Implemented** since `231ccd3`. Before that commit the constant was `XP-TT426B-2`.

**BR-005 — A label whose `size` is the string `small` uses `Small-Label-Printer` and `templates/labels_small.zpl`.**
Both the route and `printLabel` test `size == 'small'`. **Implemented.**

**BR-006 — Every other label size uses `Big-Label-Printer` and `templates/labels.zpl`.**
That includes a missing `size`, which the route also passes as `"standard"`. **Implemented.** There is no list of allowed sizes.

**BR-007 — Queue names are the current product names, not the earlier model strings.**
`231ccd3` replaced `XP-TT426B` and `XP-TT426B-2`. **Documented** by that diff. Whether the operating system still has queues under the new names is **Unknown / Requires Confirmation.**

## How many labels, and how they are numbered

**BR-008 — `quantity` is how many ZPL documents are concatenated into one raw job.**
The loop runs while `x < quantity`. Each document is rendered with template `quantity` of `1`, so `^PQ{{quantity}}` on the batch template is `^PQ1`. **Implemented.**

**BR-009 — Numbering starts at `starting_number`, or at 1 when that value is falsy.**
The route uses `starting_number ? starting_number : 1`. A value of `0` therefore starts at 1. **Implemented.** **Requires Confirmation** whether a zero start was ever a real request.

**BR-010 — The numeric suffix is four characters, keeping the last four if the number is longer.**
`("0000" + (starting_number + x)).slice(-4)`. **Implemented.**

**BR-011 — A batch code is the batch number, a dash, then that suffix.**
`` `${batch_number}-${padded_string}` ``. **Implemented.** Commit `b39d737` introduced batches. Commit `a44f442` renamed the request field from `barcode` to `batch_number`.

**BR-012 — A medicine code is the medicine number, a dash, then that suffix.**
`` `${medicine_number}-${padded_string}` ``. **Documented** by commit `a3fdeb9` ("fixed missing dash"), which put the dash back the same day `a44f442` had removed it. **Implemented.**

**BR-013 — One HTTP request produces one print job, not one job per copy.**
`printDirect` sits after the loop in both functions. For labels, that placement dates from `16e84dc` ("fixed print loop"). Before that commit each label iteration sent a job that contained every label built so far. **Documented** by the commit subject. **Implemented** at HEAD.

## What the label is allowed to show

**BR-014 — The batch label shows description, expiry, batch number, and a QR of the batch code.**
`templates/barcodes.zpl` placeholders: `description`, `expiry`, `batch_number`, `barcode`, `quantity`. **Implemented.**

**BR-015 — The standard medicine label shows description, directions, client, Rx number, prescriber, contained quantity, date, and a QR.**
`templates/labels.zpl`. The Rx number is `order_number`. **Implemented** since `a44f442`.

**BR-016 — The small medicine label does not show directions.**
`templates/labels_small.zpl` has no `directions` placeholder. `print.js` still passes `directions` into the Handlebars context. **Inferred** from the template. **Requires Confirmation** that small labels are supposed to omit directions.

**BR-017 — Dispensed-by is accepted and is not printed.**
`POST /label` still reads `dispensed_by` and `printLabel` still puts it in the template context. Neither label file contains `{{dispensed_by}}`. Until `a44f442` the standard template contained `Dispensed By:{{dispensed_by}}`. **Implemented.** **Requires Confirmation** whether dropping that line was intentional.

**BR-018 — The description field on the wire is `description`.**
`07aa3df` renamed `label_description`. **Documented** by the commit subject "updated description". A client that still sends `label_description` will print an empty description. **Inferred** from Handlebars rendering a missing value as empty.

## QR encoding

**BR-019 — The code is a ZPL QR field, not a linear barcode.**
All three templates use `^BQ`. Commit subjects `e6dd1b9`, `474a821`, and `31a691d` say the change was to QR. **Documented** by those subjects. **Implemented** in the templates.

**BR-020 — The QR payload is placed after commas so it is data, not a mode switch.**
`593ccfa` ("fixed qr codes ignoring letters") changed `^FD{{barcode}}` to `^FD,,{{barcode}}`. At HEAD the field is `^FD,,,{{barcode}}`. **Documented** by that commit. **Implemented.**

**BR-021 — The batch template also contains a ZPL serialisation field.**
`templates/barcodes.zpl` includes `^SF%%%%%%dddd` beside a print quantity of 1 per copy. The label templates do not include `^SF`. **Implemented** as file content. What the printer does with `^SF` when `^PQ` is 1 is **Unknown / Requires Confirmation.**

## Validation and failure

**BR-022 — Required fields are not validated.**
Missing strings are passed through. A missing `quantity` skips the loop and still calls `printDirect`. **Inferred** from the absence of checks.

**BR-023 — A barcode printer error is reported as the message `Printing failed`.**
The route does not set an HTTP error status. **Implemented.**

**BR-024 — A label printer error is not turned into a response body.**
`POST /label` does not catch. **Implemented.**

**BR-025 — The library error is logged and not returned to the client.**
`print.js` calls `console.error(err)` and `reject()` with no value. **Implemented.**

## Relationship to Heisenberg packaging

**BR-026 — This repository does not enforce the pack-scan rules.**
It does not check that a code is unique, that a batch exists, or that a medicine number matches a catalogue. **Inferred** from the absence of those calls.

**BR-027 — The printed code has the shape the pack-scan parser splits on.**
Silicon Ark's `magalies-pharma-heisenberg-serverless` business rules say the batch number and the catalogue medicine number are the substring before the first `-`. This service prints `{batch_number}-{suffix}` and `{medicine_number}-{suffix}`. **Inferred** by comparing the two trees. It is not stated in this repository. **Requires Confirmation** that the two were designed to match.
