Medication Service — User Stories
Service: medication-service Story prefix: MED-US Last updated: 2026-04-17
Stories
MED-US-001 — Draft a prescription
| Field | Value |
|---|---|
| Issue type | Story |
| Summary | Prescriber drafts a prescription with structured sig |
| Epic link | MED-EPIC-01 |
| Status | To Do |
| Priority | Must |
| Story points | 5 |
| Labels | service:medication-service, type:backend, slice:S1 |
| Components | prescription, api |
| FR references | FR-MED-010 |
| Legacy FR refs | FR-MEDS-010 |
| Dependencies | — |
User story: As a prescriber, when I start writing a medication order, I want to draft with structured dose/route/frequency so that downstream systems can parse it reliably.
Acceptance criteria:
- Given authenticated prescriber with
medication:prescribe, whenPOST /api/v1/medications, then201withid, statusdraft. - Given invalid UCUM unit, when posting sig, then
400with validation error. - Given code from terminology-service, when draft saved, then
medicationCodeandmedicationDisplaystored together.
Technical notes:
- Use
DraftPrescriptionUseCase; validates againstSigVO. - Calls
TerminologyService.lookupfor medication display.
Definition of Done:
- Unit + integration tests added; coverage ≥ thresholds.
- OpenAPI updated; Pact consumer tests green.
- Telemetry spans added.
MED-US-002 — Sign a prescription with safety checks
| Field | Value |
|---|---|
| Issue type | Story |
| Summary | Sign prescription runs allergy/interaction checks, blocks on critical alerts |
| Epic link | MED-EPIC-02 |
| Status | To Do |
| Priority | Must |
| Story points | 8 |
| Labels | service:medication-service, type:backend, slice:S1 |
| Components | prescription, safety-check |
| FR references | FR-MED-011, FR-MED-030..032 |
| Legacy FR refs | FR-MEDS-030..032, BR-MEDS-001 |
| Dependencies | MED-US-001 |
User story: As a prescriber, when I sign a prescription, I want safety checks to run automatically so that I cannot inadvertently prescribe a medication that would harm the patient.
Acceptance criteria:
- Given patient has penicillin allergy, when prescribing amoxicillin and signing, then
422 MED_ALERT_BLOCKINGwith alert array. - Given override with ≥10-char reason, when signing with overrides, then
201+medication.alert.overridden.v1event +medication.prescription.signed.v1event. - Given drug-KB timeout, when signing, then
503— sign refused.
Technical notes:
SignPrescriptionUseCasefetchesAllergyIntolerance+Conditionvia FhirClient.- Captures
drugKbVersionin override record.
Definition of Done: tests, OpenAPI, Pact, event schema conformance.
MED-US-003 — Discontinue / hold / resume
| Field | Value |
|---|---|
| Issue type | Story |
| Summary | Prescriber changes prescription lifecycle state |
| Epic link | MED-EPIC-01 |
| Status | To Do |
| Priority | Must |
| Story points | 3 |
| Labels | service:medication-service, type:backend, slice:S1 |
| Components | prescription |
| FR references | FR-MED-012 |
| Legacy FR refs | FR-MEDS-001 (discontinued) |
| Dependencies | MED-US-002 |
User story: As a prescriber, when clinical context changes, I want to discontinue, hold, or resume a prescription with a reason so that the medication list reflects current intent.
Acceptance criteria:
- Given active prescription, when
PUT /discontinuewith reason, then statusdiscontinuedandmedication.prescription.discontinued.v1emitted. - Given on-hold prescription, when
PUT /resume, then statusactive. - Given stale ETag, when
PUT, then412.
Technical notes:
- State machine enforced in aggregate; discontinuing terminal.
Definition of Done: tests, OpenAPI, event schema.
MED-US-004 — Renewal and refill
| Field | Value |
|---|---|
| Issue type | Story |
| Summary | Support renewal and refill per policy |
| Epic link | MED-EPIC-01 |
| Status | To Do |
| Priority | Should |
| Story points | 5 |
| Labels | service:medication-service, type:backend, slice:S1 |
| Components | prescription, refill |
| FR references | FR-MED-011 |
| Legacy FR refs | FR-MEDS-011, BR-PHARM-008 |
| Dependencies | MED-US-002 |
User story: As a prescriber, when authorized, I want to add refills or renew a prescription so patients can continue therapy without re-drafting.
Acceptance criteria:
- Given active prescription with refillsAuthorized < regulatory max, when
POST /refill, then increments refillsAuthorized. - Given controlled substance Schedule II, when attempting refill, then
403(no refills on Schedule II per policy).
Technical notes: policy engine checks controlled schedule.
Definition of Done: tests, OpenAPI, audit event.
MED-US-010 — Allergy + drug-drug interaction check
| Field | Value |
|---|---|
| Issue type | Story |
| Summary | Call drug KB for cross-checks before sign |
| Epic link | MED-EPIC-02 |
| Status | To Do |
| Priority | Must |
| Story points | 5 |
| Labels | service:medication-service, type:backend, slice:S1 |
| Components | safety-check, drug-kb |
| FR references | FR-MED-030..031 |
| Legacy FR refs | FR-MEDS-030..031 |
| Dependencies | — |
User story: As a prescriber, before signing, I want drug-drug and drug-allergy checks run automatically so that I do not need to remember every interaction.
Acceptance criteria: KB call occurs, results returned with severity; high-severity alerts block sign; KB version captured.
Definition of Done: unit tests across matrix; p95 < 700ms.
MED-US-011 — Override alert with reason
| Field | Value |
|---|---|
| Issue type | Story |
| Summary | Record override reason + KB snapshot |
| Epic link | MED-EPIC-02 |
| Status | To Do |
| Priority | Must |
| Story points | 3 |
| Labels | service:medication-service, type:backend, slice:S1 |
| Components | override, audit |
| FR references | FR-MED-032 |
| Legacy FR refs | FR-MEDS-032, FR-PHARM-023 |
| Dependencies | MED-US-010 |
User story: As a prescriber or pharmacist, when I intentionally override a safety alert, I want to record a reason so the decision is auditable.
Acceptance criteria: reason ≥10 chars enforced server-side; override event emitted; DetectedIssue filed.
Definition of Done: tests, event schema.
MED-US-012 — Alert-override governance view
| Field | Value |
|---|---|
| Issue type | Story |
| Summary | Governance dashboard of override rates |
| Epic link | MED-EPIC-02 |
| Status | To Do |
| Priority | Should |
| Story points | 3 |
| Labels | service:medication-service, type:analytics, slice:S2 |
| Components | analytics, observability |
| FR references | FR-MED-032 (governance) |
| Legacy FR refs | — |
| Dependencies | MED-US-011 |
User story: As clinical governance, when reviewing alert fatigue, I want an override rate dashboard so I can tune rules.
Acceptance criteria: Grafana panel exists; rate aggregated by alert type and prescriber role.
Definition of Done: dashboard JSON committed; rule-tuning cadence documented.
MED-US-020 — Start reconciliation session
| Field | Value |
|---|---|
| Issue type | Story |
| Summary | Start reconciliation at admission / discharge / transfer |
| Epic link | MED-EPIC-03 |
| Status | To Do |
| Priority | Must |
| Story points | 5 |
| Labels | service:medication-service, type:backend, slice:S1 |
| Components | reconciliation |
| FR references | FR-MED-020 |
| Legacy FR refs | FR-MEDS-020 |
| Dependencies | REG-US-004 (encounter) |
User story: As a prescriber, when a patient transitions care, I want a reconciliation session so I can review and reconcile medications.
Acceptance criteria: session created with type; diff loaded.
Definition of Done: unit + integration tests.
MED-US-021 — Act on reconciliation line items
| Field | Value |
|---|---|
| Issue type | Story |
| Summary | Continue / Discontinue / Modify each line |
| Epic link | MED-EPIC-03 |
| Status | To Do |
| Priority | Must |
| Story points | 5 |
| Labels | service:medication-service, type:backend, slice:S1 |
| Components | reconciliation |
| FR references | FR-MED-021 |
| Legacy FR refs | FR-MEDS-021, BR-MEDS-002 |
| Dependencies | MED-US-020 |
User story: As a prescriber, when reviewing reconciliation, I want per-line actions so the outcome is unambiguous.
Acceptance criteria: action recorded with actor + timestamp; completion freezes decisions.
Definition of Done: tests, event on complete.
MED-US-022 — Record MAR event
| Field | Value |
|---|---|
| Issue type | Story |
| Summary | Nurse records given/held/refused |
| Epic link | MED-EPIC-03 |
| Status | To Do |
| Priority | Must |
| Story points | 3 |
| Labels | service:medication-service, type:backend, slice:S1 |
| Components | mar |
| FR references | FR-MED-022 |
| Legacy FR refs | FR-MEDS-001 (administration) |
| Dependencies | MED-US-002 |
User story: As a nurse, when I administer or hold a medication, I want to record it so the clinical team has an accurate MAR.
Acceptance criteria: immutable entry; corrections linked to prior.
Definition of Done: tests, event schema, FHIR MedicationAdministration.
MED-US-030 — Ingest gateway MedicationRequest
| Field | Value |
|---|---|
| Issue type | Story |
| Summary | Consumer upserts prescription from gateway event |
| Epic link | MED-EPIC-04 |
| Status | To Do |
| Priority | Must |
| Story points | 5 |
| Labels | service:medication-service, type:worker, slice:S2 |
| Components | fulfillment, ingestion |
| FR references | FR-MED-040 |
| Legacy FR refs | FR-PHARM-009 |
| Dependencies | EPRX-US-001 |
User story: As a pharmacy system, when a prescription arrives via the gateway, I want it in my queue within 5s so I can begin fulfillment.
Acceptance criteria: idempotent upsert by gateway_medication_request_fhir_id; consumer lag p95 ≤ 5s.
Definition of Done: inbox test, Pact consumer test.
MED-US-031 — Dispense with inventory decrement
| Field | Value |
|---|---|
| Issue type | Story |
| Summary | Pharmacist completes dispense; atomic decrement |
| Epic link | MED-EPIC-04 |
| Status | To Do |
| Priority | Must |
| Story points | 8 |
| Labels | service:medication-service, type:backend, slice:S2 |
| Components | dispense, inventory |
| FR references | FR-MED-060..062 |
| Legacy FR refs | FR-PHARM-042, BR-PHARM-004 |
| Dependencies | MED-US-030 |
User story: As a pharmacist, when I dispense medication, I want inventory to decrement atomically so stock counts stay accurate.
Acceptance criteria: single DB transaction; idempotency key prevents double-dispense; expired/recalled lot blocked; post to gateway on success.
Definition of Done: integration test for atomicity; chaos test for NATS partition.
MED-US-032 — Partial dispense + return
| Field | Value |
|---|---|
| Issue type | Story |
| Summary | Partial fulfillment and returns/undispense |
| Epic link | MED-EPIC-04 |
| Status | To Do |
| Priority | Should |
| Story points | 5 |
| Labels | service:medication-service, type:backend, slice:S2 |
| Components | dispense |
| FR references | FR-MED-063..064 |
| Legacy FR refs | FR-PHARM-043..044 |
| Dependencies | MED-US-031 |
User story: As a pharmacist, when only partial stock is available, I want partial-dispense; and I want to record returns.
Acceptance criteria: status partially-dispensed; return creates inventory credit with reason.
Definition of Done: tests, events.
MED-US-033 — Post MedicationDispense to gateway
| Field | Value |
|---|---|
| Issue type | Story |
| Summary | Publish dispense to national interop gateway |
| Epic link | MED-EPIC-04 |
| Status | To Do |
| Priority | Must |
| Story points | 5 |
| Labels | service:medication-service, type:backend, slice:S2 |
| Components | gateway, outbox |
| FR references | FR-MED-040 |
| Legacy FR refs | FR-PHARM-009 |
| Dependencies | MED-US-031, EPRX-US-011 |
User story: As an integration flow, when dispense completes locally, I want it posted to the gateway so the interop record is complete.
Acceptance criteria: outbox publish; retries with DLQ; pact against gateway.
Definition of Done: pact test, DLQ metric.
MED-US-040 — Receive stock (GRN)
| Field | Value |
|---|---|
| Issue type | Story |
| Summary | Pharmacy technician receives stock with lot/expiry |
| Epic link | MED-EPIC-05 |
| Status | To Do |
| Priority | Must |
| Story points | 3 |
| Labels | service:medication-service, type:backend, slice:S2 |
| Components | inventory, grn |
| FR references | FR-MED-070..071 |
| Legacy FR refs | FR-PHARM-050..051 |
| Dependencies | — |
User story: As a pharmacy technician, when new stock arrives, I want to record lot and expiry so dispensing honors them.
Acceptance criteria: creates stock item; emits medication.inventory.stock_received.v1.
Definition of Done: tests.
MED-US-041 — Reorder and expiry alerts
| Field | Value |
|---|---|
| Issue type | Story |
| Summary | Alerts when stock low or expiry approaching |
| Epic link | MED-EPIC-05 |
| Status | To Do |
| Priority | Must |
| Story points | 3 |
| Labels | service:medication-service, type:worker, slice:S2 |
| Components | inventory, alerts |
| FR references | FR-MED-073..074 |
| Legacy FR refs | FR-PHARM-053..054 |
| Dependencies | MED-US-040 |
User story: As a pharmacist, when stock is low or expiring, I want an alert so I can act before stock-out or waste.
Acceptance criteria: scheduled job emits alerts; dedup per day per lot.
Definition of Done: tests, scheduling documented.
MED-US-042 — Recall blocks dispense
| Field | Value |
|---|---|
| Issue type | Story |
| Summary | Mark lot recalled; dispense hard-blocks |
| Epic link | MED-EPIC-05 |
| Status | To Do |
| Priority | Must |
| Story points | 5 |
| Labels | service:medication-service, type:backend, slice:S2 |
| Components | inventory, recall |
| FR references | FR-MED-075 |
| Legacy FR refs | FR-PHARM-055 |
| Dependencies | MED-US-040 |
User story: As a pharmacist-in-charge, when a lot is recalled, I want dispense blocked so no patient receives recalled product.
Acceptance criteria: dispense attempt against recalled lot returns 409 LOT_UNAVAILABLE; DetectedIssue emitted.
Definition of Done: tests, audit event.
MED-US-050 — Counter-sign for Schedule II dispense
| Field | Value |
|---|---|
| Issue type | Story |
| Summary | Second pharmacist counter-signs Schedule II dispense |
| Epic link | MED-EPIC-06 |
| Status | To Do |
| Priority | Must |
| Story points | 5 |
| Labels | service:medication-service, type:backend, slice:S3 |
| Components | controlled-substance, dispense |
| FR references | FR-MED-050 |
| Legacy FR refs | FR-PHARM-033, BR-PHARM-005 |
| Dependencies | MED-US-031 |
User story: As a regulator, when Schedule II is dispensed, I want a distinct counter-sign so chain-of-custody is clear.
Acceptance criteria: counter-sign actor ≠ dispenser; dispense without counter-sign returns 403 COUNTER_SIGN_REQUIRED.
Definition of Done: tests, enhanced audit event.
MED-US-051 — Step-up MFA for controlled-substance prescribe
| Field | Value |
|---|---|
| Issue type | Story |
| Summary | Require MFA step-up for CS prescription sign |
| Epic link | MED-EPIC-06 |
| Status | To Do |
| Priority | Must |
| Story points | 5 |
| Labels | service:medication-service, type:backend, slice:S3 |
| Components | controlled-substance, identity |
| FR references | FR-MED-051 |
| Legacy FR refs | BR-PHARM-003 |
| Dependencies | IDENT-US-040 |
User story: As a prescriber, when signing a Schedule II order, I want a fresh MFA challenge so only I can sign.
Acceptance criteria: JWT amr must include recent MFA factor; else 403.
Definition of Done: tests, identity integration.
MED-US-052 — Disclosure-accounting export
| Field | Value |
|---|---|
| Issue type | Story |
| Summary | Export CS disclosure-accounting ledger per regulator format |
| Epic link | MED-EPIC-06 |
| Status | To Do |
| Priority | Should |
| Story points | 5 |
| Labels | service:medication-service, type:export, slice:S3 |
| Components | controlled-substance, compliance |
| FR references | FR-MED-091..092 |
| Legacy FR refs | FR-PHARM-072 |
| Dependencies | MED-US-050 |
User story: As a compliance officer, when regulators request a disclosure-accounting, I want a one-click export so I can respond within the required window.
Acceptance criteria: async job; file signed; retention respected.
Definition of Done: tests; sample MoPH export verified.
MED-US-060 — Pharmacy portal offline queue
| Field | Value |
|---|---|
| Issue type | Story |
| Summary | Portal accepts dispense actions offline for 4h+ |
| Epic link | MED-EPIC-07 |
| Status | To Do |
| Priority | Should |
| Story points | 8 |
| Labels | service:medication-service, type:ui, slice:S3 |
| Components | portal, offline |
| FR references | FR-MED-081 |
| Legacy FR refs | FR-PHARM-061, NFR-PHARM-005 |
| Dependencies | IDENT-US-020 |
User story: As a pharmacist in a low-connectivity site, when internet fails, I want to continue dispensing so care does not halt.
Acceptance criteria: offline queue retains 4h; idempotency prevents duplicate on sync; banner at 3h.
Definition of Done: E2E offline test.
MED-US-061 — Label printing
| Field | Value |
|---|---|
| Issue type | Story |
| Summary | Print prescription + dispense labels directly |
| Epic link | MED-EPIC-07 |
| Status | To Do |
| Priority | Should |
| Story points | 3 |
| Labels | service:medication-service, type:ui, slice:S3 |
| Components | portal, label |
| FR references | FR-MED-084 |
| Legacy FR refs | FR-PHARM-064 |
| Dependencies | MED-US-031 |
User story: As a pharmacist, when I dispense, I want to print a label so the patient receives correct directions.
Acceptance criteria: PDF generated <4s p95; RTL/LTR rendered correctly.
Definition of Done: tests.
MED-US-062 — Device binding for portal
| Field | Value |
|---|---|
| Issue type | Story |
| Summary | Bind portal device for offline PHI encryption |
| Epic link | MED-EPIC-07 |
| Status | To Do |
| Priority | Must |
| Story points | 5 |
| Labels | service:medication-service, type:security, slice:S3 |
| Components | portal, identity |
| FR references | FR-MED-082 |
| Legacy FR refs | FR-PHARM-063 |
| Dependencies | IDENT-US-030 |
User story: As a security officer, when portals go offline, I want device-bound encryption so PHI is protected on stolen devices.
Acceptance criteria: device key provisioned; cache encrypted at rest; revocation within 5 min online.
Definition of Done: security test.
MED-US-070 — NCPDP SCRIPT adapter (optional)
| Field | Value |
|---|---|
| Issue type | Story |
| Summary | Optional NCPDP NewRx / CancelRx adapter |
| Epic link | MED-EPIC-08 |
| Status | To Do |
| Priority | Could |
| Story points | 8 |
| Labels | service:medication-service, type:integration, slice:S4 |
| Components | ncpdp |
| FR references | FR-MED-041 |
| Legacy FR refs | FR-PHARM-010, BR-PHARM-010 |
| Dependencies | INTEROP-US-020 |
User story: As a retail pharmacy partner using NCPDP, when I receive a prescription, I want NewRx over SCRIPT so legacy integration works.
Acceptance criteria: NewRx sent; retry with DLQ; prescriber notified on final failure.
Definition of Done: adapter tests with fixture messages.
MED-US-071 — HL7 v2 RDE / RDS for legacy pharmacies
| Field | Value |
|---|---|
| Issue type | Story |
| Summary | HL7 v2 adapter via hl7v2-interop |
| Epic link | MED-EPIC-08 |
| Status | To Do |
| Priority | Could |
| Story points | 5 |
| Labels | service:medication-service, type:integration, slice:S4 |
| Components | hl7v2 |
| FR references | FR-MED-042 |
| Legacy FR refs | — |
| Dependencies | INTEROP-US-030 |
User story: As a legacy hospital pharmacy system operator, when new orders are placed, I want RDE^O11 messages so I can integrate without FHIR uplift.
Acceptance criteria: message generated + routed; RDS^O13 ack consumed back.
Definition of Done: integration tests.