Skip to content
PharmaFlow Knowledgebase
Esc
↑↓navigate↵open⌘Jpreview
On this page

Prescriptions

Create, list, dispense, and cancel branch prescriptions in PharmaFlow.

You handle every prescription as a branch-scoped record that moves through PENDING, DISPENSED, and CANCELLED states. The implementation lives in apps/server/src/features/prescriptions/route.ts with read helpers in apps/server/src/features/prescriptions/queries.ts and the create, dispense, and cancel business logic in apps/server/src/features/prescriptions/lifecycle/service.ts.

List prescriptions by status

You fetch your branch queue with GET /prescriptions, optionally filtered by status. Pending rows sort first so your dispensers always see what needs work.

curl -H "Authorization: Bearer $TOKEN" \
  "$API/prescriptions?status=PENDING&page=1&page_size=20"

Pass status as PENDING, DISPENSED, or CANCELLED; the route uppercases your value before filtering. Leave it empty to list everything in your branch. Pagination uses the shared parsePagination helper from @pharmaflow/shared, and each row includes its lines through loadItems in apps/server/src/features/prescriptions/queries.ts.

Create a pending prescription

You create a prescription with POST /prescriptions. You must send patient_name, prescriber_name, and a non-empty items array.

curl -X POST -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"patient_name":"Jane Doe","patient_id_number":"CM123456","prescriber_name":"Dr. Smith","prescriber_license":"LIC-42","items":[{"medicine_id":"<id>","quantity":2,"dosage_instructions":"1 tab twice daily"}]}' \
  "$API/prescriptions"

The server mints your rx_number with nextRxNumber in apps/server/src/features/prescriptions/lifecycle/queries.ts, formatted as RX-<branch>-<year>-<sequence> per branch and year. Your new record starts in PENDING status, and each line is written to prescription_items with a sync change emitted per line. If you omit a required field or send zero items, you get a 400 response.

Read one prescription

You fetch a single record with GET /prescriptions/:prescriptionId when you need dosages, prescriber details, or the linked sale. The response includes the header plus every line with medicine names and string prices.

curl -H "Authorization: Bearer $TOKEN" \
  "$API/prescriptions/<rx-id>"

The lookup is scoped to your branch, so an id from another branch returns 404 exactly like a missing record. Use dispensed_by_name on the response to see who completed the dispense, and sale_id to jump to the linked sale.

Dispense into a linked sale

You convert a pending prescription into stock movement and money with POST /prescriptions/:id/dispense. This is the only path that marks a prescription DISPENSED.

curl -X POST -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"payment_method":"cash"}' \
  "$API/prescriptions/<rx-id>/dispense"

The route re-prices every line at the current selling_price from medicines, applies VAT per line through vatRateForCategory in apps/server/src/features/settings/tax.ts against the policy loaded by loadVatPolicy in apps/server/src/features/prescriptions/lifecycle/queries.ts, and deducts FEFO batches via loadFefoBatches in the same file. It writes one sales row with a fresh invoice number, one sale_items row per batch slice, matching stock_movements rows, and links the sale back onto the prescription with sale_id, dispensed_by, and dispensed_at. Your response carries rx_id, rx_number, sale_id, invoice_number, total_amount, and dispensed_at. Dispensing an already dispensed or cancelled prescription returns 400.

Cancel a prescription

You void a pending prescription with POST /prescriptions/:id/cancel. Only PENDING records can be cancelled, and cancelPrescription in apps/server/src/features/prescriptions/lifecycle/service.ts refuses anything else with a 400.

curl -X POST -H "Authorization: Bearer $TOKEN" \
  "$API/prescriptions/<rx-id>/cancel"

Cancellation sets the status to CANCELLED and emits a sync change so other branches converge. You cannot reopen a cancelled or dispensed prescription through this endpoint; create a new one instead.

Track controlled lines at dispense time

When a line has is_controlled = 1, the dispense transaction also writes one controlled_dispense_log row per batch slice, capturing the patient national id and doctor license. The log rows are installed by insertControlledDispenseLogs in apps/server/src/features/prescriptions/lifecycle/mutations.ts and each one is emitted as a controlled_dispense_log sync change. You can read the full audit trail in Controlled drugs.

Next steps

Review your statutory registers in Compliance before your next NDA inspection, and park finished orders for pickup using Will-call and refills.

Last updated on October 20, 2026