# Leads Edit Invoice Packages Integration Plan

## Goal

Update the lead edit invoice flow so track invoices work with pricing packages as described in `docs/track-pricing-package-flow.md`.

The target invoice price lookup is:

```text
track_id + track_type_id + pricing_plan_type_id + package_id
```

Invoice creation must use the saved `track_pricing_plans.price` for the selected package, then apply the pricing row discount. It must not multiply the selected pricing row price by `levels_count` again.

The database and migrations are already ready. This plan only covers integrating packages into the existing lead edit invoice flow.

## Current State

- The lead edit screen renders the invoice modal from `resources/views/dashboard/leads/modals/invoice-modal.blade.php`.
- The modal supports selecting invoice type, track category, track, track type, level count, payment type, deposit, referral discount, referral promo code, and promo code.
- The modal does not expose a package selector.
- `InvoiceRequest` validates `levels_count` but does not validate `package_id`.
- `InvoiceCreationService` builds invoice data with `levels_count` and `track_type_id`, but not `package_id`.
- `InvoicePreviewService` validates preview payload without `package_id`.
- `InvoicePricingService` looks up track pricing by `pricing_plan_type_id`, `track_id`, and `track_type_id` only.
- `InvoicePricingService` currently calculates track final price as `pricingPlan.price * levels_count`, which conflicts with the package flow doc because package pricing rows already store final package price.
- Package database structure already exists and should be used as-is.

## Integration Assumptions

1. Use the existing package table/model.
   - Required package fields for this flow: `id`, display name, and `levels_count`.
   - Load all active/available packages for the invoice form.

2. Use the existing `track_pricing_plans` schema.
   - Expected columns: `track_id`, `track_type_id`, `pricing_plan_type_id`, `package_id`, `level_price`, `price`, `discount_percentage`.
   - Pricing rows are already unique by:

```text
track_id, track_type_id, pricing_plan_type_id, package_id
```

3. Use the one-level package as the starting point.
   - The one-level package is the package where `levels_count = 1`.
   - Admin pricing starts from the one-level row.
   - Multi-level package prices are already saved in `track_pricing_plans.price`.
   - Invoice creation must use the selected package row directly and must not recalculate multi-level package totals from `levels_count`.

4. Persist selected package on invoices using the existing invoice `package_id` column.

## Backend Implementation

1. Lead edit view data
   - Load all packages in `LeadEditService::editViewData()`.
   - Pass them to `dashboard.leads.edit`.
   - Keep the list small: only `id`, display name, and `levels_count`.
   - Sort the one-level package first so it is the starting/default package.

2. Invoice request validation
   - Add `package_id` to `InvoiceRequest`.
   - Required only when `type=track`.
   - Validate that it exists in the existing package table.
   - For track invoices, validate that `product_id`, `track_type_id`, and `package_id` are present.
   - Keep `levels_count` for compatibility with existing invoice usage, but derive it from the selected package for track invoices.

3. Invoice data construction
   - Add `package_id` to `InvoiceCreationService::buildInvoiceDataFromRequest()`.
   - For track invoices, set `levels_count` from `package.levels_count`.
   - For product invoices, preserve current `levels_count` behavior.

4. Pricing lookup
   - Update `InvoicePricingService::findTrackPricingPlan()` to include `package_id`.
   - Lookup criteria:

```php
[
    'pricing_plan_type_id' => $lead->plan_type_id,
    'track_id' => $invoiceData['product_id'],
    'track_type_id' => $invoiceData['track_type_id'],
    'package_id' => $invoiceData['package_id'],
]
```

5. Pricing calculation
   - Replace track final price calculation with saved package price:

```php
$finalPrice = (float) $pricingPlan->price;
```

   - Apply `discount_percentage` from the pricing row directly to `$finalPrice`.
   - Do not multiply by `levels_count` for track package invoices.
   - Keep product invoice calculation unchanged.
   - If legacy `discount_plan_id` still exists in old rows, do not let it override package `discount_percentage` for the package flow.

6. Preview pricing
   - Add `package_id` validation in `InvoicePreviewService`.
   - Pass `package_id` into `InvoicePricingService::previewBasePricing()`.
   - Use the same saved package price and discount calculation as final invoice creation.
   - Return package price details in the JSON only if useful for the sidebar.

7. Finance integration
   - Include `package_id` in local invoice attributes.
   - Ensure `CrmFinanceIntegrationService::attachCrmMetadata()` persists it through its existing `Schema::hasColumn()` filter.
   - Include package in `bookingReference()` for non-scheduled track invoices to avoid idempotency collisions when the same lead/student/track can buy different packages.

## Frontend Implementation

1. Add package selector to the invoice modal.
   - Show only for `type=track`.
   - Hide and disable for `type=product`.
   - Options should include package name and level count.
   - Load all packages.
   - Put the one-level package first and select it by default when track invoice data is ready.

2. Replace manual level count for track packages.
   - When a package is selected, set the hidden or readonly `levels_count` value from the package `levels_count`.
   - For track invoices, users select Package instead of typing arbitrary level count.
   - Keep product invoice level count unchanged.

3. Update price preview payload.
   - Add `package_id`.
   - Require `package_id` before previewing track invoices.
   - Trigger preview refresh when package changes.

4. Update client-side submit validation.
   - Track invoices require track, track type, and package.
   - Product invoices keep current category/product validation.

5. Update invoice sidebar labels if needed.
   - Rename `Base Price` to `Package Price` for track invoices if the UI needs clarity.
   - Keep discount lines compatible with existing promo/referral UI.

## Compatibility Notes

- Existing invoices without `package_id` should remain readable.
- Existing track invoices created before this change should not be recalculated.
- New track invoices require a package.
- Existing package pricing data is authoritative.
- If there are scheduled course invoices, confirm whether the schedule implies a fixed package or whether staff still chooses a package manually.

## Testing Plan

1. Unit test `InvoicePricingService`.
   - Finds pricing with exact `track_id`, `track_type_id`, `pricing_plan_type_id`, and `package_id`.
   - Does not match pricing rows with a different package.
   - Uses saved `price` directly.
   - Applies `discount_percentage` correctly.

2. Feature test invoice preview.
   - Track preview fails when `package_id` is missing.
   - Track preview returns expected package price and net price.
   - Product preview still works without package.

3. Feature test invoice creation.
   - Track invoice saves `package_id`.
   - Track invoice saves `levels_count` from the selected package.
   - Track invoice total/net price match preview.

4. Regression test existing product invoice flow.
   - Product price still equals product price multiplied by submitted `levels_count`.

5. Manual browser test on lead edit.
   - Open lead edit.
   - Create a track invoice.
   - Select category, track, track type, package, payment type.
   - Confirm preview updates.
   - Submit invoice.
   - Confirm invoice appears with expected total, net, package, and level count.

## Open Questions Before Coding

1. What is the existing package model/table name to use in code?
2. Should track `levels_count` be hidden or shown readonly after package selection?
3. For scheduled course invoices, should the package be auto-selected from schedule context or selected manually by staff?
4. Should package data be sent to the finance service, or is local CRM persistence enough?

## Recommended Implementation Order

1. Identify the existing package model/table name.
2. Add package loading to lead edit data, sorted with `levels_count = 1` first.
3. Add package selector and frontend preview payload.
4. Add request and preview validation.
5. Update invoice data construction and pricing lookup.
6. Persist package on invoice and include it in booking references where needed.
7. Add tests.
8. Run the targeted test suite and manually verify lead edit invoice creation.
