Pharmacy Reference Public

Administration

Administer the ERPat Pharmacy module: the full permission families and who should hold them, branch-profile operating rules, the two advanced sub-toggles, the pharmacist-on-duty gate, data-privacy controls, the audit trail, cron jobs, dashboard widgets, the dual read-only API, migrations and the demo seeder.

Guide version: r1 Module version: 1.0.0 Updated: 2026-07-23 Estimated time: 10 min
ERPat · Pharmacy Guide

Administration

Everything an administrator sets up once and rarely touches again: who can see and change what, the per-branch operating rules, the two advanced areas that stay off until validated, the data-privacy controls, the audit trail, the scheduled jobs and dashboard widgets, the read-only API surface, and the migration and seeding commands that stand a tenant up.

Menu group: Pharmacy  |  Enable flag: module_pharmacy = 1  |  Sub-toggles: module_pharmacy_controlled, module_pharmacy_quality

Permissions & roles

Pharmacy contributes its own RBAC keys to the Roles editor while the module is enabled. The base pharmacy key is the module Enable toggle; the rest gate specific surfaces. Keys are serialized into a user's permissions by name, so they are frozen once shipped. Administrators bypass every check.

Permission keyGrantsSuggested holders
pharmacyBase access and the Overview command center. Without it the Pharmacy menu never appears.Every pharmacy staff member.
pharmacy_compliance (+_create/_update/_delete)The Compliance Center tabs — licenses, credentials, renewal tasks, inspection packs — and their CRUD.Compliance officer, branch manager, PIC.
pharmacy_dutyStart and end pharmacist duty sessions — the source of the on-duty gate.Pharmacists, shift supervisors, the PIC.
pharmacy_product (+_create/_update/_delete)The Drug Catalog, generic groups, regulatory verifications and supplier licensing.Pharmacist, inventory lead.
pharmacy_expiryThe read-only Near-Expiry & Lots dashboard.Pharmacist, inventory lead.
pharmacy_prescription (+_create/_update/_delete)View the Rx queue; capture, edit and delete prescriptions.Assistants (capture), pharmacists.
pharmacy_prescription_validateValidate or reject a submitted prescription (the pharmacist's check).Pharmacists only.
pharmacy_dispenseRecord dispensing fills against a validated Rx.Pharmacists.
pharmacy_patient (+_create/_update/_delete)Patients & prescribers and their representatives / medication profile.Front desk, pharmacists.
pharmacy_patient_view_sensitiveUnmask a patient's full ID number (an audited action).Pharmacist / PIC only.
pharmacy_controlled_view / _post / _correctView the controlled register; post entries and reconcile; post reversing corrections. (Needs the sub-toggle.)Pharmacist-in-Charge.
pharmacy_quality (+_create/_update/_delete)Recalls, adverse events, cold-chain records and their CRUD. (Needs the sub-toggle.)Quality lead, PIC.
pharmacy_recall_manageActivate / close a recall (which quarantines the batch).PIC / owner.
pharmacy_settingsBranch profiles and the advanced sub-toggles.Owner / branch manager only.
Principle of least privilege. Reserve pharmacy_settings, pharmacy_patient_view_sensitive, pharmacy_compliance_delete, pharmacy_recall_manage and the controlled-register keys for owners/managers/PIC. Most counter staff need only a subset of the compliance, prescription and patient families.

Branch profiles & operating rules

A branch profile (one per pharmacy location, created in Pharmacy → Settings) holds that store's operating profile and the rule flags the module reads. Multi-branch operators keep a separate profile per store; single-branch installs keep one. Every save is written to the audit trail.

FlagDefaultWhat it does
Operating profileCommunity drugstorecommunity_drugstore / non_prescription / institutional.
Deployment modeMicromicro / multi_branch / enterprise — a sizing hint.
Pharmacist-in-ChargeThe staff user recorded as the branch PIC.
Require pharmacist on dutyOnThe hard gate for regulated dispensing (see below). Leave on for a licensed drugstore.
FEFOOnFirst-Expiry-First-Out handling of medicine stock — reuses Warehouse's lot/batch/expiry engine.
Senior Citizen booklet requiredOffOff by default per FDA Circular 2025-005. Turn on only if your policy still requires it.
PWD booklet requiredConfigurablePer-branch to match your PWD-discount recording policy.
Controlled-drug register (branch flag)OffA per-branch marker; the register itself is gated by the module sub-toggle (below).
Prescription retention (years)5 (minimum enforced at 2)How long dispensing records are kept. The statutory minimum is 2 years.
Expiry warning horizon (days)180How far ahead a license or credential is flagged as "expiring" on the Overview and in reminders.
Two flags are safety switches, not conveniences. The Senior booklet defaults to off to match current FDA guidance, and the controlled-register area defaults to off so it can never be half-configured. Change them only with the corresponding real-world procedure in place.

Advanced sub-modules (Settings → Advanced Modules)

Two regulated areas are gated by their own tenant settings, editable in Pharmacy → Settings → Advanced Modules (needs pharmacy_settings). Both default off. While a sub-toggle is off, its menu item is hidden and a direct URL to its controller is redirected — so the area can never be reached half-configured.

Sub-moduleSettingEnables
Controlled-drug registermodule_pharmacy_controlledThe append-only RA 9165 ledger, running balance, correction-by-reversal, and physical-count reconciliation.
Quality & Safetymodule_pharmacy_qualityRecalls / stop-sale (batch quarantine), adverse events, and cold-chain temperature logging.

Toggling either is itself written to the audit trail. Enable each only after your tenant validates the corresponding compliance procedure (PDEA / DDB for the controlled register; your pharmacovigilance and cold-chain SOPs for Quality & Safety).

The pharmacist-on-duty gate

A community pharmacy must have a credentialed pharmacist physically present whenever prescription medicines are dispensed. Pharmacy makes that a first-class, enforceable state rather than a paper log:

  • Going on duty needs a valid credential. A pharmacist can only start a duty session if they have a non-expired PRC / PIC credential on file. The session records who, when, at which branch/counter, and which credential authorised it.
  • The session is the gate. Whether a pharmacist is on duty is derived from these sessions, and it is the exact signal that prescription validation and dispensing block on when Require pharmacist on duty is enabled — validate and record-fill both refuse with a "pharmacist required" message when nobody is on duty.
  • Every start/end is audited at warning severity, so the on-duty record is part of the permanent trail.
If a pharmacist can't go on duty, check that they have a credential on file that has not expired — that is the single precondition. Grant pharmacy_duty to the people who actually work the counter.

Data privacy — patient IDs (RA 10173)

Retail patient records carry a government ID number (Senior / PWD / PhilHealth), which is personal data under the Data Privacy Act. Pharmacy handles it defensively:

  • Encrypted at rest. The full ID number is stored as an AES-256-GCM envelope; only the last 4 digits are kept in the clear for display.
  • Masked by default. Lists and the patient view show only a masked last-4 — never the full number.
  • Permission-gated, audited unmasking. Reading the full number requires pharmacy_patient_view_sensitive and writes a "full patient ID was viewed" entry to the audit trail against the user who revealed it.
  • Edit-safe. The ID field is never pre-filled on the edit form; leaving it blank keeps the stored value, so an edit never accidentally overwrites or exposes it.

Grant pharmacy_patient_view_sensitive sparingly — to the pharmacist/PIC roles that genuinely need to verify an ID at the counter.

Audit trail (system_logs)

Every mutating action in the module writes a system_logs entry through registered action:component keys. These labels are merged into the core log config ungated by active state, so historical entries still resolve even if the module is later disabled. Filter the core Activity Logs by the Pharmacy category to review who changed what and when.

AreaLogged actionsNotable severity
Licenses & Credentialscreate · update · deletecritical on delete
Pharmacist Dutysession started / endedwarning
Renewal Tasks / Inspection Packscreate · update · delete / generate · deleteinfo
Branch profile / settingscreate · update (incl. sub-toggle changes)warning on update
Drug Catalog / Generic Groups / Suppliers / Verificationscreate · update · deleteinfo
Patientscreate · update · delete · view (unmasked ID)warning on delete
Prescriptions / Dispensingcreate · status (submit/validate/reject/cancel) · delete / fill createdwarning on status
Statutory benefitbenefit decision appliedinfo
Controlled registerentry / correction / reconciliation postedwarning
Recalls / Adverse eventscreate · update (recall activate)warning (recall)

Deletes are soft deletes, so a removed record is still recoverable and still present in the trail. The controlled register additionally never deletes at all — a correction is a linked reversing entry.

Cron jobs (per-tenant, daily)

Two scheduled jobs run per tenant. Both self-gate on module_pharmacy (a disabled tenant is a clean no-op) and fire best-effort notifications; the near-expiry job also skips cleanly when the Warehouse tables are absent.

Job (slug)ScheduleWhat it does
pharmacy_license_expiryDaily, 07:00Flags FDA licenses, permits and pharmacist credentials expiring within 30 days (or already expired) and notifies.
pharmacy_near_expiry_stockDaily, 06:30Scans pharmacy-classified stock lots expiring within 90 days from the Warehouse batch ledger and notifies.

They are discovered automatically by the cron runtime and appear in php erpat cron:list. Per-event notification templates and recipients are a follow-up; the jobs currently raise a best-effort notification.

Dashboard widgets

Two staff dashboard widgets ship with the module, both gated on module_pharmacy + the pharmacy_compliance permission and on by default:

  • Compliance Readiness — the branch readiness score at a glance.
  • Expiring Licenses & Credentials — what's lapsing soon.

Users add or remove them like any other dashboard widget; they render through the core Dashboard.

The dual read-only API

Pharmacy exposes two read-only API surfaces. Neither exposes patient, dispensing or controlled-register data. A tenant with the module disabled returns 404 module_unavailable (hiding the resource's existence) rather than a 403.

SurfaceBase pathAuth & gatingEndpoints
Integration API (client / machine-to-machine) /api/v1/pharmacy Bearer token with the pharmacy:read scope. GET /products, GET /products/{id}, GET /compliance (aggregate readiness counts only)
End-User API (employee self-service) /v1/api/pharmacy Authenticated employee, gated by module_pharmacy + the matching web permit (pharmacy_product / pharmacy_expiry). GET /products, GET /products/{id}, GET /near-expiry (needs the Warehouse WMS; returns an empty list cleanly without it)

The pharmacy:read scope appears in the API client-management UI as "Pharmacy (Read)". Both surfaces are paginated (?page, ?per_page) and support a ?classification filter on the product list; near-expiry accepts ?within (days).

Migrations & the demo seeder

The module owns its schema and ships its slices as six timestamped migrations, tracked in the per-module migrations_pharmacy table (separate from the core migrations table). Run them with:

php erpat migrate:modules

  • Compliance tables — branch profiles, licenses, credentials, duty sessions, compliance tasks, inspection packs.
  • Catalog tables — product profiles, generic groups, regulatory verifications, supplier profiles.
  • Prescription tables — patients, representatives, prescribers, prescriptions, items, documents, dispensing records and items.
  • Benefit tables — benefit rule versions, decisions, and per-line calculations.
  • Safety tables — controlled ledger, reconciliations, recall cases, adverse events, temperature readings.
  • Patient ID privacy hardening — the encryption-at-rest columns for patient IDs.

Every migration is idempotent (guarded creates and index checks) and reversible, and runs against the primary database and every tenant database. No stock, payment, supplier or product master tables are created — those live in the modules Pharmacy reuses.

Demo seeder

Load realistic sample data into a fresh install with php erpat db:seed PharmacyDemo, and roll it back out (a soft delete of exactly what it inserted) with php erpat db:seed PharmacyDemo --remove. It is idempotent and reversible, and also available from the tenant Bulk Database Seeder modal.

Reuse boundaries & multi-tenancy

Per ERPat's no-duplication rules, Pharmacy never stands up a second stock ledger, payment ledger, supplier master or product catalog. It reads and writes through the modules that already own those concerns:

ReusesForPharmacy never clones
POSThe retail register and BIR receipt series the dispensing receipt reuses.A separate sales register / receipt numbering.
WarehouseThe stock ledger and lot/batch/expiry (FEFO) engine; the FEFO stock issue and recall batch quarantine compose its public API.A second stock ledger or expiry engine.
ProcurementPurchase orders, goods receipts, and the vendor master (extended with supplier FDA-LTO licensing).A parallel supplier master.
Finance (tax)The VAT / tax master for pharmacy line taxes.Its own tax tables.
ClientsThe customer master (retail patient records are kept separately for privacy).A duplicate customer table.

The catalog, near-expiry dashboard and FEFO dispensing all degrade gracefully — with a clear notice — when the Warehouse (WMS) or Procurement module is not installed.

Multi-tenancy. Everything Pharmacy stores lives in each company's own tenant database, so branches, licenses, credentials, patients, prescriptions and settings are fully isolated between tenants. Because migrate:modules runs across the primary DB and every tenant DB, each tenant that enables the module gets the same schema — enable and configure Pharmacy per tenant.

Readiness, not certification. A green Compliance Center reflects that your configured evidence is complete. It is not a government certification and remains subject to FDA / PRC / BIR validation.

Next: Reference → — the controllers, routes, permission keys, tables and API endpoints.

Was this guide helpful?

Report a content problem