# Caresoft Retail Pharmacy Platform

**1,602 assertions across 49 suites, 0 failing.** One command proves it:

```bash
bash bin/rx-test
```

That drops a throwaway database, applies all 29 schema packs, runs every
suite, and exits non-zero on the first failure. If it is green, the
platform installs, bills, prints, syncs, backs up and restores.

---

## What is in here

| Folder | What it is |
|---|---|
| `lib/` | 30 engine modules. PHP 8.3, PDO, no framework, no Composer. |
| `public/` | 16 screens, all executed under jsdom on every build. |
| `schema/` | 29 migration packs, 111 tables. Applied in order by `bin/rx-migrate`. |
| `tests/` | 49 suites. `tests/screens/` drives each screen against mocked responses. |
| `bin/` | `rx-install`, `rx-migrate`, `rx-test`, `rx-worker`, `rx-release`, `rx-tool`. |
| `CounterHelper/` | The Windows service that gets bytes onto paper. Build and install scripts included. |
| `release/` | A signed 1.0.0 package plus its manifest and public key. |

**Read `README.md` first**, then `PENDING.md` — that one is the honest
register of what is not done and why.

---

## Installing

**The server is Linux. The counter is Windows.** That split is
deliberate: raw printing goes through `winspool.drv`, which is how an old
LX-300 on a parallel-to-USB adapter gets bytes without a driver dialog.
It cannot be done from the server.

**On the Linux server:**

```bash
cp config.sample.php config.php     # then edit it
bash bin/rx-install
```

`config.php` reads credentials from the environment and ships with none
filled in. It belongs outside the document root and must never be
committed.

**On each Windows counter**, from the `CounterHelper` folder:

```
build-counter-helper.cmd            (once, on a machine with the .NET 8 SDK)
install-counter-helper.cmd          (per till, as Administrator)
```

The installer registers the service, starts it on boot, sets
restart-on-failure, and blocks remote access to the loopback port. It is
safe to re-run.

Then copy the token from `C:\ProgramData\CaresoftCounter\helper.json`
into the setup wizard. **Nothing prints until the node and the helper
share that token.**

---

## Before a shop uses this

Three things cannot be verified from a build environment, and no amount
of test coverage substitutes for them:

1. **No byte has ever reached paper.** `winspool.drv` is Windows-only and
   has never executed. Work the checklist that `build-counter-helper.cmd`
   prints — including five genuinely old LX-300s sourced from real shops,
   not new stock.

2. **Nobody has opened a screen in a browser.** Layout width, contrast
   ratio, type size and F-key bindings are all measured on every build. A
   ratio is not a judgement: whether the hierarchy reads at a glance
   during a rush needs a person and half a day.

3. **Every test runs on data I generated.** The matcher declines rather
   than guesses against 6,000 deliberately confusable items, but the real
   auto-match rate on a real catalogue is unknown until a real catalogue
   is imported.

**The kill criterion is unchanged.** If the counter is not faster than
what the chemist already has, on the same hardware in the same shop, stop
the roadmap and fix that before anything else.

---

## The signing key is not in this archive

`release/` holds the package, its manifest and the **public** key. The
private key was generated during the build and deliberately left out —
a store accepts any package that key signs, so the key *is* the update
channel. Generate your own and keep it on one machine:

```bash
php bin/rx-release keygen /secure/path
```

Losing it means every shop stops updating. Leaking it means somebody else
can update every shop.
