# Audit: `$colab->getDate()` usage across the codebase

**Analysis only. No code changes.**

`Colab::getDate()` delegates to `$this->collabable->getDateTime()` for datable types (Delivery, Lodging, Activity, Restaurant) and returns `null` otherwise. If `collabable` is not loaded, accessing `$this->collabable` can trigger a lazy load; in some contexts (e.g. serialization, or when the relation was not loaded and the colab came from a query that bypassed the global scope) this can fail or return null unexpectedly.

**Note:** The `Colab` model has a global scope that adds `->with('collabable')` (see `Colab::booted()`). So any colab loaded via normal Eloquent queries on `Colab` will have `collabable` loaded. Risk appears when colabs are passed from jobs/events, or from code that uses `Colab::withoutGlobalScopes()`, or when the colab is the serialized payload (e.g. in a Mailable) and was built before the recent `loadMissing('collabable')` fixes.

---

## Step 1 — All occurrences of `->getDate()` (code only; excludes docs and model definition)

| File | Line | Context | Risk | Reason |
|------|------|--------|------|--------|
| **HandlesHistoryTransitions.php** | 134 | Trait – `handlePendingCompanyState()` | **SAFE** | `loadMissing('collabable')` immediately before this line (fix applied). |
| **HandlesHistoryTransitions.php** | 259 | Trait – `handleAcceptedState()` | **SAFE** | `loadMissing('collabable')` immediately before (fix applied). |
| **HandlesHistoryTransitions.php** | 329 | Trait – `handleDeliveryAcceptedState()` | **SAFE** | `loadMissing('collabable')` immediately before (fix applied). Uses **Carbon::parse($colab->getDate())** – see optional flag below. |
| **HandlesHistoryTransitions.php** | 474 | Trait – `handleModificationState()` | **SAFE** | `loadMissing('collabable')` immediately before (fix applied). |
| **NotificationService.php** | 302 | Service – `sendDateChangedColabCompanyNotification()` | **POTENTIALLY UNSAFE** | Colab passed in; no explicit load. Callers may use Colab from various sources. |
| **NotificationService.php** | 1628 | Service – `scheduleReservationReminderInfluencerNotification()` | **POTENTIALLY UNSAFE** | Colab passed in; no explicit load. Called from scheduled job / scheduler; colab may be refetched with `with('collabable')` in job (see SendScheduledNotificationJob). |
| **NotificationService.php** | 1642 | Service – `schedule24HoursCalendarReminderNotification()` | **POTENTIALLY UNSAFE** | Same as above. |
| **NotificationService.php** | 1703 | Service – `schedule2HoursCalendarReminderNotification()` | **POTENTIALLY UNSAFE** | Same as above. |
| **NotificationService.php** | 1762 | Service – `schedule1HourCalendarReminderCompanyNotification()` | **POTENTIALLY UNSAFE** | Same as above. |
| **NotificationService.php** | 1779 | Service – `schedulePublishReminderInfluencerNotification()` | **POTENTIALLY UNSAFE** | Same as above. |
| **NotificationService.php** | 1793 | Service – `scheduleReservationReminderCompanyNotification()` | **POTENTIALLY UNSAFE** | Same as above. |
| **NotificationService.php** | 1800 | Service – `schedulePendingColabReminder1CompanyNotification()` | **POTENTIALLY UNSAFE** | Colab from HandlesHistoryTransitions (now has loadMissing there); other callers could pass colab without load. |
| **NotificationService.php** | 1808 | Service – `schedulePendingColabReminder2CompanyNotification()` | **POTENTIALLY UNSAFE** | Same as above. |
| **NotificationService.php** | 1831 | Service – `schedulePendingColabModificationReminderCompanyNotification()` | **POTENTIALLY UNSAFE** | Colab from HandlesHistoryTransitions (now has loadMissing); no guarantee at service boundary. |
| **NotificationService.php** | 1839 | Service – `scheduleInfluencerModificationReminderCompanyNotification()` | **POTENTIALLY UNSAFE** | Same pattern. |
| **NotificationService.php** | 1847 | Service – `scheduleInfluencerModificationReminder2CompanyNotification()` | **POTENTIALLY UNSAFE** | Same pattern. |
| **NotificationService.php** | 1937 | Service – body building for notification | **POTENTIALLY UNSAFE** | Colab passed into method; no explicit load. |
| **NotificationService.php** | 2814–2815 | Service – (two uses) | **POTENTIALLY UNSAFE** | Colab from context; no explicit load before getDate(). |
| **NotificationService.php** | 2906–2907 | Service – (two uses) | **POTENTIALLY UNSAFE** | Same as above. |
| **MailingService.php** | 203 | Service – `sendDateChangedColabInfluencerEmail()` – **dispatch** | **POTENTIALLY UNSAFE** | getDate() evaluated at dispatch time; colab from caller (e.g. ColabService). ColabService may load collabable elsewhere but not guaranteed at this call site. |
| **MailingService.php** | 210 | Service – `sendDataChangedColabInfluencerEmail()` – **dispatch** | **POTENTIALLY UNSAFE** | Same as above. |
| **MailingService.php** | 217 | Service – `sendDateChangedColabCompanyEmail()` – **dispatch** | **POTENTIALLY UNSAFE** | Same as above. |
| **MailingService.php** | 224 | Service – `sendDataChangedColabCompanyEmail()` – **dispatch** | **POTENTIALLY UNSAFE** | Same as above. |
| **MailingService.php** | 304 | Service – `schedulePublishReminderInfluencerEmail()` | **POTENTIALLY UNSAFE** | Colab passed in; no explicit load. |
| **MailingService.php** | 320 | Service – (similar schedule method) | **POTENTIALLY UNSAFE** | Same. |
| **MailingService.php** | 336 | Service – `scheduleReservationReminderInfluencerEmail()` | **POTENTIALLY UNSAFE** | Same. |
| **MailingService.php** | 352 | Service – `schedule24HoursCalendarReminderInfluencerEmail()` | **POTENTIALLY UNSAFE** | Same. |
| **MailingService.php** | 381 | Service – `schedule2HoursCalendarReminderInfluencerEmail()` | **POTENTIALLY UNSAFE** | Same. |
| **MailingService.php** | 406 | Service – `scheduleReservationReminderCompanyEmail()` | **POTENTIALLY UNSAFE** | Same. |
| **MailingService.php** | 422 | Service – `schedule1HourCalendarReminderCompanyEmail()` | **POTENTIALLY UNSAFE** | Same. |
| **MailingService.php** | 498 | Service – `schedulePendingColabReminder1CompanyEmail()` | **POTENTIALLY UNSAFE** | Colab from HandlesHistoryTransitions (now has loadMissing); service does not enforce load. |
| **MailingService.php** | 508 | Service – `schedulePendingColabReminder2CompanyEmail()` | **POTENTIALLY UNSAFE** | Same. |
| **MailingService.php** | 555 | Service – `schedulePendingColabModificationReminderCompanyEmail()` | **POTENTIALLY UNSAFE** | Same. |
| **MailingService.php** | 565 | Service – `scheduleInfluencerModificationReminderCompanyEmail()` | **POTENTIALLY UNSAFE** | Same. |
| **MailingService.php** | 580 | Service – `scheduleInfluencerModificationReminder2CompanyEmail()` | **POTENTIALLY UNSAFE** | Same. |
| **PendingColabReminder2CompanyEmail.php** | 61 | Mailable – `build()` | **POTENTIALLY UNSAFE** | Colab comes from Mailable constructor (often from Job). SendEmailJob refetches with `Colab::with('collabable')->find($this->collabId)` for state check but the Mailable’s `$this->colab` is the one passed at schedule time; when the job runs, SerializesModels may refetch colab (then global scope applies). So risk is at schedule time when colab was passed without load. |
| **PendingColabReminder1CompanyEmail.php** | 61 | Mailable – `build()` | **POTENTIALLY UNSAFE** | Same as above. |
| **ConfirmedModificationRequestCompanyEmail.php** | 86 | Mailable – constructor / build | **POTENTIALLY UNSAFE** | Colab passed in; getDate() used twice (conditional). No load in Mailable. |
| **PublishedContentInternalEmail.php** | 78 | Mailable – constructor / build | **POTENTIALLY UNSAFE** | Same pattern; null-safe with ternary. |
| **ModificationRequestInfluencerEmail.php** | 84 | Mailable – constructor / build | **POTENTIALLY UNSAFE** | Same pattern. |
| **InfluencerModificationRequestInfluencerEmail.php** | 84 | Mailable – constructor / build | **POTENTIALLY UNSAFE** | Same pattern. |
| **ConfirmedModificationRequestInfluencerEmail.php** | 84 | Mailable – constructor / build | **POTENTIALLY UNSAFE** | Same pattern. |
| **CancelExpiredCollaborations.php** | 127 | Console Command – `getExpiredCollaborations()` filter | **SAFE** | Colabs from `ColabService::get()` which uses Colab model (global scope loads collabable). Filter also uses `$colab->collabable` on line 106 before getDate(). |
| **SendPublishContentReminders.php** | 110 | Console Command | **SAFE** | Colabs from query on Colab model (via ColabService); global scope. |
| **SendPublishContentReminders.php** | 237 | Console Command | **SAFE** | Same. |
| **SendPublishContentReminders.php** | 352 | Console Command | **SAFE** | Same. |
| **pending-colab-modification-reminder.blade.php** | 22, 41 | View (email) | **POTENTIALLY UNSAFE** | Uses `$colab->getDate()->format(...)` with no null check. If getDate() is null, calling ->format() will error. Colab in view is from Mailable (SerializesModels may refetch with global scope at job run time). |

---

## Step 2 – Classification summary

- **SAFE:** 8 usages (HandlesHistoryTransitions 4× after fix, CancelExpiredCollaborations 1×, SendPublishContentReminders 3×).
- **POTENTIALLY UNSAFE:** All other usages in Services, Mailables, and the Blade view – colab is passed in without the method/view guaranteeing `collabable` is loaded, or getDate() is used without null check.

---

## Step 3 – Risk summary

- **HandlesHistoryTransitions:** All four `getDate()` usages are now **SAFE** (loadMissing('collabable') added before each).
- **Services (NotificationService, MailingService):** All usages are **POTENTIALLY UNSAFE** at the service boundary: they do not call `loadMissing('collabable')` and depend on callers or (for scheduled jobs) on the job refetching the colab with `with('collabable')`. If a colab is ever passed from a path that did not load collabable (e.g. relation without eager load, or without global scope), getDate() can be wrong or trigger lazy-load issues.
- **Mailables:** Colab is constructor data; at send time SerializesModels may refetch the colab (with global scope). Risk is when the Mailable is built (e.g. at schedule time) with a colab that did not have collabable loaded.
- **Blade view:** In addition to relation risk, **no null check** on getDate() before ->format(); if getDate() is null, the view will throw.

---

## Optional: `Carbon::parse($colab->getDate())`

| File | Line | Note |
|------|------|------|
| **HandlesHistoryTransitions.php** | 329 | `$date = \Illuminate\Support\Carbon::parse($colab->getDate());` – If getDate() is null, Carbon::parse(null) can throw or behave unexpectedly. This path is now protected by loadMissing('collabable') just above; still, adding a null check before parse would make it robust if getDate() ever returns null (e.g. Brand or missing day). |

---

## Where issues like the one fixed could happen again

1. **Any new or existing caller** of NotificationService / MailingService methods that take a `Colab` and that use `getDate()` internally, if that caller passes a colab that was obtained without the global scope or without explicit `load('collabable')`.
2. **Scheduled jobs / listeners** that receive a colab (or colab id) and call a service method that uses getDate() without the job first loading the colab with `Colab::with('collabable')->find($id)` (SendScheduledNotificationJob and SendEmailJob already refetch with collabable for state checks, but the Mailable’s colab is still the one from schedule time unless the Mailable refetches).
3. **Mailables** that use `$this->colab->getDate()` in build(): safe at send time only if the colab is refetched with global scope; at build/schedule time the colab may not have had collabable loaded.
4. **Blade view** `pending-colab-modification-reminder.blade.php`: no null check on getDate() before ->format(); any path that renders this view with a colab that has null getDate() will throw.

A consistent mitigation would be to ensure **every** code path that calls `$colab->getDate()` either (a) has just run `$colab->loadMissing('collabable')`, or (b) can guarantee the colab was loaded with `collabable` (e.g. via Colab model global scope or explicit with/load), and (c) where getDate() can be null, check before using (e.g. before Carbon::parse or ->format()).
