# MedNotify — Notifications shared service (SIFA plan P2-T1)

A single `notify()` service every medical module calls. It **wraps** the ERP's
existing delivery machinery — it never re-implements it:

| Channel   | Delivered through                                                        |
|-----------|--------------------------------------------------------------------------|
| `in_app`  | Laravel database notifications (core `notifications` table) → the bell    |
| `email`   | The same pathway `NotificationUtil::autoSendNotification` uses (business `email_settings` + `CustomerNotification`) |
| `sms`     | `NotificationUtil::sendSms()` (business `sms_settings`; Nexmo / Twilio / generic HTTP) |
| `whatsapp`| The WhatsApp module's `WaSender` — **only if that module is installed**, else soft-skipped |

Every adapter is **fail-soft**: it catches internally, logs a `failed` row and
never throws. A missing WhatsApp module, unconfigured SMS, or a bad recipient
degrades gracefully.

The core ERP is never modified. All tables are prefixed `mednotify_`, every
per-tenant row carries `business_id`, and all queries scope by the session
business id.

## Sending a notification

```php
use Modules\MedNotify\Services\MedNotify;

app(MedNotify::class)->notify('appointment.created', [
    'business_id' => $businessId,           // required
    'users'       => [$doctorUserId],       // ERP users.id (optional)
    'contacts'    => [$patientContactId],   // ERP contacts.id (optional)
    'variables'   => [                       // {placeholders} for the template
        'patient_name'     => $patient->name,
        'doctor_name'      => $doctor->name,
        'facility_name'    => $facility->name,
        'appointment_date' => $appt->start_at,
        'link'             => route('...'),  // used as the in-app deep link
    ],
    'channels'    => ['in_app', 'sms'],      // OPTIONAL — overrides the event default
]);
```

`{business_name}`, `{user_name}` and `{contact_name}` are always resolvable in
templates in addition to the variables you pass.

### Flow

1. **Resolve the event** (`mednotify_events`) for the business (business row overrides the system row).
2. For each **channel**: resolve the template (business override → system default; skip if disabled/absent).
3. For each **recipient**: check the user's **preference** (disabled → skip). If the user is in **quiet hours** or on an **hourly/daily digest**, the message is queued to `mednotify_digest_queue` instead of sent.
4. Otherwise **send via the channel adapter** and write a `mednotify_log` audit row.

## Registering events from another module

Modules add their own events so they surface in the Templates + Preferences UI.
Insert a **system** row (`business_id = null`) plus a system-default template
per channel — idempotently — from your module's seeder:

```php
use Modules\MedNotify\Entities\MednotifyEvent;
use Modules\MedNotify\Entities\MednotifyTemplate;

MednotifyEvent::updateOrCreate(
    ['business_id' => null, 'event_key' => 'lab.critical_value'],
    [
        'module_key'          => 'LabCore',
        'label'               => 'Critical lab value',
        'description'         => 'A result crossed a critical bound.',
        'default_channels'    => ['in_app', 'sms'],
        'available_variables' => [
            ['name' => 'patient_name', 'label' => 'Patient name'],
            ['name' => 'test_name',    'label' => 'Test name'],
        ],
        'is_active'           => true,
    ]
);

MednotifyTemplate::updateOrCreate(
    ['business_id' => null, 'event_key' => 'lab.critical_value', 'channel' => 'in_app'],
    ['subject' => 'Critical value', 'body' => 'Critical {test_name} for {patient_name}.', 'enabled' => true]
);
```

Then just call `app(MedNotify::class)->notify('lab.critical_value', [...])`.

## Digests

`mednotify:flush-digests {hourly|daily}` groups queued rows per user/channel
into one summary send. Scheduled automatically (hourly + daily at 07:00) by the
service provider.

## Install

Superadmin → Modules → MedNotify → Install. This runs the migrations, publishes
assets, seeds the 5 system events + templates, writes the lowercase
`mednotify_version` system property (the core install gate) and assigns the
`mednotify.*` permissions to the Admin role.

## Permissions

- `mednotify.templates.manage` — edit templates
- `mednotify.preferences.manage_own` — manage own preferences
- `mednotify.log.view` — view the delivery log
