# Paid Services ↔ Portal Permissions Sync

## Date: 2026-02-23

## Goal

When `admin/paid_services.cgi` updates a service for any employer, keep `portal_permissions` in sync so both tables reflect the same state.

## Design

### Definitions

- **Admin with invoice**: `invoices.employer_id` for the `associated_invoice` on the form
- **Quantity-based service**: `proposal_options.has_quantity = 1`
- **Date-based service**: `proposal_options.has_quantity = 0`

### Case 1: Employer IS the Admin with the invoice

- Create/update `portal_permission` with form values (end_date, qty, used)
- No `paid_services` write needed (already handled by existing DELETE+INSERT)

### Case 2: Employer is NOT the Admin

#### 2a — New permission (no portal_permission exists for this employer+service)

**If admin already has a portal_permission for this service:**
1. Add form's qty/used to admin's existing qty/used
2. Copy admin's updated totals back to employer's new permission
3. `end_date` = admin's end_date
4. Sync both employer and admin to `paid_services`

**If neither employer nor admin has a portal_permission:**
1. Look up `invoices.service_expiration` for the invoice_id from the form
2. Create both employer and admin permissions with form values + invoice_id, using invoice expiration as end_date
3. Create `paid_services` record for admin for that service

#### 2b — Existing permission, date-based service

- Update employer's `portal_permission` end_date → admin's end_date
- Update employer's `paid_services` expire_date → admin's end_date

#### 2c — Existing permission, quantity-based service

- Set both employer's and admin's `portal_permission` to form values (qty, used)
- `end_date` = admin's end_date on employer's record
- Sync both to `paid_services`

## Files to Modify

1. **`admin/paid_services.cgi`** — Add `syncPortalPermissions()` call after each INSERT in `updateService()`
2. **`cgi-bin/lib/HBCU/Portal/Common.pm`** — New function `syncFromPaidServices()` implementing the cases above
