
  [30;42;1m PASS [39;49;22m[39m Tests\Unit\ExampleTest[39m
  [32;1m✓[39;22m[90m [39m[90mthat true is true[39m[90m                                                                                           [39m [90m0.01s[39m  

  [30;42;1m PASS [39;49;22m[39m Tests\Feature\Auth\AuthenticationTest[39m
  [32;1m✓[39;22m[90m [39m[90mlogin screen can be rendered[39m[90m                                                                                [39m [90m0.69s[39m  
  [32;1m✓[39;22m[90m [39m[90musers can authenticate using the login screen[39m[90m                                                               [39m [90m0.08s[39m  
  [32;1m✓[39;22m[90m [39m[90musers can not authenticate with invalid password[39m[90m                                                            [39m [90m0.27s[39m  
  [32;1m✓[39;22m[90m [39m[90musers can logout[39m[90m                                                                                            [39m [90m0.05s[39m  

  [30;42;1m PASS [39;49;22m[39m Tests\Feature\Auth\EmailVerificationTest[39m
  [32;1m✓[39;22m[90m [39m[90memail verification screen can be rendered[39m[90m                                                                   [39m [90m0.04s[39m  
  [32;1m✓[39;22m[90m [39m[90memail can be verified[39m[90m                                                                                       [39m [90m0.04s[39m  
  [32;1m✓[39;22m[90m [39m[90memail is not verified with invalid hash[39m[90m                                                                     [39m [90m0.04s[39m  

  [30;42;1m PASS [39;49;22m[39m Tests\Feature\Auth\PasswordConfirmationTest[39m
  [32;1m✓[39;22m[90m [39m[90mconfirm password screen can be rendered[39m[90m                                                                     [39m [90m0.05s[39m  
  [32;1m✓[39;22m[90m [39m[90mpassword can be confirmed[39m[90m                                                                                   [39m [90m0.03s[39m  
  [32;1m✓[39;22m[90m [39m[90mpassword is not confirmed with invalid password[39m[90m                                                             [39m [90m0.23s[39m  

  [30;42;1m PASS [39;49;22m[39m Tests\Feature\Auth\PasswordResetTest[39m
  [32;1m✓[39;22m[90m [39m[90mreset password link screen can be rendered[39m[90m                                                                  [39m [90m0.05s[39m  
  [32;1m✓[39;22m[90m [39m[90mreset password link can be requested[39m[90m                                                                        [39m [90m0.24s[39m  
  [32;1m✓[39;22m[90m [39m[90mreset password screen can be rendered[39m[90m                                                                       [39m [90m0.26s[39m  
  [32;1m✓[39;22m[90m [39m[90mpassword can be reset with valid token[39m[90m                                                                      [39m [90m0.26s[39m  

  [30;42;1m PASS [39;49;22m[39m Tests\Feature\Auth\PasswordUpdateTest[39m
  [32;1m✓[39;22m[90m [39m[90mpassword can be updated[39m[90m                                                                                     [39m [90m0.07s[39m  
  [32;1m✓[39;22m[90m [39m[90mcorrect password must be provided to update password[39m[90m                                                        [39m [90m0.06s[39m  

  [30;42;1m PASS [39;49;22m[39m Tests\Feature\Auth\RegistrationTest[39m
  [32;1m✓[39;22m[90m [39m[90mregistration screen can be rendered[39m[90m                                                                         [39m [90m0.10s[39m  
  [32;1m✓[39;22m[90m [39m[90mnew users can register[39m[90m                                                                                      [39m [90m0.10s[39m  

  [30;42;1m PASS [39;49;22m[39m Tests\Feature\Backup\BackupControllerTest[39m
  [32;1m✓[39;22m[90m [39m[90man admin can download a backup[39m[90m                                                                              [39m [90m0.14s[39m  
  [32;1m✓[39;22m[90m [39m[90ma non admin cannot download a backup[39m[90m                                                                        [39m [90m0.11s[39m  
  [32;1m✓[39;22m[90m [39m[90ma guest is redirected away from backup routes[39m[90m                                                               [39m [90m0.09s[39m  
  [32;1m✓[39;22m[90m [39m[90ma non admin cannot reach the backup and restore card[39m[90m                                                        [39m [90m0.15s[39m  
  [32;1m✓[39;22m[90m [39m[90man admin can restore from an uploaded backup[39m[90m                                                                [39m [90m0.21s[39m  
  [32;1m✓[39;22m[90m [39m[90ma non admin cannot restore a backup[39m[90m                                                                         [39m [90m0.08s[39m  
  [32;1m✓[39;22m[90m [39m[90mrestoring an invalid backup file shows an error and touches nothing[39m[90m                                         [39m [90m0.12s[39m  
  [32;1m✓[39;22m[90m [39m[90mrestoring never touches another companys data[39m[90m                                                               [39m [90m0.11s[39m  

  [30;42;1m PASS [39;49;22m[39m Tests\Feature\Backup\BackupServiceTest[39m
  [32;1m✓[39;22m[90m [39m[90mdump and restore round trip preserves data and foreign key relationships[39m[90m                                    [39m [90m0.22s[39m  
  [32;1m✓[39;22m[90m [39m[90mrestore is destructive data created after the backup is gone afterward[39m[90m                                      [39m [90m0.14s[39m  
  [32;1m✓[39;22m[90m [39m[90mrestore never touches another companys data[39m[90m                                                                 [39m [90m0.19s[39m  
  [32;1m✓[39;22m[90m [39m[90mdump only includes the given companys rows[39m[90m                                                                  [39m [90m0.17s[39m  
  [32;1m✓[39;22m[90m [39m[90mrestore rejects a wrong version[39m[90m                                                                             [39m [90m0.07s[39m  
  [32;1m✓[39;22m[90m [39m[90mrestore rejects a missing tables section[39m[90m                                                                    [39m [90m0.06s[39m  
  [32;1m✓[39;22m[90m [39m[90mrestore rejects an unknown table name[39m[90m                                                                       [39m [90m0.06s[39m  
  [32;1m✓[39;22m[90m [39m[90monly admins can manage settings[39m[90m                                                                             [39m [90m0.10s[39m  

  [30;42;1m PASS [39;49;22m[39m Tests\Feature\Dashboard\DashboardTest[39m
  [32;1m✓[39;22m[90m [39m[90mdashboard index shows only the users company overview[39m[90m                                                       [39m [90m0.27s[39m  
  [32;1m✓[39;22m[90m [39m[90mfuel analytics tab is isolated[39m[90m                                                                              [39m [90m0.20s[39m  
  [32;1m✓[39;22m[90m [39m[90minventory tab is isolated[39m[90m                                                                                   [39m [90m0.18s[39m  
  [32;1m✓[39;22m[90m [39m[90minventory tab shows recent stock movements and is isolated[39m[90m                                                  [39m [90m0.18s[39m  
  [32;1m✓[39;22m[90m [39m[90mvehicle expenses tab is isolated[39m[90m                                                                            [39m [90m0.21s[39m  
  [32;1m✓[39;22m[90m [39m[90mvehicle expenses tab excludes in house maintenance jobs from the outside workshop list[39m[90m                      [39m [90m0.20s[39m  
  [32;1m✓[39;22m[90m [39m[90mlive operations tab is isolated[39m[90m                                                                             [39m [90m0.16s[39m  
  [32;1m✓[39;22m[90m [39m[90mlive operations tab shows only trips within the last 12 hours[39m[90m                                               [39m [90m0.15s[39m  
  [32;1m✓[39;22m[90m [39m[90mlive operations tab todays trips is isolated[39m[90m                                                                [39m [90m0.17s[39m  
  [32;1m✓[39;22m[90m [39m[90mcompliance tab is isolated[39m[90m                                                                                  [39m [90m0.15s[39m  
  [32;1m✓[39;22m[90m [39m[90mthe reverse company is also isolated[39m[90m                                                                        [39m [90m0.14s[39m  
  [32;1m✓[39;22m[90m [39m[90mdocuments of soft deleted vehicles and drivers are excluded from compliance tab[39m[90m                             [39m [90m0.15s[39m  
  [32;1m✓[39;22m[90m [39m[90moverview and operations tabs handle a trip with no vehicle or driver[39m[90m                                        [39m [90m0.23s[39m  
  [32;1m✓[39;22m[90m [39m[90man unknown tab returns not found[39m[90m                                                                            [39m [90m0.14s[39m  
  [32;1m✓[39;22m[90m [39m[90mnotifications is no longer a dashboard tab[39m[90m                                                                  [39m [90m0.14s[39m  
  [32;1m✓[39;22m[90m [39m[90ma guest is redirected away from the dashboard and tabs[39m[90m                                                      [39m [90m0.14s[39m  

  [30;42;1m PASS [39;49;22m[39m Tests\Feature\ExampleTest[39m
  [32;1m✓[39;22m[90m [39m[90mthe application returns a successful response[39m[90m                                                               [39m [90m0.08s[39m  

  [39;41;1m FAIL [39;49;22m[39m Tests\Feature\Fleet\ChallanTest[39m
  [31;1m⨯[39;22m[90m [39m[90ma user can add a challan to their companys vehicle[39m[90m                                                          [39m [90m0.13s[39m  
  [31;1m⨯[39;22m[90m [39m[90mchallan status reflects due date and payment[39m[90m                                                                [39m [90m0.10s[39m  
  [31;1m⨯[39;22m[90m [39m[90ma user can update and soft delete a challan[39m[90m                                                                 [39m [90m0.10s[39m  
  [31;1m⨯[39;22m[90m [39m[90ma user cannot log a challan against another companys vehicle[39m[90m                                                [39m [90m0.09s[39m  
  [31;1m⨯[39;22m[90m [39m[90ma company cannot see or touch another companys challans[39m[90m                                                     [39m [90m0.08s[39m  

  [30;42;1m PASS [39;49;22m[39m Tests\Feature\Fleet\CompanySettingsTest[39m
  [32;1m✓[39;22m[90m [39m[90ma user can set the notification email[39m[90m                                                                       [39m [90m0.11s[39m  
  [32;1m✓[39;22m[90m [39m[90mnotification recipients parses the comma separated list[39m[90m                                                     [39m [90m0.06s[39m  
  [32;1m✓[39;22m[90m [39m[90mnotification recipients is empty when unset[39m[90m                                                                 [39m [90m0.04s[39m  
  [32;1m✓[39;22m[90m [39m[90man invalid email in the list is rejected and names the offending entry[39m[90m                                      [39m [90m0.09s[39m  
  [32;1m✓[39;22m[90m [39m[90mupdating the setting only affects the users own company[39m[90m                                                     [39m [90m0.09s[39m  
  [32;1m✓[39;22m[90m [39m[90ma non admin cannot update settings[39m[90m                                                                          [39m [90m0.09s[39m  
  [32;1m✓[39;22m[90m [39m[90ma guest is redirected away[39m[90m                                                                                  [39m [90m0.06s[39m  

  [39;41;1m FAIL [39;49;22m[39m Tests\Feature\Fleet\CsvImportTest[39m
  [31;1m⨯[39;22m[90m [39m[90ma valid vehicles csv imports every row[39m[90m                                                                      [39m [90m0.10s[39m  
  [31;1m⨯[39;22m[90m [39m[90ma blank vehicle status defaults to active[39m[90m                                                                   [39m [90m0.09s[39m  
  [31;1m⨯[39;22m[90m [39m[90ma vehicles csv with one bad row imports nothing[39m[90m                                                             [39m [90m0.08s[39m  
  [31;1m⨯[39;22m[90m [39m[90ma duplicate plate within the file is rejected[39m[90m                                                               [39m [90m0.08s[39m  
  [31;1m⨯[39;22m[90m [39m[90ma plate already used by the company is rejected[39m[90m                                                             [39m [90m0.10s[39m  
  [31;1m⨯[39;22m[90m [39m[90ma plate used by another company does not block the import[39m[90m                                                   [39m [90m0.10s[39m  
  [31;1m⨯[39;22m[90m [39m[90mvehicles csv wrong headers are rejected before any row is read[39m[90m                                              [39m [90m0.09s[39m  
  [31;1m⨯[39;22m[90m [39m[90ma valid drivers csv imports every row[39m[90m                                                                       [39m [90m0.11s[39m  
  [31;1m⨯[39;22m[90m [39m[90ma drivers csv with one bad row imports nothing[39m[90m                                                              [39m [90m0.09s[39m  
  [31;1m⨯[39;22m[90m [39m[90ma duplicate cnic within the file is rejected[39m[90m                                                                [39m [90m0.08s[39m  
  [31;1m⨯[39;22m[90m [39m[90ma cnic already used by the company is rejected[39m[90m                                                              [39m [90m0.09s[39m  
  [31;1m⨯[39;22m[90m [39m[90ma cnic used by another company does not block the import[39m[90m                                                    [39m [90m0.09s[39m  
  [31;1m⨯[39;22m[90m [39m[90ma valid vehicle documents csv imports every row by plate lookup[39m[90m                                             [39m [90m0.09s[39m  
  [31;1m⨯[39;22m[90m [39m[90ma vehicle document of type other requires a custom name[39m[90m                                                     [39m [90m0.09s[39m  
  [31;1m⨯[39;22m[90m [39m[90ma vehicle document referencing an unknown plate is rejected[39m[90m                                                 [39m [90m0.09s[39m  
  [31;1m⨯[39;22m[90m [39m[90ma vehicle documents csv cannot attach to another companys vehicle[39m[90m                                           [39m [90m0.11s[39m  
  [31;1m⨯[39;22m[90m [39m[90ma valid driver documents csv imports every row by cnic lookup[39m[90m                                               [39m [90m0.09s[39m  
  [31;1m⨯[39;22m[90m [39m[90ma driver document referencing an unknown cnic is rejected[39m[90m                                                   [39m [90m0.08s[39m  
  [31;1m⨯[39;22m[90m [39m[90ma driver documents csv cannot attach to another companys driver[39m[90m                                             [39m [90m0.13s[39m  
  [32;1m✓[39;22m[90m [39m[90mthe sidebar links to both import hub pages[39m[90m                                                                  [39m [90m0.14s[39m  
  [31;1m⨯[39;22m[90m [39m[90mfleet templates download with the expected headers[39m[90m                                                          [39m [90m0.11s[39m  
  [31;1m⨯[39;22m[90m [39m[90mthe vehicles import forms back link returns to the vehicles list[39m[90m                                            [39m [90m0.08s[39m  
  [31;1m⨯[39;22m[90m [39m[90mthe drivers import forms back link returns to the drivers list[39m[90m                                              [39m [90m0.08s[39m  
  [31;1m⨯[39;22m[90m [39m[90mthe vehicle documents import forms back link returns to the vehicle documents tab[39m[90m                           [39m [90m0.08s[39m  
  [31;1m⨯[39;22m[90m [39m[90mthe driver documents import forms back link returns to the driver documents tab[39m[90m                             [39m [90m0.08s[39m  
  [32;1m✓[39;22m[90m [39m[90man unknown fleet import type is not found[39m[90m                                                                   [39m [90m0.06s[39m  
  [32;1m✓[39;22m[90m [39m[90ma guest is redirected away from fleet import pages[39m[90m                                                          [39m [90m0.06s[39m  

  [39;41;1m FAIL [39;49;22m[39m Tests\Feature\Fleet\CustomerFieldTest[39m
  [31;1m⨯[39;22m[90m [39m[90ma user can add a custom field to their companys customer[39m[90m                                                    [39m [90m0.14s[39m  
  [31;1m⨯[39;22m[90m [39m[90ma second field gets the next sort order[39m[90m                                                                     [39m [90m0.09s[39m  
  [31;1m⨯[39;22m[90m [39m[90mfield name is required[39m[90m                                                                                      [39m [90m0.09s[39m  
  [31;1m⨯[39;22m[90m [39m[90ma user can delete a custom field[39m[90m                                                                            [39m [90m0.09s[39m  
  [32;1m✓[39;22m[90m [39m[90ma company cannot add a field to another companys customer[39m[90m                                                   [39m [90m0.07s[39m  
  [32;1m✓[39;22m[90m [39m[90ma company cannot delete another companys custom field[39m[90m                                                       [39m [90m0.07s[39m  
  [31;1m⨯[39;22m[90m [39m[90ma company cannot see another companys custom fields on the customer page[39m[90m                                    [39m [90m0.09s[39m  
  [32;1m✓[39;22m[90m [39m[90ma guest is redirected away[39m[90m                                                                                  [39m [90m0.06s[39m  

  [39;41;1m FAIL [39;49;22m[39m Tests\Feature\Fleet\CustomerTest[39m
  [31;1m⨯[39;22m[90m [39m[90ma user can create and see their companys customers[39m[90m                                                          [39m [90m0.10s[39m  
  [31;1m⨯[39;22m[90m [39m[90ma user can update their companys customer[39m[90m                                                                   [39m [90m0.12s[39m  
  [31;1m⨯[39;22m[90m [39m[90mdeleting a customer soft deletes it[39m[90m                                                                         [39m [90m0.09s[39m  
  [31;1m⨯[39;22m[90m [39m[90ma company cannot see or touch another companys customers[39m[90m                                                    [39m [90m0.09s[39m  
  [32;1m✓[39;22m[90m [39m[90ma guest is redirected away from customers[39m[90m                                                                   [39m [90m0.06s[39m  

  [39;41;1m FAIL [39;49;22m[39m Tests\Feature\Fleet\DocumentsTest[39m
  [31;1m⨯[39;22m[90m [39m[90mthe vehicle tab lists every vehicle including ones with no documents[39m[90m                                        [39m [90m0.10s[39m  
  [31;1m⨯[39;22m[90m [39m[90mthe driver tab lists every driver including ones with no documents[39m[90m                                          [39m [90m0.09s[39m  
  [31;1m⨯[39;22m[90m [39m[90ma fixed type cell shows that documents number expiry and status[39m[90m                                             [39m [90m0.09s[39m  
  [31;1m⨯[39;22m[90m [39m[90ma missing document type renders an empty cell[39m[90m                                                               [39m [90m0.08s[39m  
  [31;1m⨯[39;22m[90m [39m[90mtwo documents of the same type show only the one with the latest expiry[39m[90m                                     [39m [90m0.10s[39m  
  [31;1m⨯[39;22m[90m [39m[90man other type document is excluded from the matrix and counted as extra[39m[90m                                     [39m [90m0.09s[39m  
  [31;1m⨯[39;22m[90m [39m[90mthe status filter narrows to owners with a matching document[39m[90m                                                [39m [90m0.09s[39m  
  [31;1m⨯[39;22m[90m [39m[90mthe search filter matches plate or document number[39m[90m                                                          [39m [90m0.09s[39m  
  [31;1m⨯[39;22m[90m [39m[90msoft deleted vehicles and drivers are excluded[39m[90m                                                              [39m [90m0.09s[39m  
  [31;1m⨯[39;22m[90m [39m[90ma company cannot see another companys vehicles or drivers on either tab[39m[90m                                     [39m [90m0.09s[39m  
  [32;1m✓[39;22m[90m [39m[90ma guest is redirected away from the documents page[39m[90m                                                          [39m [90m0.06s[39m  

  [39;41;1m FAIL [39;49;22m[39m Tests\Feature\Fleet\DriverDocumentTest[39m
  [31;1m⨯[39;22m[90m [39m[90ma user can add a document to their companys driver[39m[90m                                                          [39m [90m0.11s[39m  
  [31;1m⨯[39;22m[90m [39m[90mdocument status uses the shared definition[39m[90m                                                                  [39m [90m0.09s[39m  
  [31;1m⨯[39;22m[90m [39m[90ma user can update and soft delete a document[39m[90m                                                                [39m [90m0.09s[39m  
  [31;1m⨯[39;22m[90m [39m[90mupsert creates a document for a type the driver does not have yet[39m[90m                                           [39m [90m0.10s[39m  
  [31;1m⨯[39;22m[90m [39m[90mupsert updates the existing document in place instead of creating a duplicate[39m[90m                               [39m [90m0.09s[39m  
  [32;1m✓[39;22m[90m [39m[90mupsert rejects a driver belonging to another company[39m[90m                                                        [39m [90m0.07s[39m  
  [32;1m✓[39;22m[90m [39m[90mupsert rejects the other type[39m[90m                                                                               [39m [90m0.07s[39m  
  [31;1m⨯[39;22m[90m [39m[90mupsert validates expiry date is a real date[39m[90m                                                                 [39m [90m0.09s[39m  
  [32;1m✓[39;22m[90m [39m[90ma company cannot see or touch another companys driver documents[39m[90m                                             [39m [90m0.10s[39m  

  [39;41;1m FAIL [39;49;22m[39m Tests\Feature\Fleet\DriverTest[39m
  [31;1m⨯[39;22m[90m [39m[90ma user can create and see their companys drivers[39m[90m                                                            [39m [90m0.10s[39m  
  [31;1m⨯[39;22m[90m [39m[90ma user can update and soft delete a driver[39m[90m                                                                  [39m [90m0.09s[39m  
  [31;1m⨯[39;22m[90m [39m[90mcnic must be unique within a company but not across companies[39m[90m                                               [39m [90m0.09s[39m  
  [31;1m⨯[39;22m[90m [39m[90ma company cannot see or touch another companys drivers[39m[90m                                                      [39m [90m0.08s[39m  

  [39;41;1m FAIL [39;49;22m[39m Tests\Feature\Fleet\FuelEntryTest[39m
  [31;1m⨯[39;22m[90m [39m[90ma user can add a fuel entry and total cost is calculated[39m[90m                                                    [39m [90m0.10s[39m  
  [31;1m⨯[39;22m[90m [39m[90mefficiency is derived from consecutive odometer readings[39m[90m                                                    [39m [90m0.09s[39m  
  [31;1m⨯[39;22m[90m [39m[90ma user cannot log fuel against another companys vehicle or trip[39m[90m                                             [39m [90m0.09s[39m  
  [31;1m⨯[39;22m[90m [39m[90ma company cannot see or touch another companys fuel entries[39m[90m                                                 [39m [90m0.09s[39m  
  [31;1m⨯[39;22m[90m [39m[90mdeleting a fuel entry soft deletes it[39m[90m                                                                       [39m [90m0.09s[39m  

  [39;41;1m FAIL [39;49;22m[39m Tests\Feature\Fleet\ReportTest[39m
  [31;1m⨯[39;22m[90m [39m[90mtrip report shows own trips and respects date filters[39m[90m                                                       [39m [90m0.12s[39m  
  [31;1m⨯[39;22m[90m [39m[90mtrip report and csv never contain another companys trips[39m[90m                                                    [39m [90m0.10s[39m  
  [31;1m⨯[39;22m[90m [39m[90mfuel report totals and isolation[39m[90m                                                                            [39m [90m0.10s[39m  
  [31;1m⨯[39;22m[90m [39m[90mvehicle expense report aggregates per vehicle and is isolated[39m[90m                                               [39m [90m0.10s[39m  
  [31;1m⨯[39;22m[90m [39m[90mcompliance report lists both document kinds with statuses and is isolated[39m[90m                                   [39m [90m0.11s[39m  
  [31;1m⨯[39;22m[90m [39m[90mreport filters reject another companys vehicle or driver[39m[90m                                                    [39m [90m0.11s[39m  

  [39;41;1m FAIL [39;49;22m[39m Tests\Feature\Fleet\RouteTest[39m
  [31;1m⨯[39;22m[90m [39m[90ma user can create and see their companys routes[39m[90m                                                             [39m [90m0.10s[39m  
  [31;1m⨯[39;22m[90m [39m[90mexpected distance is required and fuel max cannot be below fuel min[39m[90m                                         [39m [90m0.10s[39m  
  [31;1m⨯[39;22m[90m [39m[90ma user can update their companys route[39m[90m                                                                      [39m [90m0.11s[39m  
  [31;1m⨯[39;22m[90m [39m[90mdeleting a route soft deletes it[39m[90m                                                                            [39m [90m0.10s[39m  
  [31;1m⨯[39;22m[90m [39m[90ma company cannot see or touch another companys routes[39m[90m                                                       [39m [90m0.10s[39m  

  [39;41;1m FAIL [39;49;22m[39m Tests\Feature\Fleet\TripAuditLogTest[39m
  [31;1m⨯[39;22m[90m [39m[90mcreating a trip via the form view is logged[39m[90m                                                                 [39m [90m0.09s[39m  
  [31;1m⨯[39;22m[90m [39m[90mcreating a trip via add row is logged[39m[90m                                                                       [39m [90m0.09s[39m  
  [31;1m⨯[39;22m[90m [39m[90mupdating a trip logs only the changed fields[39m[90m                                                                [39m [90m0.10s[39m  
  [31;1m⨯[39;22m[90m [39m[90mreassigning the vehicle cell is logged with the new vehicle id[39m[90m                                              [39m [90m0.10s[39m  
  [31;1m⨯[39;22m[90m [39m[90mdeleting a trip is logged[39m[90m                                                                                   [39m [90m0.13s[39m  
  [31;1m⨯[39;22m[90m [39m[90ma companys trip audit entries are isolated[39m[90m                                                                  [39m [90m0.14s[39m  
  [31;1m⨯[39;22m[90m [39m[90mthe index can filter to trips only[39m[90m                                                                          [39m [90m0.15s[39m  

  [39;41;1m FAIL [39;49;22m[39m Tests\Feature\Fleet\TripFieldValueTest[39m
  [31;1m⨯[39;22m[90m [39m[90msubmitting a value for the customers own field persists it[39m[90m                                                  [39m [90m0.13s[39m  
  [31;1m⨯[39;22m[90m [39m[90ma value for a field belonging to a different customer is ignored[39m[90m                                            [39m [90m0.12s[39m  
  [31;1m⨯[39;22m[90m [39m[90mchanging a trips customer clears the old customers field values[39m[90m                                             [39m [90m0.12s[39m  
  [31;1m⨯[39;22m[90m [39m[90mclearing a trips customer clears its field values[39m[90m                                                           [39m [90m0.11s[39m  
  [31;1m⨯[39;22m[90m [39m[90mupdating a value for the same customer upserts rather than duplicates[39m[90m                                       [39m [90m0.12s[39m  
  [31;1m⨯[39;22m[90m [39m[90mthe edit page prefills existing field values[39m[90m                                                                [39m [90m0.09s[39m  
  [31;1m⨯[39;22m[90m [39m[90ma company cannot use another companys customer field[39m[90m                                                        [39m [90m0.09s[39m  

  [39;41;1m FAIL [39;49;22m[39m Tests\Feature\Fleet\TripGridFieldValueTest[39m
  [31;1m⨯[39;22m[90m [39m[90ma user can set a field value via the grid endpoint[39m[90m                                                          [39m [90m0.17s[39m  
  [31;1m⨯[39;22m[90m [39m[90mupdating a value twice upserts rather than duplicates[39m[90m                                                       [39m [90m0.11s[39m  
  [31;1m⨯[39;22m[90m [39m[90ma field belonging to a different customer is rejected[39m[90m                                                       [39m [90m0.16s[39m  
  [32;1m✓[39;22m[90m [39m[90ma company cannot set a value using another companys field[39m[90m                                                   [39m [90m0.10s[39m  
  [32;1m✓[39;22m[90m [39m[90ma company cannot set a value on another companys trip[39m[90m                                                       [39m [90m0.09s[39m  
  [31;1m⨯[39;22m[90m [39m[90mreassigning the customer cell prunes the old customers field values[39m[90m                                         [39m [90m0.12s[39m  
  [31;1m⨯[39;22m[90m [39m[90mexport includes custom field columns when filtered to one customer[39m[90m                                          [39m [90m0.10s[39m  
  [31;1m⨯[39;22m[90m [39m[90mexport omits custom field columns when no customer filter is set[39m[90m                                            [39m [90m0.09s[39m  

  [39;41;1m FAIL [39;49;22m[39m Tests\Feature\Fleet\TripGridTest[39m
  [31;1m⨯[39;22m[90m [39m[90mthe grid lists the companys trips and respects filters[39m[90m                                                      [39m [90m0.11s[39m  
  [31;1m⨯[39;22m[90m [39m[90mthe grid can be filtered by customer[39m[90m                                                                        [39m [90m0.11s[39m  
  [31;1m⨯[39;22m[90m [39m[90mthe grid customer filter rejects another companys customer[39m[90m                                                  [39m [90m0.08s[39m  
  [31;1m⨯[39;22m[90m [39m[90mthe grid can be filtered by pickup and dropoff with partial matches[39m[90m                                         [39m [90m0.09s[39m  
  [31;1m⨯[39;22m[90m [39m[90mthe grid defaults to the current month when no date filter is given[39m[90m                                         [39m [90m0.10s[39m  
  [31;1m⨯[39;22m[90m [39m[90man explicit date filter is not overridden by the current month default[39m[90m                                      [39m [90m0.12s[39m  
  [31;1m⨯[39;22m[90m [39m[90ma company cannot see another companys trips in the grid[39m[90m                                                     [39m [90m0.10s[39m  
  [32;1m✓[39;22m[90m [39m[90ma guest is redirected away from the grid[39m[90m                                                                    [39m [90m0.07s[39m  
  [31;1m⨯[39;22m[90m [39m[90madd row creates a trip defaulted to today with vehicle and driver left blank[39m[90m                                [39m [90m0.08s[39m  
  [31;1m⨯[39;22m[90m [39m[90madd row captures who created it[39m[90m                                                                             [39m [90m0.08s[39m  
  [31;1m⨯[39;22m[90m [39m[90madd row succeeds even with no vehicles or drivers in the company[39m[90m                                            [39m [90m0.08s[39m  
  [31;1m⨯[39;22m[90m [39m[90madd row with a customer param persists that customer[39m[90m                                                        [39m [90m0.08s[39m  
  [31;1m⨯[39;22m[90m [39m[90madd row without a customer param leaves it null[39m[90m                                                             [39m [90m0.09s[39m  
  [31;1m⨯[39;22m[90m [39m[90madd row rejects another companys customer[39m[90m                                                                   [39m [90m0.09s[39m  
  [32;1m✓[39;22m[90m [39m[90ma company cannot add a row without authentication[39m[90m                                                           [39m [90m0.05s[39m  
  [31;1m⨯[39;22m[90m [39m[90madd row still auto fills date and time to now while vehicle and driver stay blank[39m[90m                           [39m [90m0.08s[39m  
  [32;1m✓[39;22m[90m [39m[90ma user can persist hidden column choices[39m[90m                                                                    [39m [90m0.07s[39m  
  [31;1m⨯[39;22m[90m [39m[90mhidden columns are reflected back in the grid on next visit[39m[90m                                                 [39m [90m0.08s[39m  
  [31;1m⨯[39;22m[90m [39m[90ma legacy hidden columns preference never hides the customer column[39m[90m                                          [39m [90m0.08s[39m  
  [31;1m⨯[39;22m[90m [39m[90mthe customer column can still be explicitly hidden and stays hidden[39m[90m                                         [39m [90m0.08s[39m  
  [32;1m✓[39;22m[90m [39m[90man invalid column name is rejected[39m[90m                                                                          [39m [90m0.06s[39m  
  [32;1m✓[39;22m[90m [39m[90man empty hidden array is accepted meaning every column is visible[39m[90m                                           [39m [90m0.06s[39m  
  [32;1m✓[39;22m[90m [39m[90ma user can persist a column order[39m[90m                                                                           [39m [90m0.06s[39m  
  [31;1m⨯[39;22m[90m [39m[90mthe persisted column order is reflected back in the grid on next visit[39m[90m                                      [39m [90m0.07s[39m  
  [32;1m✓[39;22m[90m [39m[90man order containing an invalid column name is rejected[39m[90m                                                      [39m [90m0.06s[39m  
  [32;1m✓[39;22m[90m [39m[90momitting order leaves the stored column order untouched[39m[90m                                                     [39m [90m0.06s[39m  
  [31;1m⨯[39;22m[90m [39m[90ma user can update a single cell[39m[90m                                                                             [39m [90m0.08s[39m  
  [31;1m⨯[39;22m[90m [39m[90ma cell update can reassign the vehicle within the company[39m[90m                                                   [39m [90m0.09s[39m  
  [31;1m⨯[39;22m[90m [39m[90ma cell update rejects a non string vehicle value[39m[90m                                                            [39m [90m0.09s[39m  
  [31;1m⨯[39;22m[90m [39m[90ma cell update rejects a non string driver value[39m[90m                                                             [39m [90m0.09s[39m  
  [32;1m✓[39;22m[90m [39m[90mbulk cells rejects a non string vehicle value[39m[90m                                                               [39m [90m0.09s[39m  
  [31;1m⨯[39;22m[90m [39m[90ma cell update can reassign to a lowercase plated vehicle[39m[90m                                                    [39m [90m0.09s[39m  
  [31;1m⨯[39;22m[90m [39m[90ma cell update cannot assign another companys vehicle[39m[90m                                                        [39m [90m0.09s[39m  
  [31;1m⨯[39;22m[90m [39m[90ma cell update can set and clear the customer[39m[90m                                                                [39m [90m0.10s[39m  
  [31;1m⨯[39;22m[90m [39m[90ma cell update rejects a non string customer value[39m[90m                                                           [39m [90m0.11s[39m  
  [32;1m✓[39;22m[90m [39m[90mbulk cells rejects a non string customer value[39m[90m                                                              [39m [90m0.09s[39m  
  [31;1m⨯[39;22m[90m [39m[90ma cell update cannot assign another companys customer[39m[90m                                                       [39m [90m0.09s[39m  
  [31;1m⨯[39;22m[90m [39m[90ma cell update rejects an invalid status[39m[90m                                                                     [39m [90m0.09s[39m  
  [32;1m✓[39;22m[90m [39m[90ma company cannot edit a cell on another companys trip[39m[90m                                                       [39m [90m0.07s[39m  
  [31;1m⨯[39;22m[90m [39m[90mpasting a block of cells updates every targeted cell[39m[90m                                                        [39m [90m0.12s[39m  
  [32;1m✓[39;22m[90m [39m[90ma paste with one bad cell applies nothing[39m[90m                                                                   [39m [90m0.09s[39m  
  [32;1m✓[39;22m[90m [39m[90ma paste cannot touch another companys trip[39m[90m                                                                  [39m [90m0.07s[39m  
  [31;1m⨯[39;22m[90m [39m[90mbulk edit applies a field to every selected trip[39m[90m                                                            [39m [90m0.10s[39m  
  [31;1m⨯[39;22m[90m [39m[90mbulk assign vehicle applies to every selected trip[39m[90m                                                          [39m [90m0.09s[39m  
  [32;1m✓[39;22m[90m [39m[90ma company cannot bulk edit another companys trips[39m[90m                                                           [39m [90m0.08s[39m  
  [32;1m✓[39;22m[90m [39m[90ma company cannot bulk assign another companys vehicle[39m[90m                                                       [39m [90m0.07s[39m  
  [31;1m⨯[39;22m[90m [39m[90mbulk delete soft deletes every selected trip[39m[90m                                                                [39m [90m0.11s[39m  
  [32;1m✓[39;22m[90m [39m[90ma company cannot bulk delete another companys trips[39m[90m                                                         [39m [90m0.08s[39m  
  [31;1m⨯[39;22m[90m [39m[90mexport streams an xlsx file respecting filters[39m[90m                                                              [39m [90m0.09s[39m  
  [31;1m⨯[39;22m[90m [39m[90mexport succeeds for trips with and without a customer[39m[90m                                                       [39m [90m0.09s[39m  
  [31;1m⨯[39;22m[90m [39m[90ma valid trips csv imports every row by plate and cnic lookup[39m[90m                                                [39m [90m0.09s[39m  
  [31;1m⨯[39;22m[90m [39m[90ma trips csv with one bad row imports nothing[39m[90m                                                                [39m [90m0.09s[39m  
  [31;1m⨯[39;22m[90m [39m[90ma trips csv referencing an unknown plate or cnic is rejected[39m[90m                                                [39m [90m0.09s[39m  
  [31;1m⨯[39;22m[90m [39m[90ma trips csv cannot reference another companys vehicle or driver[39m[90m                                             [39m [90m0.09s[39m  
  [31;1m⨯[39;22m[90m [39m[90mtrips template downloads with the expected headers[39m[90m                                                          [39m [90m0.08s[39m  
  [31;1m⨯[39;22m[90m [39m[90ma trips csv resolves customer by name[39m[90m                                                                       [39m [90m0.09s[39m  
  [31;1m⨯[39;22m[90m [39m[90ma trips csv with a blank customer cell leaves the trip unassigned[39m[90m                                           [39m [90m0.09s[39m  
  [31;1m⨯[39;22m[90m [39m[90ma trips csv referencing an unknown customer name is rejected[39m[90m                                                [39m [90m0.13s[39m  
  [31;1m⨯[39;22m[90m [39m[90ma trips csv with an ambiguous customer name is rejected[39m[90m                                                     [39m [90m0.09s[39m  
  [31;1m⨯[39;22m[90m [39m[90mthe trips import forms back link returns to the spreadsheet view[39m[90m                                            [39m [90m0.08s[39m  
  [31;1m⨯[39;22m[90m [39m[90ma trips csv cannot resolve another companys customer[39m[90m                                                        [39m [90m0.10s[39m  

  [39;41;1m FAIL [39;49;22m[39m Tests\Feature\Fleet\TripTest[39m
  [31;1m⨯[39;22m[90m [39m[90ma user can create and see their companys trips[39m[90m                                                              [39m [90m0.19s[39m  
  [31;1m⨯[39;22m[90m [39m[90mthe trip list can be filtered by vehicle and date range[39m[90m                                                     [39m [90m0.10s[39m  
  [31;1m⨯[39;22m[90m [39m[90ma user cannot assign another companys vehicle or driver to a trip[39m[90m                                           [39m [90m0.09s[39m  
  [31;1m⨯[39;22m[90m [39m[90ma trip can be created with or without a customer[39m[90m                                                            [39m [90m0.10s[39m  
  [31;1m⨯[39;22m[90m [39m[90ma user cannot assign another companys customer to a trip[39m[90m                                                    [39m [90m0.09s[39m  
  [31;1m⨯[39;22m[90m [39m[90ma company cannot see or touch another companys trips[39m[90m                                                        [39m [90m0.09s[39m  
  [31;1m⨯[39;22m[90m [39m[90mdeleting a trip soft deletes it[39m[90m                                                                             [39m [90m0.09s[39m  
  [31;1m⨯[39;22m[90m [39m[90mcreating a trip captures who created it[39m[90m                                                                     [39m [90m0.09s[39m  
  [31;1m⨯[39;22m[90m [39m[90mdeleting a trip captures who deleted it[39m[90m                                                                     [39m [90m0.10s[39m  
  [31;1m⨯[39;22m[90m [39m[90mthe trip detail page shows who added it[39m[90m                                                                     [39m [90m0.09s[39m  

  [39;41;1m FAIL [39;49;22m[39m Tests\Feature\Fleet\VehicleDocumentTest[39m
  [31;1m⨯[39;22m[90m [39m[90ma user can add a document to their companys vehicle[39m[90m                                                         [39m [90m0.10s[39m  
  [31;1m⨯[39;22m[90m [39m[90man other document requires a custom name[39m[90m                                                                    [39m [90m0.08s[39m  
  [31;1m⨯[39;22m[90m [39m[90mdocument status reflects expiry date[39m[90m                                                                        [39m [90m0.10s[39m  
  [31;1m⨯[39;22m[90m [39m[90ma user can update and soft delete a document[39m[90m                                                                [39m [90m0.09s[39m  
  [31;1m⨯[39;22m[90m [39m[90mupsert creates a document for a type the vehicle does not have yet[39m[90m                                          [39m [90m0.08s[39m  
  [31;1m⨯[39;22m[90m [39m[90mupsert updates the existing document in place instead of creating a duplicate[39m[90m                               [39m [90m0.08s[39m  
  [32;1m✓[39;22m[90m [39m[90mupsert rejects a vehicle belonging to another company[39m[90m                                                       [39m [90m0.07s[39m  
  [32;1m✓[39;22m[90m [39m[90mupsert rejects the other type[39m[90m                                                                               [39m [90m0.07s[39m  
  [31;1m⨯[39;22m[90m [39m[90mupsert validates expiry date is a real date[39m[90m                                                                 [39m [90m0.08s[39m  
  [32;1m✓[39;22m[90m [39m[90ma company cannot see or touch another companys vehicle documents[39m[90m                                            [39m [90m0.10s[39m  

  [39;41;1m FAIL [39;49;22m[39m Tests\Feature\Fleet\VehicleTest[39m
  [31;1m⨯[39;22m[90m [39m[90ma user can create and see their companys vehicles[39m[90m                                                           [39m [90m0.10s[39m  
  [31;1m⨯[39;22m[90m [39m[90ma user can update their companys vehicle[39m[90m                                                                    [39m [90m0.08s[39m  
  [31;1m⨯[39;22m[90m [39m[90mdeleting a vehicle soft deletes it[39m[90m                                                                          [39m [90m0.08s[39m  
  [31;1m⨯[39;22m[90m [39m[90mplate must be unique within a company but not across companies[39m[90m                                              [39m [90m0.08s[39m  
  [31;1m⨯[39;22m[90m [39m[90ma company cannot see or touch another companys vehicles[39m[90m                                                     [39m [90m0.08s[39m  

  [39;41;1m FAIL [39;49;22m[39m Tests\Feature\Inventory\AuditLogTest[39m
  [31;1m⨯[39;22m[90m [39m[90mcreating a part is logged[39m[90m                                                                                   [39m [90m0.09s[39m  
  [31;1m⨯[39;22m[90m [39m[90mupdating a part logs only the changed fields[39m[90m                                                                [39m [90m0.08s[39m  
  [31;1m⨯[39;22m[90m [39m[90mdeleting a part is logged[39m[90m                                                                                   [39m [90m0.08s[39m  
  [31;1m⨯[39;22m[90m [39m[90mcreating a supplier and mechanic is logged[39m[90m                                                                  [39m [90m0.09s[39m  
  [31;1m⨯[39;22m[90m [39m[90ma direct stock movement is logged with its foreign keys[39m[90m                                                     [39m [90m0.10s[39m  
  [31;1m⨯[39;22m[90m [39m[90ma csv import is logged as imported not created[39m[90m                                                              [39m [90m0.08s[39m  
  [31;1m⨯[39;22m[90m [39m[90mthe index lists only the users company entries[39m[90m                                                              [39m [90m0.09s[39m  
  [31;1m⨯[39;22m[90m [39m[90mthe index can be filtered by type and action[39m[90m                                                                [39m [90m0.09s[39m  
  [32;1m✓[39;22m[90m [39m[90ma guest is redirected away[39m[90m                                                                                  [39m [90m0.05s[39m  

  [39;41;1m FAIL [39;49;22m[39m Tests\Feature\Inventory\CsvImportTest[39m
  [31;1m⨯[39;22m[90m [39m[90ma valid parts csv imports every row[39m[90m                                                                         [39m [90m0.10s[39m  
  [31;1m⨯[39;22m[90m [39m[90ma blank category cell defaults to other instead of crashing[39m[90m                                                 [39m [90m0.08s[39m  
  [31;1m⨯[39;22m[90m [39m[90ma parts csv with one bad row imports nothing[39m[90m                                                                [39m [90m0.08s[39m  
  [31;1m⨯[39;22m[90m [39m[90ma duplicate part number within the file is rejected[39m[90m                                                         [39m [90m0.08s[39m  
  [31;1m⨯[39;22m[90m [39m[90ma part number already used by the company is rejected[39m[90m                                                       [39m [90m0.10s[39m  
  [31;1m⨯[39;22m[90m [39m[90man unknown supplier name is rejected[39m[90m                                                                        [39m [90m0.08s[39m  
  [31;1m⨯[39;22m[90m [39m[90ma parts csv cannot reference another companys supplier[39m[90m                                                      [39m [90m0.09s[39m  
  [31;1m⨯[39;22m[90m [39m[90mwrong headers are rejected before any row is read[39m[90m                                                           [39m [90m0.09s[39m  
  [31;1m⨯[39;22m[90m [39m[90ma valid stock in csv creates movements[39m[90m                                                                      [39m [90m0.09s[39m  
  [31;1m⨯[39;22m[90m [39m[90ma stock in csv referencing an unknown part is rejected[39m[90m                                                      [39m [90m0.08s[39m  
  [31;1m⨯[39;22m[90m [39m[90ma stock in csv cannot pull stock into another companys part[39m[90m                                                 [39m [90m0.09s[39m  
  [31;1m⨯[39;22m[90m [39m[90ma valid stock out csv creates movements and reduces stock[39m[90m                                                   [39m [90m0.10s[39m  
  [31;1m⨯[39;22m[90m [39m[90ma stock out csv cannot exceed available stock across multiple rows[39m[90m                                          [39m [90m0.10s[39m  
  [31;1m⨯[39;22m[90m [39m[90ma stock out csv cannot reference another companys vehicle[39m[90m                                                   [39m [90m0.09s[39m  
  [31;1m⨯[39;22m[90m [39m[90mtemplates download with the expected headers[39m[90m                                                                [39m [90m0.10s[39m  
  [31;1m⨯[39;22m[90m [39m[90mthe parts import forms back link returns to the parts list[39m[90m                                                  [39m [90m0.08s[39m  
  [31;1m⨯[39;22m[90m [39m[90mthe stock in import forms back link returns to the stock entry in tab[39m[90m                                       [39m [90m0.08s[39m  
  [31;1m⨯[39;22m[90m [39m[90mthe stock out import forms back link returns to the stock entry out tab[39m[90m                                     [39m [90m0.08s[39m  
  [32;1m✓[39;22m[90m [39m[90man unknown import type is not found[39m[90m                                                                         [39m [90m0.07s[39m  
  [32;1m✓[39;22m[90m [39m[90ma guest is redirected away from import pages[39m[90m                                                                [39m [90m0.06s[39m  

  [39;41;1m FAIL [39;49;22m[39m Tests\Feature\Inventory\InventoryReportTest[39m
  [31;1m⨯[39;22m[90m [39m[90mvaluation report is isolated[39m[90m                                                                                [39m [90m0.15s[39m  
  [31;1m⨯[39;22m[90m [39m[90mparts usage report is isolated and totals correctly[39m[90m                                                         [39m [90m0.13s[39m  
  [31;1m⨯[39;22m[90m [39m[90mstock movement report is isolated[39m[90m                                                                           [39m [90m0.12s[39m  
  [31;1m⨯[39;22m[90m [39m[90msupplier purchases report is isolated[39m[90m                                                                       [39m [90m0.13s[39m  
  [31;1m⨯[39;22m[90m [39m[90mconsumables usage report is isolated[39m[90m                                                                        [39m [90m0.13s[39m  
  [31;1m⨯[39;22m[90m [39m[90mvehicle wise expense report is isolated and combines maintenance and parts[39m[90m                                  [39m [90m0.13s[39m  
  [31;1m⨯[39;22m[90m [39m[90mvehicle wise expense detail shows the underlying jobs and movements[39m[90m                                         [39m [90m0.11s[39m  
  [32;1m✓[39;22m[90m [39m[90mvehicle wise expense detail is isolated[39m[90m                                                                     [39m [90m0.09s[39m  
  [31;1m⨯[39;22m[90m [39m[90mcsv export is isolated[39m[90m                                                                                      [39m [90m0.12s[39m  
  [31;1m⨯[39;22m[90m [39m[90mexcel export downloads a valid spreadsheet[39m[90m                                                                  [39m [90m0.12s[39m  
  [31;1m⨯[39;22m[90m [39m[90mpdf export downloads a valid pdf[39m[90m                                                                            [39m [90m0.10s[39m  
  [32;1m✓[39;22m[90m [39m[90ma guest is redirected away[39m[90m                                                                                  [39m [90m0.06s[39m  
  [31;1m⨯[39;22m[90m [39m[90mfilters reject another companys part supplier or vehicle[39m[90m                                                    [39m [90m0.11s[39m  

  [39;41;1m FAIL [39;49;22m[39m Tests\Feature\Inventory\MaintenanceJobTest[39m
  [31;1m⨯[39;22m[90m [39m[90ma user can create a job and total cost is calculated[39m[90m                                                        [39m [90m0.13s[39m  
  [31;1m⨯[39;22m[90m [39m[90mupdating labour cost recalculates total cost[39m[90m                                                                [39m [90m0.11s[39m  
  [31;1m⨯[39;22m[90m [39m[90mthe job list can be filtered by vehicle and date range[39m[90m                                                      [39m [90m0.12s[39m  
  [31;1m⨯[39;22m[90m [39m[90missuing a part records a stock out and updates parts and total cost[39m[90m                                         [39m [90m0.14s[39m  
  [31;1m⨯[39;22m[90m [39m[90missuing a part cannot exceed available stock[39m[90m                                                                [39m [90m0.13s[39m  
  [31;1m⨯[39;22m[90m [39m[90ma job cannot be assigned another companys vehicle or mechanic[39m[90m                                               [39m [90m0.12s[39m  
  [31;1m⨯[39;22m[90m [39m[90ma job cannot have another companys part issued to it[39m[90m                                                        [39m [90m0.12s[39m  
  [31;1m⨯[39;22m[90m [39m[90ma company cannot see or touch another companys maintenance jobs[39m[90m                                             [39m [90m0.12s[39m  
  [31;1m⨯[39;22m[90m [39m[90mdeleting a job soft deletes it[39m[90m                                                                              [39m [90m0.10s[39m  
  [31;1m⨯[39;22m[90m [39m[90mvehicle page rolls up fuel and maintenance into total operating cost[39m[90m                                        [39m [90m0.09s[39m  

  [39;41;1m FAIL [39;49;22m[39m Tests\Feature\Inventory\MechanicTest[39m
  [31;1m⨯[39;22m[90m [39m[90ma user can create update and soft delete mechanics[39m[90m                                                          [39m [90m0.12s[39m  
  [31;1m⨯[39;22m[90m [39m[90ma company cannot see or touch another companys mechanics[39m[90m                                                    [39m [90m0.09s[39m  

  [39;41;1m FAIL [39;49;22m[39m Tests\Feature\Inventory\PartBarcodeTest[39m
  [31;1m⨯[39;22m[90m [39m[90mlookup finds a part by barcode within the users company[39m[90m                                                     [39m [90m0.09s[39m  
  [31;1m⨯[39;22m[90m [39m[90mlookup does not find another companys barcode[39m[90m                                                               [39m [90m0.10s[39m  
  [31;1m⨯[39;22m[90m [39m[90mlookup reports not found for an unknown barcode[39m[90m                                                             [39m [90m0.12s[39m  
  [32;1m✓[39;22m[90m [39m[90ma single label page is isolated[39m[90m                                                                             [39m [90m0.10s[39m  
  [31;1m⨯[39;22m[90m [39m[90mthe label sheet only includes the users own selected parts[39m[90m                                                  [39m [90m0.12s[39m  
  [31;1m⨯[39;22m[90m [39m[90mthe all labels sheet only includes the users own company[39m[90m                                                    [39m [90m0.11s[39m  
  [31;1m⨯[39;22m[90m [39m[90mthe scan page requires auth[39m[90m                                                                                 [39m [90m0.11s[39m  
  [32;1m✓[39;22m[90m [39m[90ma guest cannot use lookup[39m[90m                                                                                   [39m [90m0.07s[39m  

  [39;41;1m FAIL [39;49;22m[39m Tests\Feature\Inventory\PartTest[39m
  [31;1m⨯[39;22m[90m [39m[90ma user can create a part with their own supplier[39m[90m                                                            [39m [90m0.12s[39m  
  [31;1m⨯[39;22m[90m [39m[90mbarcode is auto generated when left blank[39m[90m                                                                   [39m [90m0.08s[39m  
  [31;1m⨯[39;22m[90m [39m[90ma manually entered barcode is respected instead of auto generating[39m[90m                                          [39m [90m0.08s[39m  
  [31;1m⨯[39;22m[90m [39m[90mbarcode sequences are independent per company[39m[90m                                                               [39m [90m0.10s[39m  
  [31;1m⨯[39;22m[90m [39m[90ma part can be created without a supplier[39m[90m                                                                    [39m [90m0.08s[39m  
  [31;1m⨯[39;22m[90m [39m[90ma blank category field defaults to other instead of crashing[39m[90m                                                [39m [90m0.09s[39m  
  [31;1m⨯[39;22m[90m [39m[90mclearing a parts category on update defaults to other instead of crashing[39m[90m                                   [39m [90m0.09s[39m  
  [31;1m⨯[39;22m[90m [39m[90mpart number and barcode are unique per company but not across companies[39m[90m                                     [39m [90m0.09s[39m  
  [31;1m⨯[39;22m[90m [39m[90ma part cannot be assigned another companys supplier[39m[90m                                                         [39m [90m0.08s[39m  
  [31;1m⨯[39;22m[90m [39m[90ma company cannot see or touch another companys parts[39m[90m                                                        [39m [90m0.08s[39m  

  [39;41;1m FAIL [39;49;22m[39m Tests\Feature\Inventory\StockEntryTest[39m
  [31;1m⨯[39;22m[90m [39m[90mno form element is nested inside another form[39m[90m                                                               [39m [90m0.12s[39m  
  [31;1m⨯[39;22m[90m [39m[90mthe page renders and defaults to the stock in tab[39m[90m                                                           [39m [90m0.10s[39m  
  [31;1m⨯[39;22m[90m [39m[90mthe tab query parameter selects the requested tab[39m[90m                                                           [39m [90m0.09s[39m  
  [31;1m⨯[39;22m[90m [39m[90man unknown tab falls back to stock in[39m[90m                                                                       [39m [90m0.12s[39m  
  [31;1m⨯[39;22m[90m [39m[90mthe part query parameter preselects a part owned by the company[39m[90m                                             [39m [90m0.13s[39m  
  [31;1m⨯[39;22m[90m [39m[90mthe part query parameter is ignored for another companys part[39m[90m                                               [39m [90m0.10s[39m  
  [31;1m⨯[39;22m[90m [39m[90mpart search is isolated to the users company[39m[90m                                                                [39m [90m0.12s[39m  
  [31;1m⨯[39;22m[90m [39m[90mpart search returns current stock[39m[90m                                                                           [39m [90m0.12s[39m  
  [31;1m⨯[39;22m[90m [39m[90mstock in submitted from the page redirects back to it[39m[90m                                                       [39m [90m0.11s[39m  
  [31;1m⨯[39;22m[90m [39m[90mstock out submitted from the page redirects back to the out tab[39m[90m                                             [39m [90m0.10s[39m  
  [31;1m⨯[39;22m[90m [39m[90ma stock out without return to still redirects to the part page[39m[90m                                              [39m [90m0.06s[39m  
  [31;1m⨯[39;22m[90m [39m[90mquick adding a part from the page redirects back with it preselected[39m[90m                                        [39m [90m0.05s[39m  
  [31;1m⨯[39;22m[90m [39m[90mquick adding a supplier from the page redirects back with it preselected[39m[90m                                    [39m [90m0.05s[39m  
  [31;1m⨯[39;22m[90m [39m[90mquick adding a maintenance job from the page redirects back to the maintenance tab[39m[90m                          [39m [90m0.05s[39m  
  [32;1m✓[39;22m[90m [39m[90ma guest is redirected away[39m[90m                                                                                  [39m [90m0.04s[39m  
  [31;1m⨯[39;22m[90m [39m[90mthe recent lists show data scoped to the users company[39m[90m                                                      [39m [90m0.06s[39m  
  [32;1m✓[39;22m[90m [39m[90mthe quick add part form has no barcode field[39m[90m                                                                [39m [90m0.05s[39m  
  [31;1m⨯[39;22m[90m [39m[90mmanage parts suppliers and mechanics pages link back to stock entry[39m[90m                                         [39m [90m0.05s[39m  

  [39;41;1m FAIL [39;49;22m[39m Tests\Feature\Inventory\StockInReceiptTest[39m
  [31;1m⨯[39;22m[90m [39m[90mthe index lists only the users company stock in entries[39m[90m                                                     [39m [90m0.11s[39m  
  [31;1m⨯[39;22m[90m [39m[90mthe index excludes stock out entries[39m[90m                                                                        [39m [90m0.08s[39m  
  [31;1m⨯[39;22m[90m [39m[90ma valid single supplier selection renders a receipt[39m[90m                                                         [39m [90m0.12s[39m  
  [31;1m⨯[39;22m[90m [39m[90ma mixed supplier selection is rejected[39m[90m                                                                      [39m [90m0.09s[39m  
  [31;1m⨯[39;22m[90m [39m[90ma company cannot print a receipt for another companys stock in entries[39m[90m                                      [39m [90m0.09s[39m  
  [32;1m✓[39;22m[90m [39m[90ma guest is redirected away[39m[90m                                                                                  [39m [90m0.07s[39m  

  [39;41;1m FAIL [39;49;22m[39m Tests\Feature\Inventory\StockMovementTest[39m
  [31;1m⨯[39;22m[90m [39m[90ma stock in increases derived stock and resolves supplier[39m[90m                                                    [39m [90m0.10s[39m  
  [31;1m⨯[39;22m[90m [39m[90ma stock out decreases derived stock and resolves vehicle and mechanic[39m[90m                                       [39m [90m0.11s[39m  
  [31;1m⨯[39;22m[90m [39m[90mstock out freezes the parts current unit cost and is immune to later price changes[39m[90m                          [39m [90m0.09s[39m  
  [31;1m⨯[39;22m[90m [39m[90man adjustment has no cost basis[39m[90m                                                                             [39m [90m0.10s[39m  
  [31;1m⨯[39;22m[90m [39m[90mstock out cannot exceed available stock[39m[90m                                                                     [39m [90m0.12s[39m  
  [31;1m⨯[39;22m[90m [39m[90madjustment can increase or decrease stock[39m[90m                                                                   [39m [90m0.10s[39m  
  [31;1m⨯[39;22m[90m [39m[90mparts list and show report low stock and out of stock[39m[90m                                                       [39m [90m0.13s[39m  
  [32;1m✓[39;22m[90m [39m[90ma user cannot record a movement against another companys part[39m[90m                                               [39m [90m0.10s[39m  
  [31;1m⨯[39;22m[90m [39m[90ma movement cannot reference another companys supplier vehicle or mechanic[39m[90m                                   [39m [90m0.10s[39m  
  [31;1m⨯[39;22m[90m [39m[90ma companys stock ledger and derived stock are isolated[39m[90m                                                      [39m [90m0.10s[39m  

  [39;41;1m FAIL [39;49;22m[39m Tests\Feature\Inventory\SupplierTest[39m
  [31;1m⨯[39;22m[90m [39m[90ma user can create and see their companys suppliers[39m[90m                                                          [39m [90m0.12s[39m  
  [31;1m⨯[39;22m[90m [39m[90ma user can update and soft delete a supplier[39m[90m                                                                [39m [90m0.09s[39m  
  [31;1m⨯[39;22m[90m [39m[90ma company cannot see or touch another companys suppliers[39m[90m                                                    [39m [90m0.08s[39m  

  [30;42;1m PASS [39;49;22m[39m Tests\Feature\NotificationTest[39m
  [32;1m✓[39;22m[90m [39m[90mthe bell feed is isolated to the users company[39m[90m                                                              [39m [90m0.13s[39m  
  [32;1m✓[39;22m[90m [39m[90mthe reverse company is also isolated[39m[90m                                                                        [39m [90m0.14s[39m  
  [32;1m✓[39;22m[90m [39m[90mtotal reflects the unread count and alerts are red before amber[39m[90m                                             [39m [90m0.09s[39m  
  [32;1m✓[39;22m[90m [39m[90malerts link to the relevant dashboard tab[39m[90m                                                                   [39m [90m0.10s[39m  
  [32;1m✓[39;22m[90m [39m[90ma guest is unauthorized[39m[90m                                                                                     [39m [90m0.05s[39m  

  [30;42;1m PASS [39;49;22m[39m Tests\Feature\Notifications\DailyCriticalDigestTest[39m
  [32;1m✓[39;22m[90m [39m[90mthe daily digest is isolated per company[39m[90m                                                                    [39m [90m0.11s[39m  
  [32;1m✓[39;22m[90m [39m[90mthe mail message renders without error[39m[90m                                                                      [39m [90m0.27s[39m  
  [32;1m✓[39;22m[90m [39m[90ma company with no recipients configured is skipped[39m[90m                                                          [39m [90m0.07s[39m  
  [32;1m✓[39;22m[90m [39m[90ma company with nothing critical is skipped even with recipients configured[39m[90m                                  [39m [90m0.06s[39m  
  [32;1m✓[39;22m[90m [39m[90mexpired driver documents are included too[39m[90m                                                                   [39m [90m0.11s[39m  

  [30;42;1m PASS [39;49;22m[39m Tests\Feature\Notifications\WeeklyFullSummaryTest[39m
  [32;1m✓[39;22m[90m [39m[90mthe weekly summary is isolated per company and includes the fuller picture[39m[90m                                  [39m [90m0.14s[39m  
  [32;1m✓[39;22m[90m [39m[90mthe mail message renders without error[39m[90m                                                                      [39m [90m0.15s[39m  
  [32;1m✓[39;22m[90m [39m[90ma company with no recipients configured is skipped[39m[90m                                                          [39m [90m0.07s[39m  
  [32;1m✓[39;22m[90m [39m[90man all clear week still sends a summary[39m[90m                                                                     [39m [90m0.07s[39m  

  [30;42;1m PASS [39;49;22m[39m Tests\Feature\ProfileTest[39m
  [32;1m✓[39;22m[90m [39m[90mprofile page is displayed[39m[90m                                                                                   [39m [90m0.17s[39m  
  [32;1m✓[39;22m[90m [39m[90mprofile information can be updated[39m[90m                                                                          [39m [90m0.06s[39m  
  [32;1m✓[39;22m[90m [39m[90memail verification status is unchanged when the email address is unchanged[39m[90m                                  [39m [90m0.05s[39m  
  [32;1m✓[39;22m[90m [39m[90muser can delete their account[39m[90m                                                                               [39m [90m0.06s[39m  
  [32;1m✓[39;22m[90m [39m[90mcorrect password must be provided to delete account[39m[90m                                                         [39m [90m0.06s[39m  

  [30;42;1m PASS [39;49;22m[39m Tests\Feature\Tenancy\CompanyIsolationTest[39m
  [32;1m✓[39;22m[90m [39m[90mcompany a cannot see company bs users[39m[90m                                                                       [39m [90m0.18s[39m  
  [32;1m✓[39;22m[90m [39m[90mregistration creates a company and makes the user its first member[39m[90m                                          [39m [90m0.06s[39m  
  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\ChallanTest[22m [90m>[39m a user can add a challan to their companys vehicle                         
[39;1m  Expected response status code [201, 301, 302, 303, 307, 308] but received 403.
Failed asserting that false is true.[39;22m

  at [32mtests\Feature\Fleet\ChallanTest.php[39m:[32m33[39m
     29▕             'amount' => '1500.00',
     30▕             'due_date' => now()->addDays(14)->toDateString(),
     31▕         ]);
     32▕ 
  ➜  33▕         $response->assertRedirect(route('challans.index'));
     34▕ 
     35▕         $challan = Challan::where('challan_number', 'CH-12345')->first();
     36▕         $this->assertNotNull($challan);
     37▕         $this->assertSame($user->company_id, $challan->company_id);

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\ChallanTest[22m [90m>[39m challan status reflects due date and payment                               
[39;1m  Expected response status code [200] but received 403.
Failed asserting that 403 is identical to 200.[39;22m

  at [32mtests\Feature\Fleet\ChallanTest.php[39m:[32m61[39m
     57▕         $this->assertSame(ChallanStatus::Overdue, $overdue->status());
     58▕         $this->assertSame(ChallanStatus::Paid, $paid->status());
     59▕ 
     60▕         $this->actingAs($user)->get(route('challans.index'))
  ➜  61▕             ->assertOk()
     62▕             ->assertSee('Pending')
     63▕             ->assertSee('Overdue')
     64▕             ->assertSee('Paid');
     65▕     }

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\ChallanTest[22m [90m>[39m a user can update and soft delete a challan                                
[39;1m  Expected response status code [201, 301, 302, 303, 307, 308] but received 403.
Failed asserting that false is true.[39;22m

  at [32mtests\Feature\Fleet\ChallanTest.php[39m:[32m82[39m
     78▕                 'issue_date' => $challan->issue_date->toDateString(),
     79▕                 'amount' => $challan->amount,
     80▕                 'due_date' => $challan->due_date->toDateString(),
     81▕             ])
  ➜  82▕             ->assertRedirect(route('challans.index'));
     83▕ 
     84▕         $challan->refresh();
     85▕         $this->assertSame('CH-UPDATED', $challan->challan_number);
     86▕

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\ChallanTest[22m [90m>[39m a user cannot log a challan against another companys vehicle               
[39;1m  Session is missing expected key [errors].
Failed asserting that false is true.[39;22m

  at [32mtests\Feature\Fleet\ChallanTest.php[39m:[32m107[39m
    103▕             'violation_type' => 'Overspeeding',
    104▕             'issue_date' => now()->toDateString(),
    105▕             'amount' => 1000,
    106▕             'due_date' => now()->addDays(14)->toDateString(),
  ➜ 107▕         ])->assertSessionHasErrors('vehicle');
    108▕ 
    109▕         $this->assertSame(0, Challan::withoutGlobalScopes()->count());
    110▕     }
    111▕

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\ChallanTest[22m [90m>[39m a company cannot see or touch another companys challans                    
[39;1m  Expected response status code [200] but received 403.
Failed asserting that 403 is identical to 200.[39;22m

  at [32mtests\Feature\Fleet\ChallanTest.php[39m:[32m123[39m
    119▕             'challan_number' => 'SECRET-CH-42',
    120▕         ]);
    121▕ 
    122▕         $this->actingAs($userA)->get(route('challans.index'))
  ➜ 123▕             ->assertOk()
    124▕             ->assertDontSee('SECRET-CH-42');
    125▕ 
    126▕         $this->actingAs($userA)->get(route('challans.edit', $challanB))->assertNotFound();
    127▕         $this->actingAs($userA)

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\CsvImportTest[22m [90m>[39m a valid vehicles csv imports every row                                   
[39;1m  Expected response status code [201, 301, 302, 303, 307, 308] but received 403.
Failed asserting that false is true.[39;22m

  at [32mtests\Feature\Fleet\CsvImportTest.php[39m:[32m38[39m
     34▕         $response = $this->actingAs($user)->post(route('fleet.import.store', 'vehicles'), [
     35▕             'file' => $this->csvFile($csv),
     36▕         ]);
     37▕ 
  ➜  38▕         $response->assertRedirect(route('fleet.import.form', 'vehicles'));
     39▕         $response->assertSessionHas('status');
     40▕ 
     41▕         $this->assertSame(2, Vehicle::query()->count());
     42▕         $vehicle = Vehicle::where('plate', 'LEA-1234')->first();

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\CsvImportTest[22m [90m>[39m a blank vehicle status defaults to active                                
[39;1m  Session is missing expected key [status].
Failed asserting that false is true.[39;22m

  at [32mtests\Feature\Fleet\CsvImportTest.php[39m:[32m57[39m
     53▕             ."LEA-1234,truck,Hino,500 Series,,,,,\n";
     54▕ 
     55▕         $this->actingAs($user)->post(route('fleet.import.store', 'vehicles'), [
     56▕             'file' => $this->csvFile($csv),
  ➜  57▕         ])->assertSessionHas('status');
     58▕ 
     59▕         $this->assertSame('active', Vehicle::first()->status->value);
     60▕     }
     61▕

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\CsvImportTest[22m [90m>[39m a vehicles csv with one bad row imports nothing                          
[39;1m  Session is missing expected key [importErrors].
Failed asserting that false is true.[39;22m

  at [32mtests\Feature\Fleet\CsvImportTest.php[39m:[32m72[39m
     68▕             .",van,Toyota,Hiace,,,,active,\n"; // missing required plate
     69▕ 
     70▕         $this->actingAs($user)->post(route('fleet.import.store', 'vehicles'), [
     71▕             'file' => $this->csvFile($csv),
  ➜  72▕         ])->assertSessionHas('importErrors');
     73▕ 
     74▕         $this->assertSame(0, Vehicle::query()->count());
     75▕     }
     76▕

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\CsvImportTest[22m [90m>[39m a duplicate plate within the file is rejected                            
[39;1m  Session is missing expected key [importErrors].
Failed asserting that false is true.[39;22m

  at [32mtests\Feature\Fleet\CsvImportTest.php[39m:[32m87[39m
     83▕             ."DUP-1,van,,,,,,active,\n";
     84▕ 
     85▕         $this->actingAs($user)->post(route('fleet.import.store', 'vehicles'), [
     86▕             'file' => $this->csvFile($csv),
  ➜  87▕         ])->assertSessionHas('importErrors');
     88▕ 
     89▕         $this->assertSame(0, Vehicle::query()->count());
     90▕     }
     91▕

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\CsvImportTest[22m [90m>[39m a plate already used by the company is rejected                          
[39;1m  Session is missing expected key [importErrors].
Failed asserting that false is true.[39;22m

  at [32mtests\Feature\Fleet\CsvImportTest.php[39m:[32m102[39m
     98▕             ."EXIST-1,truck,,,,,,active,\n";
     99▕ 
    100▕         $this->actingAs($user)->post(route('fleet.import.store', 'vehicles'), [
    101▕             'file' => $this->csvFile($csv),
  ➜ 102▕         ])->assertSessionHas('importErrors');
    103▕ 
    104▕         $this->assertSame(1, Vehicle::query()->count());
    105▕     }
    106▕

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\CsvImportTest[22m [90m>[39m a plate used by another company does not block the import                
[39;1m  Session is missing expected key [status].
Failed asserting that false is true.[39;22m

  at [32mtests\Feature\Fleet\CsvImportTest.php[39m:[32m122[39m
    118▕             ."SHARED-1,truck,,,,,,active,\n";
    119▕ 
    120▕         $this->actingAs($userA)->post(route('fleet.import.store', 'vehicles'), [
    121▕             'file' => $this->csvFile($csv),
  ➜ 122▕         ])->assertSessionHas('status');
    123▕ 
    124▕         $this->assertSame(1, Vehicle::query()->where('company_id', $userA->company_id)->count());
    125▕         $this->assertSame(2, Vehicle::withoutGlobalScopes()->where('plate', 'SHARED-1')->count());
    126▕     }

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\CsvImportTest[22m [90m>[39m vehicles csv wrong headers are rejected before any row is read           
[39;1m  Session is missing expected key [importErrors].
Failed asserting that false is true.[39;22m

  at [32mtests\Feature\Fleet\CsvImportTest.php[39m:[32m136[39m
    132▕         $csv = "wrong,headers,here\nfoo,bar,baz\n";
    133▕ 
    134▕         $this->actingAs($user)->post(route('fleet.import.store', 'vehicles'), [
    135▕             'file' => $this->csvFile($csv),
  ➜ 136▕         ])->assertSessionHas('importErrors');
    137▕ 
    138▕         $this->assertSame(0, Vehicle::query()->count());
    139▕     }
    140▕

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\CsvImportTest[22m [90m>[39m a valid drivers csv imports every row                                    
[39;1m  Expected response status code [201, 301, 302, 303, 307, 308] but received 403.
Failed asserting that false is true.[39;22m

  at [32mtests\Feature\Fleet\CsvImportTest.php[39m:[32m155[39m
    151▕         $response = $this->actingAs($user)->post(route('fleet.import.store', 'drivers'), [
    152▕             'file' => $this->csvFile($csv),
    153▕         ]);
    154▕ 
  ➜ 155▕         $response->assertRedirect(route('fleet.import.form', 'drivers'));
    156▕         $response->assertSessionHas('status');
    157▕ 
    158▕         $this->assertSame(2, Driver::query()->count());
    159▕         $driver = Driver::where('cnic', '35202-1234567-1')->first();

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\CsvImportTest[22m [90m>[39m a drivers csv with one bad row imports nothing                           
[39;1m  Session is missing expected key [importErrors].
Failed asserting that false is true.[39;22m

  at [32mtests\Feature\Fleet\CsvImportTest.php[39m:[32m175[39m
    171▕             .",35202-0000000-0,,,,,active,\n"; // missing required name
    172▕ 
    173▕         $this->actingAs($user)->post(route('fleet.import.store', 'drivers'), [
    174▕             'file' => $this->csvFile($csv),
  ➜ 175▕         ])->assertSessionHas('importErrors');
    176▕ 
    177▕         $this->assertSame(0, Driver::query()->count());
    178▕     }
    179▕

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\CsvImportTest[22m [90m>[39m a duplicate cnic within the file is rejected                             
[39;1m  Session is missing expected key [importErrors].
Failed asserting that false is true.[39;22m

  at [32mtests\Feature\Fleet\CsvImportTest.php[39m:[32m190[39m
    186▕             ."Bilal Ahmed,DUP-1,,,,,active,\n";
    187▕ 
    188▕         $this->actingAs($user)->post(route('fleet.import.store', 'drivers'), [
    189▕             'file' => $this->csvFile($csv),
  ➜ 190▕         ])->assertSessionHas('importErrors');
    191▕ 
    192▕         $this->assertSame(0, Driver::query()->count());
    193▕     }
    194▕

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\CsvImportTest[22m [90m>[39m a cnic already used by the company is rejected                           
[39;1m  Session is missing expected key [importErrors].
Failed asserting that false is true.[39;22m

  at [32mtests\Feature\Fleet\CsvImportTest.php[39m:[32m205[39m
    201▕             ."Ali Raza,EXIST-1,,,,,active,\n";
    202▕ 
    203▕         $this->actingAs($user)->post(route('fleet.import.store', 'drivers'), [
    204▕             'file' => $this->csvFile($csv),
  ➜ 205▕         ])->assertSessionHas('importErrors');
    206▕ 
    207▕         $this->assertSame(1, Driver::query()->count());
    208▕     }
    209▕

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\CsvImportTest[22m [90m>[39m a cnic used by another company does not block the import                 
[39;1m  Session is missing expected key [status].
Failed asserting that false is true.[39;22m

  at [32mtests\Feature\Fleet\CsvImportTest.php[39m:[32m225[39m
    221▕             ."Ali Raza,SHARED-1,,,,,active,\n";
    222▕ 
    223▕         $this->actingAs($userA)->post(route('fleet.import.store', 'drivers'), [
    224▕             'file' => $this->csvFile($csv),
  ➜ 225▕         ])->assertSessionHas('status');
    226▕ 
    227▕         $this->assertSame(1, Driver::query()->where('company_id', $userA->company_id)->count());
    228▕         $this->assertSame(2, Driver::withoutGlobalScopes()->where('cnic', 'SHARED-1')->count());
    229▕     }

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\CsvImportTest[22m [90m>[39m a valid vehicle documents csv imports every row by plate lookup          
[39;1m  Expected response status code [201, 301, 302, 303, 307, 308] but received 403.
Failed asserting that false is true.[39;22m

  at [32mtests\Feature\Fleet\CsvImportTest.php[39m:[32m246[39m
    242▕         $response = $this->actingAs($user)->post(route('fleet.import.store', 'vehicle-documents'), [
    243▕             'file' => $this->csvFile($csv),
    244▕         ]);
    245▕ 
  ➜ 246▕         $response->assertRedirect(route('fleet.import.form', 'vehicle-documents'));
    247▕         $response->assertSessionHas('status');
    248▕ 
    249▕         $this->assertSame(2, VehicleDocument::query()->count());
    250▕         $document = VehicleDocument::where('document_number', 'INS-98765')->first();

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\CsvImportTest[22m [90m>[39m a vehicle document of type other requires a custom name                  
[39;1m  Session is missing expected key [importErrors].
Failed asserting that false is true.[39;22m

  at [32mtests\Feature\Fleet\CsvImportTest.php[39m:[32m268[39m
    264▕             ."LEA-1234,other,,,,,\n";
    265▕ 
    266▕         $this->actingAs($user)->post(route('fleet.import.store', 'vehicle-documents'), [
    267▕             'file' => $this->csvFile($csv),
  ➜ 268▕         ])->assertSessionHas('importErrors');
    269▕ 
    270▕         $this->assertSame(0, VehicleDocument::query()->count());
    271▕     }
    272▕

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\CsvImportTest[22m [90m>[39m a vehicle document referencing an unknown plate is rejected              
[39;1m  Session is missing expected key [importErrors].
Failed asserting that false is true.[39;22m

  at [32mtests\Feature\Fleet\CsvImportTest.php[39m:[32m282[39m
    278▕             ."NOPE-999,insurance,,INS-1,,,\n";
    279▕ 
    280▕         $this->actingAs($user)->post(route('fleet.import.store', 'vehicle-documents'), [
    281▕             'file' => $this->csvFile($csv),
  ➜ 282▕         ])->assertSessionHas('importErrors');
    283▕ 
    284▕         $this->assertSame(0, VehicleDocument::query()->count());
    285▕     }
    286▕

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\CsvImportTest[22m [90m>[39m a vehicle documents csv cannot attach to another companys vehicle        
[39;1m  Session is missing expected key [importErrors].
Failed asserting that false is true.[39;22m

  at [32mtests\Feature\Fleet\CsvImportTest.php[39m:[32m298[39m
    294▕             ."RIVAL-99,insurance,,INS-1,,,\n";
    295▕ 
    296▕         $this->actingAs($userA)->post(route('fleet.import.store', 'vehicle-documents'), [
    297▕             'file' => $this->csvFile($csv),
  ➜ 298▕         ])->assertSessionHas('importErrors');
    299▕ 
    300▕         $this->assertSame(0, VehicleDocument::withoutGlobalScopes()->where('document_number', 'INS-1')->count());
    301▕     }
    302▕

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\CsvImportTest[22m [90m>[39m a valid driver documents csv imports every row by cnic lookup            
[39;1m  Expected response status code [201, 301, 302, 303, 307, 308] but received 403.
Failed asserting that false is true.[39;22m

  at [32mtests\Feature\Fleet\CsvImportTest.php[39m:[32m317[39m
    313▕         $response = $this->actingAs($user)->post(route('fleet.import.store', 'driver-documents'), [
    314▕             'file' => $this->csvFile($csv),
    315▕         ]);
    316▕ 
  ➜ 317▕         $response->assertRedirect(route('fleet.import.form', 'driver-documents'));
    318▕         $response->assertSessionHas('status');
    319▕ 
    320▕         $document = DriverDocument::where('document_number', 'LHR-DL-9981')->first();
    321▕         $this->assertNotNull($document);

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\CsvImportTest[22m [90m>[39m a driver document referencing an unknown cnic is rejected                
[39;1m  Session is missing expected key [importErrors].
Failed asserting that false is true.[39;22m

  at [32mtests\Feature\Fleet\CsvImportTest.php[39m:[32m335[39m
    331▕             ."00000-0000000-0,license,,LIC-1,,,\n";
    332▕ 
    333▕         $this->actingAs($user)->post(route('fleet.import.store', 'driver-documents'), [
    334▕             'file' => $this->csvFile($csv),
  ➜ 335▕         ])->assertSessionHas('importErrors');
    336▕ 
    337▕         $this->assertSame(0, DriverDocument::query()->count());
    338▕     }
    339▕

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\CsvImportTest[22m [90m>[39m a driver documents csv cannot attach to another companys driver          
[39;1m  Session is missing expected key [importErrors].
Failed asserting that false is true.[39;22m

  at [32mtests\Feature\Fleet\CsvImportTest.php[39m:[32m351[39m
    347▕             ."99999-9999999-9,license,,LIC-1,,,\n";
    348▕ 
    349▕         $this->actingAs($userA)->post(route('fleet.import.store', 'driver-documents'), [
    350▕             'file' => $this->csvFile($csv),
  ➜ 351▕         ])->assertSessionHas('importErrors');
    352▕ 
    353▕         $this->assertSame(0, DriverDocument::withoutGlobalScopes()->where('document_number', 'LIC-1')->count());
    354▕     }
    355▕

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\CsvImportTest[22m [90m>[39m fleet templates download with the expected headers                       
[39;1m  Expected response status code [200] but received 403.
Failed asserting that 403 is identical to 200.[39;22m

  at [32mtests\Feature\Fleet\CsvImportTest.php[39m:[32m378[39m
    374▕     {
    375▕         $user = User::factory()->create();
    376▕ 
    377▕         $vehiclesResponse = $this->actingAs($user)->get(route('fleet.import.template', 'vehicles'));
  ➜ 378▕         $vehiclesResponse->assertOk();
    379▕         $this->assertStringContainsString('text/csv', $vehiclesResponse->headers->get('content-type'));
    380▕         $this->assertStringStartsWith('plate,vehicle_type,make', $vehiclesResponse->getContent());
    381▕ 
    382▕         $driversResponse = $this->actingAs($user)->get(route('fleet.import.template', 'drivers'));

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\CsvImportTest[22m [90m>[39m the vehicles import forms back link returns to the vehicles list         
[39;1m  Expected response status code [200] but received 403.
Failed asserting that 403 is identical to 200.[39;22m

  at [32mtests\Feature\Fleet\CsvImportTest.php[39m:[32m406[39m
    402▕     {
    403▕         $user = User::factory()->create();
    404▕ 
    405▕         $this->actingAs($user)->get(route('fleet.import.form', 'vehicles'))
  ➜ 406▕             ->assertOk()
    407▕             ->assertSee(route('vehicles.index'), false);
    408▕     }
    409▕ 
    410▕     public function test_the_drivers_import_forms_back_link_returns_to_the_drivers_list(): void

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\CsvImportTest[22m [90m>[39m the drivers import forms back link returns to the drivers list           
[39;1m  Expected response status code [200] but received 403.
Failed asserting that 403 is identical to 200.[39;22m

  at [32mtests\Feature\Fleet\CsvImportTest.php[39m:[32m415[39m
    411▕     {
    412▕         $user = User::factory()->create();
    413▕ 
    414▕         $this->actingAs($user)->get(route('fleet.import.form', 'drivers'))
  ➜ 415▕             ->assertOk()
    416▕             ->assertSee(route('drivers.index'), false);
    417▕     }
    418▕ 
    419▕     public function test_the_vehicle_documents_import_forms_back_link_returns_to_the_vehicle_documents_tab(): void

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\CsvImportTest[22m [90m>[39m the vehicle documents import forms back link returns to the vehicle d…   
[39;1m  Expected response status code [200] but received 403.
Failed asserting that 403 is identical to 200.[39;22m

  at [32mtests\Feature\Fleet\CsvImportTest.php[39m:[32m424[39m
    420▕     {
    421▕         $user = User::factory()->create();
    422▕ 
    423▕         $this->actingAs($user)->get(route('fleet.import.form', 'vehicle-documents'))
  ➜ 424▕             ->assertOk()
    425▕             ->assertSee(route('documents.index', ['tab' => 'vehicle']), false);
    426▕     }
    427▕ 
    428▕     public function test_the_driver_documents_import_forms_back_link_returns_to_the_driver_documents_tab(): void

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\CsvImportTest[22m [90m>[39m the driver documents import forms back link returns to the driver doc…   
[39;1m  Expected response status code [200] but received 403.
Failed asserting that 403 is identical to 200.[39;22m

  at [32mtests\Feature\Fleet\CsvImportTest.php[39m:[32m433[39m
    429▕     {
    430▕         $user = User::factory()->create();
    431▕ 
    432▕         $this->actingAs($user)->get(route('fleet.import.form', 'driver-documents'))
  ➜ 433▕             ->assertOk()
    434▕             ->assertSee(route('documents.index', ['tab' => 'driver']), false);
    435▕     }
    436▕ 
    437▕     public function test_an_unknown_fleet_import_type_is_not_found(): void

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\CustomerFieldTest[22m [90m>[39m a user can add a custom field to their companys customer             
[39;1m  Failed asserting that null is not null.[39;22m

  at [32mtests\Feature\Fleet\CustomerFieldTest.php[39m:[32m26[39m
     22▕             'field_name' => 'Gatepass Number',
     23▕         ]);
     24▕ 
     25▕         $field = CustomerField::where('customer_id', $customer->id)->first();
  ➜  26▕         $this->assertNotNull($field);
     27▕         $this->assertSame('Gatepass Number', $field->field_name);
     28▕         $this->assertSame('text', $field->type);
     29▕         $this->assertSame(1, $field->sort_order);
     30▕         $this->assertSame($user->company_id, $field->company_id);

  [33m1   [39m[39;1mtests\Feature\Fleet\CustomerFieldTest.php[39;22m:[39;1m26[39;22m

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\CustomerFieldTest[22m [90m>[39m a second field gets the next sort order                              
[39;1m  Failed asserting that actual size 0 matches expected size 2.[39;22m

  at [32mtests\Feature\Fleet\CustomerFieldTest.php[39m:[32m48[39m
     44▕         $this->actingAs($user)->post(route('customer-fields.store', $customer), ['field_name' => 'Gatepass Number']);
     45▕         $this->actingAs($user)->post(route('customer-fields.store', $customer), ['field_name' => 'Bill Number']);
     46▕ 
     47▕         $fields = CustomerField::where('customer_id', $customer->id)->orderBy('sort_order')->get();
  ➜  48▕         $this->assertCount(2, $fields);
     49▕         $this->assertSame(1, $fields[0]->sort_order);
     50▕         $this->assertSame(2, $fields[1]->sort_order);
     51▕     }
     52▕

  [33m1   [39m[39;1mtests\Feature\Fleet\CustomerFieldTest.php[39;22m:[39;1m48[39;22m

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\CustomerFieldTest[22m [90m>[39m field name is required                                               
[39;1m  Session is missing expected key [errors].
Failed asserting that false is true.[39;22m

  at [32mtests\Feature\Fleet\CustomerFieldTest.php[39m:[32m59[39m
     55▕         $user = User::factory()->create();
     56▕         $customer = Customer::factory()->for($user->company)->create();
     57▕ 
     58▕         $this->actingAs($user)->post(route('customer-fields.store', $customer), ['field_name' => ''])
  ➜  59▕             ->assertSessionHasErrors('field_name');
     60▕ 
     61▕         $this->assertSame(0, CustomerField::where('customer_id', $customer->id)->count());
     62▕     }
     63▕

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\CustomerFieldTest[22m [90m>[39m a user can delete a custom field                                     
[39;1m  Expected response status code [201, 301, 302, 303, 307, 308] but received 403.
Failed asserting that false is true.[39;22m

  at [32mtests\Feature\Fleet\CustomerFieldTest.php[39m:[32m72[39m
     68▕         $field = CustomerField::factory()->for($user->company)->create(['customer_id' => $customer->id]);
     69▕ 
     70▕         $this->actingAs($user)
     71▕             ->delete(route('customer-fields.destroy', $field))
  ➜  72▕             ->assertRedirect(route('customers.show', $customer));
     73▕ 
     74▕         $this->assertSoftDeleted('customer_fields', ['id' => $field->id]);
     75▕     }
     76▕

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\CustomerFieldTest[22m [90m>[39m a company cannot see another companys custom fields on the custom…   
[39;1m  Expected response status code [200] but received 403.
Failed asserting that 403 is identical to 200.[39;22m

  at [32mtests\Feature\Fleet\CustomerFieldTest.php[39m:[32m119[39m
    115▕         $customerB = Customer::factory()->for($companyB)->create();
    116▕         CustomerField::factory()->for($companyB)->create(['customer_id' => $customerB->id, 'field_name' => 'RIVAL Field']);
    117▕ 
    118▕         $this->actingAs($userA)->get(route('customers.show', $customerA))
  ➜ 119▕             ->assertOk()
    120▕             ->assertSee('A Field')
    121▕             ->assertDontSee('RIVAL Field');
    122▕     }
    123▕

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\CustomerTest[22m [90m>[39m a user can create and see their companys customers                        
[39;1m  Failed asserting that null is not null.[39;22m

  at [32mtests\Feature\Fleet\CustomerTest.php[39m:[32m28[39m
     24▕             'notes' => 'Preferred customer.',
     25▕         ]);
     26▕ 
     27▕         $customer = Customer::where('name', 'City Traders')->first();
  ➜  28▕         $this->assertNotNull($customer);
     29▕         $this->assertSame($user->company_id, $customer->company_id);
     30▕         $this->assertNotEmpty($customer->ulid);
     31▕ 
     32▕         $response->assertRedirect(route('customers.show', $customer));

  [33m1   [39m[39;1mtests\Feature\Fleet\CustomerTest.php[39;22m:[39;1m28[39;22m

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\CustomerTest[22m [90m>[39m a user can update their companys customer                                 
[39;1m  Expected response status code [201, 301, 302, 303, 307, 308] but received 403.
Failed asserting that false is true.[39;22m

  at [32mtests\Feature\Fleet\CustomerTest.php[39m:[32m56[39m
     52▕                 'phone' => $customer->phone,
     53▕                 'email' => $customer->email,
     54▕                 'address' => $customer->address,
     55▕             ])
  ➜  56▕             ->assertRedirect(route('customers.show', $customer));
     57▕ 
     58▕         $this->assertSame('New Name', $customer->fresh()->name);
     59▕     }
     60▕

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\CustomerTest[22m [90m>[39m deleting a customer soft deletes it                                       
[39;1m  Expected response status code [201, 301, 302, 303, 307, 308] but received 403.
Failed asserting that false is true.[39;22m

  at [32mtests\Feature\Fleet\CustomerTest.php[39m:[32m68[39m
     64▕         $customer = Customer::factory()->for($user->company)->create();
     65▕ 
     66▕         $this->actingAs($user)
     67▕             ->delete(route('customers.destroy', $customer))
  ➜  68▕             ->assertRedirect(route('customers.index'));
     69▕ 
     70▕         $this->assertSoftDeleted('customers', ['id' => $customer->id]);
     71▕         $this->actingAs($user)->get('/customers')->assertDontSee($customer->name);
     72▕     }

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\CustomerTest[22m [90m>[39m a company cannot see or touch another companys customers                  
[39;1m  Expected response status code [200] but received 403.
Failed asserting that 403 is identical to 200.[39;22m

  at [32mtests\Feature\Fleet\CustomerTest.php[39m:[32m82[39m
     78▕         $userA = User::factory()->for($companyA)->create();
     79▕         $customerB = Customer::factory()->for($companyB)->create(['name' => 'Rival Co']);
     80▕ 
     81▕         $this->actingAs($userA)->get('/customers')
  ➜  82▕             ->assertOk()
     83▕             ->assertDontSee('Rival Co');
     84▕ 
     85▕         $this->actingAs($userA)->get(route('customers.show', $customerB))->assertNotFound();
     86▕         $this->actingAs($userA)->get(route('customers.edit', $customerB))->assertNotFound();

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\DocumentsTest[22m [90m>[39m the vehicle tab lists every vehicle including ones with no documents     
[39;1m  Expected response status code [200] but received 403.
Failed asserting that 403 is identical to 200.[39;22m

  at [32mtests\Feature\Fleet\DocumentsTest.php[39m:[32m45[39m
     41▕         $withDocs = Vehicle::factory()->for($user->company)->create(['plate' => 'HAS-DOCS']);
     42▕         $withoutDocs = Vehicle::factory()->for($user->company)->create(['plate' => 'NO-DOCS']);
     43▕         VehicleDocument::factory()->for($withDocs)->create(['type' => 'fitness']);
     44▕ 
  ➜  45▕         $response = $this->actingAs($user)->get(route('documents.index'))->assertOk();
     46▕ 
     47▕         $labels = null;
     48▕         $response->assertViewHas('rows', function ($rows) use (&$labels) {
     49▕             $labels = collect($rows->items())->pluck('label');

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\DocumentsTest[22m [90m>[39m the driver tab lists every driver including ones with no documents       
[39;1m  Expected response status code [200] but received 403.
Failed asserting that 403 is identical to 200.[39;22m

  at [32mtests\Feature\Fleet\DocumentsTest.php[39m:[32m64[39m
     60▕         $withDocs = Driver::factory()->for($user->company)->create(['name' => 'Ali Raza']);
     61▕         $withoutDocs = Driver::factory()->for($user->company)->create(['name' => 'Bilal Ahmed']);
     62▕         DriverDocument::factory()->for($withDocs)->create(['type' => 'cnic']);
     63▕ 
  ➜  64▕         $response = $this->actingAs($user)->get(route('documents.index', ['tab' => 'driver']))->assertOk();
     65▕ 
     66▕         $labels = null;
     67▕         $response->assertViewHas('rows', function ($rows) use (&$labels) {
     68▕             $labels = collect($rows->items())->pluck('label');

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\DocumentsTest[22m [90m>[39m a fixed type cell shows that documents number expiry and status          
[39;1m  Expected response status code [200] but received 403.
Failed asserting that 403 is identical to 200.[39;22m

  at [32mtests\Feature\Fleet\DocumentsTest.php[39m:[32m84[39m
     80▕         VehicleDocument::factory()->for($vehicle)->create([
     81▕             'type' => 'insurance', 'document_number' => 'INS-12345', 'expiry_date' => now()->addMonths(6),
     82▕         ]);
     83▕ 
  ➜  84▕         $response = $this->actingAs($user)->get(route('documents.index'))->assertOk();
     85▕         $row = $this->rowFor($response, 'ALPHA-1');
     86▕ 
     87▕         $this->assertNotNull($row);
     88▕         $this->assertSame('INS-12345', $row['cells']['insurance']['number']);

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\DocumentsTest[22m [90m>[39m a missing document type renders an empty cell                            
[39;1m  Expected response status code [200] but received 403.
Failed asserting that 403 is identical to 200.[39;22m

  at [32mtests\Feature\Fleet\DocumentsTest.php[39m:[32m98[39m
     94▕     {
     95▕         $user = User::factory()->create();
     96▕         Vehicle::factory()->for($user->company)->create(['plate' => 'BARE-1']);
     97▕ 
  ➜  98▕         $response = $this->actingAs($user)->get(route('documents.index'))->assertOk();
     99▕         $row = $this->rowFor($response, 'BARE-1');
    100▕ 
    101▕         $this->assertNotNull($row);
    102▕         foreach (['fitness', 'route_permit', 'tax', 'insurance'] as $type) {

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\DocumentsTest[22m [90m>[39m two documents of the same type show only the one with the latest expi…   
[39;1m  Expected response status code [200] but received 403.
Failed asserting that 403 is identical to 200.[39;22m

  at [32mtests\Feature\Fleet\DocumentsTest.php[39m:[32m118[39m
    114▕         VehicleDocument::factory()->for($vehicle)->create([
    115▕             'type' => 'tax', 'document_number' => 'NEW-TAX-DOC', 'expiry_date' => now()->addYear(),
    116▕         ]);
    117▕ 
  ➜ 118▕         $response = $this->actingAs($user)->get(route('documents.index'))->assertOk();
    119▕         $row = $this->rowFor($response, 'DUAL-TAX');
    120▕ 
    121▕         $this->assertSame('NEW-TAX-DOC', $row['cells']['tax']['number']);
    122▕         // The superseded document isn't lost — it still counts toward "+N more".

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\DocumentsTest[22m [90m>[39m an other type document is excluded from the matrix and counted as ext…   
[39;1m  Expected response status code [200] but received 403.
Failed asserting that 403 is identical to 200.[39;22m

  at [32mtests\Feature\Fleet\DocumentsTest.php[39m:[32m134[39m
    130▕         VehicleDocument::factory()->for($vehicle)->create([
    131▕             'type' => 'other', 'custom_name' => 'Tracker Certificate', 'document_number' => 'OTHER-DOC-NUM',
    132▕         ]);
    133▕ 
  ➜ 134▕         $response = $this->actingAs($user)->get(route('documents.index'))->assertOk();
    135▕         $row = $this->rowFor($response, 'HAS-OTHER');
    136▕ 
    137▕         foreach (['fitness', 'route_permit', 'tax', 'insurance'] as $type) {
    138▕             $this->assertNull($row['cells'][$type]['document_ulid']);

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\DocumentsTest[22m [90m>[39m the status filter narrows to owners with a matching document             
[39;1m  Expected response status code [200] but received 403.
Failed asserting that 403 is identical to 200.[39;22m

  at [32mtests\Feature\Fleet\DocumentsTest.php[39m:[32m151[39m
    147▕         $validOwner = Vehicle::factory()->for($user->company)->create(['plate' => 'HAS-VALID']);
    148▕         VehicleDocument::factory()->for($expiredOwner)->expired()->create(['type' => 'fitness']);
    149▕         VehicleDocument::factory()->for($validOwner)->create(['type' => 'fitness', 'expiry_date' => now()->addYear()]);
    150▕ 
  ➜ 151▕         $response = $this->actingAs($user)->get(route('documents.index', ['status' => 'expired']))->assertOk();
    152▕ 
    153▕         $labels = null;
    154▕         $response->assertViewHas('rows', function ($rows) use (&$labels) {
    155▕             $labels = collect($rows->items())->pluck('label');

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\DocumentsTest[22m [90m>[39m the search filter matches plate or document number                       
[39;1m  Expected response status code [200] but received 403.
Failed asserting that 403 is identical to 200.[39;22m

  at [32mtests\Feature\Fleet\DocumentsTest.php[39m:[32m171[39m
    167▕         $other = Vehicle::factory()->for($user->company)->create(['plate' => 'OTHER-2']);
    168▕         VehicleDocument::factory()->for($vehicle)->create(['type' => 'tax', 'document_number' => 'DOC-1']);
    169▕         VehicleDocument::factory()->for($other)->create(['type' => 'tax', 'document_number' => 'DOC-2']);
    170▕ 
  ➜ 171▕         $byPlate = $this->actingAs($user)->get(route('documents.index', ['q' => 'FINDME']))->assertOk();
    172▕         $labels = null;
    173▕         $byPlate->assertViewHas('rows', function ($rows) use (&$labels) {
    174▕             $labels = collect($rows->items())->pluck('label');
    175▕

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\DocumentsTest[22m [90m>[39m soft deleted vehicles and drivers are excluded                           
[39;1m  Expected response status code [200] but received 403.
Failed asserting that 403 is identical to 200.[39;22m

  at [32mtests\Feature\Fleet\DocumentsTest.php[39m:[32m198[39m
    194▕         $vehicle = Vehicle::factory()->for($user->company)->create(['plate' => 'GONE-PLATE']);
    195▕         VehicleDocument::factory()->for($vehicle)->create(['type' => 'tax', 'document_number' => 'GONE-DOC']);
    196▕         $vehicle->delete();
    197▕ 
  ➜ 198▕         $response = $this->actingAs($user)->get(route('documents.index'))->assertOk();
    199▕ 
    200▕         $labels = null;
    201▕         $response->assertViewHas('rows', function ($rows) use (&$labels) {
    202▕             $labels = collect($rows->items())->pluck('label');

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\DocumentsTest[22m [90m>[39m a company cannot see another companys vehicles or drivers on either t…   
[39;1m  Expected response status code [200] but received 403.
Failed asserting that 403 is identical to 200.[39;22m

  at [32mtests\Feature\Fleet\DocumentsTest.php[39m:[32m218[39m
    214▕         $driverB = Driver::factory()->for($companyB)->create(['name' => 'Rival Driver']);
    215▕         VehicleDocument::factory()->for($vehicleB)->create(['type' => 'tax', 'document_number' => 'RIVAL-VEH-DOC']);
    216▕         DriverDocument::factory()->for($driverB)->create(['type' => 'cnic', 'document_number' => 'RIVAL-DRV-DOC']);
    217▕ 
  ➜ 218▕         $vehicleResponse = $this->actingAs($userA)->get(route('documents.index'))->assertOk();
    219▕         $vehicleLabels = null;
    220▕         $vehicleResponse->assertViewHas('rows', function ($rows) use (&$vehicleLabels) {
    221▕             $vehicleLabels = collect($rows->items())->pluck('label');
    222▕

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\DriverDocumentTest[22m [90m>[39m a user can add a document to their companys driver                  
[39;1m  Expected response status code [201, 301, 302, 303, 307, 308] but received 403.
Failed asserting that false is true.[39;22m

  at [32mtests\Feature\Fleet\DriverDocumentTest.php[39m:[32m29[39m
     25▕             'issue_date' => now()->subYear()->toDateString(),
     26▕             'expiry_date' => now()->addYear()->toDateString(),
     27▕         ]);
     28▕ 
  ➜  29▕         $response->assertRedirect(route('drivers.show', $driver));
     30▕ 
     31▕         $document = DriverDocument::where('document_number', 'LIC-777')->first();
     32▕         $this->assertNotNull($document);
     33▕         $this->assertSame($user->company_id, $document->company_id);

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\DriverDocumentTest[22m [90m>[39m document status uses the shared definition                          
[39;1m  Expected response status code [200] but received 403.
Failed asserting that 403 is identical to 200.[39;22m

  at [32mtests\Feature\Fleet\DriverDocumentTest.php[39m:[32m56[39m
     52▕         $this->assertSame(DocumentStatus::ExpiringSoon, $expiring->status());
     53▕         $this->assertSame(DocumentStatus::Valid, $valid->status());
     54▕ 
     55▕         $this->actingAs($user)->get(route('drivers.show', $driver))
  ➜  56▕             ->assertOk()
     57▕             ->assertSee('Expired')
     58▕             ->assertSee('Expiring Soon')
     59▕             ->assertSee('Valid');
     60▕     }

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\DriverDocumentTest[22m [90m>[39m a user can update and soft delete a document                        
[39;1m  Expected response status code [201, 301, 302, 303, 307, 308] but received 403.
Failed asserting that false is true.[39;22m

  at [32mtests\Feature\Fleet\DriverDocumentTest.php[39m:[32m73[39m
     69▕             ->patch(route('driver-documents.update', $document), [
     70▕                 'type' => 'medical',
     71▕                 'document_number' => 'MED-1',
     72▕             ])
  ➜  73▕             ->assertRedirect(route('drivers.show', $driver));
     74▕ 
     75▕         $document->refresh();
     76▕         $this->assertSame('medical', $document->type->value);
     77▕

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\DriverDocumentTest[22m [90m>[39m upsert creates a document for a type the driver does not have ye…   
[39;1m  Expected response status code [200] but received 403.
Failed asserting that 403 is identical to 200.[39;22m

  at [32mtests\Feature\Fleet\DriverDocumentTest.php[39m:[32m96[39m
     92▕         $this->actingAs($user)
     93▕             ->putJson(route('driver-documents.upsert', [$driver, 'cnic']), [
     94▕                 'document_number' => 'CNIC-001', 'expiry_date' => now()->addYear()->toDateString(),
     95▕             ])
  ➜  96▕             ->assertOk()
     97▕             ->assertJson(['success' => true]);
     98▕ 
     99▕         $document = $driver->documents()->where('type', 'cnic')->first();
    100▕         $this->assertNotNull($document);

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\DriverDocumentTest[22m [90m>[39m upsert updates the existing document in place instead of creatin…   
[39;1m  Expected response status code [200] but received 403.
Failed asserting that 403 is identical to 200.[39;22m

  at [32mtests\Feature\Fleet\DriverDocumentTest.php[39m:[32m117[39m
    113▕         $this->actingAs($user)
    114▕             ->putJson(route('driver-documents.upsert', [$driver, 'license']), [
    115▕                 'document_number' => 'LIC-NEW', 'expiry_date' => now()->addYear()->toDateString(),
    116▕             ])
  ➜ 117▕             ->assertOk();
    118▕ 
    119▕         $this->assertSame(1, $driver->documents()->where('type', 'license')->count());
    120▕         $existing->refresh();
    121▕         $this->assertSame('LIC-NEW', $existing->document_number);

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\DriverDocumentTest[22m [90m>[39m upsert validates expiry date is a real date                         
[39;1m  Expected response status code [422] but received 403.
Failed asserting that 403 is identical to 422.[39;22m

  at [32mtests\Feature\Fleet\DriverDocumentTest.php[39m:[32m154[39m
    150▕         $driver = Driver::factory()->for($user->company)->create();
    151▕ 
    152▕         $this->actingAs($user)
    153▕             ->putJson(route('driver-documents.upsert', [$driver, 'cnic']), ['expiry_date' => 'not-a-date'])
  ➜ 154▕             ->assertStatus(422);
    155▕ 
    156▕         $this->assertSame(0, $driver->documents()->count());
    157▕     }
    158▕

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\DriverTest[22m [90m>[39m a user can create and see their companys drivers                            
[39;1m  Failed asserting that null is not null.[39;22m

  at [32mtests\Feature\Fleet\DriverTest.php[39m:[32m31[39m
     27▕             'notes' => 'Long-haul routes.',
     28▕         ]);
     29▕ 
     30▕         $driver = Driver::where('cnic', '35202-1234567-1')->first();
  ➜  31▕         $this->assertNotNull($driver);
     32▕         $this->assertSame($user->company_id, $driver->company_id);
     33▕         $this->assertNotEmpty($driver->ulid);
     34▕ 
     35▕         $response->assertRedirect(route('drivers.show', $driver));

  [33m1   [39m[39;1mtests\Feature\Fleet\DriverTest.php[39;22m:[39;1m31[39;22m

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\DriverTest[22m [90m>[39m a user can update and soft delete a driver                                  
[39;1m  Expected response status code [201, 301, 302, 303, 307, 308] but received 403.
Failed asserting that false is true.[39;22m

  at [32mtests\Feature\Fleet\DriverTest.php[39m:[32m58[39m
     54▕                 'name' => 'Renamed Driver',
     55▕                 'cnic' => $driver->cnic,
     56▕                 'status' => 'inactive',
     57▕             ])
  ➜  58▕             ->assertRedirect(route('drivers.show', $driver));
     59▕ 
     60▕         $driver->refresh();
     61▕         $this->assertSame('Renamed Driver', $driver->name);
     62▕         $this->assertSame('inactive', $driver->status->value);

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\DriverTest[22m [90m>[39m cnic must be unique within a company but not across companies               
[39;1m  Session is missing expected key [errors].
Failed asserting that false is true.[39;22m

  at [32mtests\Feature\Fleet\DriverTest.php[39m:[32m79[39m
     75▕         Driver::factory()->for($userA->company)->create(['cnic' => '11111-1111111-1']);
     76▕ 
     77▕         $this->actingAs($userA)
     78▕             ->post('/drivers', ['name' => 'Dup', 'cnic' => '11111-1111111-1', 'status' => 'active'])
  ➜  79▕             ->assertSessionHasErrors('cnic');
     80▕ 
     81▕         $this->actingAs($userB)
     82▕             ->post('/drivers', ['name' => 'Ok', 'cnic' => '11111-1111111-1', 'status' => 'active'])
     83▕             ->assertSessionHasNoErrors();

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\DriverTest[22m [90m>[39m a company cannot see or touch another companys drivers                      
[39;1m  Expected response status code [200] but received 403.
Failed asserting that 403 is identical to 200.[39;22m

  at [32mtests\Feature\Fleet\DriverTest.php[39m:[32m96[39m
     92▕         $userA = User::factory()->for($companyA)->create();
     93▕         $driverB = Driver::factory()->for($companyB)->create(['name' => 'Rival Driver']);
     94▕ 
     95▕         $this->actingAs($userA)->get('/drivers')
  ➜  96▕             ->assertOk()
     97▕             ->assertDontSee('Rival Driver');
     98▕ 
     99▕         $this->actingAs($userA)->get(route('drivers.show', $driverB))->assertNotFound();
    100▕         $this->actingAs($userA)->get(route('drivers.edit', $driverB))->assertNotFound();

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\FuelEntryTest[22m [90m>[39m a user can add a fuel entry and total cost is calculated                 
[39;1m  Expected response status code [201, 301, 302, 303, 307, 308] but received 403.
Failed asserting that false is true.[39;22m

  at [32mtests\Feature\Fleet\FuelEntryTest.php[39m:[32m30[39m
     26▕             'odometer' => 120000,
     27▕             'fuel_station' => 'PSO Thokar',
     28▕         ]);
     29▕ 
  ➜  30▕         $response->assertRedirect(route('fuel.index'));
     31▕ 
     32▕         $entry = FuelEntry::first();
     33▕         $this->assertNotNull($entry);
     34▕         $this->assertSame($user->company_id, $entry->company_id);

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\FuelEntryTest[22m [90m>[39m efficiency is derived from consecutive odometer readings                 
[39;1m  Expected response status code [200] but received 403.
Failed asserting that 403 is identical to 200.[39;22m

  at [32mtests\Feature\Fleet\FuelEntryTest.php[39m:[32m68[39m
     64▕         $this->assertNull($entries[0]->kmPerLitre());
     65▕         // (100500 - 100000) / 50 = 10.0 km/L.
     66▕         $this->assertSame(10.0, $entries[1]->kmPerLitre());
     67▕ 
  ➜  68▕         $this->actingAs($user)->get('/fuel')->assertOk()->assertSee('10');
     69▕     }
     70▕ 
     71▕     public function test_a_user_cannot_log_fuel_against_another_companys_vehicle_or_trip(): void
     72▕     {

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\FuelEntryTest[22m [90m>[39m a user cannot log fuel against another companys vehicle or trip          
[39;1m  Session is missing expected key [errors].
Failed asserting that false is true.[39;22m

  at [32mtests\Feature\Fleet\FuelEntryTest.php[39m:[32m83[39m
     79▕             'vehicle' => $vehicleB->ulid,
     80▕             'litres' => 30,
     81▕             'cost_per_litre' => 270,
     82▕             'odometer' => 5000,
  ➜  83▕         ])->assertSessionHasErrors('vehicle');
     84▕ 
     85▕         $this->assertSame(0, FuelEntry::withoutGlobalScopes()->count());
     86▕     }
     87▕

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\FuelEntryTest[22m [90m>[39m a company cannot see or touch another companys fuel entries              
[39;1m  Expected response status code [200] but received 403.
Failed asserting that 403 is identical to 200.[39;22m

  at [32mtests\Feature\Fleet\FuelEntryTest.php[39m:[32m95[39m
     91▕         $companyB = Company::factory()->create();
     92▕         $userA = User::factory()->for($companyA)->create();
     93▕         $entryB = FuelEntry::factory()->for($companyB)->create(['fuel_station' => 'RIVAL-STATION']);
     94▕ 
  ➜  95▕         $this->actingAs($userA)->get('/fuel')->assertOk()->assertDontSee('RIVAL-STATION');
     96▕         $this->actingAs($userA)->get(route('fuel.edit', $entryB))->assertNotFound();
     97▕         $this->actingAs($userA)->delete(route('fuel.destroy', $entryB))->assertNotFound();
     98▕ 
     99▕         // Filtering the list by B's vehicle ULID is rejected by validation.

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\FuelEntryTest[22m [90m>[39m deleting a fuel entry soft deletes it                                    
[39;1m  Expected response status code [201, 301, 302, 303, 307, 308] but received 403.
Failed asserting that false is true.[39;22m

  at [32mtests\Feature\Fleet\FuelEntryTest.php[39m:[32m116[39m
    112▕         $entry = FuelEntry::factory()->for($user->company)->create();
    113▕ 
    114▕         $this->actingAs($user)
    115▕             ->delete(route('fuel.destroy', $entry))
  ➜ 116▕             ->assertRedirect(route('fuel.index'));
    117▕ 
    118▕         $this->assertSoftDeleted('fuel_entries', ['id' => $entry->id]);
    119▕     }
    120▕ }

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\ReportTest[22m [90m>[39m trip report shows own trips and respects date filters                       
[39;1m  Expected response status code [200] but received 403.
Failed asserting that 403 is identical to 200.[39;22m

  at [32mtests\Feature\Fleet\ReportTest.php[39m:[32m28[39m
     24▕         Trip::factory()->for($user->company)->create(['date' => '2026-01-05', 'pickup_location' => 'OUT-OF-RANGE']);
     25▕ 
     26▕         $this->actingAs($user)
     27▕             ->get('/reports/trips?from=2026-07-01&to=2026-07-31')
  ➜  28▕             ->assertOk()
     29▕             ->assertSee('IN-RANGE')
     30▕             ->assertDontSee('OUT-OF-RANGE');
     31▕     }
     32▕

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\ReportTest[22m [90m>[39m trip report and csv never contain another companys trips                    
[39;1m  Expected response status code [200] but received 403.
Failed asserting that 403 is identical to 200.[39;22m

  at [32mtests\Feature\Fleet\ReportTest.php[39m:[32m41[39m
     37▕         Trip::factory()->for($userA->company)->create(['pickup_location' => 'OWN-TRIP']);
     38▕         Trip::factory()->for($companyB)->create(['pickup_location' => 'RIVAL-TRIP']);
     39▕ 
     40▕         $this->actingAs($userA)->get('/reports/trips')
  ➜  41▕             ->assertOk()
     42▕             ->assertSee('OWN-TRIP')
     43▕             ->assertDontSee('RIVAL-TRIP');
     44▕ 
     45▕         $csv = $this->actingAs($userA)->get('/reports/trips?format=csv');

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\ReportTest[22m [90m>[39m fuel report totals and isolation                                            
[39;1m  Expected response status code [200] but received 403.
Failed asserting that 403 is identical to 200.[39;22m

  at [32mtests\Feature\Fleet\ReportTest.php[39m:[32m67[39m
     63▕         ]);
     64▕         FuelEntry::factory()->for($companyB)->create(['litres' => 999, 'cost_per_litre' => 100]);
     65▕ 
     66▕         $response = $this->actingAs($userA)->get('/reports/fuel');
  ➜  67▕         $response->assertOk()
     68▕             ->assertSee('OWN-FUEL')
     69▕             // Totals: 90 litres, 9,000 — B's 999 litres never counted.
     70▕             ->assertSee('90.00')
     71▕             ->assertSee('9,000.00')

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\ReportTest[22m [90m>[39m vehicle expense report aggregates per vehicle and is isolated               
[39;1m  Expected response status code [200] but received 403.
Failed asserting that 403 is identical to 200.[39;22m

  at [32mtests\Feature\Fleet\ReportTest.php[39m:[32m89[39m
     85▕         FuelEntry::factory()->for($vehicleA)->for($userA->company)->create(['litres' => 20, 'cost_per_litre' => 100]);
     86▕         FuelEntry::factory()->for($vehicleB)->for($companyB)->create(['litres' => 500, 'cost_per_litre' => 100]);
     87▕ 
     88▕         $this->actingAs($userA)->get('/reports/vehicle-expenses')
  ➜  89▕             ->assertOk()
     90▕             ->assertSee('EXP-A')
     91▕             ->assertSee('3,000.00')
     92▕             ->assertDontSee('EXP-B')
     93▕             ->assertDontSee('50,000.00');

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\ReportTest[22m [90m>[39m compliance report lists both document kinds with statuses and is isolate…   
[39;1m  Expected response status code [200] but received 403.
Failed asserting that 403 is identical to 200.[39;22m

  at [32mtests\Feature\Fleet\ReportTest.php[39m:[32m110[39m
    106▕         $vehicleB = Vehicle::factory()->for($companyB)->create(['plate' => 'CMP-RIVAL']);
    107▕         VehicleDocument::factory()->for($vehicleB)->create(['document_number' => 'RIVAL-DOC']);
    108▕ 
    109▕         $this->actingAs($userA)->get('/reports/compliance')
  ➜ 110▕             ->assertOk()
    111▕             ->assertSee('CMP-V')
    112▕             ->assertSee('Compliance Driver')
    113▕             ->assertSee('Expired')
    114▕             ->assertSee('Expiring Soon')

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\ReportTest[22m [90m>[39m report filters reject another companys vehicle or driver                    
[39;1m  Session is missing expected key [errors].
Failed asserting that false is true.[39;22m

  at [32mtests\Feature\Fleet\ReportTest.php[39m:[32m128[39m
    124▕         $driverB = Driver::factory()->for($companyB)->create();
    125▕ 
    126▕         $this->actingAs($userA)
    127▕             ->get('/reports/trips?vehicle='.$vehicleB->ulid)
  ➜ 128▕             ->assertSessionHasErrors('vehicle');
    129▕ 
    130▕         $this->actingAs($userA)
    131▕             ->get('/reports/compliance?driver='.$driverB->ulid)
    132▕             ->assertSessionHasErrors('driver');

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\RouteTest[22m [90m>[39m a user can create and see their companys routes                              
[39;1m  Failed asserting that null is not null.[39;22m

  at [32mtests\Feature\Fleet\RouteTest.php[39m:[32m31[39m
     27▕             'notes' => 'Main north-south corridor.',
     28▕         ]);
     29▕ 
     30▕         $route = Route::where('origin', 'Karachi')->where('destination', 'Lahore')->first();
  ➜  31▕         $this->assertNotNull($route);
     32▕         $this->assertSame($user->company_id, $route->company_id);
     33▕         $this->assertNotEmpty($route->ulid);
     34▕         $this->assertSame(['Truck', 'Trailer'], $route->vehicle_types);
     35▕

  [33m1   [39m[39;1mtests\Feature\Fleet\RouteTest.php[39;22m:[39;1m31[39;22m

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\RouteTest[22m [90m>[39m expected distance is required and fuel max cannot be below fuel min          
[39;1m  Session is missing expected key [errors].
Failed asserting that false is true.[39;22m

  at [32mtests\Feature\Fleet\RouteTest.php[39m:[32m55[39m
     51▕         $user = User::factory()->create();
     52▕ 
     53▕         $this->actingAs($user)
     54▕             ->post('/routes', ['origin' => 'A', 'destination' => 'B'])
  ➜  55▕             ->assertSessionHasErrors('expected_distance_km');
     56▕ 
     57▕         $this->actingAs($user)
     58▕             ->post('/routes', [
     59▕                 'origin' => 'A',

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\RouteTest[22m [90m>[39m a user can update their companys route                                       
[39;1m  Expected response status code [201, 301, 302, 303, 307, 308] but received 403.
Failed asserting that false is true.[39;22m

  at [32mtests\Feature\Fleet\RouteTest.php[39m:[32m79[39m
     75▕                 'origin' => 'New Origin',
     76▕                 'destination' => $route->destination,
     77▕                 'expected_distance_km' => 300,
     78▕             ])
  ➜  79▕             ->assertRedirect(route('routes.show', $route));
     80▕ 
     81▕         $route->refresh();
     82▕         $this->assertSame('New Origin', $route->origin);
     83▕         $this->assertSame('300.00', $route->expected_distance_km);

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\RouteTest[22m [90m>[39m deleting a route soft deletes it                                             
[39;1m  Expected response status code [201, 301, 302, 303, 307, 308] but received 403.
Failed asserting that false is true.[39;22m

  at [32mtests\Feature\Fleet\RouteTest.php[39m:[32m93[39m
     89▕         $route = Route::factory()->for($user->company)->create();
     90▕ 
     91▕         $this->actingAs($user)
     92▕             ->delete(route('routes.destroy', $route))
  ➜  93▕             ->assertRedirect(route('routes.index'));
     94▕ 
     95▕         $this->assertSoftDeleted('routes', ['id' => $route->id]);
     96▕         $this->actingAs($user)->get('/routes')->assertDontSee($route->origin);
     97▕     }

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\RouteTest[22m [90m>[39m a company cannot see or touch another companys routes                        
[39;1m  Expected response status code [200] but received 403.
Failed asserting that 403 is identical to 200.[39;22m

  at [32mtests\Feature\Fleet\RouteTest.php[39m:[32m108[39m
    104▕         $routeB = Route::factory()->for($companyB)->create(['origin' => 'RivalOrigin', 'destination' => 'RivalDestination']);
    105▕ 
    106▕         // Not in Company A's list.
    107▕         $this->actingAs($userA)->get('/routes')
  ➜ 108▕             ->assertOk()
    109▕             ->assertDontSee('RivalOrigin');
    110▕ 
    111▕         // Direct URL access by ULID → 404 (scope hides it from route binding).
    112▕         $this->actingAs($userA)->get(route('routes.show', $routeB))->assertNotFound();

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\TripAuditLogTest[22m [90m>[39m creating a trip via the form view is logged                           
[39;1m  Failed asserting that null is not null.[39;22m

  at [32mtests\Feature\Fleet\TripAuditLogTest.php[39m:[32m38[39m
     34▕             'pickup_location' => 'Lahore Depot', 'dropoff_location' => 'Karachi Port', 'status' => 'planned',
     35▕         ]);
     36▕ 
     37▕         $log = AuditLog::where('auditable_type', Trip::class)->first();
  ➜  38▕         $this->assertNotNull($log);
     39▕         $this->assertSame('created', $log->action);
     40▕         $this->assertSame($user->id, $log->user_id);
     41▕         $this->assertSame($user->company_id, $log->company_id);
     42▕         $this->assertSame('Lahore Depot', $log->payload['pickup_location']);

  [33m1   [39m[39;1mtests\Feature\Fleet\TripAuditLogTest.php[39;22m:[39;1m38[39;22m

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\TripAuditLogTest[22m [90m>[39m creating a trip via add row is logged                                 
[39;1m  Failed asserting that null is not null.[39;22m

  at [32mtests\Feature\Fleet\TripAuditLogTest.php[39m:[32m57[39m
     53▕ 
     54▕         $this->actingAs($user)->postJson(route('trips.grid.store'), []);
     55▕ 
     56▕         $log = AuditLog::where('auditable_type', Trip::class)->where('action', 'created')->first();
  ➜  57▕         $this->assertNotNull($log);
     58▕         $this->assertSame($user->id, $log->user_id);
     59▕     }
     60▕ 
     61▕     public function test_updating_a_trip_logs_only_the_changed_fields(): void

  [33m1   [39m[39;1mtests\Feature\Fleet\TripAuditLogTest.php[39;22m:[39;1m57[39;22m

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\TripAuditLogTest[22m [90m>[39m updating a trip logs only the changed fields                          
[39;1m  Failed asserting that null is not null.[39;22m

  at [32mtests\Feature\Fleet\TripAuditLogTest.php[39m:[32m73[39m
     69▕             'field' => 'pickup_location', 'value' => 'New Pickup',
     70▕         ]);
     71▕ 
     72▕         $log = AuditLog::where('auditable_type', Trip::class)->where('action', 'updated')->first();
  ➜  73▕         $this->assertNotNull($log);
     74▕         $this->assertSame('New Pickup', $log->payload['pickup_location']);
     75▕         $this->assertArrayNotHasKey('dropoff_location', $log->payload);
     76▕     }
     77▕

  [33m1   [39m[39;1mtests\Feature\Fleet\TripAuditLogTest.php[39;22m:[39;1m73[39;22m

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\TripAuditLogTest[22m [90m>[39m reassigning the vehicle cell is logged with the new vehicle id        
[39;1m  Failed asserting that null is not null.[39;22m

  at [32mtests\Feature\Fleet\TripAuditLogTest.php[39m:[32m89[39m
     85▕             'field' => 'vehicle', 'value' => $newVehicle->ulid,
     86▕         ]);
     87▕ 
     88▕         $log = AuditLog::where('auditable_type', Trip::class)->where('action', 'updated')->first();
  ➜  89▕         $this->assertNotNull($log);
     90▕         $this->assertSame($newVehicle->id, $log->payload['vehicle_id']);
     91▕     }
     92▕ 
     93▕     public function test_deleting_a_trip_is_logged(): void

  [33m1   [39m[39;1mtests\Feature\Fleet\TripAuditLogTest.php[39;22m:[39;1m89[39;22m

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\TripAuditLogTest[22m [90m>[39m deleting a trip is logged                                             
[39;1m  Failed asserting that null is not null.[39;22m

  at [32mtests\Feature\Fleet\TripAuditLogTest.php[39m:[32m101[39m
     97▕ 
     98▕         $this->actingAs($user)->delete(route('trips.destroy', $trip));
     99▕ 
    100▕         $log = AuditLog::where('auditable_type', Trip::class)->where('action', 'deleted')->first();
  ➜ 101▕         $this->assertNotNull($log);
    102▕         $this->assertSame('Gone Trip', $log->payload['pickup_location']);
    103▕     }
    104▕ 
    105▕     public function test_a_companys_trip_audit_entries_are_isolated(): void

  [33m1   [39m[39;1mtests\Feature\Fleet\TripAuditLogTest.php[39;22m:[39;1m101[39;22m

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\TripAuditLogTest[22m [90m>[39m a companys trip audit entries are isolated                            
[39;1m  Expected response status code [200] but received 403.
Failed asserting that 403 is identical to 200.[39;22m

  at [32mtests\Feature\Fleet\TripAuditLogTest.php[39m:[32m121[39m
    117▕         ]);
    118▕ 
    119▕         $response = $this->actingAs($userA)->get(route('inventory.audit-log.index', ['type' => 'trip']));
    120▕ 
  ➜ 121▕         $response->assertOk();
    122▕         $response->assertDontSee('RIVAL-PICKUP');
    123▕         $this->assertSame(1, AuditLog::where('auditable_type', Trip::class)->where('company_id', $userA->company_id)->count());
    124▕     }
    125▕

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\TripAuditLogTest[22m [90m>[39m the index can filter to trips only                                    
[39;1m  Expected response status code [200] but received 403.
Failed asserting that 403 is identical to 200.[39;22m

  at [32mtests\Feature\Fleet\TripAuditLogTest.php[39m:[32m134[39m
    130▕         AuditLog::factory()->for($user->company)->create(['payload' => ['name' => 'Unrelated Part Entry']]);
    131▕ 
    132▕         $response = $this->actingAs($user)->get(route('inventory.audit-log.index', ['type' => 'trip']));
    133▕ 
  ➜ 134▕         $response->assertOk();
    135▕         $response->assertSee('Trip Pickup');
    136▕         $response->assertDontSee('Unrelated Part Entry');
    137▕     }
    138▕ }

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\TripFieldValueTest[22m [90m>[39m submitting a value for the customers own field persists it          
[39;1m  trip was not created
Failed asserting that null is not null.[39;22m

  at [32mtests\Feature\Fleet\TripFieldValueTest.php[39m:[32m50[39m
     46▕         ));
     47▕         $response->assertSessionHasNoErrors();
     48▕ 
     49▕         $trip = Trip::where('pickup_location', 'X')->first();
  ➜  50▕         $this->assertNotNull($trip, 'trip was not created');
     51▕         $this->assertSame($customer->id, $trip->customer_id, 'customer_id mismatch');
     52▕         $value = TripFieldValue::where('trip_id', $trip->id)->where('customer_field_id', $field->id)->first();
     53▕         $this->assertNotNull($value);
     54▕         $this->assertSame('GP-12345', $value->value);

  [33m1   [39m[39;1mtests\Feature\Fleet\TripFieldValueTest.php[39;22m:[39;1m50[39;22m

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\TripFieldValueTest[22m [90m>[39m a value for a field belonging to a diffe… [41;1m UrlGenerationException [49;22m  
[39;1m  Missing required parameter for [Route: trips.show] [URI: trips/{trip}] [Missing parameter: trip].[39;22m

  at [32mvendor\laravel\framework\src\Illuminate\Routing\Exceptions\UrlGenerationException.php[39m:[32m35[39m
     31▕         }
     32▕ 
     33▕         $message .= '.';
     34▕ 
  ➜  35▕         return new static($message);
     36▕     }
     37▕ }
     38▕

  [33m1   [39m[39;1mvendor\laravel\framework\src\Illuminate\Routing\Exceptions\UrlGenerationException.php[39;22m:[39;1m35[39;22m
  [33m2   [39m[39;1mvendor\laravel\framework\src\Illuminate\Routing\RouteUrlGenerator.php[39;22m:[39;1m94[39;22m

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\TripFieldValueTest[22m [90m>[39m changing a trips customer clears the old customers field values     
[39;1m  Failed asserting that 1 is identical to 0.[39;22m

  at [32mtests\Feature\Fleet\TripFieldValueTest.php[39m:[32m96[39m
     92▕         $this->actingAs($user)->patch(route('trips.update', $trip), $this->tripPayload(
     93▕             $user, $vehicle, $driver, $customerB
     94▕         ));
     95▕ 
  ➜  96▕         $this->assertSame(0, TripFieldValue::where('trip_id', $trip->id)->count());
     97▕     }
     98▕ 
     99▕     public function test_clearing_a_trips_customer_clears_its_field_values(): void
    100▕     {

  [33m1   [39m[39;1mtests\Feature\Fleet\TripFieldValueTest.php[39;22m:[39;1m96[39;22m

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\TripFieldValueTest[22m [90m>[39m clearing a trips customer clears its field values                   
[39;1m  Failed asserting that 1 is identical to 0.[39;22m

  at [32mtests\Feature\Fleet\TripFieldValueTest.php[39m:[32m116[39m
    112▕         $this->actingAs($user)->patch(route('trips.update', $trip), $this->tripPayload(
    113▕             $user, $vehicle, $driver, null
    114▕         ));
    115▕ 
  ➜ 116▕         $this->assertSame(0, TripFieldValue::where('trip_id', $trip->id)->count());
    117▕     }
    118▕ 
    119▕     public function test_updating_a_value_for_the_same_customer_upserts_rather_than_duplicates(): void
    120▕     {

  [33m1   [39m[39;1mtests\Feature\Fleet\TripFieldValueTest.php[39;22m:[39;1m116[39;22m

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\TripFieldValueTest[22m [90m>[39m updating a value for the same customer upserts rather than dupli…   
[39;1m  Failed asserting that two strings are identical.
--- Expected
+++ Actual
@@ @@
-'New value'
+'Old value'[39;22m

  at [32mtests\Feature\Fleet\TripFieldValueTest.php[39m:[32m137[39m
    133▕             $user, $vehicle, $driver, $customer, [$field->ulid => 'New value']
    134▕         ));
    135▕ 
    136▕         $this->assertSame(1, TripFieldValue::where('trip_id', $trip->id)->count());
  ➜ 137▕         $this->assertSame('New value', TripFieldValue::where('trip_id', $trip->id)->first()->value);
    138▕     }
    139▕ 
    140▕     public function test_the_edit_page_prefills_existing_field_values(): void
    141▕     {

  [33m1   [39m[39;1mtests\Feature\Fleet\TripFieldValueTest.php[39;22m:[39;1m137[39;22m

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\TripFieldValueTest[22m [90m>[39m the edit page prefills existing field values                        
[39;1m  Expected response status code [200] but received 403.
Failed asserting that 403 is identical to 200.[39;22m

  at [32mtests\Feature\Fleet\TripFieldValueTest.php[39m:[32m151[39m
    147▕             'trip_id' => $trip->id, 'customer_field_id' => $field->id, 'value' => 'GP-99999',
    148▕         ]);
    149▕ 
    150▕         $this->actingAs($user)->get(route('trips.edit', $trip))
  ➜ 151▕             ->assertOk()
    152▕             ->assertSee('Gatepass Number')
    153▕             ->assertSee('GP-99999', false);
    154▕     }
    155▕

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\TripFieldValueTest[22m [90m>[39m a company cannot use another companys customer f… [41;1m ErrorException [49;22m  
[39;1m  Attempt to read property "id" on null[39;22m

  at [32mtests\Feature\Fleet\TripFieldValueTest.php[39m:[32m172[39m
    168▕             $userA, $vehicle, $driver, $customer, [$fieldB->ulid => 'Cross-tenant']
    169▕         ));
    170▕ 
    171▕         $trip = Trip::where('pickup_location', 'X')->first();
  ➜ 172▕         $this->assertSame(0, TripFieldValue::where('trip_id', $trip->id)->count());
    173▕     }
    174▕ }
    175▕

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\TripGridFieldValueTest[22m [90m>[39m a user can set a field value via the grid endpoint              
[39;1m  Expected response status code [200] but received 403.
Failed asserting that 403 is identical to 200.[39;22m

  at [32mtests\Feature\Fleet\TripGridFieldValueTest.php[39m:[32m33[39m
     29▕         $trip = Trip::factory()->for($user->company)->create(['customer_id' => $customer->id]);
     30▕ 
     31▕         $this->actingAs($user)
     32▕             ->patchJson(route('trips.grid.update-field', [$trip, $field]), ['value' => 'GP-55555'])
  ➜  33▕             ->assertOk()
     34▕             ->assertJson(['success' => true]);
     35▕ 
     36▕         $this->assertSame('GP-55555', TripFieldValue::where('trip_id', $trip->id)->where('customer_field_id', $field->id)->value('value'));
     37▕     }

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\TripGridFieldValueTest[22m [90m>[39m updating a value twice upserts rather than duplicates           
[39;1m  Failed asserting that 0 is identical to 1.[39;22m

  at [32mtests\Feature\Fleet\TripGridFieldValueTest.php[39m:[32m49[39m
     45▕ 
     46▕         $this->actingAs($user)->patchJson(route('trips.grid.update-field', [$trip, $field]), ['value' => 'First']);
     47▕         $this->actingAs($user)->patchJson(route('trips.grid.update-field', [$trip, $field]), ['value' => 'Second']);
     48▕ 
  ➜  49▕         $this->assertSame(1, TripFieldValue::where('trip_id', $trip->id)->count());
     50▕         $this->assertSame('Second', TripFieldValue::where('trip_id', $trip->id)->first()->value);
     51▕     }
     52▕ 
     53▕     public function test_a_field_belonging_to_a_different_customer_is_rejected(): void

  [33m1   [39m[39;1mtests\Feature\Fleet\TripGridFieldValueTest.php[39;22m:[39;1m49[39;22m

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\TripGridFieldValueTest[22m [90m>[39m a field belonging to a different customer is rejected           
[39;1m  Expected response status code [422] but received 403.
Failed asserting that 403 is identical to 422.[39;22m

  at [32mtests\Feature\Fleet\TripGridFieldValueTest.php[39m:[32m63[39m
     59▕         $trip = Trip::factory()->for($user->company)->create(['customer_id' => $customerA->id]);
     60▕ 
     61▕         $this->actingAs($user)
     62▕             ->patchJson(route('trips.grid.update-field', [$trip, $fieldB]), ['value' => 'X'])
  ➜  63▕             ->assertStatus(422);
     64▕ 
     65▕         $this->assertSame(0, TripFieldValue::where('trip_id', $trip->id)->count());
     66▕     }
     67▕

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\TripGridFieldValueTest[22m [90m>[39m reassigning the customer cell prunes the old customers field…   
[39;1m  Failed asserting that 1 is identical to 0.[39;22m

  at [32mtests\Feature\Fleet\TripGridFieldValueTest.php[39m:[32m112[39m
    108▕         $this->actingAs($user)->patchJson(route('trips.grid.update-cell', $trip), [
    109▕             'field' => 'customer', 'value' => $customerB->ulid,
    110▕         ]);
    111▕ 
  ➜ 112▕         $this->assertSame(0, TripFieldValue::where('trip_id', $trip->id)->count());
    113▕     }
    114▕ 
    115▕     public function test_export_includes_custom_field_columns_when_filtered_to_one_customer(): void
    116▕     {

  [33m1   [39m[39;1mtests\Feature\Fleet\TripGridFieldValueTest.php[39;22m:[39;1m112[39;22m

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\TripGridFieldValueTest[22m [90m>[39m export includes custom field columns when filtered to one cu…   
[39;1m  Expected response status code [200] but received 403.
Failed asserting that 403 is identical to 200.[39;22m

  at [32mtests\Feature\Fleet\TripGridFieldValueTest.php[39m:[32m127[39m
    123▕         ]);
    124▕ 
    125▕         $response = $this->actingAs($user)->get(route('trips.export', ['customer' => $customer->ulid]));
    126▕ 
  ➜ 127▕         $response->assertOk();
    128▕         $this->assertStringContainsString('spreadsheetml.sheet', $response->headers->get('content-type'));
    129▕     }
    130▕ 
    131▕     public function test_export_omits_custom_field_columns_when_no_customer_filter_is_set(): void

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\TripGridFieldValueTest[22m [90m>[39m export omits custom field columns when no customer filter is…   
[39;1m  Expected response status code [200] but received 403.
Failed asserting that 403 is identical to 200.[39;22m

  at [32mtests\Feature\Fleet\TripGridFieldValueTest.php[39m:[32m140[39m
    136▕         Trip::factory()->for($user->company)->create(['customer_id' => $customer->id]);
    137▕ 
    138▕         $response = $this->actingAs($user)->get(route('trips.export'));
    139▕ 
  ➜ 140▕         $response->assertOk();
    141▕     }
    142▕ }
    143▕

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\TripGridTest[22m [90m>[39m the grid lists the companys trips and respects filters                    
[39;1m  Expected response status code [200] but received 403.
Failed asserting that 403 is identical to 200.[39;22m

  at [32mtests\Feature\Fleet\TripGridTest.php[39m:[32m39[39m
     35▕         ]);
     36▕ 
     37▕         $this->actingAs($user)
     38▕             ->get(route('trips.grid', ['vehicle' => $vehicleA->ulid]))
  ➜  39▕             ->assertOk()
     40▕             ->assertSee('MATCH-TRIP')
     41▕             ->assertDontSee('WRONG-VEHICLE');
     42▕     }
     43▕

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\TripGridTest[22m [90m>[39m the grid can be filtered by customer                                      
[39;1m  Expected response status code [200] but received 403.
Failed asserting that 403 is identical to 200.[39;22m

  at [32mtests\Feature\Fleet\TripGridTest.php[39m:[32m58[39m
     54▕         ]);
     55▕ 
     56▕         $this->actingAs($user)
     57▕             ->get(route('trips.grid', ['customer' => $customerA->ulid]))
  ➜  58▕             ->assertOk()
     59▕             ->assertSee('MATCH-CUSTOMER')
     60▕             ->assertDontSee('WRONG-CUSTOMER');
     61▕     }
     62▕

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\TripGridTest[22m [90m>[39m the grid customer filter rejects another companys customer                
[39;1m  Session is missing expected key [errors].
Failed asserting that false is true.[39;22m

  at [32mtests\Feature\Fleet\TripGridTest.php[39m:[32m70[39m
     66▕         $companyB = Company::factory()->create();
     67▕         $rivalCustomer = Customer::factory()->for($companyB)->create();
     68▕ 
     69▕         $this->actingAs($user)->get(route('trips.grid', ['customer' => $rivalCustomer->ulid]))
  ➜  70▕             ->assertSessionHasErrors('customer');
     71▕     }
     72▕ 
     73▕     public function test_the_grid_can_be_filtered_by_pickup_and_dropoff_with_partial_matches(): void
     74▕     {

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\TripGridTest[22m [90m>[39m the grid can be filtered by pickup and dropoff with partial matches       
[39;1m  Expected response status code [200] but received 403.
Failed asserting that 403 is identical to 200.[39;22m

  at [32mtests\Feature\Fleet\TripGridTest.php[39m:[32m85[39m
     81▕         ]);
     82▕ 
     83▕         $this->actingAs($user)
     84▕             ->get(route('trips.grid', ['pickup' => 'lahore']))
  ➜  85▕             ->assertOk()
     86▕             ->assertSee('Lahore Depot')
     87▕             ->assertDontSee('Multan Yard');
     88▕ 
     89▕         $this->actingAs($user)

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\TripGridTest[22m [90m>[39m the grid defaults to the current month when no date filter is given       
[39;1m  Expected response status code [200] but received 403.
Failed asserting that 403 is identical to 200.[39;22m

  at [32mtests\Feature\Fleet\TripGridTest.php[39m:[32m116[39m
    112▕         ]);
    113▕ 
    114▕         $response = $this->actingAs($user)->get(route('trips.grid'));
    115▕ 
  ➜ 116▕         $response->assertOk()
    117▕             ->assertSee('THIS-MONTH-START')
    118▕             ->assertSee('THIS-MONTH-END')
    119▕             ->assertDontSee('LAST-MONTH');
    120▕

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\TripGridTest[22m [90m>[39m an explicit date filter is not overridden by the current month default    
[39;1m  Expected response status code [200] but received 403.
Failed asserting that 403 is identical to 200.[39;22m

  at [32mtests\Feature\Fleet\TripGridTest.php[39m:[32m146[39m
    142▕         $response = $this->actingAs($user)->get(route('trips.grid', [
    143▕             'from' => '2026-01-01', 'to' => '2026-01-31',
    144▕         ]));
    145▕ 
  ➜ 146▕         $response->assertOk()
    147▕             ->assertSee('JANUARY-TRIP')
    148▕             ->assertDontSee('THIS-MONTH-TRIP');
    149▕     }
    150▕

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\TripGridTest[22m [90m>[39m a company cannot see another companys trips in the grid                   
[39;1m  Expected response status code [200] but received 403.
Failed asserting that 403 is identical to 200.[39;22m

  at [32mtests\Feature\Fleet\TripGridTest.php[39m:[32m158[39m
    154▕         $companyB = Company::factory()->create();
    155▕         Trip::factory()->for($companyB)->create(['pickup_location' => 'RIVAL-ROUTE']);
    156▕ 
    157▕         $this->actingAs($userA)->get(route('trips.grid'))
  ➜ 158▕             ->assertOk()
    159▕             ->assertDontSee('RIVAL-ROUTE');
    160▕     }
    161▕ 
    162▕     public function test_a_guest_is_redirected_away_from_the_grid(): void

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\TripGridTest[22m [90m>[39m add row creates a trip defaulted to today with vehicle and driver left…   
[39;1m  Expected response status code [200] but received 403.
Failed asserting that 403 is identical to 200.[39;22m

  at [32mtests\Feature\Fleet\TripGridTest.php[39m:[32m183[39m
    179▕         Driver::factory()->for($user->company)->create(['name' => 'Aaron']);
    180▕ 
    181▕         $response = $this->actingAs($user)->postJson(route('trips.grid.store'), []);
    182▕ 
  ➜ 183▕         $response->assertOk()->assertJson(['success' => true, 'row' => ['vehicle' => null, 'driver' => null]]);
    184▕         $tripUlid = $response->json('row.ulid');
    185▕ 
    186▕         $trip = Trip::where('ulid', $tripUlid)->first();
    187▕         $this->assertNotNull($trip);

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\TripGridTest[22m [90m>[39m add row captures who created it                         [41;1m ErrorException [49;22m  
[39;1m  Attempt to read property "created_by" on null[39;22m

  at [32mtests\Feature\Fleet\TripGridTest.php[39m:[32m204[39m
    200▕ 
    201▕         $response = $this->actingAs($user)->postJson(route('trips.grid.store'), []);
    202▕ 
    203▕         $trip = Trip::where('ulid', $response->json('row.ulid'))->first();
  ➜ 204▕         $this->assertSame($user->id, $trip->created_by);
    205▕     }
    206▕ 
    207▕     /**
    208▕      * Regression: Add Row used to 422 if the company had no vehicles or

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\TripGridTest[22m [90m>[39m add row succeeds even with no vehicles or drivers in the company          
[39;1m  Expected response status code [200] but received 403.
Failed asserting that 403 is identical to 200.[39;22m

  at [32mtests\Feature\Fleet\TripGridTest.php[39m:[32m218[39m
    214▕         $user = User::factory()->create();
    215▕ 
    216▕         $response = $this->actingAs($user)->postJson(route('trips.grid.store'), []);
    217▕ 
  ➜ 218▕         $response->assertOk()->assertJson(['success' => true]);
    219▕         $trip = Trip::where('ulid', $response->json('row.ulid'))->first();
    220▕         $this->assertNotNull($trip);
    221▕         $this->assertNull($trip->vehicle_id);
    222▕         $this->assertNull($trip->driver_id);

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\TripGridTest[22m [90m>[39m add row with a customer param persists that customer                      
[39;1m  Expected response status code [200] but received 403.
Failed asserting that 403 is identical to 200.[39;22m

  at [32mtests\Feature\Fleet\TripGridTest.php[39m:[32m234[39m
    230▕         $customer = Customer::factory()->for($user->company)->create();
    231▕ 
    232▕         $response = $this->actingAs($user)->postJson(route('trips.grid.store'), ['customer' => $customer->ulid]);
    233▕ 
  ➜ 234▕         $response->assertOk()->assertJson(['success' => true, 'row' => ['customer' => $customer->ulid]]);
    235▕ 
    236▕         $trip = Trip::where('ulid', $response->json('row.ulid'))->first();
    237▕         $this->assertSame($customer->id, $trip->customer_id);
    238▕     }

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\TripGridTest[22m [90m>[39m add row without a customer param leaves it null                           
[39;1m  Expected response status code [200] but received 403.
Failed asserting that 403 is identical to 200.[39;22m

  at [32mtests\Feature\Fleet\TripGridTest.php[39m:[32m248[39m
    244▕         Driver::factory()->for($user->company)->create();
    245▕ 
    246▕         $response = $this->actingAs($user)->postJson(route('trips.grid.store'), []);
    247▕ 
  ➜ 248▕         $response->assertOk();
    249▕         $trip = Trip::where('ulid', $response->json('row.ulid'))->first();
    250▕         $this->assertNull($trip->customer_id);
    251▕     }
    252▕

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\TripGridTest[22m [90m>[39m add row rejects another companys customer                                 
[39;1m  Expected response status code [422] but received 403.
Failed asserting that 403 is identical to 422.[39;22m

  at [32mtests\Feature\Fleet\TripGridTest.php[39m:[32m262[39m
    258▕         $companyB = Company::factory()->create();
    259▕         $rivalCustomer = Customer::factory()->for($companyB)->create();
    260▕ 
    261▕         $this->actingAs($user)->postJson(route('trips.grid.store'), ['customer' => $rivalCustomer->ulid])
  ➜ 262▕             ->assertUnprocessable();
    263▕ 
    264▕         $this->assertSame(0, Trip::query()->count());
    265▕     }
    266▕

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\TripGridTest[22m [90m>[39m add row still auto fills date and time to now while vehicle and driver…   
[39;1m  Expected response status code [200] but received 403.
Failed asserting that 403 is identical to 200.[39;22m

  at [32mtests\Feature\Fleet\TripGridTest.php[39m:[32m285[39m
    281▕         $user = User::factory()->create();
    282▕ 
    283▕         $response = $this->actingAs($user)->postJson(route('trips.grid.store'), []);
    284▕ 
  ➜ 285▕         $response->assertOk();
    286▕         $trip = Trip::where('ulid', $response->json('row.ulid'))->first();
    287▕ 
    288▕         $this->assertSame(now()->toDateString(), $trip->date->toDateString());
    289▕         $this->assertSame(now()->format('H:i'), substr($trip->departure_time, 0, 5));

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\TripGridTest[22m [90m>[39m hidden columns are reflected back in the grid on next visit               
[39;1m  Expected response status code [200] but received 403.
Failed asserting that 403 is identical to 200.[39;22m

  at [32mtests\Feature\Fleet\TripGridTest.php[39m:[32m323[39m
    319▕         $user->trip_grid_hidden_columns = ['status'];
    320▕         $user->save();
    321▕ 
    322▕         $this->actingAs($user)->get(route('trips.grid'))
  ➜ 323▕             ->assertOk()
    324▕             ->assertViewHas('hiddenColumns', ['status']);
    325▕     }
    326▕ 
    327▕     /**

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\TripGridTest[22m [90m>[39m a legacy hidden columns preference never hides the customer column        
[39;1m  Expected response status code [200] but received 403.
Failed asserting that 403 is identical to 200.[39;22m

  at [32mtests\Feature\Fleet\TripGridTest.php[39m:[32m343[39m
    339▕         $user->trip_grid_hidden_columns = ['status', 'notes']; // saved before Customer existed
    340▕         $user->save();
    341▕ 
    342▕         $this->actingAs($user)->get(route('trips.grid'))
  ➜ 343▕             ->assertOk()
    344▕             ->assertViewHas('hiddenColumns', function (array $hidden) {
    345▕                 return ! in_array('customer', $hidden, true)
    346▕                     && $hidden === ['status', 'notes'];
    347▕             });

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\TripGridTest[22m [90m>[39m the customer column can still be explicitly hidden and stays hidden       
[39;1m  Expected response status code [200] but received 403.
Failed asserting that 403 is identical to 200.[39;22m

  at [32mtests\Feature\Fleet\TripGridTest.php[39m:[32m361[39m
    357▕ 
    358▕         $this->assertSame(['customer'], $user->fresh()->trip_grid_hidden_columns);
    359▕ 
    360▕         $this->actingAs($user)->get(route('trips.grid'))
  ➜ 361▕             ->assertOk()
    362▕             ->assertViewHas('hiddenColumns', ['customer']);
    363▕     }
    364▕ 
    365▕     public function test_an_invalid_column_name_is_rejected(): void

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\TripGridTest[22m [90m>[39m the persisted column order is reflected back in the grid on next visit    
[39;1m  Expected response status code [200] but received 403.
Failed asserting that 403 is identical to 200.[39;22m

  at [32mtests\Feature\Fleet\TripGridTest.php[39m:[32m408[39m
    404▕         $user->trip_grid_column_order = ['customer', 'vehicle', 'date'];
    405▕         $user->save();
    406▕ 
    407▕         $this->actingAs($user)->get(route('trips.grid'))
  ➜ 408▕             ->assertOk()
    409▕             ->assertViewHas('columnOrder', ['customer', 'vehicle', 'date']);
    410▕     }
    411▕ 
    412▕     public function test_an_order_containing_an_invalid_column_name_is_rejected(): void

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\TripGridTest[22m [90m>[39m a user can update a single cell                                           
[39;1m  Expected response status code [200] but received 403.
Failed asserting that 403 is identical to 200.[39;22m

  at [32mtests\Feature\Fleet\TripGridTest.php[39m:[32m446[39m
    442▕         $trip = Trip::factory()->for($user->company)->create(['pickup_location' => 'Old Pickup']);
    443▕ 
    444▕         $this->actingAs($user)
    445▕             ->patchJson(route('trips.grid.update-cell', $trip), ['field' => 'pickup_location', 'value' => 'New Pickup'])
  ➜ 446▕             ->assertOk()
    447▕             ->assertJson(['success' => true]);
    448▕ 
    449▕         $this->assertSame('New Pickup', $trip->fresh()->pickup_location);
    450▕     }

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\TripGridTest[22m [90m>[39m a cell update can reassign the vehicle within the company                 
[39;1m  Expected response status code [200] but received 403.
Failed asserting that 403 is identical to 200.[39;22m

  at [32mtests\Feature\Fleet\TripGridTest.php[39m:[32m460[39m
    456▕         $newVehicle = Vehicle::factory()->for($user->company)->create();
    457▕ 
    458▕         $this->actingAs($user)
    459▕             ->patchJson(route('trips.grid.update-cell', $trip), ['field' => 'vehicle', 'value' => $newVehicle->ulid])
  ➜ 460▕             ->assertOk();
    461▕ 
    462▕         $this->assertSame($newVehicle->id, $trip->fresh()->vehicle_id);
    463▕     }
    464▕

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\TripGridTest[22m [90m>[39m a cell update rejects a non string vehicle value                          
[39;1m  Expected response status code [422] but received 403.
Failed asserting that 403 is identical to 422.[39;22m

  at [32mtests\Feature\Fleet\TripGridTest.php[39m:[32m482[39m
    478▕         $originalVehicleId = $trip->vehicle_id;
    479▕ 
    480▕         $this->actingAs($user)
    481▕             ->patchJson(route('trips.grid.update-cell', $trip), ['field' => 'vehicle', 'value' => [$newVehicle->ulid]])
  ➜ 482▕             ->assertUnprocessable()
    483▕             ->assertJsonValidationErrors('value');
    484▕ 
    485▕         $this->assertSame($originalVehicleId, $trip->fresh()->vehicle_id);
    486▕     }

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\TripGridTest[22m [90m>[39m a cell update rejects a non string driver value                           
[39;1m  Expected response status code [422] but received 403.
Failed asserting that 403 is identical to 422.[39;22m

  at [32mtests\Feature\Fleet\TripGridTest.php[39m:[32m497[39m
    493▕         $originalDriverId = $trip->driver_id;
    494▕ 
    495▕         $this->actingAs($user)
    496▕             ->patchJson(route('trips.grid.update-cell', $trip), ['field' => 'driver', 'value' => [$newDriver->ulid]])
  ➜ 497▕             ->assertUnprocessable()
    498▕             ->assertJsonValidationErrors('value');
    499▕ 
    500▕         $this->assertSame($originalDriverId, $trip->fresh()->driver_id);
    501▕     }

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\TripGridTest[22m [90m>[39m a cell update can reassign to a lowercase plated vehicle                  
[39;1m  Expected response status code [200] but received 403.
Failed asserting that 403 is identical to 200.[39;22m

  at [32mtests\Feature\Fleet\TripGridTest.php[39m:[32m539[39m
    535▕         $lowerVehicle = Vehicle::factory()->for($user->company)->create(['plate' => 'jv-2123']);
    536▕ 
    537▕         $this->actingAs($user)
    538▕             ->patchJson(route('trips.grid.update-cell', $trip), ['field' => 'vehicle', 'value' => $lowerVehicle->ulid])
  ➜ 539▕             ->assertOk();
    540▕ 
    541▕         $this->assertSame($lowerVehicle->id, $trip->fresh()->vehicle_id);
    542▕     }
    543▕

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\TripGridTest[22m [90m>[39m a cell update cannot assign another companys vehicle                      
[39;1m  Expected response status code [422] but received 403.
Failed asserting that 403 is identical to 422.[39;22m

  at [32mtests\Feature\Fleet\TripGridTest.php[39m:[32m554[39m
    550▕         $originalVehicleId = $trip->vehicle_id;
    551▕ 
    552▕         $this->actingAs($user)
    553▕             ->patchJson(route('trips.grid.update-cell', $trip), ['field' => 'vehicle', 'value' => $rivalVehicle->ulid])
  ➜ 554▕             ->assertUnprocessable()
    555▕             ->assertJsonValidationErrors('value');
    556▕ 
    557▕         $this->assertSame($originalVehicleId, $trip->fresh()->vehicle_id);
    558▕     }

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\TripGridTest[22m [90m>[39m a cell update can set and clear the customer                              
[39;1m  Expected response status code [200] but received 403.
Failed asserting that 403 is identical to 200.[39;22m

  at [32mtests\Feature\Fleet\TripGridTest.php[39m:[32m568[39m
    564▕         $trip = Trip::factory()->for($user->company)->create();
    565▕ 
    566▕         $this->actingAs($user)
    567▕             ->patchJson(route('trips.grid.update-cell', $trip), ['field' => 'customer', 'value' => $customer->ulid])
  ➜ 568▕             ->assertOk();
    569▕         $this->assertSame($customer->id, $trip->fresh()->customer_id);
    570▕ 
    571▕         // customer is nullable — clearing it back to null must succeed too.
    572▕         $this->actingAs($user)

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\TripGridTest[22m [90m>[39m a cell update rejects a non string customer value                         
[39;1m  Expected response status code [422] but received 403.
Failed asserting that 403 is identical to 422.[39;22m

  at [32mtests\Feature\Fleet\TripGridTest.php[39m:[32m586[39m
    582▕         $customer = Customer::factory()->for($user->company)->create();
    583▕ 
    584▕         $this->actingAs($user)
    585▕             ->patchJson(route('trips.grid.update-cell', $trip), ['field' => 'customer', 'value' => [$customer->ulid]])
  ➜ 586▕             ->assertUnprocessable()
    587▕             ->assertJsonValidationErrors('value');
    588▕ 
    589▕         $this->assertNull($trip->fresh()->customer_id);
    590▕     }

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\TripGridTest[22m [90m>[39m a cell update cannot assign another companys customer                     
[39;1m  Expected response status code [422] but received 403.
Failed asserting that 403 is identical to 422.[39;22m

  at [32mtests\Feature\Fleet\TripGridTest.php[39m:[32m617[39m
    613▕         $rivalCustomer = Customer::factory()->for($companyB)->create();
    614▕ 
    615▕         $this->actingAs($user)
    616▕             ->patchJson(route('trips.grid.update-cell', $trip), ['field' => 'customer', 'value' => $rivalCustomer->ulid])
  ➜ 617▕             ->assertUnprocessable()
    618▕             ->assertJsonValidationErrors('value');
    619▕ 
    620▕         $this->assertNull($trip->fresh()->customer_id);
    621▕     }

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\TripGridTest[22m [90m>[39m a cell update rejects an invalid status                                   
[39;1m  Expected response status code [422] but received 403.
Failed asserting that 403 is identical to 422.[39;22m

  at [32mtests\Feature\Fleet\TripGridTest.php[39m:[32m630[39m
    626▕         $trip = Trip::factory()->for($user->company)->create(['status' => 'planned']);
    627▕ 
    628▕         $this->actingAs($user)
    629▕             ->patchJson(route('trips.grid.update-cell', $trip), ['field' => 'status', 'value' => 'not-a-status'])
  ➜ 630▕             ->assertUnprocessable();
    631▕ 
    632▕         $this->assertSame('planned', $trip->fresh()->status->value);
    633▕     }
    634▕

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\TripGridTest[22m [90m>[39m pasting a block of cells updates every targeted cell                      
[39;1m  Expected response status code [200] but received 422.
Failed asserting that 422 is identical to 200.

The following errors occurred during the last request:

{
    "success": false,
    "errors": [
        "Cell 1: trip not found.",
        "Cell 2: trip not found.",
        "Cell 3: trip not found.",
        "Cell 4: trip not found."
    ]
}[39;22m

  at [32mtests\Feature\Fleet\TripGridTest.php[39m:[32m663[39m
    659▕                 ['trip' => $tripA->ulid, 'field' => 'dropoff_location', 'value' => 'Pasted A2'],
    660▕                 ['trip' => $tripB->ulid, 'field' => 'pickup_location', 'value' => 'Pasted B'],
    661▕                 ['trip' => $tripB->ulid, 'field' => 'dropoff_location', 'value' => 'Pasted B2'],
    662▕             ],
  ➜ 663▕         ])->assertOk()->assertJson(['success' => true, 'updated' => 4]);
    664▕ 
    665▕         $this->assertSame('Pasted A', $tripA->fresh()->pickup_location);
    666▕         $this->assertSame('Pasted A2', $tripA->fresh()->dropoff_location);
    667▕         $this->assertSame('Pasted B', $tripB->fresh()->pickup_location);

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\TripGridTest[22m [90m>[39m bulk edit applies a field to every selected trip                          
[39;1m  Expected response status code [200] but received 403.
Failed asserting that 403 is identical to 200.[39;22m

  at [32mtests\Feature\Fleet\TripGridTest.php[39m:[32m714[39m
    710▕         $this->actingAs($user)->postJson(route('trips.grid.bulk-update'), [
    711▕             'trips' => $trips->pluck('ulid')->all(),
    712▕             'field' => 'status',
    713▕             'value' => 'completed',
  ➜ 714▕         ])->assertOk()->assertJson(['success' => true, 'updated' => 3]);
    715▕ 
    716▕         $trips->each(fn (Trip $trip) => $this->assertSame('completed', $trip->fresh()->status->value));
    717▕     }
    718▕

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\TripGridTest[22m [90m>[39m bulk assign vehicle applies to every selected trip                        
[39;1m  Expected response status code [200] but received 403.
Failed asserting that 403 is identical to 200.[39;22m

  at [32mtests\Feature\Fleet\TripGridTest.php[39m:[32m729[39m
    725▕         $this->actingAs($user)->postJson(route('trips.grid.bulk-update'), [
    726▕             'trips' => $trips->pluck('ulid')->all(),
    727▕             'field' => 'vehicle',
    728▕             'value' => $newVehicle->ulid,
  ➜ 729▕         ])->assertOk()->assertJson(['success' => true, 'updated' => 2]);
    730▕ 
    731▕         $trips->each(fn (Trip $trip) => $this->assertSame($newVehicle->id, $trip->fresh()->vehicle_id));
    732▕     }
    733▕

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\TripGridTest[22m [90m>[39m bulk delete soft deletes every selected trip                              
[39;1m  Expected response status code [200] but received 403.
Failed asserting that 403 is identical to 200.[39;22m

  at [32mtests\Feature\Fleet\TripGridTest.php[39m:[32m779[39m
    775▕         $trips = Trip::factory()->for($user->company)->count(3)->create();
    776▕ 
    777▕         $this->actingAs($user)->postJson(route('trips.grid.bulk-delete'), [
    778▕             'trips' => $trips->pluck('ulid')->all(),
  ➜ 779▕         ])->assertOk()->assertJson(['success' => true, 'deleted' => 3]);
    780▕ 
    781▕         $trips->each(fn (Trip $trip) => $this->assertSoftDeleted('trips', ['id' => $trip->id]));
    782▕     }
    783▕

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\TripGridTest[22m [90m>[39m export streams an xlsx file respecting filters                            
[39;1m  Expected response status code [200] but received 403.
Failed asserting that 403 is identical to 200.[39;22m

  at [32mtests\Feature\Fleet\TripGridTest.php[39m:[32m810[39m
    806▕         Trip::factory()->for($user->company)->create(['date' => '2026-05-01']);
    807▕ 
    808▕         $response = $this->actingAs($user)->get(route('trips.export', ['vehicle' => $vehicle->ulid]));
    809▕ 
  ➜ 810▕         $response->assertOk();
    811▕         $this->assertStringContainsString(
    812▕             'spreadsheetml.sheet',
    813▕             $response->headers->get('content-type')
    814▕         );

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\TripGridTest[22m [90m>[39m export succeeds for trips with and without a customer                     
[39;1m  Expected response status code [200] but received 403.
Failed asserting that 403 is identical to 200.[39;22m

  at [32mtests\Feature\Fleet\TripGridTest.php[39m:[32m826[39m
    822▕         Trip::factory()->for($user->company)->create(['customer_id' => null]);
    823▕ 
    824▕         $response = $this->actingAs($user)->get(route('trips.export'));
    825▕ 
  ➜ 826▕         $response->assertOk();
    827▕         $this->assertStringContainsString(
    828▕             'spreadsheetml.sheet',
    829▕             $response->headers->get('content-type')
    830▕         );

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\TripGridTest[22m [90m>[39m a valid trips csv imports every row by plate and cnic lookup              
[39;1m  Expected response status code [201, 301, 302, 303, 307, 308] but received 403.
Failed asserting that false is true.[39;22m

  at [32mtests\Feature\Fleet\TripGridTest.php[39m:[32m853[39m
    849▕         $response = $this->actingAs($user)->post(route('fleet.import.store', 'trips'), [
    850▕             'file' => $this->csvFile($csv),
    851▕         ]);
    852▕ 
  ➜ 853▕         $response->assertRedirect(route('fleet.import.form', 'trips'));
    854▕         $response->assertSessionHas('status');
    855▕ 
    856▕         $trip = Trip::where('pickup_location', 'Lahore')->first();
    857▕         $this->assertNotNull($trip);

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\TripGridTest[22m [90m>[39m a trips csv with one bad row imports nothing                              
[39;1m  Session is missing expected key [importErrors].
Failed asserting that false is true.[39;22m

  at [32mtests\Feature\Fleet\TripGridTest.php[39m:[32m875[39m
    871▕             .",08:30,LEA-1234,35202-1234567-1,,Lahore,Karachi,planned,\n"; // missing required date
    872▕ 
    873▕         $this->actingAs($user)->post(route('fleet.import.store', 'trips'), [
    874▕             'file' => $this->csvFile($csv),
  ➜ 875▕         ])->assertSessionHas('importErrors');
    876▕ 
    877▕         $this->assertSame(0, Trip::query()->count());
    878▕     }
    879▕

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\TripGridTest[22m [90m>[39m a trips csv referencing an unknown plate or cnic is rejected              
[39;1m  Session is missing expected key [importErrors].
Failed asserting that false is true.[39;22m

  at [32mtests\Feature\Fleet\TripGridTest.php[39m:[32m890[39m
    886▕             ."2026-07-01,08:30,NOPE-999,35202-1234567-1,,Lahore,Karachi,planned,\n";
    887▕ 
    888▕         $this->actingAs($user)->post(route('fleet.import.store', 'trips'), [
    889▕             'file' => $this->csvFile($csv),
  ➜ 890▕         ])->assertSessionHas('importErrors');
    891▕ 
    892▕         $this->assertSame(0, Trip::query()->count());
    893▕     }
    894▕

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\TripGridTest[22m [90m>[39m a trips csv cannot reference another companys vehicle or driver           
[39;1m  Session is missing expected key [importErrors].
Failed asserting that false is true.[39;22m

  at [32mtests\Feature\Fleet\TripGridTest.php[39m:[32m907[39m
    903▕             ."2026-07-01,08:30,RIVAL-99,99999-9999999-9,,Lahore,Karachi,planned,\n";
    904▕ 
    905▕         $this->actingAs($userA)->post(route('fleet.import.store', 'trips'), [
    906▕             'file' => $this->csvFile($csv),
  ➜ 907▕         ])->assertSessionHas('importErrors');
    908▕ 
    909▕         $this->assertSame(0, Trip::withoutGlobalScopes()->where('pickup_location', 'Lahore')->count());
    910▕     }
    911▕

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\TripGridTest[22m [90m>[39m trips template downloads with the expected headers                        
[39;1m  Expected response status code [200] but received 403.
Failed asserting that 403 is identical to 200.[39;22m

  at [32mtests\Feature\Fleet\TripGridTest.php[39m:[32m918[39m
    914▕         $user = User::factory()->create();
    915▕ 
    916▕         $response = $this->actingAs($user)->get(route('fleet.import.template', 'trips'));
    917▕ 
  ➜ 918▕         $response->assertOk();
    919▕         $this->assertStringStartsWith('date,departure_time,vehicle_plate,driver_cnic,customer_name', $response->getContent());
    920▕     }
    921▕ 
    922▕     public function test_a_trips_csv_resolves_customer_by_name(): void

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\TripGridTest[22m [90m>[39m a trips csv resolves customer by name                                     
[39;1m  Session is missing expected key [status].
Failed asserting that false is true.[39;22m

  at [32mtests\Feature\Fleet\TripGridTest.php[39m:[32m934[39m
    930▕             ."2026-07-01,08:30,LEA-1234,35202-1234567-1,soorty enterprises,Lahore,Karachi,planned,\n";
    931▕ 
    932▕         $this->actingAs($user)->post(route('fleet.import.store', 'trips'), [
    933▕             'file' => $this->csvFile($csv),
  ➜ 934▕         ])->assertSessionHas('status');
    935▕ 
    936▕         $trip = Trip::where('pickup_location', 'Lahore')->first();
    937▕         $this->assertSame($customer->id, $trip->customer_id);
    938▕     }

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\TripGridTest[22m [90m>[39m a trips csv with a blank customer cell leaves the trip unassigned         
[39;1m  Session is missing expected key [status].
Failed asserting that false is true.[39;22m

  at [32mtests\Feature\Fleet\TripGridTest.php[39m:[32m951[39m
    947▕             ."2026-07-01,08:30,LEA-1234,35202-1234567-1,,Lahore,Karachi,planned,\n";
    948▕ 
    949▕         $this->actingAs($user)->post(route('fleet.import.store', 'trips'), [
    950▕             'file' => $this->csvFile($csv),
  ➜ 951▕         ])->assertSessionHas('status');
    952▕ 
    953▕         $this->assertNull(Trip::where('pickup_location', 'Lahore')->first()->customer_id);
    954▕     }
    955▕

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\TripGridTest[22m [90m>[39m a trips csv referencing an unknown customer name is rejected              
[39;1m  Session is missing expected key [importErrors].
Failed asserting that false is true.[39;22m

  at [32mtests\Feature\Fleet\TripGridTest.php[39m:[32m967[39m
    963▕             ."2026-07-01,08:30,LEA-1234,35202-1234567-1,No Such Customer,Lahore,Karachi,planned,\n";
    964▕ 
    965▕         $this->actingAs($user)->post(route('fleet.import.store', 'trips'), [
    966▕             'file' => $this->csvFile($csv),
  ➜ 967▕         ])->assertSessionHas('importErrors');
    968▕ 
    969▕         $this->assertSame(0, Trip::query()->count());
    970▕     }
    971▕

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\TripGridTest[22m [90m>[39m a trips csv with an ambiguous customer name is rejected                   
[39;1m  Session is missing expected key [importErrors].
Failed asserting that false is true.[39;22m

  at [32mtests\Feature\Fleet\TripGridTest.php[39m:[32m985[39m
    981▕             ."2026-07-01,08:30,LEA-1234,35202-1234567-1,Acme,Lahore,Karachi,planned,\n";
    982▕ 
    983▕         $this->actingAs($user)->post(route('fleet.import.store', 'trips'), [
    984▕             'file' => $this->csvFile($csv),
  ➜ 985▕         ])->assertSessionHas('importErrors');
    986▕ 
    987▕         $this->assertSame(0, Trip::query()->count());
    988▕     }
    989▕

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\TripGridTest[22m [90m>[39m the trips import forms back link returns to the spreadsheet view          
[39;1m  Expected response status code [200] but received 403.
Failed asserting that 403 is identical to 200.[39;22m

  at [32mtests\Feature\Fleet\TripGridTest.php[39m:[32m995[39m
    991▕     {
    992▕         $user = User::factory()->create();
    993▕ 
    994▕         $this->actingAs($user)->get(route('fleet.import.form', 'trips'))
  ➜ 995▕             ->assertOk()
    996▕             ->assertSee(route('trips.grid'), false);
    997▕     }
    998▕ 
    999▕     public function test_a_trips_csv_cannot_resolve_another_companys_customer(): void

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\TripGridTest[22m [90m>[39m a trips csv cannot resolve another companys customer                      
[39;1m  Session is missing expected key [importErrors].
Failed asserting that false is true.[39;22m

  at [32mtests\Feature\Fleet\TripGridTest.php[39m:[32m1012[39m
    1008▕             ."2026-07-01,08:30,LEA-1234,35202-1234567-1,Rival Customer,Lahore,Karachi,planned,\n";
    1009▕ 
    1010▕         $this->actingAs($userA)->post(route('fleet.import.store', 'trips'), [
    1011▕             'file' => $this->csvFile($csv),
  ➜ 1012▕         ])->assertSessionHas('importErrors');
    1013▕ 
    1014▕         $this->assertSame(0, Trip::withoutGlobalScopes()->where('pickup_location', 'Lahore')->count());
    1015▕     }
    1016▕ }

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\TripTest[22m [90m>[39m a user can create and see their companys trips                                
[39;1m  Failed asserting that null is not null.[39;22m

  at [32mtests\Feature\Fleet\TripTest.php[39m:[32m35[39m
     31▕             'status' => 'planned',
     32▕         ]);
     33▕ 
     34▕         $trip = Trip::where('pickup_location', 'Lahore Depot')->first();
  ➜  35▕         $this->assertNotNull($trip);
     36▕         $this->assertSame($user->company_id, $trip->company_id);
     37▕         $this->assertSame($vehicle->id, $trip->vehicle_id);
     38▕         $this->assertSame($driver->id, $trip->driver_id);
     39▕

  [33m1   [39m[39;1mtests\Feature\Fleet\TripTest.php[39;22m:[39;1m35[39;22m

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\TripTest[22m [90m>[39m the trip list can be filtered by vehicle and date range                       
[39;1m  Expected response status code [200] but received 403.
Failed asserting that 403 is identical to 200.[39;22m

  at [32mtests\Feature\Fleet\TripTest.php[39m:[32m67[39m
     63▕         ]);
     64▕ 
     65▕         $this->actingAs($user)
     66▕             ->get('/trips?vehicle='.$vehicleA->ulid.'&from=2026-06-01&to=2026-07-31')
  ➜  67▕             ->assertOk()
     68▕             ->assertSee('MATCH-TRIP')
     69▕             ->assertDontSee('WRONG-VEHICLE')
     70▕             ->assertDontSee('TOO-EARLY');
     71▕     }

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\TripTest[22m [90m>[39m a user cannot assign another companys vehicle or driver to a trip             
[39;1m  Session is missing expected key [errors].
Failed asserting that false is true.[39;22m

  at [32mtests\Feature\Fleet\TripTest.php[39m:[32m88[39m
     84▕             'date' => now()->toDateString(),
     85▕             'vehicle' => $vehicleB->ulid,
     86▕             'driver' => $ownDriver->ulid,
     87▕             'pickup_location' => 'X', 'dropoff_location' => 'Y', 'status' => 'planned',
  ➜  88▕         ])->assertSessionHasErrors('vehicle');
     89▕ 
     90▕         // Own vehicle with B's driver → rejected by validation.
     91▕         $this->actingAs($userA)->post('/trips', [
     92▕             'date' => now()->toDateString(),

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\TripTest[22m [90m>[39m a trip can be created with or without a customer            [41;1m ErrorException [49;22m  
[39;1m  Attempt to read property "customer_id" on null[39;22m

  at [32mtests\Feature\Fleet\TripTest.php[39m:[32m118[39m
    114▕             'pickup_location' => 'X', 'dropoff_location' => 'Y', 'status' => 'planned',
    115▕         ])->assertSessionHasNoErrors();
    116▕ 
    117▕         $trip = Trip::where('pickup_location', 'X')->first();
  ➜ 118▕         $this->assertSame($customer->id, $trip->customer_id);
    119▕ 
    120▕         // Without a customer — still optional.
    121▕         $this->actingAs($user)->post('/trips', [
    122▕             'date' => now()->toDateString(),

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\TripTest[22m [90m>[39m a user cannot assign another companys customer to a trip                      
[39;1m  Session is missing expected key [errors].
Failed asserting that false is true.[39;22m

  at [32mtests\Feature\Fleet\TripTest.php[39m:[32m146[39m
    142▕             'vehicle' => $vehicle->ulid,
    143▕             'driver' => $driver->ulid,
    144▕             'customer' => $customerB->ulid,
    145▕             'pickup_location' => 'X', 'dropoff_location' => 'Y', 'status' => 'planned',
  ➜ 146▕         ])->assertSessionHasErrors('customer');
    147▕ 
    148▕         $this->assertSame(0, Trip::withoutGlobalScopes()->count());
    149▕     }
    150▕

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\TripTest[22m [90m>[39m a company cannot see or touch another companys trips                          
[39;1m  Expected response status code [200] but received 403.
Failed asserting that 403 is identical to 200.[39;22m

  at [32mtests\Feature\Fleet\TripTest.php[39m:[32m158[39m
    154▕         $companyB = Company::factory()->create();
    155▕         $userA = User::factory()->for($companyA)->create();
    156▕         $tripB = Trip::factory()->for($companyB)->create(['pickup_location' => 'RIVAL-ROUTE']);
    157▕ 
  ➜ 158▕         $this->actingAs($userA)->get('/trips')->assertOk()->assertDontSee('RIVAL-ROUTE');
    159▕         $this->actingAs($userA)->get(route('trips.show', $tripB))->assertNotFound();
    160▕         $this->actingAs($userA)->get(route('trips.edit', $tripB))->assertNotFound();
    161▕         $this->actingAs($userA)->delete(route('trips.destroy', $tripB))->assertNotFound();
    162▕

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\TripTest[22m [90m>[39m deleting a trip soft deletes it                                               
[39;1m  Expected response status code [201, 301, 302, 303, 307, 308] but received 403.
Failed asserting that false is true.[39;22m

  at [32mtests\Feature\Fleet\TripTest.php[39m:[32m175[39m
    171▕         $trip = Trip::factory()->for($user->company)->create();
    172▕ 
    173▕         $this->actingAs($user)
    174▕             ->delete(route('trips.destroy', $trip))
  ➜ 175▕             ->assertRedirect(route('trips.index'));
    176▕ 
    177▕         $this->assertSoftDeleted('trips', ['id' => $trip->id]);
    178▕     }
    179▕

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\TripTest[22m [90m>[39m creating a trip captures who created it                     [41;1m ErrorException [49;22m  
[39;1m  Attempt to read property "created_by" on null[39;22m

  at [32mtests\Feature\Fleet\TripTest.php[39m:[32m200[39m
    196▕             'pickup_location' => 'X', 'dropoff_location' => 'Y', 'status' => 'planned',
    197▕         ]);
    198▕ 
    199▕         $trip = Trip::where('pickup_location', 'X')->first();
  ➜ 200▕         $this->assertSame($user->id, $trip->created_by);
    201▕         $this->assertNull($trip->deleted_by);
    202▕     }
    203▕ 
    204▕     public function test_deleting_a_trip_captures_who_deleted_it(): void

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\TripTest[22m [90m>[39m deleting a trip captures who deleted it                                       
[39;1m  Failed asserting that null is identical to 2.[39;22m

  at [32mtests\Feature\Fleet\TripTest.php[39m:[32m213[39m
    209▕ 
    210▕         $this->actingAs($deleter)->delete(route('trips.destroy', $trip));
    211▕ 
    212▕         $trip = Trip::withTrashed()->find($trip->id);
  ➜ 213▕         $this->assertSame($deleter->id, $trip->deleted_by);
    214▕     }
    215▕ 
    216▕     public function test_the_trip_detail_page_shows_who_added_it(): void
    217▕     {

  [33m1   [39m[39;1mtests\Feature\Fleet\TripTest.php[39;22m:[39;1m213[39;22m

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\TripTest[22m [90m>[39m the trip detail page shows who added it                                       
[39;1m  Expected response status code [200] but received 403.
Failed asserting that 403 is identical to 200.[39;22m

  at [32mtests\Feature\Fleet\TripTest.php[39m:[32m224[39m
    220▕         $trip->created_by = $user->id;
    221▕         $trip->save();
    222▕ 
    223▕         $this->actingAs($user)->get(route('trips.show', $trip))
  ➜ 224▕             ->assertOk()
    225▕             ->assertSee('Added by')
    226▕             ->assertSee('Fahad Aly');
    227▕     }
    228▕ }

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\VehicleDocumentTest[22m [90m>[39m a user can add a document to their companys vehicle                
[39;1m  Expected response status code [201, 301, 302, 303, 307, 308] but received 403.
Failed asserting that false is true.[39;22m

  at [32mtests\Feature\Fleet\VehicleDocumentTest.php[39m:[32m30[39m
     26▕             'expiry_date' => now()->addYear()->toDateString(),
     27▕             'notes' => 'Comprehensive cover.',
     28▕         ]);
     29▕ 
  ➜  30▕         $response->assertRedirect(route('vehicles.show', $vehicle));
     31▕ 
     32▕         $document = VehicleDocument::where('document_number', 'INS-12345')->first();
     33▕         $this->assertNotNull($document);
     34▕         $this->assertSame($user->company_id, $document->company_id);

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\VehicleDocumentTest[22m [90m>[39m an other document requires a custom name                           
[39;1m  Session is missing expected key [errors].
Failed asserting that false is true.[39;22m

  at [32mtests\Feature\Fleet\VehicleDocumentTest.php[39m:[32m52[39m
     48▕         $vehicle = Vehicle::factory()->for($user->company)->create();
     49▕ 
     50▕         $this->actingAs($user)
     51▕             ->post(route('vehicle-documents.store', $vehicle), ['type' => 'other'])
  ➜  52▕             ->assertSessionHasErrors('custom_name');
     53▕ 
     54▕         $this->actingAs($user)
     55▕             ->post(route('vehicle-documents.store', $vehicle), [
     56▕                 'type' => 'other',

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\VehicleDocumentTest[22m [90m>[39m document status reflects expiry date                               
[39;1m  Expected response status code [200] but received 403.
Failed asserting that 403 is identical to 200.[39;22m

  at [32mtests\Feature\Fleet\VehicleDocumentTest.php[39m:[32m80[39m
     76▕         $this->assertSame(DocumentStatus::Valid, $valid->status());
     77▕         $this->assertSame(DocumentStatus::NoExpiry, $noExpiry->status());
     78▕ 
     79▕         $this->actingAs($user)->get(route('vehicles.show', $vehicle))
  ➜  80▕             ->assertOk()
     81▕             ->assertSee('Expired')
     82▕             ->assertSee('Expiring Soon')
     83▕             ->assertSee('Valid');
     84▕     }

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\VehicleDocumentTest[22m [90m>[39m a user can update and soft delete a document                       
[39;1m  Expected response status code [201, 301, 302, 303, 307, 308] but received 403.
Failed asserting that false is true.[39;22m

  at [32mtests\Feature\Fleet\VehicleDocumentTest.php[39m:[32m97[39m
     93▕             ->patch(route('vehicle-documents.update', $document), [
     94▕                 'type' => 'tax',
     95▕                 'document_number' => 'TAX-999',
     96▕             ])
  ➜  97▕             ->assertRedirect(route('vehicles.show', $vehicle));
     98▕ 
     99▕         $document->refresh();
    100▕         $this->assertSame('tax', $document->type->value);
    101▕         $this->assertSame('TAX-999', $document->document_number);

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\VehicleDocumentTest[22m [90m>[39m upsert creates a document for a type the vehicle does not have…    
[39;1m  Expected response status code [200] but received 403.
Failed asserting that 403 is identical to 200.[39;22m

  at [32mtests\Feature\Fleet\VehicleDocumentTest.php[39m:[32m121[39m
    117▕         $this->actingAs($user)
    118▕             ->putJson(route('vehicle-documents.upsert', [$vehicle, 'fitness']), [
    119▕                 'document_number' => 'FIT-001', 'expiry_date' => now()->addYear()->toDateString(),
    120▕             ])
  ➜ 121▕             ->assertOk()
    122▕             ->assertJson(['success' => true]);
    123▕ 
    124▕         $document = $vehicle->documents()->where('type', 'fitness')->first();
    125▕         $this->assertNotNull($document);

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\VehicleDocumentTest[22m [90m>[39m upsert updates the existing document in place instead of creati…   
[39;1m  Expected response status code [200] but received 403.
Failed asserting that 403 is identical to 200.[39;22m

  at [32mtests\Feature\Fleet\VehicleDocumentTest.php[39m:[32m142[39m
    138▕         $this->actingAs($user)
    139▕             ->putJson(route('vehicle-documents.upsert', [$vehicle, 'tax']), [
    140▕                 'document_number' => 'TAX-NEW', 'expiry_date' => now()->addYear()->toDateString(),
    141▕             ])
  ➜ 142▕             ->assertOk();
    143▕ 
    144▕         $this->assertSame(1, $vehicle->documents()->where('type', 'tax')->count());
    145▕         $existing->refresh();
    146▕         $this->assertSame('TAX-NEW', $existing->document_number);

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\VehicleDocumentTest[22m [90m>[39m upsert validates expiry date is a real date                        
[39;1m  Expected response status code [422] but received 403.
Failed asserting that 403 is identical to 422.[39;22m

  at [32mtests\Feature\Fleet\VehicleDocumentTest.php[39m:[32m179[39m
    175▕         $vehicle = Vehicle::factory()->for($user->company)->create();
    176▕ 
    177▕         $this->actingAs($user)
    178▕             ->putJson(route('vehicle-documents.upsert', [$vehicle, 'fitness']), ['expiry_date' => 'not-a-date'])
  ➜ 179▕             ->assertStatus(422);
    180▕ 
    181▕         $this->assertSame(0, $vehicle->documents()->count());
    182▕     }
    183▕

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\VehicleTest[22m [90m>[39m a user can create and see their companys vehicles                          
[39;1m  Failed asserting that null is not null.[39;22m

  at [32mtests\Feature\Fleet\VehicleTest.php[39m:[32m32[39m
     28▕             'notes' => 'First truck.',
     29▕         ]);
     30▕ 
     31▕         $vehicle = Vehicle::where('plate', 'ABC-123')->first();
  ➜  32▕         $this->assertNotNull($vehicle);
     33▕         $this->assertSame($user->company_id, $vehicle->company_id);
     34▕         $this->assertNotEmpty($vehicle->ulid);
     35▕ 
     36▕         $response->assertRedirect(route('vehicles.show', $vehicle));

  [33m1   [39m[39;1mtests\Feature\Fleet\VehicleTest.php[39;22m:[39;1m32[39;22m

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\VehicleTest[22m [90m>[39m a user can update their companys vehicle                                   
[39;1m  Expected response status code [201, 301, 302, 303, 307, 308] but received 403.
Failed asserting that false is true.[39;22m

  at [32mtests\Feature\Fleet\VehicleTest.php[39m:[32m58[39m
     54▕             ->patch(route('vehicles.update', $vehicle), [
     55▕                 'plate' => 'NEW-001',
     56▕                 'status' => 'maintenance',
     57▕             ])
  ➜  58▕             ->assertRedirect(route('vehicles.show', $vehicle));
     59▕ 
     60▕         $vehicle->refresh();
     61▕         $this->assertSame('NEW-001', $vehicle->plate);
     62▕         $this->assertSame('maintenance', $vehicle->status->value);

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\VehicleTest[22m [90m>[39m deleting a vehicle soft deletes it                                         
[39;1m  Expected response status code [201, 301, 302, 303, 307, 308] but received 403.
Failed asserting that false is true.[39;22m

  at [32mtests\Feature\Fleet\VehicleTest.php[39m:[32m72[39m
     68▕         $vehicle = Vehicle::factory()->for($user->company)->create();
     69▕ 
     70▕         $this->actingAs($user)
     71▕             ->delete(route('vehicles.destroy', $vehicle))
  ➜  72▕             ->assertRedirect(route('vehicles.index'));
     73▕ 
     74▕         // Gone from lists, but the row still exists (recoverable).
     75▕         $this->assertSoftDeleted('vehicles', ['id' => $vehicle->id]);
     76▕         $this->actingAs($user)->get('/vehicles')->assertDontSee($vehicle->plate);

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\VehicleTest[22m [90m>[39m plate must be unique within a company but not across companies             
[39;1m  Session is missing expected key [errors].
Failed asserting that false is true.[39;22m

  at [32mtests\Feature\Fleet\VehicleTest.php[39m:[32m88[39m
     84▕ 
     85▕         // Same company, same plate → rejected.
     86▕         $this->actingAs($userA)
     87▕             ->post('/vehicles', ['plate' => 'SAME-01', 'status' => 'active'])
  ➜  88▕             ->assertSessionHasErrors('plate');
     89▕ 
     90▕         // Different company, same plate → allowed.
     91▕         $this->actingAs($userB)
     92▕             ->post('/vehicles', ['plate' => 'SAME-01', 'status' => 'active'])

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Fleet\VehicleTest[22m [90m>[39m a company cannot see or touch another companys vehicles                    
[39;1m  Expected response status code [200] but received 403.
Failed asserting that 403 is identical to 200.[39;22m

  at [32mtests\Feature\Fleet\VehicleTest.php[39m:[32m107[39m
    103▕         $vehicleB = Vehicle::factory()->for($companyB)->create(['plate' => 'RIVAL-99']);
    104▕ 
    105▕         // Not in Company A's list.
    106▕         $this->actingAs($userA)->get('/vehicles')
  ➜ 107▕             ->assertOk()
    108▕             ->assertDontSee('RIVAL-99');
    109▕ 
    110▕         // Direct URL access by ULID → 404 (scope hides it from route binding).
    111▕         $this->actingAs($userA)->get(route('vehicles.show', $vehicleB))->assertNotFound();

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Inventory\AuditLogTest[22m [90m>[39m creating a part is logged                                             
[39;1m  Failed asserting that null is not null.[39;22m

  at [32mtests\Feature\Inventory\AuditLogTest.php[39m:[32m35[39m
     31▕             'name' => 'Oil Filter', 'min_stock_level' => 5, 'unit_cost' => 100,
     32▕         ]);
     33▕ 
     34▕         $log = AuditLog::where('auditable_type', Part::class)->first();
  ➜  35▕         $this->assertNotNull($log);
     36▕         $this->assertSame('created', $log->action);
     37▕         $this->assertSame($user->id, $log->user_id);
     38▕         $this->assertSame($user->company_id, $log->company_id);
     39▕         $this->assertSame('Oil Filter', $log->payload['name']);

  [33m1   [39m[39;1mtests\Feature\Inventory\AuditLogTest.php[39;22m:[39;1m35[39;22m

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Inventory\AuditLogTest[22m [90m>[39m updating a part logs only the changed fields                          
[39;1m  Failed asserting that null is not null.[39;22m

  at [32mtests\Feature\Inventory\AuditLogTest.php[39m:[32m60[39m
     56▕             'name' => 'New Name', 'location' => 'A1', 'min_stock_level' => $part->min_stock_level, 'unit_cost' => $part->unit_cost,
     57▕         ]);
     58▕ 
     59▕         $log = AuditLog::where('auditable_type', Part::class)->where('action', 'updated')->first();
  ➜  60▕         $this->assertNotNull($log);
     61▕         $this->assertSame('New Name', $log->payload['name']);
     62▕         // location didn't change, so it shouldn't be in the diff.
     63▕         $this->assertArrayNotHasKey('location', $log->payload);
     64▕     }

  [33m1   [39m[39;1mtests\Feature\Inventory\AuditLogTest.php[39;22m:[39;1m60[39;22m

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Inventory\AuditLogTest[22m [90m>[39m deleting a part is logged                                             
[39;1m  Failed asserting that null is not null.[39;22m

  at [32mtests\Feature\Inventory\AuditLogTest.php[39m:[32m74[39m
     70▕ 
     71▕         $this->actingAs($user)->delete(route('parts.destroy', $part));
     72▕ 
     73▕         $log = AuditLog::where('auditable_type', Part::class)->where('action', 'deleted')->first();
  ➜  74▕         $this->assertNotNull($log);
     75▕         $this->assertSame('Gone Part', $log->payload['name']);
     76▕     }
     77▕ 
     78▕     public function test_creating_a_supplier_and_mechanic_is_logged(): void

  [33m1   [39m[39;1mtests\Feature\Inventory\AuditLogTest.php[39;22m:[39;1m74[39;22m

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Inventory\AuditLogTest[22m [90m>[39m creating a supplier and mechanic is logged                            
[39;1m  Failed asserting that 0 is identical to 1.[39;22m

  at [32mtests\Feature\Inventory\AuditLogTest.php[39m:[32m85[39m
     81▕ 
     82▕         $this->actingAs($user)->post('/suppliers', ['name' => 'AutoParts Ltd']);
     83▕         $this->actingAs($user)->post('/mechanics', ['name' => 'John Doe']);
     84▕ 
  ➜  85▕         $this->assertSame(1, AuditLog::where('auditable_type', Supplier::class)->count());
     86▕         $this->assertSame(1, AuditLog::where('auditable_type', Mechanic::class)->count());
     87▕     }
     88▕ 
     89▕     public function test_a_direct_stock_movement_is_logged_with_its_foreign_keys(): void

  [33m1   [39m[39;1mtests\Feature\Inventory\AuditLogTest.php[39;22m:[39;1m85[39;22m

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Inventory\AuditLogTest[22m [90m>[39m a direct stock movement is logged with its foreign keys               
[39;1m  Failed asserting that null is not null.[39;22m

  at [32mtests\Feature\Inventory\AuditLogTest.php[39m:[32m101[39m
     97▕             'supplier' => $supplier->ulid,
     98▕         ]);
     99▕ 
    100▕         $log = AuditLog::where('auditable_type', StockMovement::class)->first();
  ➜ 101▕         $this->assertNotNull($log);
    102▕         $this->assertSame('created', $log->action);
    103▕         // part_id isn't fillable on StockMovement, but must still show up
    104▕         // in the audit snapshot — otherwise the entry is meaningless.
    105▕         $this->assertSame($part->id, $log->payload['part_id']);

  [33m1   [39m[39;1mtests\Feature\Inventory\AuditLogTest.php[39;22m:[39;1m101[39;22m

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Inventory\AuditLogTest[22m [90m>[39m a csv import is logged as imported not created                        
[39;1m  Failed asserting that null is not null.[39;22m

  at [32mtests\Feature\Inventory\AuditLogTest.php[39m:[32m121[39m
    117▕             'file' => $this->csvFile($csv),
    118▕         ]);
    119▕ 
    120▕         $log = AuditLog::where('auditable_type', Part::class)->first();
  ➜ 121▕         $this->assertNotNull($log);
    122▕         $this->assertSame('imported', $log->action);
    123▕         $this->assertSame('CSV import', $log->note);
    124▕     }
    125▕

  [33m1   [39m[39;1mtests\Feature\Inventory\AuditLogTest.php[39;22m:[39;1m121[39;22m

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Inventory\AuditLogTest[22m [90m>[39m the index lists only the users company entries                        
[39;1m  Expected response status code [200] but received 403.
Failed asserting that 403 is identical to 200.[39;22m

  at [32mtests\Feature\Inventory\AuditLogTest.php[39m:[32m135[39m
    131▕         AuditLog::factory()->for($companyB)->create(['payload' => ['name' => 'RIVAL Entry']]);
    132▕ 
    133▕         $response = $this->actingAs($userA)->get(route('inventory.audit-log.index'));
    134▕ 
  ➜ 135▕         $response->assertOk();
    136▕         $response->assertSee('A Entry');
    137▕         $response->assertDontSee('RIVAL Entry');
    138▕     }
    139▕

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Inventory\AuditLogTest[22m [90m>[39m the index can be filtered by type and action                          
[39;1m  Expected: [39;22m[31m<!DOCTYPE html>\n
[39m[31m<html lang="en">[39m[31m\n
    [39m[31m<head>[39m[31m\n
        [39m[31m<meta charset="utf-8">[39m[31m\n
        [39m[31m<meta name="viewport" content="width=device-width, initial-scale=1">[39m[31m\n
\n
        [39m[31m<title>[39m[31mForbidden[39m[31m</title>[39m[31m\n
\n
        [39m[31m<style>[39m[31m\n
            /*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */html{line-height:1.15;-webkit-text-size-adjust:100%}body{margin:0}a{background-color:transparent}code{font-family:monospace,monospace;font-size:1em}[hidden]{display:none}html{font-family:system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;line-height:1.5}*,:after,:before{box-sizing:border-box;border:0 solid #e2e8f0}a{color:inherit;text-decoration:inherit}code{font-family:Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}svg,video{display:block;vertical-align:middle}video{max-width:100%;height:auto}.bg-white{--bg-opacity:1;background-color:#fff;background-color:rgba(255,255,255,var(--bg-opacity))}.bg-gray-100{--bg-opacity:1;background-color:#f7fafc;background-color:rgba(247,250,252,var(--bg-opacity))}.border-gray-200{--border-opacity:1;border-color:#edf2f7;border-color:rgba(237,242,247,var(--border-opacity))}.border-gray-400{--border-opacity:1;border-color:#cbd5e0;border-color:rgba(203,213,224,var(--border-opacity))}.border-t{border-top-width:1px}.border-r{border-right-width:1px}.flex{display:flex}.grid{display:grid}.hidden{display:none}.items-center{align-items:center}.justify-center{justify-content:center}.font-semibold{font-weight:600}.h-5{height:1.25rem}.h-8{height:2rem}.h-16{height:4rem}.text-sm{font-size:.875rem}.text-lg{font-size:1.125rem}.leading-7{line-height:1.75rem}.mx-auto{margin-left:auto;margin-right:auto}.ml-1{margin-left:.25rem}.mt-2{margin-top:.5rem}.mr-2{margin-right:.5rem}.ml-2{margin-left:.5rem}.mt-4{margin-top:1rem}.ml-4{margin-left:1rem}.mt-8{margin-top:2rem}.ml-12{margin-left:3rem}.-mt-px{margin-top:-1px}.max-w-xl{max-width:36rem}.max-w-6xl{max-width:72rem}.min-h-screen{min-height:100vh}.overflow-hidden{overflow:hidden}.p-6{padding:1.5rem}.py-4{padding-top:1rem;padding-bottom:1rem}.px-4{padding-left:1rem;padding-right:1rem}.px-6{padding-left:1.5rem;padding-right:1.5rem}.pt-8{padding-top:2rem}.fixed{position:fixed}.relative{position:relative}.top-0{top:0}.right-0{right:0}.shadow{box-shadow:0 1px 3px 0 rgba(0,0,0,.1),0 1px 2px 0 rgba(0,0,0,.06)}.text-center{text-align:center}.text-gray-200{--text-opacity:1;color:#edf2f7;color:rgba(237,242,247,var(--text-opacity))}.text-gray-300{--text-opacity:1;color:#e2e8f0;color:rgba(226,232,240,var(--text-opacity))}.text-gray-400{--text-opacity:1;color:#cbd5e0;color:rgba(203,213,224,var(--text-opacity))}.text-gray-500{--text-opacity:1;color:#a0aec0;color:rgba(160,174,192,var(--text-opacity))}.text-gray-600{--text-opacity:1;color:#718096;color:rgba(113,128,150,var(--text-opacity))}.text-gray-700{--text-opacity:1;color:#4a5568;color:rgba(74,85,104,var(--text-opacity))}.text-gray-900{--text-opacity:1;color:#1a202c;color:rgba(26,32,44,var(--text-opacity))}.uppercase{text-transform:uppercase}.underline{text-decoration:underline}.antialiased{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.tracking-wider{letter-spacing:.05em}.w-5{width:1.25rem}.w-8{width:2rem}.w-auto{width:auto}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}@-webkit-keyframes spin{0%{transform:rotate(0deg)}to{transform:rotate(1turn)}}@keyframes spin{0%{transform:rotate(0deg)}to{transform:rotate(1turn)}}@-webkit-keyframes ping{0%{transform:scale(1);opacity:1}75%,to{transform:scale(2);opacity:0}}@keyframes ping{0%{transform:scale(1);opacity:1}75%,to{transform:scale(2);opacity:0}}@-webkit-keyframes pulse{0%,to{opacity:1}50%{opacity:.5}}@keyframes pulse{0%,to{opacity:1}50%{opacity:.5}}@-webkit-keyframes bounce{0%,to{transform:translateY(-25%);-webkit-animation-timing-function:cubic-bezier(.8,0,1,1);animation-timing-function:cubic-bezier(.8,0,1,1)}50%{transform:translateY(0);-webkit-animation-timing-function:cubic-bezier(0,0,.2,1);animation-timing-function:cubic-bezier(0,0,.2,1)}}@keyframes bounce{0%,to{transform:translateY(-25%);-webkit-animation-timing-function:cubic-bezier(.8,0,1,1);animation-timing-function:cubic-bezier(.8,0,1,1)}50%{transform:translateY(0);-webkit-animation-timing-function:cubic-bezier(0,0,.2,1);animation-timing-function:cubic-bezier(0,0,.2,1)}}@media (min-width:640px){.sm\:rounded-lg{border-radius:.5rem}.sm\:block{display:block}.sm\:items-center{align-items:center}.sm\:justify-start{justify-content:flex-start}.sm\:justify-between{justify-content:space-between}.sm\:h-20{height:5rem}.sm\:ml-0{margin-left:0}.sm\:px-6{padding-left:1.5rem;padding-right:1.5rem}.sm\:pt-0{padding-top:0}.sm\:text-left{text-align:left}.sm\:text-right{text-align:right}}@media (min-width:768px){.md\:border-t-0{border-top-width:0}.md\:border-l{border-left-width:1px}.md\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}}@media (min-width:1024px){.lg\:px-8{padding-left:2rem;padding-right:2rem}}@media (prefers-color-scheme:dark){.dark\:bg-gray-800{--bg-opacity:1;background-color:#2d3748;background-color:rgba(45,55,72,var(--bg-opacity))}.dark\:bg-gray-900{--bg-opacity:1;background-color:#1a202c;background-color:rgba(26,32,44,var(--bg-opacity))}.dark\:border-gray-700{--border-opacity:1;border-color:#4a5568;border-color:rgba(74,85,104,var(--border-opacity))}.dark\:text-white{--text-opacity:1;color:#fff;color:rgba(255,255,255,var(--text-opacity))}.dark\:text-gray-300 { --text-opacity: 1; color: #e2e8f0; color: rgba(226,232,240,var(--text-opacity)) }}\n
        [39m[31m</style>[39m[31m\n
\n
        [39m[31m<style>[39m[31m\n
            body {\n
                font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";\n
            }\n
        [39m[31m</style>[39m[31m\n
    [39m[31m</head>[39m[31m\n
    [39m[31m<body class="antialiased">[39m[31m\n
        [39m[31m<div class="relative flex items-top justify-center min-h-screen bg-gray-100 dark:bg-gray-900 sm:items-center sm:pt-0" role="main">[39m[31m\n
            [39m[31m<div class="max-w-xl mx-auto sm:px-6 lg:px-8">[39m[31m\n
                [39m[31m<div class="flex items-center pt-8 sm:justify-start sm:pt-0">[39m[31m\n
                    [39m[31m<h1 class="px-4 text-lg dark:text-gray-300 text-gray-700 border-r border-gray-400 tracking-wider">[39m[31m\n
                        403                    [39m[31m</h1>[39m[31m\n
\n
                    [39m[31m<div class="ml-4 text-lg dark:text-gray-300 text-gray-700 uppercase tracking-wider">[39m[31m\n
                        This action is unauthorized.                    [39m[31m</div>[39m[31m\n
                [39m[31m</div>[39m[31m\n
            [39m[31m</div>[39m[31m\n
        [39m[31m</div>[39m[31m\n
    [39m[31m</body>[39m[31m\n
[39m[31m</html>[39m[31m\n
[39m[39;1m

  To contain: [39;22m[32mCreated Part[39m

  at [32mtests\Feature\Inventory\AuditLogTest.php[39m:[32m147[39m
    143▕         AuditLog::factory()->for($user->company)->create(['auditable_type' => Part::class, 'action' => 'created', 'payload' => ['name' => 'Created Part']]);
    144▕         AuditLog::factory()->for($user->company)->create(['auditable_type' => Supplier::class, 'action' => 'deleted', 'payload' => ['name' => 'Deleted Supplier']]);
    145▕ 
    146▕         $response = $this->actingAs($user)->get(route('inventory.audit-log.index', ['type' => 'part']));
  ➜ 147▕         $response->assertSee('Created Part')->assertDontSee('Deleted Supplier');
    148▕ 
    149▕         $response = $this->actingAs($user)->get(route('inventory.audit-log.index', ['action' => 'deleted']));
    150▕         $response->assertSee('Deleted Supplier')->assertDontSee('Created Part');
    151▕     }

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Inventory\CsvImportTest[22m [90m>[39m a valid parts csv imports every row                                  
[39;1m  Expected response status code [201, 301, 302, 303, 307, 308] but received 403.
Failed asserting that false is true.[39;22m

  at [32mtests\Feature\Inventory\CsvImportTest.php[39m:[32m40[39m
     36▕         $response = $this->actingAs($user)->post(route('inventory.import.store', 'parts'), [
     37▕             'file' => $this->csvFile($csv),
     38▕         ]);
     39▕ 
  ➜  40▕         $response->assertRedirect(route('inventory.import.form', 'parts'));
     41▕         $response->assertSessionHas('status');
     42▕ 
     43▕         $this->assertSame(2, Part::query()->count());
     44▕         $oilFilter = Part::where('part_number', 'OF-100')->first();

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Inventory\CsvImportTest[22m [90m>[39m a blank category cell defaults to other instead of crashing          
[39;1m  Expected response status code [201, 301, 302, 303, 307, 308] but received 403.
Failed asserting that false is true.[39;22m

  at [32mtests\Feature\Inventory\CsvImportTest.php[39m:[32m69[39m
     65▕         $response = $this->actingAs($user)->post(route('inventory.import.store', 'parts'), [
     66▕             'file' => $this->csvFile($csv),
     67▕         ]);
     68▕ 
  ➜  69▕         $response->assertRedirect(route('inventory.import.form', 'parts'));
     70▕         $this->assertSame('Other', Part::where('name', 'No Category Part')->first()->category);
     71▕     }
     72▕ 
     73▕     public function test_a_parts_csv_with_one_bad_row_imports_nothing(): void

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Inventory\CsvImportTest[22m [90m>[39m a parts csv with one bad row imports nothing                         
[39;1m  Expected response status code [201, 301, 302, 303, 307, 308] but received 403.
Failed asserting that false is true.[39;22m

  at [32mtests\Feature\Inventory\CsvImportTest.php[39m:[32m85[39m
     81▕         $response = $this->actingAs($user)->post(route('inventory.import.store', 'parts'), [
     82▕             'file' => $this->csvFile($csv),
     83▕         ]);
     84▕ 
  ➜  85▕         $response->assertRedirect();
     86▕         $response->assertSessionHas('importErrors');
     87▕         $this->assertSame(0, Part::query()->count());
     88▕     }
     89▕

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Inventory\CsvImportTest[22m [90m>[39m a duplicate part number within the file is rejected                  
[39;1m  Session is missing expected key [importErrors].
Failed asserting that false is true.[39;22m

  at [32mtests\Feature\Inventory\CsvImportTest.php[39m:[32m100[39m
     96▕             ."Another,DUP-1,,Filters,pcs,5,850.00,A1,,\n";
     97▕ 
     98▕         $this->actingAs($user)->post(route('inventory.import.store', 'parts'), [
     99▕             'file' => $this->csvFile($csv),
  ➜ 100▕         ])->assertSessionHas('importErrors');
    101▕ 
    102▕         $this->assertSame(0, Part::query()->count());
    103▕     }
    104▕

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Inventory\CsvImportTest[22m [90m>[39m a part number already used by the company is rejected                
[39;1m  Session is missing expected key [importErrors].
Failed asserting that false is true.[39;22m

  at [32mtests\Feature\Inventory\CsvImportTest.php[39m:[32m115[39m
    111▕             ."Oil Filter,EXIST-1,,Filters,pcs,5,850.00,A1,,\n";
    112▕ 
    113▕         $this->actingAs($user)->post(route('inventory.import.store', 'parts'), [
    114▕             'file' => $this->csvFile($csv),
  ➜ 115▕         ])->assertSessionHas('importErrors');
    116▕ 
    117▕         $this->assertSame(1, Part::query()->count());
    118▕     }
    119▕

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Inventory\CsvImportTest[22m [90m>[39m an unknown supplier name is rejected                                 
[39;1m  Session is missing expected key [importErrors].
Failed asserting that false is true.[39;22m

  at [32mtests\Feature\Inventory\CsvImportTest.php[39m:[32m129[39m
    125▕             ."Oil Filter,OF-100,,Filters,pcs,5,850.00,A1,,Nonexistent Supplier\n";
    126▕ 
    127▕         $this->actingAs($user)->post(route('inventory.import.store', 'parts'), [
    128▕             'file' => $this->csvFile($csv),
  ➜ 129▕         ])->assertSessionHas('importErrors');
    130▕ 
    131▕         $this->assertSame(0, Part::query()->count());
    132▕     }
    133▕

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Inventory\CsvImportTest[22m [90m>[39m a parts csv cannot reference another companys supplier               
[39;1m  Session is missing expected key [importErrors].
Failed asserting that false is true.[39;22m

  at [32mtests\Feature\Inventory\CsvImportTest.php[39m:[32m145[39m
    141▕             ."Oil Filter,OF-100,,Filters,pcs,5,850.00,A1,,Rival Supplier\n";
    142▕ 
    143▕         $this->actingAs($userA)->post(route('inventory.import.store', 'parts'), [
    144▕             'file' => $this->csvFile($csv),
  ➜ 145▕         ])->assertSessionHas('importErrors');
    146▕ 
    147▕         $this->assertSame(0, Part::withoutGlobalScopes()->where('name', 'Oil Filter')->count());
    148▕     }
    149▕

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Inventory\CsvImportTest[22m [90m>[39m wrong headers are rejected before any row is read                    
[39;1m  Session is missing expected key [importErrors].
Failed asserting that false is true.[39;22m

  at [32mtests\Feature\Inventory\CsvImportTest.php[39m:[32m158[39m
    154▕         $csv = "wrong,headers,here\nfoo,bar,baz\n";
    155▕ 
    156▕         $this->actingAs($user)->post(route('inventory.import.store', 'parts'), [
    157▕             'file' => $this->csvFile($csv),
  ➜ 158▕         ])->assertSessionHas('importErrors');
    159▕ 
    160▕         $this->assertSame(0, Part::query()->count());
    161▕     }
    162▕

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Inventory\CsvImportTest[22m [90m>[39m a valid stock in csv creates movements                               
[39;1m  Session is missing expected key [status].
Failed asserting that false is true.[39;22m

  at [32mtests\Feature\Inventory\CsvImportTest.php[39m:[32m176[39m
    172▕             ."OF-100,20,850.00,2026-01-05,AutoParts Ltd,PO-1042,\n";
    173▕ 
    174▕         $this->actingAs($user)->post(route('inventory.import.store', 'stock-in'), [
    175▕             'file' => $this->csvFile($csv),
  ➜ 176▕         ])->assertSessionHas('status');
    177▕ 
    178▕         $this->assertSame(20, $part->fresh()->currentStock());
    179▕         $movement = StockMovement::first();
    180▕         $this->assertSame($supplier->id, $movement->supplier_id);

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Inventory\CsvImportTest[22m [90m>[39m a stock in csv referencing an unknown part is rejected               
[39;1m  Session is missing expected key [importErrors].
Failed asserting that false is true.[39;22m

  at [32mtests\Feature\Inventory\CsvImportTest.php[39m:[32m194[39m
    190▕             ."NOPE,20,850.00,2026-01-05,,,\n";
    191▕ 
    192▕         $this->actingAs($user)->post(route('inventory.import.store', 'stock-in'), [
    193▕             'file' => $this->csvFile($csv),
  ➜ 194▕         ])->assertSessionHas('importErrors');
    195▕ 
    196▕         $this->assertSame(0, StockMovement::query()->count());
    197▕     }
    198▕

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Inventory\CsvImportTest[22m [90m>[39m a stock in csv cannot pull stock into another companys part          
[39;1m  Session is missing expected key [importErrors].
Failed asserting that false is true.[39;22m

  at [32mtests\Feature\Inventory\CsvImportTest.php[39m:[32m210[39m
    206▕             ."RIVAL-1,20,850.00,2026-01-05,,,\n";
    207▕ 
    208▕         $this->actingAs($userA)->post(route('inventory.import.store', 'stock-in'), [
    209▕             'file' => $this->csvFile($csv),
  ➜ 210▕         ])->assertSessionHas('importErrors');
    211▕ 
    212▕         $this->assertSame(0, StockMovement::withoutGlobalScopes()->where('company_id', $userA->company_id)->count());
    213▕     }
    214▕

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Inventory\CsvImportTest[22m [90m>[39m a valid stock out csv creates movements and reduces stock            
[39;1m  Session is missing expected key [status].
Failed asserting that false is true.[39;22m

  at [32mtests\Feature\Inventory\CsvImportTest.php[39m:[32m232[39m
    228▕             ."OF-100,3,2026-01-05,ABC-123,John Doe,,Fitted during service\n";
    229▕ 
    230▕         $this->actingAs($user)->post(route('inventory.import.store', 'stock-out'), [
    231▕             'file' => $this->csvFile($csv),
  ➜ 232▕         ])->assertSessionHas('status');
    233▕ 
    234▕         $this->assertSame(7, $part->fresh()->currentStock());
    235▕         $movement = StockMovement::where('type', 'out')->first();
    236▕         $this->assertSame($vehicle->id, $movement->vehicle_id);

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Inventory\CsvImportTest[22m [90m>[39m a stock out csv cannot exceed available stock across multiple row…   
[39;1m  Session is missing expected key [importErrors].
Failed asserting that false is true.[39;22m

  at [32mtests\Feature\Inventory\CsvImportTest.php[39m:[32m259[39m
    255▕             ."OF-100,3,2026-01-06,ABC-123,,,\n";
    256▕ 
    257▕         $this->actingAs($user)->post(route('inventory.import.store', 'stock-out'), [
    258▕             'file' => $this->csvFile($csv),
  ➜ 259▕         ])->assertSessionHas('importErrors');
    260▕ 
    261▕         // Nothing written — all-or-nothing.
    262▕         $this->assertSame(5, $part->fresh()->currentStock());
    263▕         $this->assertSame(1, StockMovement::query()->count());

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Inventory\CsvImportTest[22m [90m>[39m a stock out csv cannot reference another companys vehicle            
[39;1m  Session is missing expected key [importErrors].
Failed asserting that false is true.[39;22m

  at [32mtests\Feature\Inventory\CsvImportTest.php[39m:[32m281[39m
    277▕             ."OF-100,1,2026-01-05,RIVAL-99,,,\n";
    278▕ 
    279▕         $this->actingAs($userA)->post(route('inventory.import.store', 'stock-out'), [
    280▕             'file' => $this->csvFile($csv),
  ➜ 281▕         ])->assertSessionHas('importErrors');
    282▕ 
    283▕         $this->assertSame(10, $part->fresh()->currentStock());
    284▕     }
    285▕

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Inventory\CsvImportTest[22m [90m>[39m templates download with the expected headers                         
[39;1m  Expected response status code [200] but received 403.
Failed asserting that 403 is identical to 200.[39;22m

  at [32mtests\Feature\Inventory\CsvImportTest.php[39m:[32m294[39m
    290▕         $user = User::factory()->create();
    291▕ 
    292▕         $response = $this->actingAs($user)->get(route('inventory.import.template', 'parts'));
    293▕ 
  ➜ 294▕         $response->assertOk();
    295▕         $this->assertStringContainsString('text/csv', $response->headers->get('content-type'));
    296▕         $this->assertStringStartsWith('name,part_number,barcode', $response->getContent());
    297▕     }
    298▕

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Inventory\CsvImportTest[22m [90m>[39m the parts import forms back link returns to the parts list           
[39;1m  Expected response status code [200] but received 403.
Failed asserting that 403 is identical to 200.[39;22m

  at [32mtests\Feature\Inventory\CsvImportTest.php[39m:[32m309[39m
    305▕     {
    306▕         $user = User::factory()->create();
    307▕ 
    308▕         $this->actingAs($user)->get(route('inventory.import.form', 'parts'))
  ➜ 309▕             ->assertOk()
    310▕             ->assertSee(route('parts.index'), false);
    311▕     }
    312▕ 
    313▕     public function test_the_stock_in_import_forms_back_link_returns_to_the_stock_entry_in_tab(): void

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Inventory\CsvImportTest[22m [90m>[39m the stock in import forms back link returns to the stock entry in…   
[39;1m  Expected response status code [200] but received 403.
Failed asserting that 403 is identical to 200.[39;22m

  at [32mtests\Feature\Inventory\CsvImportTest.php[39m:[32m318[39m
    314▕     {
    315▕         $user = User::factory()->create();
    316▕ 
    317▕         $this->actingAs($user)->get(route('inventory.import.form', 'stock-in'))
  ➜ 318▕             ->assertOk()
    319▕             ->assertSee(route('stock-entry.index', ['tab' => 'in']), false);
    320▕     }
    321▕ 
    322▕     public function test_the_stock_out_import_forms_back_link_returns_to_the_stock_entry_out_tab(): void

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Inventory\CsvImportTest[22m [90m>[39m the stock out import forms back link returns to the stock entry o…   
[39;1m  Expected response status code [200] but received 403.
Failed asserting that 403 is identical to 200.[39;22m

  at [32mtests\Feature\Inventory\CsvImportTest.php[39m:[32m327[39m
    323▕     {
    324▕         $user = User::factory()->create();
    325▕ 
    326▕         $this->actingAs($user)->get(route('inventory.import.form', 'stock-out'))
  ➜ 327▕             ->assertOk()
    328▕             ->assertSee(route('stock-entry.index', ['tab' => 'out']), false);
    329▕     }
    330▕ 
    331▕     public function test_an_unknown_import_type_is_not_found(): void

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Inventory\InventoryReportTest[22m [90m>[39m valuation report is isolated                                   
[39;1m  Expected response status code [200] but received 403.
Failed asserting that 403 is identical to 200.[39;22m

  at [32mtests\Feature\Inventory\InventoryReportTest.php[39m:[32m53[39m
     49▕         $this->seedCompany($companyB, 'RIVAL');
     50▕ 
     51▕         $response = $this->actingAs($userA)->get(route('reports.inventory.valuation'));
     52▕ 
  ➜  53▕         $response->assertOk()->assertSee('A Part')->assertDontSee('RIVAL Part');
     54▕     }
     55▕ 
     56▕     public function test_parts_usage_report_is_isolated_and_totals_correctly(): void
     57▕     {

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Inventory\InventoryReportTest[22m [90m>[39m parts usage report is isolated and totals correctly            
[39;1m  Expected response status code [200] but received 403.
Failed asserting that 403 is identical to 200.[39;22m

  at [32mtests\Feature\Inventory\InventoryReportTest.php[39m:[32m65[39m
     61▕         $this->seedCompany($companyB, 'RIVAL');
     62▕ 
     63▕         $response = $this->actingAs($userA)->get(route('reports.inventory.parts-usage'));
     64▕ 
  ➜  65▕         $response->assertOk()->assertSee('A Part')->assertDontSee('RIVAL Part');
     66▕         // 5 units issued at 100 cost = 500.
     67▕         $response->assertSee('500.00');
     68▕     }
     69▕

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Inventory\InventoryReportTest[22m [90m>[39m stock movement report is isolated                              
[39;1m  Expected response status code [200] but received 403.
Failed asserting that 403 is identical to 200.[39;22m

  at [32mtests\Feature\Inventory\InventoryReportTest.php[39m:[32m79[39m
     75▕         $this->seedCompany($companyB, 'RIVAL');
     76▕ 
     77▕         $response = $this->actingAs($userA)->get(route('reports.inventory.stock-movements'));
     78▕ 
  ➜  79▕         $response->assertOk()->assertSee('A Part')->assertDontSee('RIVAL Part');
     80▕     }
     81▕ 
     82▕     public function test_supplier_purchases_report_is_isolated(): void
     83▕     {

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Inventory\InventoryReportTest[22m [90m>[39m supplier purchases report is isolated                          
[39;1m  Expected response status code [200] but received 403.
Failed asserting that 403 is identical to 200.[39;22m

  at [32mtests\Feature\Inventory\InventoryReportTest.php[39m:[32m91[39m
     87▕         $this->seedCompany($companyB, 'RIVAL');
     88▕ 
     89▕         $response = $this->actingAs($userA)->get(route('reports.inventory.supplier-purchases'));
     90▕ 
  ➜  91▕         $response->assertOk()->assertSee('A Supplier')->assertDontSee('RIVAL Supplier');
     92▕     }
     93▕ 
     94▕     public function test_consumables_usage_report_is_isolated(): void
     95▕     {

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Inventory\InventoryReportTest[22m [90m>[39m consumables usage report is isolated                           
[39;1m  Expected response status code [200] but received 403.
Failed asserting that 403 is identical to 200.[39;22m

  at [32mtests\Feature\Inventory\InventoryReportTest.php[39m:[32m103[39m
     99▕         $this->seedCompany($companyB, 'RIVAL');
    100▕ 
    101▕         $response = $this->actingAs($userA)->get(route('reports.inventory.consumables-usage'));
    102▕ 
  ➜ 103▕         $response->assertOk()->assertSee('ACategory')->assertDontSee('RIVALCategory');
    104▕     }
    105▕ 
    106▕     public function test_vehicle_wise_expense_report_is_isolated_and_combines_maintenance_and_parts(): void
    107▕     {

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Inventory\InventoryReportTest[22m [90m>[39m vehicle wise expense report is isolated and combines mainte…   
[39;1m  Expected response status code [200] but received 403.
Failed asserting that 403 is identical to 200.[39;22m

  at [32mtests\Feature\Inventory\InventoryReportTest.php[39m:[32m115[39m
    111▕         $this->seedCompany($companyB, 'RIVAL');
    112▕ 
    113▕         $response = $this->actingAs($userA)->get(route('reports.inventory.vehicle-wise-expense'));
    114▕ 
  ➜ 115▕         $response->assertOk()->assertSee('A-PLATE')->assertDontSee('RIVAL-PLATE');
    116▕         // 500 labour (maintenance) + 500 direct parts (5 * 100) = 1000.
    117▕         $response->assertSee('1,000.00');
    118▕     }
    119▕

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Inventory\InventoryReportTest[22m [90m>[39m vehicle wise expense detail shows the underlying jobs and m…   
[39;1m  Expected response status code [200] but received 403.
Failed asserting that 403 is identical to 200.[39;22m

  at [32mtests\Feature\Inventory\InventoryReportTest.php[39m:[32m127[39m
    123▕         $seeded = $this->seedCompany($user->company, 'A');
    124▕ 
    125▕         $response = $this->actingAs($user)->get(route('reports.inventory.vehicle-wise-expense.detail', $seeded['vehicle']));
    126▕ 
  ➜ 127▕         $response->assertOk();
    128▕         $response->assertSee('A-WORKSHOP');
    129▕         $response->assertSee('A Part');
    130▕     }
    131▕

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Inventory\InventoryReportTest[22m [90m>[39m csv export is isolated                                         
[39;1m  Expected response status code [200] but received 403.
Failed asserting that 403 is identical to 200.[39;22m

  at [32mtests\Feature\Inventory\InventoryReportTest.php[39m:[32m152[39m
    148▕         $this->seedCompany($companyB, 'RIVAL');
    149▕ 
    150▕         $response = $this->actingAs($userA)->get(route('reports.inventory.valuation', ['format' => 'csv']));
    151▕ 
  ➜ 152▕         $response->assertOk();
    153▕         $content = $response->streamedContent();
    154▕         $this->assertStringContainsString('A Part', $content);
    155▕         $this->assertStringNotContainsString('RIVAL Part', $content);
    156▕     }

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Inventory\InventoryReportTest[22m [90m>[39m excel export downloads a valid spreadsheet                     
[39;1m  Expected response status code [200] but received 403.
Failed asserting that 403 is identical to 200.[39;22m

  at [32mtests\Feature\Inventory\InventoryReportTest.php[39m:[32m165[39m
    161▕         $this->seedCompany($user->company, 'A');
    162▕ 
    163▕         $response = $this->actingAs($user)->get(route('reports.inventory.valuation', ['format' => 'xlsx']));
    164▕ 
  ➜ 165▕         $response->assertOk();
    166▕         $response->assertHeader('Content-Type', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
    167▕         $this->assertStringStartsWith('PK', $response->getContent()); // xlsx is a zip archive
    168▕     }
    169▕

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Inventory\InventoryReportTest[22m [90m>[39m pdf export downloads a valid pdf                               
[39;1m  Expected response status code [200] but received 403.
Failed asserting that 403 is identical to 200.[39;22m

  at [32mtests\Feature\Inventory\InventoryReportTest.php[39m:[32m177[39m
    173▕         $this->seedCompany($user->company, 'A');
    174▕ 
    175▕         $response = $this->actingAs($user)->get(route('reports.inventory.valuation', ['format' => 'pdf']));
    176▕ 
  ➜ 177▕         $response->assertOk();
    178▕         $this->assertStringStartsWith('%PDF', $response->getContent());
    179▕     }
    180▕ 
    181▕     public function test_a_guest_is_redirected_away(): void

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Inventory\InventoryReportTest[22m [90m>[39m filters reject another companys part supplier or vehicle       
[39;1m  Session is missing expected key [errors].
Failed asserting that false is true.[39;22m

  at [32mtests\Feature\Inventory\InventoryReportTest.php[39m:[32m194[39m
    190▕         $seededB = $this->seedCompany($companyB, 'RIVAL');
    191▕ 
    192▕         $this->actingAs($userA)
    193▕             ->get(route('reports.inventory.parts-usage', ['part' => $seededB['part']->ulid]))
  ➜ 194▕             ->assertSessionHasErrors('part');
    195▕ 
    196▕         $this->actingAs($userA)
    197▕             ->get(route('reports.inventory.supplier-purchases', ['supplier' => $seededB['supplier']->ulid]))
    198▕             ->assertSessionHasErrors('supplier');

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Inventory\MaintenanceJobTest[22m [90m>[39m a user can create a job and total cost is calculated            
[39;1m  Failed asserting that null is not null.[39;22m

  at [32mtests\Feature\Inventory\MaintenanceJobTest.php[39m:[32m34[39m
     30▕             'status' => 'scheduled',
     31▕         ]);
     32▕ 
     33▕         $job = MaintenanceJob::first();
  ➜  34▕         $this->assertNotNull($job);
     35▕         $this->assertSame($user->company_id, $job->company_id);
     36▕         $this->assertSame($vehicle->id, $job->vehicle_id);
     37▕         $this->assertNotEmpty($job->ulid);
     38▕         $this->assertSame('0.00', $job->parts_cost);

  [33m1   [39m[39;1mtests\Feature\Inventory\MaintenanceJobTest.php[39;22m:[39;1m34[39;22m

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Inventory\MaintenanceJobTest[22m [90m>[39m updating labour cost recalculates total cost                    
[39;1m  Expected response status code [201, 301, 302, 303, 307, 308] but received 403.
Failed asserting that false is true.[39;22m

  at [32mtests\Feature\Inventory\MaintenanceJobTest.php[39m:[32m57[39m
     53▕             'type' => $job->type->value,
     54▕             'date_in' => $job->date_in->toDateString(),
     55▕             'labour_cost' => 2500,
     56▕             'status' => $job->status->value,
  ➜  57▕         ])->assertRedirect(route('maintenance.show', $job));
     58▕ 
     59▕         $job->refresh();
     60▕         $this->assertSame('2500.00', $job->labour_cost);
     61▕         $this->assertSame('2500.00', $job->total_cost);

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Inventory\MaintenanceJobTest[22m [90m>[39m the job list can be filtered by vehicle and date range          
[39;1m  Expected response status code [200] but received 403.
Failed asserting that 403 is identical to 200.[39;22m

  at [32mtests\Feature\Inventory\MaintenanceJobTest.php[39m:[32m82[39m
     78▕         ]);
     79▕ 
     80▕         $this->actingAs($user)
     81▕             ->get('/maintenance?vehicle='.$vehicleA->ulid.'&from=2026-06-01&to=2026-07-31')
  ➜  82▕             ->assertOk()
     83▕             ->assertSee('MATCH-JOB')
     84▕             ->assertDontSee('WRONG-VEHICLE')
     85▕             ->assertDontSee('TOO-EARLY');
     86▕     }

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Inventory\MaintenanceJobTest[22m [90m>[39m issuing a part records a stock out and updates parts and tot…   
[39;1m  Expected response status code [201, 301, 302, 303, 307, 308] but received 403.
Failed asserting that false is true.[39;22m

  at [32mtests\Feature\Inventory\MaintenanceJobTest.php[39m:[32m103[39m
     99▕ 
    100▕         $this->actingAs($user)->post(route('maintenance.parts.store', $job), [
    101▕             'part' => $part->ulid,
    102▕             'quantity' => 3,
  ➜ 103▕         ])->assertRedirect(route('maintenance.show', $job));
    104▕ 
    105▕         // Stock decremented.
    106▕         $this->assertSame(7, $part->fresh()->currentStock());
    107▕

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Inventory\MaintenanceJobTest[22m [90m>[39m issuing a part cannot exceed available stock                    
[39;1m  Session is missing expected key [errors].
Failed asserting that false is true.[39;22m

  at [32mtests\Feature\Inventory\MaintenanceJobTest.php[39m:[32m140[39m
    136▕ 
    137▕         $this->actingAs($user)->post(route('maintenance.parts.store', $job), [
    138▕             'part' => $part->ulid,
    139▕             'quantity' => 5,
  ➜ 140▕         ])->assertSessionHasErrors('quantity');
    141▕ 
    142▕         $this->assertSame(2, $part->fresh()->currentStock());
    143▕         $this->assertSame('0.00', $job->fresh()->parts_cost);
    144▕     }

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Inventory\MaintenanceJobTest[22m [90m>[39m a job cannot be assigned another companys vehicle or mechani…   
[39;1m  Session is missing expected key [errors].
Failed asserting that false is true.[39;22m

  at [32mtests\Feature\Inventory\MaintenanceJobTest.php[39m:[32m157[39m
    153▕ 
    154▕         $this->actingAs($userA)->post('/maintenance', [
    155▕             'vehicle' => $vehicleB->ulid, 'type' => 'preventive', 'date_in' => now()->toDateString(),
    156▕             'labour_cost' => 0, 'status' => 'scheduled',
  ➜ 157▕         ])->assertSessionHasErrors('vehicle');
    158▕ 
    159▕         $this->actingAs($userA)->post('/maintenance', [
    160▕             'vehicle' => $ownVehicle->ulid, 'mechanic' => $mechanicB->ulid, 'type' => 'preventive',
    161▕             'date_in' => now()->toDateString(), 'labour_cost' => 0, 'status' => 'scheduled',

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Inventory\MaintenanceJobTest[22m [90m>[39m a job cannot have another companys part issued to it            
[39;1m  Session is missing expected key [errors].
Failed asserting that false is true.[39;22m

  at [32mtests\Feature\Inventory\MaintenanceJobTest.php[39m:[32m177[39m
    173▕ 
    174▕         $this->actingAs($userA)->post(route('maintenance.parts.store', $job), [
    175▕             'part' => $partB->ulid,
    176▕             'quantity' => 1,
  ➜ 177▕         ])->assertSessionHasErrors('part');
    178▕ 
    179▕         $this->assertSame(0, $job->fresh()->partsUsed()->count());
    180▕     }
    181▕

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Inventory\MaintenanceJobTest[22m [90m>[39m a company cannot see or touch another companys maintenance j…   
[39;1m  Expected response status code [200] but received 403.
Failed asserting that 403 is identical to 200.[39;22m

  at [32mtests\Feature\Inventory\MaintenanceJobTest.php[39m:[32m190[39m
    186▕         $userA = User::factory()->for($companyA)->create();
    187▕         $jobB = MaintenanceJob::factory()->for($companyB)->create(['workshop' => 'RIVAL-WORKSHOP']);
    188▕         $vehicleBUlid = Vehicle::withoutGlobalScopes()->findOrFail($jobB->vehicle_id)->ulid;
    189▕ 
  ➜ 190▕         $this->actingAs($userA)->get('/maintenance')->assertOk()->assertDontSee('RIVAL-WORKSHOP');
    191▕         $this->actingAs($userA)->get(route('maintenance.show', $jobB))->assertNotFound();
    192▕         $this->actingAs($userA)->get(route('maintenance.edit', $jobB))->assertNotFound();
    193▕         $this->actingAs($userA)
    194▕             ->patch(route('maintenance.update', $jobB), [

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Inventory\MaintenanceJobTest[22m [90m>[39m deleting a job soft deletes it                                  
[39;1m  Expected response status code [201, 301, 302, 303, 307, 308] but received 403.
Failed asserting that false is true.[39;22m

  at [32mtests\Feature\Inventory\MaintenanceJobTest.php[39m:[32m216[39m
    212▕         $job = MaintenanceJob::factory()->for($user->company)->create();
    213▕ 
    214▕         $this->actingAs($user)
    215▕             ->delete(route('maintenance.destroy', $job))
  ➜ 216▕             ->assertRedirect(route('maintenance.index'));
    217▕ 
    218▕         $this->assertSoftDeleted('maintenance_jobs', ['id' => $job->id]);
    219▕     }
    220▕

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Inventory\MaintenanceJobTest[22m [90m>[39m vehicle page rolls up fuel and maintenance into total operat…   
[39;1m  Expected response status code [200] but received 403.
Failed asserting that 403 is identical to 200.[39;22m

  at [32mtests\Feature\Inventory\MaintenanceJobTest.php[39m:[32m236[39m
    232▕         $this->assertSame(500.0, $vehicle->totalMaintenanceCost());
    233▕         $this->assertSame(1500.0, $vehicle->totalOperatingCost());
    234▕ 
    235▕         $this->actingAs($user)->get(route('vehicles.show', $vehicle))
  ➜ 236▕             ->assertOk()
    237▕             ->assertSee('1,000.00')
    238▕             ->assertSee('500.00')
    239▕             ->assertSee('1,500.00');
    240▕     }

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Inventory\MechanicTest[22m [90m>[39m a user can create update and soft delete mechanics                    
[39;1m  Expected response status code [201, 301, 302, 303, 307, 308] but received 403.
Failed asserting that false is true.[39;22m

  at [32mtests\Feature\Inventory\MechanicTest.php[39m:[32m23[39m
     19▕         $this->actingAs($user)->post('/mechanics', [
     20▕             'name' => 'Ustad Akram',
     21▕             'phone' => '0301-2223334',
     22▕             'specialization' => 'Engine',
  ➜  23▕         ])->assertRedirect(route('mechanics.index'));
     24▕ 
     25▕         $mechanic = Mechanic::where('name', 'Ustad Akram')->first();
     26▕         $this->assertNotNull($mechanic);
     27▕         $this->assertSame($user->company_id, $mechanic->company_id);

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Inventory\MechanicTest[22m [90m>[39m a company cannot see or touch another companys mechanics              
[39;1m  Expected response status code [200] but received 403.
Failed asserting that 403 is identical to 200.[39;22m

  at [32mtests\Feature\Inventory\MechanicTest.php[39m:[32m48[39m
     44▕         $companyB = Company::factory()->create();
     45▕         $userA = User::factory()->for($companyA)->create();
     46▕         $mechanicB = Mechanic::factory()->for($companyB)->create(['name' => 'RIVAL-MECHANIC']);
     47▕ 
  ➜  48▕         $this->actingAs($userA)->get('/mechanics')->assertOk()->assertDontSee('RIVAL-MECHANIC');
     49▕         $this->actingAs($userA)->get(route('mechanics.edit', $mechanicB))->assertNotFound();
     50▕         $this->actingAs($userA)
     51▕             ->patch(route('mechanics.update', $mechanicB), ['name' => 'HACKED'])
     52▕             ->assertNotFound();

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Inventory\PartBarcodeTest[22m [90m>[39m lookup finds a part by barcode within the users company            
[39;1m  Expected response status code [200] but received 403.
Failed asserting that 403 is identical to 200.[39;22m

  at [32mtests\Feature\Inventory\PartBarcodeTest.php[39m:[32m27[39m
     23▕         $part = Part::factory()->for($user->company)->create(['barcode' => 'P0001']);
     24▕ 
     25▕         $json = $this->actingAs($user)
     26▕             ->getJson('/parts/lookup?barcode=P0001')
  ➜  27▕             ->assertOk()
     28▕             ->json();
     29▕ 
     30▕         $this->assertTrue($json['found']);
     31▕         $this->assertSame(route('parts.show', $part), $json['url']);

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Inventory\PartBarcodeTest[22m [90m>[39m lookup does not find another companys barcode                      
[39;1m  Expected response status code [200] but received 403.
Failed asserting that 403 is identical to 200.[39;22m

  at [32mtests\Feature\Inventory\PartBarcodeTest.php[39m:[32m42[39m
     38▕         Part::factory()->for($companyB)->create(['barcode' => 'P0001']);
     39▕ 
     40▕         $json = $this->actingAs($userA)
     41▕             ->getJson('/parts/lookup?barcode=P0001')
  ➜  42▕             ->assertOk()
     43▕             ->json();
     44▕ 
     45▕         $this->assertFalse($json['found']);
     46▕     }

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Inventory\PartBarcodeTest[22m [90m>[39m lookup reports not found for an unknown barcode                    
[39;1m  Expected response status code [200] but received 403.
Failed asserting that 403 is identical to 200.[39;22m

  at [32mtests\Feature\Inventory\PartBarcodeTest.php[39m:[32m54[39m
     50▕         $user = User::factory()->create();
     51▕ 
     52▕         $json = $this->actingAs($user)
     53▕             ->getJson('/parts/lookup?barcode=NOPE')
  ➜  54▕             ->assertOk()
     55▕             ->json();
     56▕ 
     57▕         $this->assertFalse($json['found']);
     58▕     }

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Inventory\PartBarcodeTest[22m [90m>[39m the label sheet only includes the users own selected parts         
[39;1m  Expected response status code [200] but received 403.
Failed asserting that 403 is identical to 200.[39;22m

  at [32mtests\Feature\Inventory\PartBarcodeTest.php[39m:[32m78[39m
     74▕         $partB = Part::factory()->for($companyB)->create(['name' => 'RIVAL-PART']);
     75▕ 
     76▕         $response = $this->actingAs($userA)
     77▕             ->get(route('parts.labels', ['parts' => [$partA->ulid, $partB->ulid]]))
  ➜  78▕             ->assertOk();
     79▕ 
     80▕         $response->assertSee('A-PART');
     81▕         $response->assertDontSee('RIVAL-PART');
     82▕     }

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Inventory\PartBarcodeTest[22m [90m>[39m the all labels sheet only includes the users own company           
[39;1m  Expected response status code [200] but received 403.
Failed asserting that 403 is identical to 200.[39;22m

  at [32mtests\Feature\Inventory\PartBarcodeTest.php[39m:[32m93[39m
     89▕         Part::factory()->for($companyB)->create(['name' => 'RIVAL-PART']);
     90▕ 
     91▕         $response = $this->actingAs($userA)
     92▕             ->get(route('parts.labels', ['all' => 1]))
  ➜  93▕             ->assertOk();
     94▕ 
     95▕         $response->assertSee('A-PART');
     96▕         $response->assertDontSee('RIVAL-PART');
     97▕     }

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Inventory\PartBarcodeTest[22m [90m>[39m the scan page requires auth                                        
[39;1m  Expected response status code [200] but received 403.
Failed asserting that 403 is identical to 200.[39;22m

  at [32mtests\Feature\Inventory\PartBarcodeTest.php[39m:[32m104[39m
    100▕     {
    101▕         $this->get(route('parts.scan'))->assertRedirect(route('login'));
    102▕ 
    103▕         $user = User::factory()->create();
  ➜ 104▕         $this->actingAs($user)->get(route('parts.scan'))->assertOk();
    105▕     }
    106▕ 
    107▕     public function test_a_guest_cannot_use_lookup(): void
    108▕     {

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Inventory\PartTest[22m [90m>[39m a user can create a part with their own supplier                          
[39;1m  Failed asserting that null is not null.[39;22m

  at [32mtests\Feature\Inventory\PartTest.php[39m:[32m34[39m
     30▕             'supplier' => $supplier->ulid,
     31▕         ]);
     32▕ 
     33▕         $part = Part::where('name', 'Oil Filter')->first();
  ➜  34▕         $this->assertNotNull($part);
     35▕         $this->assertSame($user->company_id, $part->company_id);
     36▕         $this->assertSame($supplier->id, $part->supplier_id);
     37▕         $this->assertNotEmpty($part->ulid);
     38▕         // No stock/quantity attribute exists on the model.

  [33m1   [39m[39;1mtests\Feature\Inventory\PartTest.php[39;22m:[39;1m34[39;22m

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Inventory\PartTest[22m [90m>[39m barcode is auto generated when left blank                                 
[39;1m  Expected response status code [201, 301, 302, 303, 307, 308] but received 403.
Failed asserting that false is true.[39;22m

  at [32mtests\Feature\Inventory\PartTest.php[39m:[32m52[39m
     48▕         $user = User::factory()->create();
     49▕ 
     50▕         $this->actingAs($user)->post('/parts', [
     51▕             'name' => 'Auto Barcode Part', 'min_stock_level' => 0, 'unit_cost' => 0,
  ➜  52▕         ])->assertRedirect();
     53▕         $this->assertSame('P0001', Part::where('name', 'Auto Barcode Part')->first()->barcode);
     54▕ 
     55▕         $this->actingAs($user)->post('/parts', [
     56▕             'name' => 'Second Auto Part', 'min_stock_level' => 0, 'unit_cost' => 0,

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Inventory\PartTest[22m [90m>[39m a manually entered barcode is respected instead of auto generating        
[39;1m  Expected response status code [201, 301, 302, 303, 307, 308] but received 403.
Failed asserting that false is true.[39;22m

  at [32mtests\Feature\Inventory\PartTest.php[39m:[32m67[39m
     63▕         $user = User::factory()->create();
     64▕ 
     65▕         $this->actingAs($user)->post('/parts', [
     66▕             'name' => 'Manual Barcode Part', 'barcode' => 'SUPPLIER-XYZ', 'min_stock_level' => 0, 'unit_cost' => 0,
  ➜  67▕         ])->assertRedirect();
     68▕ 
     69▕         $this->assertSame('SUPPLIER-XYZ', Part::where('name', 'Manual Barcode Part')->first()->barcode);
     70▕     }
     71▕

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Inventory\PartTest[22m [90m>[39m barcode sequences are independent per company           [41;1m ErrorException [49;22m  
[39;1m  Attempt to read property "barcode" on null[39;22m

  at [32mtests\Feature\Inventory\PartTest.php[39m:[32m81[39m
     77▕         $this->actingAs($userA)->post('/parts', ['name' => 'A Part', 'min_stock_level' => 0, 'unit_cost' => 0]);
     78▕         $this->actingAs($userB)->post('/parts', ['name' => 'B Part', 'min_stock_level' => 0, 'unit_cost' => 0]);
     79▕ 
     80▕         // Both companies start their own sequence at P0001, independently.
  ➜  81▕         $this->assertSame('P0001', Part::withoutGlobalScopes()->where('name', 'A Part')->first()->barcode);
     82▕         $this->assertSame('P0001', Part::withoutGlobalScopes()->where('name', 'B Part')->first()->barcode);
     83▕     }
     84▕ 
     85▕     public function test_a_part_can_be_created_without_a_supplier(): void

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Inventory\PartTest[22m [90m>[39m a part can be created without a supplier                                  
[39;1m  Expected response status code [201, 301, 302, 303, 307, 308] but received 403.
Failed asserting that false is true.[39;22m

  at [32mtests\Feature\Inventory\PartTest.php[39m:[32m93[39m
     89▕         $this->actingAs($user)->post('/parts', [
     90▕             'name' => 'Generic Bolt',
     91▕             'min_stock_level' => 0,
     92▕             'unit_cost' => 10,
  ➜  93▕         ])->assertRedirect();
     94▕ 
     95▕         $this->assertNull(Part::where('name', 'Generic Bolt')->first()->supplier_id);
     96▕     }
     97▕

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Inventory\PartTest[22m [90m>[39m a blank category field defaults to other instead of crashing              
[39;1m  Expected response status code [201, 301, 302, 303, 307, 308] but received 403.
Failed asserting that false is true.[39;22m

  at [32mtests\Feature\Inventory\PartTest.php[39m:[32m118[39m
    114▕             'min_stock_level' => 0,
    115▕             'unit_cost' => 10,
    116▕         ]);
    117▕ 
  ➜ 118▕         $response->assertRedirect();
    119▕         $this->assertSame('Other', Part::where('name', 'Blank Category Part')->first()->category);
    120▕     }
    121▕ 
    122▕     public function test_clearing_a_parts_category_on_update_defaults_to_other_instead_of_crashing(): void

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Inventory\PartTest[22m [90m>[39m clearing a parts category on update defaults to other instead of crash…   
[39;1m  Expected response status code [201, 301, 302, 303, 307, 308] but received 403.
Failed asserting that false is true.[39;22m

  at [32mtests\Feature\Inventory\PartTest.php[39m:[32m134[39m
    130▕             'min_stock_level' => $part->min_stock_level,
    131▕             'unit_cost' => $part->unit_cost,
    132▕         ]);
    133▕ 
  ➜ 134▕         $response->assertRedirect();
    135▕         $this->assertSame('Other', $part->fresh()->category);
    136▕     }
    137▕ 
    138▕     public function test_part_number_and_barcode_are_unique_per_company_but_not_across_companies(): void

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Inventory\PartTest[22m [90m>[39m part number and barcode are unique per company but not across companie…   
[39;1m  Session is missing expected key [errors].
Failed asserting that false is true.[39;22m

  at [32mtests\Feature\Inventory\PartTest.php[39m:[32m147[39m
    143▕ 
    144▕         // Same company, same part number → rejected.
    145▕         $this->actingAs($userA)->post('/parts', [
    146▕             'name' => 'Dup PN', 'part_number' => 'DUP-1', 'min_stock_level' => 0, 'unit_cost' => 0,
  ➜ 147▕         ])->assertSessionHasErrors('part_number');
    148▕ 
    149▕         // Same company, same barcode → rejected.
    150▕         $this->actingAs($userA)->post('/parts', [
    151▕             'name' => 'Dup BC', 'barcode' => 'DUPBC-1', 'min_stock_level' => 0, 'unit_cost' => 0,

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Inventory\PartTest[22m [90m>[39m a part cannot be assigned another companys supplier                       
[39;1m  Session is missing expected key [errors].
Failed asserting that false is true.[39;22m

  at [32mtests\Feature\Inventory\PartTest.php[39m:[32m173[39m
    169▕             'name' => 'Sneaky Part',
    170▕             'min_stock_level' => 0,
    171▕             'unit_cost' => 0,
    172▕             'supplier' => $supplierB->ulid,
  ➜ 173▕         ])->assertSessionHasErrors('supplier');
    174▕ 
    175▕         $this->assertSame(0, Part::withoutGlobalScopes()->where('name', 'Sneaky Part')->count());
    176▕     }
    177▕

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Inventory\PartTest[22m [90m>[39m a company cannot see or touch another companys parts                      
[39;1m  Expected response status code [200] but received 403.
Failed asserting that 403 is identical to 200.[39;22m

  at [32mtests\Feature\Inventory\PartTest.php[39m:[32m185[39m
    181▕         $companyB = Company::factory()->create();
    182▕         $userA = User::factory()->for($companyA)->create();
    183▕         $partB = Part::factory()->for($companyB)->create(['name' => 'RIVAL-PART']);
    184▕ 
  ➜ 185▕         $this->actingAs($userA)->get('/parts')->assertOk()->assertDontSee('RIVAL-PART');
    186▕         $this->actingAs($userA)->get(route('parts.show', $partB))->assertNotFound();
    187▕         $this->actingAs($userA)->get(route('parts.edit', $partB))->assertNotFound();
    188▕         $this->actingAs($userA)
    189▕             ->patch(route('parts.update', $partB), ['name' => 'HACKED', 'min_stock_level' => 0, 'unit_cost' => 0])

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Inventory\StockEntryTest[22m [90m>[39m no form element is nested inside another form                       
[39;1m  Expected at least one [39;22m[39;1m<form>[39;22m[39;1m on the page.
Failed asserting that 0 is greater than 0.[39;22m

  at [32mtests\Feature\Inventory\StockEntryTest.php[39m:[32m58[39m
     54▕         $document->loadHTML($html);
     55▕         libxml_use_internal_errors($previous);
     56▕ 
     57▕         $forms = $document->getElementsByTagName('form');
  ➜  58▕         $this->assertGreaterThan(0, $forms->length, 'Expected at least one <form> on the page.');
     59▕ 
     60▕         foreach ($forms as $form) {
     61▕             $nested = $form->getElementsByTagName('form');
     62▕             $this->assertSame(

  [33m1   [39m[39;1mtests\Feature\Inventory\StockEntryTest.php[39;22m:[39;1m58[39;22m

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Inventory\StockEntryTest[22m [90m>[39m the page renders and defaults to the stock in tab                   
[39;1m  Expected response status code [200] but received 403.
Failed asserting that 403 is identical to 200.[39;22m

  at [32mtests\Feature\Inventory\StockEntryTest.php[39m:[32m76[39m
     72▕         $user = User::factory()->create();
     73▕ 
     74▕         $response = $this->actingAs($user)->get(route('stock-entry.index'));
     75▕ 
  ➜  76▕         $response->assertOk();
     77▕         $this->assertInitialTab($response->getContent(), 'in');
     78▕     }
     79▕ 
     80▕     public function test_the_tab_query_parameter_selects_the_requested_tab(): void

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Inventory\StockEntryTest[22m [90m>[39m the tab query parameter selects the requested tab                   
[39;1m  Expected the page to initialize with tab 'out'.
Failed asserting that '<!DOCTYPE html>\n
[39;22m[39;1m<html lang="en">[39;22m[39;1m\n
    [39;22m[39;1m<head>[39;22m[39;1m\n
        [39;22m[39;1m<meta charset="utf-8">[39;22m[39;1m\n
        [39;22m[39;1m<meta name="viewport" content="width=device-width, initial-scale=1">[39;22m[39;1m\n
\n
        [39;22m[39;1m<title>[39;22m[39;1mForbidden[39;22m[39;1m</title>[39;22m[39;1m\n
\n
        [39;22m[39;1m<style>[39;22m[39;1m\n
            /*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */html{line-height:1.15;-webkit-text-size-adjust:100%}body{margin:0}a{background-color:transparent}code{font-family:monospace,monospace;font-size:1em}[hidden]{display:none}html{font-family:system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;line-height:1.5}*,:after,:before{box-sizing:border-box;border:0 solid #e2e8f0}a{color:inherit;text-decoration:inherit}code{font-family:Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}svg,video{display:block;vertical-align:middle}video{max-width:100%;height:auto}.bg-white{--bg-opacity:1;background-color:#fff;background-color:rgba(255,255,255,var(--bg-opacity))}.bg-gray-100{--bg-opacity:1;background-color:#f7fafc;background-color:rgba(247,250,252,var(--bg-opacity))}.border-gray-200{--border-opacity:1;border-color:#edf2f7;border-color:rgba(237,242,247,var(--border-opacity))}.border-gray-400{--border-opacity:1;border-color:#cbd5e0;border-color:rgba(203,213,224,var(--border-opacity))}.border-t{border-top-width:1px}.border-r{border-right-width:1px}.flex{display:flex}.grid{display:grid}.hidden{display:none}.items-center{align-items:center}.justify-center{justify-content:center}.font-semibold{font-weight:600}.h-5{height:1.25rem}.h-8{height:2rem}.h-16{height:4rem}.text-sm{font-size:.875rem}.text-lg{font-size:1.125rem}.leading-7{line-height:1.75rem}.mx-auto{margin-left:auto;margin-right:auto}.ml-1{margin-left:.25rem}.mt-2{margin-top:.5rem}.mr-2{margin-right:.5rem}.ml-2{margin-left:.5rem}.mt-4{margin-top:1rem}.ml-4{margin-left:1rem}.mt-8{margin-top:2rem}.ml-12{margin-left:3rem}.-mt-px{margin-top:-1px}.max-w-xl{max-width:36rem}.max-w-6xl{max-width:72rem}.min-h-screen{min-height:100vh}.overflow-hidden{overflow:hidden}.p-6{padding:1.5rem}.py-4{padding-top:1rem;padding-bottom:1rem}.px-4{padding-left:1rem;padding-right:1rem}.px-6{padding-left:1.5rem;padding-right:1.5rem}.pt-8{padding-top:2rem}.fixed{position:fixed}.relative{position:relative}.top-0{top:0}.right-0{right:0}.shadow{box-shadow:0 1px 3px 0 rgba(0,0,0,.1),0 1px 2px 0 rgba(0,0,0,.06)}.text-center{text-align:center}.text-gray-200{--text-opacity:1;color:#edf2f7;color:rgba(237,242,247,var(--text-opacity))}.text-gray-300{--text-opacity:1;color:#e2e8f0;color:rgba(226,232,240,var(--text-opacity))}.text-gray-400{--text-opacity:1;color:#cbd5e0;color:rgba(203,213,224,var(--text-opacity))}.text-gray-500{--text-opacity:1;color:#a0aec0;color:rgba(160,174,192,var(--text-opacity))}.text-gray-600{--text-opacity:1;color:#718096;color:rgba(113,128,150,var(--text-opacity))}.text-gray-700{--text-opacity:1;color:#4a5568;color:rgba(74,85,104,var(--text-opacity))}.text-gray-900{--text-opacity:1;color:#1a202c;color:rgba(26,32,44,var(--text-opacity))}.uppercase{text-transform:uppercase}.underline{text-decoration:underline}.antialiased{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.tracking-wider{letter-spacing:.05em}.w-5{width:1.25rem}.w-8{width:2rem}.w-auto{width:auto}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}@-webkit-keyframes spin{0%{transform:rotate(0deg)}to{transform:rotate(1turn)}}@keyframes spin{0%{transform:rotate(0deg)}to{transform:rotate(1turn)}}@-webkit-keyframes ping{0%{transform:scale(1);opacity:1}75%,to{transform:scale(2);opacity:0}}@keyframes ping{0%{transform:scale(1);opacity:1}75%,to{transform:scale(2);opacity:0}}@-webkit-keyframes pulse{0%,to{opacity:1}50%{opacity:.5}}@keyframes pulse{0%,to{opacity:1}50%{opacity:.5}}@-webkit-keyframes bounce{0%,to{transform:translateY(-25%);-webkit-animation-timing-function:cubic-bezier(.8,0,1,1);animation-timing-function:cubic-bezier(.8,0,1,1)}50%{transform:translateY(0);-webkit-animation-timing-function:cubic-bezier(0,0,.2,1);animation-timing-function:cubic-bezier(0,0,.2,1)}}@keyframes bounce{0%,to{transform:translateY(-25%);-webkit-animation-timing-function:cubic-bezier(.8,0,1,1);animation-timing-function:cubic-bezier(.8,0,1,1)}50%{transform:translateY(0);-webkit-animation-timing-function:cubic-bezier(0,0,.2,1);animation-timing-function:cubic-bezier(0,0,.2,1)}}@media (min-width:640px){.sm\:rounded-lg{border-radius:.5rem}.sm\:block{display:block}.sm\:items-center{align-items:center}.sm\:justify-start{justify-content:flex-start}.sm\:justify-between{justify-content:space-between}.sm\:h-20{height:5rem}.sm\:ml-0{margin-left:0}.sm\:px-6{padding-left:1.5rem;padding-right:1.5rem}.sm\:pt-0{padding-top:0}.sm\:text-left{text-align:left}.sm\:text-right{text-align:right}}@media (min-width:768px){.md\:border-t-0{border-top-width:0}.md\:border-l{border-left-width:1px}.md\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}}@media (min-width:1024px){.lg\:px-8{padding-left:2rem;padding-right:2rem}}@media (prefers-color-scheme:dark){.dark\:bg-gray-800{--bg-opacity:1;background-color:#2d3748;background-color:rgba(45,55,72,var(--bg-opacity))}.dark\:bg-gray-900{--bg-opacity:1;background-color:#1a202c;background-color:rgba(26,32,44,var(--bg-opacity))}.dark\:border-gray-700{--border-opacity:1;border-color:#4a5568;border-color:rgba(74,85,104,var(--border-opacity))}.dark\:text-white{--text-opacity:1;color:#fff;color:rgba(255,255,255,var(--text-opacity))}.dark\:text-gray-300 { --text-opacity: 1; color: #e2e8f0; color: rgba(226,232,240,var(--text-opacity)) }}\n
        [39;22m[39;1m</style>[39;22m[39;1m\n
\n
        [39;22m[39;1m<style>[39;22m[39;1m\n
            body {\n
                font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";\n
            }\n
        [39;22m[39;1m</style>[39;22m[39;1m\n
    [39;22m[39;1m</head>[39;22m[39;1m\n
    [39;22m[39;1m<body class="antialiased">[39;22m[39;1m\n
        [39;22m[39;1m<div class="relative flex items-top justify-center min-h-screen bg-gray-100 dark:bg-gray-900 sm:items-center sm:pt-0" role="main">[39;22m[39;1m\n
            [39;22m[39;1m<div class="max-w-xl mx-auto sm:px-6 lg:px-8">[39;22m[39;1m\n
                [39;22m[39;1m<div class="flex items-center pt-8 sm:justify-start sm:pt-0">[39;22m[39;1m\n
                    [39;22m[39;1m<h1 class="px-4 text-lg dark:text-gray-300 text-gray-700 border-r border-gray-400 tracking-wider">[39;22m[39;1m\n
                        403                    [39;22m[39;1m</h1>[39;22m[39;1m\n
\n
                    [39;22m[39;1m<div class="ml-4 text-lg dark:text-gray-300 text-gray-700 uppercase tracking-wider">[39;22m[39;1m\n
                        This action is unauthorized.                    [39;22m[39;1m</div>[39;22m[39;1m\n
                [39;22m[39;1m</div>[39;22m[39;1m\n
            [39;22m[39;1m</div>[39;22m[39;1m\n
        [39;22m[39;1m</div>[39;22m[39;1m\n
    [39;22m[39;1m</body>[39;22m[39;1m\n
[39;22m[39;1m</html>[39;22m[39;1m\n
' matches PCRE pattern "/stockEntryPage\(\s*'out'/".[39;22m

  at [32mtests\Feature\Inventory\StockEntryTest.php[39m:[32m29[39m
     25▕     use RefreshDatabase;
     26▕ 
     27▕     protected function assertInitialTab(string $html, string $expectedTab): void
     28▕     {
  ➜  29▕         $this->assertMatchesRegularExpression(
     30▕             "/stockEntryPage\(\s*'{$expectedTab}'/",
     31▕             $html,
     32▕             "Expected the page to initialize with tab '{$expectedTab}'."
     33▕         );

  [33m1   [39m[39;1mtests\Feature\Inventory\StockEntryTest.php[39;22m:[39;1m29[39;22m
  [33m2   [39m[39;1mtests\Feature\Inventory\StockEntryTest.php[39;22m:[39;1m86[39;22m

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Inventory\StockEntryTest[22m [90m>[39m an unknown tab falls back to stock in                               
[39;1m  Expected the page to initialize with tab 'in'.
Failed asserting that '<!DOCTYPE html>\n
[39;22m[39;1m<html lang="en">[39;22m[39;1m\n
    [39;22m[39;1m<head>[39;22m[39;1m\n
        [39;22m[39;1m<meta charset="utf-8">[39;22m[39;1m\n
        [39;22m[39;1m<meta name="viewport" content="width=device-width, initial-scale=1">[39;22m[39;1m\n
\n
        [39;22m[39;1m<title>[39;22m[39;1mForbidden[39;22m[39;1m</title>[39;22m[39;1m\n
\n
        [39;22m[39;1m<style>[39;22m[39;1m\n
            /*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */html{line-height:1.15;-webkit-text-size-adjust:100%}body{margin:0}a{background-color:transparent}code{font-family:monospace,monospace;font-size:1em}[hidden]{display:none}html{font-family:system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;line-height:1.5}*,:after,:before{box-sizing:border-box;border:0 solid #e2e8f0}a{color:inherit;text-decoration:inherit}code{font-family:Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}svg,video{display:block;vertical-align:middle}video{max-width:100%;height:auto}.bg-white{--bg-opacity:1;background-color:#fff;background-color:rgba(255,255,255,var(--bg-opacity))}.bg-gray-100{--bg-opacity:1;background-color:#f7fafc;background-color:rgba(247,250,252,var(--bg-opacity))}.border-gray-200{--border-opacity:1;border-color:#edf2f7;border-color:rgba(237,242,247,var(--border-opacity))}.border-gray-400{--border-opacity:1;border-color:#cbd5e0;border-color:rgba(203,213,224,var(--border-opacity))}.border-t{border-top-width:1px}.border-r{border-right-width:1px}.flex{display:flex}.grid{display:grid}.hidden{display:none}.items-center{align-items:center}.justify-center{justify-content:center}.font-semibold{font-weight:600}.h-5{height:1.25rem}.h-8{height:2rem}.h-16{height:4rem}.text-sm{font-size:.875rem}.text-lg{font-size:1.125rem}.leading-7{line-height:1.75rem}.mx-auto{margin-left:auto;margin-right:auto}.ml-1{margin-left:.25rem}.mt-2{margin-top:.5rem}.mr-2{margin-right:.5rem}.ml-2{margin-left:.5rem}.mt-4{margin-top:1rem}.ml-4{margin-left:1rem}.mt-8{margin-top:2rem}.ml-12{margin-left:3rem}.-mt-px{margin-top:-1px}.max-w-xl{max-width:36rem}.max-w-6xl{max-width:72rem}.min-h-screen{min-height:100vh}.overflow-hidden{overflow:hidden}.p-6{padding:1.5rem}.py-4{padding-top:1rem;padding-bottom:1rem}.px-4{padding-left:1rem;padding-right:1rem}.px-6{padding-left:1.5rem;padding-right:1.5rem}.pt-8{padding-top:2rem}.fixed{position:fixed}.relative{position:relative}.top-0{top:0}.right-0{right:0}.shadow{box-shadow:0 1px 3px 0 rgba(0,0,0,.1),0 1px 2px 0 rgba(0,0,0,.06)}.text-center{text-align:center}.text-gray-200{--text-opacity:1;color:#edf2f7;color:rgba(237,242,247,var(--text-opacity))}.text-gray-300{--text-opacity:1;color:#e2e8f0;color:rgba(226,232,240,var(--text-opacity))}.text-gray-400{--text-opacity:1;color:#cbd5e0;color:rgba(203,213,224,var(--text-opacity))}.text-gray-500{--text-opacity:1;color:#a0aec0;color:rgba(160,174,192,var(--text-opacity))}.text-gray-600{--text-opacity:1;color:#718096;color:rgba(113,128,150,var(--text-opacity))}.text-gray-700{--text-opacity:1;color:#4a5568;color:rgba(74,85,104,var(--text-opacity))}.text-gray-900{--text-opacity:1;color:#1a202c;color:rgba(26,32,44,var(--text-opacity))}.uppercase{text-transform:uppercase}.underline{text-decoration:underline}.antialiased{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.tracking-wider{letter-spacing:.05em}.w-5{width:1.25rem}.w-8{width:2rem}.w-auto{width:auto}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}@-webkit-keyframes spin{0%{transform:rotate(0deg)}to{transform:rotate(1turn)}}@keyframes spin{0%{transform:rotate(0deg)}to{transform:rotate(1turn)}}@-webkit-keyframes ping{0%{transform:scale(1);opacity:1}75%,to{transform:scale(2);opacity:0}}@keyframes ping{0%{transform:scale(1);opacity:1}75%,to{transform:scale(2);opacity:0}}@-webkit-keyframes pulse{0%,to{opacity:1}50%{opacity:.5}}@keyframes pulse{0%,to{opacity:1}50%{opacity:.5}}@-webkit-keyframes bounce{0%,to{transform:translateY(-25%);-webkit-animation-timing-function:cubic-bezier(.8,0,1,1);animation-timing-function:cubic-bezier(.8,0,1,1)}50%{transform:translateY(0);-webkit-animation-timing-function:cubic-bezier(0,0,.2,1);animation-timing-function:cubic-bezier(0,0,.2,1)}}@keyframes bounce{0%,to{transform:translateY(-25%);-webkit-animation-timing-function:cubic-bezier(.8,0,1,1);animation-timing-function:cubic-bezier(.8,0,1,1)}50%{transform:translateY(0);-webkit-animation-timing-function:cubic-bezier(0,0,.2,1);animation-timing-function:cubic-bezier(0,0,.2,1)}}@media (min-width:640px){.sm\:rounded-lg{border-radius:.5rem}.sm\:block{display:block}.sm\:items-center{align-items:center}.sm\:justify-start{justify-content:flex-start}.sm\:justify-between{justify-content:space-between}.sm\:h-20{height:5rem}.sm\:ml-0{margin-left:0}.sm\:px-6{padding-left:1.5rem;padding-right:1.5rem}.sm\:pt-0{padding-top:0}.sm\:text-left{text-align:left}.sm\:text-right{text-align:right}}@media (min-width:768px){.md\:border-t-0{border-top-width:0}.md\:border-l{border-left-width:1px}.md\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}}@media (min-width:1024px){.lg\:px-8{padding-left:2rem;padding-right:2rem}}@media (prefers-color-scheme:dark){.dark\:bg-gray-800{--bg-opacity:1;background-color:#2d3748;background-color:rgba(45,55,72,var(--bg-opacity))}.dark\:bg-gray-900{--bg-opacity:1;background-color:#1a202c;background-color:rgba(26,32,44,var(--bg-opacity))}.dark\:border-gray-700{--border-opacity:1;border-color:#4a5568;border-color:rgba(74,85,104,var(--border-opacity))}.dark\:text-white{--text-opacity:1;color:#fff;color:rgba(255,255,255,var(--text-opacity))}.dark\:text-gray-300 { --text-opacity: 1; color: #e2e8f0; color: rgba(226,232,240,var(--text-opacity)) }}\n
        [39;22m[39;1m</style>[39;22m[39;1m\n
\n
        [39;22m[39;1m<style>[39;22m[39;1m\n
            body {\n
                font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";\n
            }\n
        [39;22m[39;1m</style>[39;22m[39;1m\n
    [39;22m[39;1m</head>[39;22m[39;1m\n
    [39;22m[39;1m<body class="antialiased">[39;22m[39;1m\n
        [39;22m[39;1m<div class="relative flex items-top justify-center min-h-screen bg-gray-100 dark:bg-gray-900 sm:items-center sm:pt-0" role="main">[39;22m[39;1m\n
            [39;22m[39;1m<div class="max-w-xl mx-auto sm:px-6 lg:px-8">[39;22m[39;1m\n
                [39;22m[39;1m<div class="flex items-center pt-8 sm:justify-start sm:pt-0">[39;22m[39;1m\n
                    [39;22m[39;1m<h1 class="px-4 text-lg dark:text-gray-300 text-gray-700 border-r border-gray-400 tracking-wider">[39;22m[39;1m\n
                        403                    [39;22m[39;1m</h1>[39;22m[39;1m\n
\n
                    [39;22m[39;1m<div class="ml-4 text-lg dark:text-gray-300 text-gray-700 uppercase tracking-wider">[39;22m[39;1m\n
                        This action is unauthorized.                    [39;22m[39;1m</div>[39;22m[39;1m\n
                [39;22m[39;1m</div>[39;22m[39;1m\n
            [39;22m[39;1m</div>[39;22m[39;1m\n
        [39;22m[39;1m</div>[39;22m[39;1m\n
    [39;22m[39;1m</body>[39;22m[39;1m\n
[39;22m[39;1m</html>[39;22m[39;1m\n
' matches PCRE pattern "/stockEntryPage\(\s*'in'/".[39;22m

  at [32mtests\Feature\Inventory\StockEntryTest.php[39m:[32m29[39m
     25▕     use RefreshDatabase;
     26▕ 
     27▕     protected function assertInitialTab(string $html, string $expectedTab): void
     28▕     {
  ➜  29▕         $this->assertMatchesRegularExpression(
     30▕             "/stockEntryPage\(\s*'{$expectedTab}'/",
     31▕             $html,
     32▕             "Expected the page to initialize with tab '{$expectedTab}'."
     33▕         );

  [33m1   [39m[39;1mtests\Feature\Inventory\StockEntryTest.php[39;22m:[39;1m29[39;22m
  [33m2   [39m[39;1mtests\Feature\Inventory\StockEntryTest.php[39;22m:[39;1m95[39;22m

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Inventory\StockEntryTest[22m [90m>[39m the part query parameter preselects a part owned by the company     
[39;1m  Expected: [39;22m[31m<!DOCTYPE html>\n
[39m[31m<html lang="en">[39m[31m\n
    [39m[31m<head>[39m[31m\n
        [39m[31m<meta charset="utf-8">[39m[31m\n
        [39m[31m<meta name="viewport" content="width=device-width, initial-scale=1">[39m[31m\n
\n
        [39m[31m<title>[39m[31mForbidden[39m[31m</title>[39m[31m\n
\n
        [39m[31m<style>[39m[31m\n
            /*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */html{line-height:1.15;-webkit-text-size-adjust:100%}body{margin:0}a{background-color:transparent}code{font-family:monospace,monospace;font-size:1em}[hidden]{display:none}html{font-family:system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;line-height:1.5}*,:after,:before{box-sizing:border-box;border:0 solid #e2e8f0}a{color:inherit;text-decoration:inherit}code{font-family:Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}svg,video{display:block;vertical-align:middle}video{max-width:100%;height:auto}.bg-white{--bg-opacity:1;background-color:#fff;background-color:rgba(255,255,255,var(--bg-opacity))}.bg-gray-100{--bg-opacity:1;background-color:#f7fafc;background-color:rgba(247,250,252,var(--bg-opacity))}.border-gray-200{--border-opacity:1;border-color:#edf2f7;border-color:rgba(237,242,247,var(--border-opacity))}.border-gray-400{--border-opacity:1;border-color:#cbd5e0;border-color:rgba(203,213,224,var(--border-opacity))}.border-t{border-top-width:1px}.border-r{border-right-width:1px}.flex{display:flex}.grid{display:grid}.hidden{display:none}.items-center{align-items:center}.justify-center{justify-content:center}.font-semibold{font-weight:600}.h-5{height:1.25rem}.h-8{height:2rem}.h-16{height:4rem}.text-sm{font-size:.875rem}.text-lg{font-size:1.125rem}.leading-7{line-height:1.75rem}.mx-auto{margin-left:auto;margin-right:auto}.ml-1{margin-left:.25rem}.mt-2{margin-top:.5rem}.mr-2{margin-right:.5rem}.ml-2{margin-left:.5rem}.mt-4{margin-top:1rem}.ml-4{margin-left:1rem}.mt-8{margin-top:2rem}.ml-12{margin-left:3rem}.-mt-px{margin-top:-1px}.max-w-xl{max-width:36rem}.max-w-6xl{max-width:72rem}.min-h-screen{min-height:100vh}.overflow-hidden{overflow:hidden}.p-6{padding:1.5rem}.py-4{padding-top:1rem;padding-bottom:1rem}.px-4{padding-left:1rem;padding-right:1rem}.px-6{padding-left:1.5rem;padding-right:1.5rem}.pt-8{padding-top:2rem}.fixed{position:fixed}.relative{position:relative}.top-0{top:0}.right-0{right:0}.shadow{box-shadow:0 1px 3px 0 rgba(0,0,0,.1),0 1px 2px 0 rgba(0,0,0,.06)}.text-center{text-align:center}.text-gray-200{--text-opacity:1;color:#edf2f7;color:rgba(237,242,247,var(--text-opacity))}.text-gray-300{--text-opacity:1;color:#e2e8f0;color:rgba(226,232,240,var(--text-opacity))}.text-gray-400{--text-opacity:1;color:#cbd5e0;color:rgba(203,213,224,var(--text-opacity))}.text-gray-500{--text-opacity:1;color:#a0aec0;color:rgba(160,174,192,var(--text-opacity))}.text-gray-600{--text-opacity:1;color:#718096;color:rgba(113,128,150,var(--text-opacity))}.text-gray-700{--text-opacity:1;color:#4a5568;color:rgba(74,85,104,var(--text-opacity))}.text-gray-900{--text-opacity:1;color:#1a202c;color:rgba(26,32,44,var(--text-opacity))}.uppercase{text-transform:uppercase}.underline{text-decoration:underline}.antialiased{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.tracking-wider{letter-spacing:.05em}.w-5{width:1.25rem}.w-8{width:2rem}.w-auto{width:auto}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}@-webkit-keyframes spin{0%{transform:rotate(0deg)}to{transform:rotate(1turn)}}@keyframes spin{0%{transform:rotate(0deg)}to{transform:rotate(1turn)}}@-webkit-keyframes ping{0%{transform:scale(1);opacity:1}75%,to{transform:scale(2);opacity:0}}@keyframes ping{0%{transform:scale(1);opacity:1}75%,to{transform:scale(2);opacity:0}}@-webkit-keyframes pulse{0%,to{opacity:1}50%{opacity:.5}}@keyframes pulse{0%,to{opacity:1}50%{opacity:.5}}@-webkit-keyframes bounce{0%,to{transform:translateY(-25%);-webkit-animation-timing-function:cubic-bezier(.8,0,1,1);animation-timing-function:cubic-bezier(.8,0,1,1)}50%{transform:translateY(0);-webkit-animation-timing-function:cubic-bezier(0,0,.2,1);animation-timing-function:cubic-bezier(0,0,.2,1)}}@keyframes bounce{0%,to{transform:translateY(-25%);-webkit-animation-timing-function:cubic-bezier(.8,0,1,1);animation-timing-function:cubic-bezier(.8,0,1,1)}50%{transform:translateY(0);-webkit-animation-timing-function:cubic-bezier(0,0,.2,1);animation-timing-function:cubic-bezier(0,0,.2,1)}}@media (min-width:640px){.sm\:rounded-lg{border-radius:.5rem}.sm\:block{display:block}.sm\:items-center{align-items:center}.sm\:justify-start{justify-content:flex-start}.sm\:justify-between{justify-content:space-between}.sm\:h-20{height:5rem}.sm\:ml-0{margin-left:0}.sm\:px-6{padding-left:1.5rem;padding-right:1.5rem}.sm\:pt-0{padding-top:0}.sm\:text-left{text-align:left}.sm\:text-right{text-align:right}}@media (min-width:768px){.md\:border-t-0{border-top-width:0}.md\:border-l{border-left-width:1px}.md\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}}@media (min-width:1024px){.lg\:px-8{padding-left:2rem;padding-right:2rem}}@media (prefers-color-scheme:dark){.dark\:bg-gray-800{--bg-opacity:1;background-color:#2d3748;background-color:rgba(45,55,72,var(--bg-opacity))}.dark\:bg-gray-900{--bg-opacity:1;background-color:#1a202c;background-color:rgba(26,32,44,var(--bg-opacity))}.dark\:border-gray-700{--border-opacity:1;border-color:#4a5568;border-color:rgba(74,85,104,var(--border-opacity))}.dark\:text-white{--text-opacity:1;color:#fff;color:rgba(255,255,255,var(--text-opacity))}.dark\:text-gray-300 { --text-opacity: 1; color: #e2e8f0; color: rgba(226,232,240,var(--text-opacity)) }}\n
        [39m[31m</style>[39m[31m\n
\n
        [39m[31m<style>[39m[31m\n
            body {\n
                font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";\n
            }\n
        [39m[31m</style>[39m[31m\n
    [39m[31m</head>[39m[31m\n
    [39m[31m<body class="antialiased">[39m[31m\n
        [39m[31m<div class="relative flex items-top justify-center min-h-screen bg-gray-100 dark:bg-gray-900 sm:items-center sm:pt-0" role="main">[39m[31m\n
            [39m[31m<div class="max-w-xl mx-auto sm:px-6 lg:px-8">[39m[31m\n
                [39m[31m<div class="flex items-center pt-8 sm:justify-start sm:pt-0">[39m[31m\n
                    [39m[31m<h1 class="px-4 text-lg dark:text-gray-300 text-gray-700 border-r border-gray-400 tracking-wider">[39m[31m\n
                        403                    [39m[31m</h1>[39m[31m\n
\n
                    [39m[31m<div class="ml-4 text-lg dark:text-gray-300 text-gray-700 uppercase tracking-wider">[39m[31m\n
                        This action is unauthorized.                    [39m[31m</div>[39m[31m\n
                [39m[31m</div>[39m[31m\n
            [39m[31m</div>[39m[31m\n
        [39m[31m</div>[39m[31m\n
    [39m[31m</body>[39m[31m\n
[39m[31m</html>[39m[31m\n
[39m[39;1m

  To contain: [39;22m[32mPreselected Part[39m

  at [32mtests\Feature\Inventory\StockEntryTest.php[39m:[32m105[39m
    101▕         $part = Part::factory()->for($user->company)->create(['name' => 'Preselected Part']);
    102▕ 
    103▕         $response = $this->actingAs($user)->get(route('stock-entry.index', ['part' => $part->ulid]));
    104▕ 
  ➜ 105▕         $response->assertSee('Preselected Part');
    106▕     }
    107▕ 
    108▕     public function test_the_part_query_parameter_is_ignored_for_another_companys_part(): void
    109▕     {

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Inventory\StockEntryTest[22m [90m>[39m the part query parameter is ignored for another companys part       
[39;1m  Expected response status code [200] but received 403.
Failed asserting that 403 is identical to 200.[39;22m

  at [32mtests\Feature\Inventory\StockEntryTest.php[39m:[32m116[39m
    112▕         $partB = Part::factory()->for($companyB)->create(['name' => 'RIVAL-PART']);
    113▕ 
    114▕         $response = $this->actingAs($userA)->get(route('stock-entry.index', ['part' => $partB->ulid]));
    115▕ 
  ➜ 116▕         $response->assertOk();
    117▕         $response->assertDontSee('RIVAL-PART');
    118▕     }
    119▕ 
    120▕     public function test_part_search_is_isolated_to_the_users_company(): void

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Inventory\StockEntryTest[22m [90m>[39m part search is isolated to the users company                        
[39;1m  Expected response status code [200] but received 403.
Failed asserting that 403 is identical to 200.[39;22m

  at [32mtests\Feature\Inventory\StockEntryTest.php[39m:[32m129[39m
    125▕         Part::factory()->for($companyB)->create(['name' => 'Rival Filter']);
    126▕ 
    127▕         $json = $this->actingAs($userA)
    128▕             ->getJson(route('parts.search', ['q' => 'Filter']))
  ➜ 129▕             ->assertOk()
    130▕             ->json();
    131▕ 
    132▕         $names = collect($json)->pluck('name');
    133▕         $this->assertTrue($names->contains('A Filter'));

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Inventory\StockEntryTest[22m [90m>[39m part search returns current stock                                   
[39;1m  Expected response status code [200] but received 403.
Failed asserting that 403 is identical to 200.[39;22m

  at [32mtests\Feature\Inventory\StockEntryTest.php[39m:[32m147[39m
    143▕         ]);
    144▕ 
    145▕         $json = $this->actingAs($user)
    146▕             ->getJson(route('parts.search', ['q' => 'Stocked']))
  ➜ 147▕             ->assertOk()
    148▕             ->json();
    149▕ 
    150▕         $this->assertSame(12, $json[0]['currentStock']);
    151▕     }

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Inventory\StockEntryTest[22m [90m>[39m stock in submitted from the page redirects back to it               
[39;1m  Expected response status code [201, 301, 302, 303, 307, 308] but received 403.
Failed asserting that false is true.[39;22m

  at [32mtests\Feature\Inventory\StockEntryTest.php[39m:[32m163[39m
    159▕             'type' => 'in', 'quantity' => 5, 'unit_cost' => 10, 'date' => now()->toDateString(),
    160▕             'return_to' => 'stock-entry',
    161▕         ]);
    162▕ 
  ➜ 163▕         $response->assertRedirect(route('stock-entry.index', ['tab' => 'in']));
    164▕     }
    165▕ 
    166▕     public function test_stock_out_submitted_from_the_page_redirects_back_to_the_out_tab(): void
    167▕     {

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Inventory\StockEntryTest[22m [90m>[39m stock out submitted from the page redirects back to the out tab     
[39;1m  Expected response status code [201, 301, 302, 303, 307, 308] but received 403.
Failed asserting that false is true.[39;22m

  at [32mtests\Feature\Inventory\StockEntryTest.php[39m:[32m181[39m
    177▕             'vehicle' => $vehicle->ulid, 'expiry_date' => now()->addYear()->toDateString(),
    178▕             'return_to' => 'stock-entry',
    179▕         ]);
    180▕ 
  ➜ 181▕         $response->assertRedirect(route('stock-entry.index', ['tab' => 'out']));
    182▕         $movement = $part->stockMovements()->where('type', 'out')->first();
    183▕         $this->assertNotNull($movement->expiry_date);
    184▕     }
    185▕

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Inventory\StockEntryTest[22m [90m>[39m a stock out without return to still redirects to the part page      
[39;1m  Expected response status code [201, 301, 302, 303, 307, 308] but received 403.
Failed asserting that false is true.[39;22m

  at [32mtests\Feature\Inventory\StockEntryTest.php[39m:[32m199[39m
    195▕         $response = $this->actingAs($user)->post(route('stock-movements.store', $part), [
    196▕             'type' => 'out', 'quantity' => 1, 'date' => now()->toDateString(), 'vehicle' => $vehicle->ulid,
    197▕         ]);
    198▕ 
  ➜ 199▕         $response->assertRedirect(route('parts.show', $part));
    200▕     }
    201▕ 
    202▕     public function test_quick_adding_a_part_from_the_page_redirects_back_with_it_preselected(): void
    203▕     {

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Inventory\StockEntryTest[22m [90m>[39m quick adding a part from the page redirects back… [41;1m ErrorException [49;22m  
[39;1m  Attempt to read property "ulid" on null[39;22m

  at [32mtests\Feature\Inventory\StockEntryTest.php[39m:[32m212[39m
    208▕             'return_to' => 'stock-entry', 'tab' => 'out',
    209▕         ]);
    210▕ 
    211▕         $part = Part::where('name', 'Quick Added Part')->first();
  ➜ 212▕         $response->assertRedirect(route('stock-entry.index', ['tab' => 'out', 'part' => $part->ulid]));
    213▕     }
    214▕ 
    215▕     public function test_quick_adding_a_supplier_from_the_page_redirects_back_with_it_preselected(): void
    216▕     {

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Inventory\StockEntryTest[22m [90m>[39m quick adding a supplier from the page redirects…  [41;1m ErrorException [49;22m  
[39;1m  Attempt to read property "ulid" on null[39;22m

  at [32mtests\Feature\Inventory\StockEntryTest.php[39m:[32m224[39m
    220▕             'name' => 'Quick Added Supplier', 'return_to' => 'stock-entry',
    221▕         ]);
    222▕ 
    223▕         $supplier = Supplier::where('name', 'Quick Added Supplier')->first();
  ➜ 224▕         $response->assertRedirect(route('stock-entry.index', ['tab' => 'in', 'supplier' => $supplier->ulid]));
    225▕     }
    226▕ 
    227▕     public function test_quick_adding_a_maintenance_job_from_the_page_redirects_back_to_the_maintenance_tab(): void
    228▕     {

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Inventory\StockEntryTest[22m [90m>[39m quick adding a maintenance job from the page redirects back to t…   
[39;1m  Expected response status code [201, 301, 302, 303, 307, 308] but received 403.
Failed asserting that false is true.[39;22m

  at [32mtests\Feature\Inventory\StockEntryTest.php[39m:[32m238[39m
    234▕             'date_in' => now()->toDateString(), 'labour_cost' => 100,
    235▕             'return_to' => 'stock-entry',
    236▕         ]);
    237▕ 
  ➜ 238▕         $response->assertRedirect(route('stock-entry.index', ['tab' => 'maintenance']));
    239▕         $this->assertSame(1, MaintenanceJob::where('vehicle_id', $vehicle->id)->count());
    240▕     }
    241▕ 
    242▕     public function test_a_guest_is_redirected_away(): void

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Inventory\StockEntryTest[22m [90m>[39m the recent lists show data scoped to the users company              
[39;1m  Expected: [39;22m[31m<!DOCTYPE html>\n
[39m[31m<html lang="en">[39m[31m\n
    [39m[31m<head>[39m[31m\n
        [39m[31m<meta charset="utf-8">[39m[31m\n
        [39m[31m<meta name="viewport" content="width=device-width, initial-scale=1">[39m[31m\n
\n
        [39m[31m<title>[39m[31mForbidden[39m[31m</title>[39m[31m\n
\n
        [39m[31m<style>[39m[31m\n
            /*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */html{line-height:1.15;-webkit-text-size-adjust:100%}body{margin:0}a{background-color:transparent}code{font-family:monospace,monospace;font-size:1em}[hidden]{display:none}html{font-family:system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;line-height:1.5}*,:after,:before{box-sizing:border-box;border:0 solid #e2e8f0}a{color:inherit;text-decoration:inherit}code{font-family:Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}svg,video{display:block;vertical-align:middle}video{max-width:100%;height:auto}.bg-white{--bg-opacity:1;background-color:#fff;background-color:rgba(255,255,255,var(--bg-opacity))}.bg-gray-100{--bg-opacity:1;background-color:#f7fafc;background-color:rgba(247,250,252,var(--bg-opacity))}.border-gray-200{--border-opacity:1;border-color:#edf2f7;border-color:rgba(237,242,247,var(--border-opacity))}.border-gray-400{--border-opacity:1;border-color:#cbd5e0;border-color:rgba(203,213,224,var(--border-opacity))}.border-t{border-top-width:1px}.border-r{border-right-width:1px}.flex{display:flex}.grid{display:grid}.hidden{display:none}.items-center{align-items:center}.justify-center{justify-content:center}.font-semibold{font-weight:600}.h-5{height:1.25rem}.h-8{height:2rem}.h-16{height:4rem}.text-sm{font-size:.875rem}.text-lg{font-size:1.125rem}.leading-7{line-height:1.75rem}.mx-auto{margin-left:auto;margin-right:auto}.ml-1{margin-left:.25rem}.mt-2{margin-top:.5rem}.mr-2{margin-right:.5rem}.ml-2{margin-left:.5rem}.mt-4{margin-top:1rem}.ml-4{margin-left:1rem}.mt-8{margin-top:2rem}.ml-12{margin-left:3rem}.-mt-px{margin-top:-1px}.max-w-xl{max-width:36rem}.max-w-6xl{max-width:72rem}.min-h-screen{min-height:100vh}.overflow-hidden{overflow:hidden}.p-6{padding:1.5rem}.py-4{padding-top:1rem;padding-bottom:1rem}.px-4{padding-left:1rem;padding-right:1rem}.px-6{padding-left:1.5rem;padding-right:1.5rem}.pt-8{padding-top:2rem}.fixed{position:fixed}.relative{position:relative}.top-0{top:0}.right-0{right:0}.shadow{box-shadow:0 1px 3px 0 rgba(0,0,0,.1),0 1px 2px 0 rgba(0,0,0,.06)}.text-center{text-align:center}.text-gray-200{--text-opacity:1;color:#edf2f7;color:rgba(237,242,247,var(--text-opacity))}.text-gray-300{--text-opacity:1;color:#e2e8f0;color:rgba(226,232,240,var(--text-opacity))}.text-gray-400{--text-opacity:1;color:#cbd5e0;color:rgba(203,213,224,var(--text-opacity))}.text-gray-500{--text-opacity:1;color:#a0aec0;color:rgba(160,174,192,var(--text-opacity))}.text-gray-600{--text-opacity:1;color:#718096;color:rgba(113,128,150,var(--text-opacity))}.text-gray-700{--text-opacity:1;color:#4a5568;color:rgba(74,85,104,var(--text-opacity))}.text-gray-900{--text-opacity:1;color:#1a202c;color:rgba(26,32,44,var(--text-opacity))}.uppercase{text-transform:uppercase}.underline{text-decoration:underline}.antialiased{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.tracking-wider{letter-spacing:.05em}.w-5{width:1.25rem}.w-8{width:2rem}.w-auto{width:auto}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}@-webkit-keyframes spin{0%{transform:rotate(0deg)}to{transform:rotate(1turn)}}@keyframes spin{0%{transform:rotate(0deg)}to{transform:rotate(1turn)}}@-webkit-keyframes ping{0%{transform:scale(1);opacity:1}75%,to{transform:scale(2);opacity:0}}@keyframes ping{0%{transform:scale(1);opacity:1}75%,to{transform:scale(2);opacity:0}}@-webkit-keyframes pulse{0%,to{opacity:1}50%{opacity:.5}}@keyframes pulse{0%,to{opacity:1}50%{opacity:.5}}@-webkit-keyframes bounce{0%,to{transform:translateY(-25%);-webkit-animation-timing-function:cubic-bezier(.8,0,1,1);animation-timing-function:cubic-bezier(.8,0,1,1)}50%{transform:translateY(0);-webkit-animation-timing-function:cubic-bezier(0,0,.2,1);animation-timing-function:cubic-bezier(0,0,.2,1)}}@keyframes bounce{0%,to{transform:translateY(-25%);-webkit-animation-timing-function:cubic-bezier(.8,0,1,1);animation-timing-function:cubic-bezier(.8,0,1,1)}50%{transform:translateY(0);-webkit-animation-timing-function:cubic-bezier(0,0,.2,1);animation-timing-function:cubic-bezier(0,0,.2,1)}}@media (min-width:640px){.sm\:rounded-lg{border-radius:.5rem}.sm\:block{display:block}.sm\:items-center{align-items:center}.sm\:justify-start{justify-content:flex-start}.sm\:justify-between{justify-content:space-between}.sm\:h-20{height:5rem}.sm\:ml-0{margin-left:0}.sm\:px-6{padding-left:1.5rem;padding-right:1.5rem}.sm\:pt-0{padding-top:0}.sm\:text-left{text-align:left}.sm\:text-right{text-align:right}}@media (min-width:768px){.md\:border-t-0{border-top-width:0}.md\:border-l{border-left-width:1px}.md\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}}@media (min-width:1024px){.lg\:px-8{padding-left:2rem;padding-right:2rem}}@media (prefers-color-scheme:dark){.dark\:bg-gray-800{--bg-opacity:1;background-color:#2d3748;background-color:rgba(45,55,72,var(--bg-opacity))}.dark\:bg-gray-900{--bg-opacity:1;background-color:#1a202c;background-color:rgba(26,32,44,var(--bg-opacity))}.dark\:border-gray-700{--border-opacity:1;border-color:#4a5568;border-color:rgba(74,85,104,var(--border-opacity))}.dark\:text-white{--text-opacity:1;color:#fff;color:rgba(255,255,255,var(--text-opacity))}.dark\:text-gray-300 { --text-opacity: 1; color: #e2e8f0; color: rgba(226,232,240,var(--text-opacity)) }}\n
        [39m[31m</style>[39m[31m\n
\n
        [39m[31m<style>[39m[31m\n
            body {\n
                font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";\n
            }\n
        [39m[31m</style>[39m[31m\n
    [39m[31m</head>[39m[31m\n
    [39m[31m<body class="antialiased">[39m[31m\n
        [39m[31m<div class="relative flex items-top justify-center min-h-screen bg-gray-100 dark:bg-gray-900 sm:items-center sm:pt-0" role="main">[39m[31m\n
            [39m[31m<div class="max-w-xl mx-auto sm:px-6 lg:px-8">[39m[31m\n
                [39m[31m<div class="flex items-center pt-8 sm:justify-start sm:pt-0">[39m[31m\n
                    [39m[31m<h1 class="px-4 text-lg dark:text-gray-300 text-gray-700 border-r border-gray-400 tracking-wider">[39m[31m\n
                        403                    [39m[31m</h1>[39m[31m\n
\n
                    [39m[31m<div class="ml-4 text-lg dark:text-gray-300 text-gray-700 uppercase tracking-wider">[39m[31m\n
                        This action is unauthorized.                    [39m[31m</div>[39m[31m\n
                [39m[31m</div>[39m[31m\n
            [39m[31m</div>[39m[31m\n
        [39m[31m</div>[39m[31m\n
    [39m[31m</body>[39m[31m\n
[39m[31m</html>[39m[31m\n
[39m[39;1m

  To contain: [39;22m[32mA-STOCK-PART[39m

  at [32mtests\Feature\Inventory\StockEntryTest.php[39m:[32m259[39m
    255▕         StockMovement::factory()->create(['company_id' => $companyB->id, 'part_id' => $partB->id, 'type' => 'in', 'quantity' => 5, 'date' => now()]);
    256▕ 
    257▕         $response = $this->actingAs($userA)->get(route('stock-entry.index'));
    258▕ 
  ➜ 259▕         $response->assertSee('A-STOCK-PART');
    260▕         $response->assertDontSee('RIVAL-STOCK-PART');
    261▕     }
    262▕ 
    263▕     public function test_the_quick_add_part_form_has_no_barcode_field(): void

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Inventory\StockEntryTest[22m [90m>[39m manage parts suppliers and mechanics pages link back to stock en…   
[39;1m  Expected response status code [200] but received 403.
Failed asserting that 403 is identical to 200.[39;22m

  at [32mtests\Feature\Inventory\StockEntryTest.php[39m:[32m278[39m
    274▕         $user = User::factory()->create();
    275▕ 
    276▕         foreach (['/parts', '/suppliers', '/mechanics'] as $path) {
    277▕             $this->actingAs($user)->get($path)
  ➜ 278▕                 ->assertOk()
    279▕                 ->assertSee(route('stock-entry.index'), false);
    280▕         }
    281▕     }
    282▕ }

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Inventory\StockInReceiptTest[22m [90m>[39m the index lists only the users company stock in entries         
[39;1m  Expected response status code [200] but received 403.
Failed asserting that 403 is identical to 200.[39;22m

  at [32mtests\Feature\Inventory\StockInReceiptTest.php[39m:[32m28[39m
     24▕         StockMovement::factory()->for($partB)->in()->create(['company_id' => $companyB->id]);
     25▕ 
     26▕         $response = $this->actingAs($userA)->get(route('stock-in.index'));
     27▕ 
  ➜  28▕         $response->assertOk();
     29▕         $response->assertSee('A-PART');
     30▕         $response->assertDontSee('RIVAL-PART');
     31▕     }
     32▕

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Inventory\StockInReceiptTest[22m [90m>[39m the index excludes stock out entries                            
[39;1m  Expected: [39;22m[31m<!DOCTYPE html>\n
[39m[31m<html lang="en">[39m[31m\n
    [39m[31m<head>[39m[31m\n
        [39m[31m<meta charset="utf-8">[39m[31m\n
        [39m[31m<meta name="viewport" content="width=device-width, initial-scale=1">[39m[31m\n
\n
        [39m[31m<title>[39m[31mForbidden[39m[31m</title>[39m[31m\n
\n
        [39m[31m<style>[39m[31m\n
            /*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */html{line-height:1.15;-webkit-text-size-adjust:100%}body{margin:0}a{background-color:transparent}code{font-family:monospace,monospace;font-size:1em}[hidden]{display:none}html{font-family:system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;line-height:1.5}*,:after,:before{box-sizing:border-box;border:0 solid #e2e8f0}a{color:inherit;text-decoration:inherit}code{font-family:Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}svg,video{display:block;vertical-align:middle}video{max-width:100%;height:auto}.bg-white{--bg-opacity:1;background-color:#fff;background-color:rgba(255,255,255,var(--bg-opacity))}.bg-gray-100{--bg-opacity:1;background-color:#f7fafc;background-color:rgba(247,250,252,var(--bg-opacity))}.border-gray-200{--border-opacity:1;border-color:#edf2f7;border-color:rgba(237,242,247,var(--border-opacity))}.border-gray-400{--border-opacity:1;border-color:#cbd5e0;border-color:rgba(203,213,224,var(--border-opacity))}.border-t{border-top-width:1px}.border-r{border-right-width:1px}.flex{display:flex}.grid{display:grid}.hidden{display:none}.items-center{align-items:center}.justify-center{justify-content:center}.font-semibold{font-weight:600}.h-5{height:1.25rem}.h-8{height:2rem}.h-16{height:4rem}.text-sm{font-size:.875rem}.text-lg{font-size:1.125rem}.leading-7{line-height:1.75rem}.mx-auto{margin-left:auto;margin-right:auto}.ml-1{margin-left:.25rem}.mt-2{margin-top:.5rem}.mr-2{margin-right:.5rem}.ml-2{margin-left:.5rem}.mt-4{margin-top:1rem}.ml-4{margin-left:1rem}.mt-8{margin-top:2rem}.ml-12{margin-left:3rem}.-mt-px{margin-top:-1px}.max-w-xl{max-width:36rem}.max-w-6xl{max-width:72rem}.min-h-screen{min-height:100vh}.overflow-hidden{overflow:hidden}.p-6{padding:1.5rem}.py-4{padding-top:1rem;padding-bottom:1rem}.px-4{padding-left:1rem;padding-right:1rem}.px-6{padding-left:1.5rem;padding-right:1.5rem}.pt-8{padding-top:2rem}.fixed{position:fixed}.relative{position:relative}.top-0{top:0}.right-0{right:0}.shadow{box-shadow:0 1px 3px 0 rgba(0,0,0,.1),0 1px 2px 0 rgba(0,0,0,.06)}.text-center{text-align:center}.text-gray-200{--text-opacity:1;color:#edf2f7;color:rgba(237,242,247,var(--text-opacity))}.text-gray-300{--text-opacity:1;color:#e2e8f0;color:rgba(226,232,240,var(--text-opacity))}.text-gray-400{--text-opacity:1;color:#cbd5e0;color:rgba(203,213,224,var(--text-opacity))}.text-gray-500{--text-opacity:1;color:#a0aec0;color:rgba(160,174,192,var(--text-opacity))}.text-gray-600{--text-opacity:1;color:#718096;color:rgba(113,128,150,var(--text-opacity))}.text-gray-700{--text-opacity:1;color:#4a5568;color:rgba(74,85,104,var(--text-opacity))}.text-gray-900{--text-opacity:1;color:#1a202c;color:rgba(26,32,44,var(--text-opacity))}.uppercase{text-transform:uppercase}.underline{text-decoration:underline}.antialiased{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.tracking-wider{letter-spacing:.05em}.w-5{width:1.25rem}.w-8{width:2rem}.w-auto{width:auto}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}@-webkit-keyframes spin{0%{transform:rotate(0deg)}to{transform:rotate(1turn)}}@keyframes spin{0%{transform:rotate(0deg)}to{transform:rotate(1turn)}}@-webkit-keyframes ping{0%{transform:scale(1);opacity:1}75%,to{transform:scale(2);opacity:0}}@keyframes ping{0%{transform:scale(1);opacity:1}75%,to{transform:scale(2);opacity:0}}@-webkit-keyframes pulse{0%,to{opacity:1}50%{opacity:.5}}@keyframes pulse{0%,to{opacity:1}50%{opacity:.5}}@-webkit-keyframes bounce{0%,to{transform:translateY(-25%);-webkit-animation-timing-function:cubic-bezier(.8,0,1,1);animation-timing-function:cubic-bezier(.8,0,1,1)}50%{transform:translateY(0);-webkit-animation-timing-function:cubic-bezier(0,0,.2,1);animation-timing-function:cubic-bezier(0,0,.2,1)}}@keyframes bounce{0%,to{transform:translateY(-25%);-webkit-animation-timing-function:cubic-bezier(.8,0,1,1);animation-timing-function:cubic-bezier(.8,0,1,1)}50%{transform:translateY(0);-webkit-animation-timing-function:cubic-bezier(0,0,.2,1);animation-timing-function:cubic-bezier(0,0,.2,1)}}@media (min-width:640px){.sm\:rounded-lg{border-radius:.5rem}.sm\:block{display:block}.sm\:items-center{align-items:center}.sm\:justify-start{justify-content:flex-start}.sm\:justify-between{justify-content:space-between}.sm\:h-20{height:5rem}.sm\:ml-0{margin-left:0}.sm\:px-6{padding-left:1.5rem;padding-right:1.5rem}.sm\:pt-0{padding-top:0}.sm\:text-left{text-align:left}.sm\:text-right{text-align:right}}@media (min-width:768px){.md\:border-t-0{border-top-width:0}.md\:border-l{border-left-width:1px}.md\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}}@media (min-width:1024px){.lg\:px-8{padding-left:2rem;padding-right:2rem}}@media (prefers-color-scheme:dark){.dark\:bg-gray-800{--bg-opacity:1;background-color:#2d3748;background-color:rgba(45,55,72,var(--bg-opacity))}.dark\:bg-gray-900{--bg-opacity:1;background-color:#1a202c;background-color:rgba(26,32,44,var(--bg-opacity))}.dark\:border-gray-700{--border-opacity:1;border-color:#4a5568;border-color:rgba(74,85,104,var(--border-opacity))}.dark\:text-white{--text-opacity:1;color:#fff;color:rgba(255,255,255,var(--text-opacity))}.dark\:text-gray-300 { --text-opacity: 1; color: #e2e8f0; color: rgba(226,232,240,var(--text-opacity)) }}\n
        [39m[31m</style>[39m[31m\n
\n
        [39m[31m<style>[39m[31m\n
            body {\n
                font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";\n
            }\n
        [39m[31m</style>[39m[31m\n
    [39m[31m</head>[39m[31m\n
    [39m[31m<body class="antialiased">[39m[31m\n
        [39m[31m<div class="relative flex items-top justify-center min-h-screen bg-gray-100 dark:bg-gray-900 sm:items-center sm:pt-0" role="main">[39m[31m\n
            [39m[31m<div class="max-w-xl mx-auto sm:px-6 lg:px-8">[39m[31m\n
                [39m[31m<div class="flex items-center pt-8 sm:justify-start sm:pt-0">[39m[31m\n
                    [39m[31m<h1 class="px-4 text-lg dark:text-gray-300 text-gray-700 border-r border-gray-400 tracking-wider">[39m[31m\n
                        403                    [39m[31m</h1>[39m[31m\n
\n
                    [39m[31m<div class="ml-4 text-lg dark:text-gray-300 text-gray-700 uppercase tracking-wider">[39m[31m\n
                        This action is unauthorized.                    [39m[31m</div>[39m[31m\n
                [39m[31m</div>[39m[31m\n
            [39m[31m</div>[39m[31m\n
        [39m[31m</div>[39m[31m\n
    [39m[31m</body>[39m[31m\n
[39m[31m</html>[39m[31m\n
[39m[39;1m

  To contain: [39;22m[32mIN-PART[39m

  at [32mtests\Feature\Inventory\StockInReceiptTest.php[39m:[32m43[39m
     39▕         StockMovement::factory()->for($outPart)->out()->create(['company_id' => $user->company_id]);
     40▕ 
     41▕         $response = $this->actingAs($user)->get(route('stock-in.index'));
     42▕ 
  ➜  43▕         $response->assertSee('IN-PART');
     44▕         $response->assertDontSee('OUT-PART');
     45▕     }
     46▕ 
     47▕     public function test_a_valid_single_supplier_selection_renders_a_receipt(): void

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Inventory\StockInReceiptTest[22m [90m>[39m a valid single supplier selection renders a receipt             
[39;1m  Expected response status code [200] but received 403.
Failed asserting that 403 is identical to 200.[39;22m

  at [32mtests\Feature\Inventory\StockInReceiptTest.php[39m:[32m58[39m
     54▕         ]);
     55▕ 
     56▕         $response = $this->actingAs($user)->get(route('stock-in.receipt', ['movements' => [$movement->ulid]]));
     57▕ 
  ➜  58▕         $response->assertOk();
     59▕         $response->assertSee('AutoParts Ltd');
     60▕         $response->assertSee('Oil Filter');
     61▕         $response->assertSee('1,000.00', false); // 10 * 100 total
     62▕     }

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Inventory\StockInReceiptTest[22m [90m>[39m a mixed supplier selection is rejected                          
[39;1m  Expected response status code [201, 301, 302, 303, 307, 308] but received 403.
Failed asserting that false is true.[39;22m

  at [32mtests\Feature\Inventory\StockInReceiptTest.php[39m:[32m76[39m
     72▕         $movementB = StockMovement::factory()->for($partB)->in()->create(['company_id' => $user->company_id, 'supplier_id' => $supplierB->id]);
     73▕ 
     74▕         $response = $this->actingAs($user)->get(route('stock-in.receipt', ['movements' => [$movementA->ulid, $movementB->ulid]]));
     75▕ 
  ➜  76▕         $response->assertRedirect(route('stock-in.index'));
     77▕         $response->assertSessionHasErrors('movements');
     78▕     }
     79▕ 
     80▕     public function test_a_company_cannot_print_a_receipt_for_another_companys_stock_in_entries(): void

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Inventory\StockInReceiptTest[22m [90m>[39m a company cannot print a receipt for another companys stock…    
[39;1m  Expected response status code [201, 301, 302, 303, 307, 308] but received 403.
Failed asserting that false is true.[39;22m

  at [32mtests\Feature\Inventory\StockInReceiptTest.php[39m:[32m89[39m
     85▕         $movementB = StockMovement::factory()->for($partB)->in()->create(['company_id' => $companyB->id]);
     86▕ 
     87▕         $response = $this->actingAs($userA)->get(route('stock-in.receipt', ['movements' => [$movementB->ulid]]));
     88▕ 
  ➜  89▕         $response->assertRedirect(route('stock-in.index'));
     90▕         $response->assertSessionHas('status');
     91▕     }
     92▕ 
     93▕     public function test_a_guest_is_redirected_away(): void

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Inventory\StockMovementTest[22m [90m>[39m a stock in increases derived stock and resolves supplier         
[39;1m  Expected response status code [201, 301, 302, 303, 307, 308] but received 403.
Failed asserting that false is true.[39;22m

  at [32mtests\Feature\Inventory\StockMovementTest.php[39m:[32m32[39m
     28▕             'unit_cost' => 100,
     29▕             'date' => now()->toDateString(),
     30▕             'supplier' => $supplier->ulid,
     31▕             'reference' => 'INV-001',
  ➜  32▕         ])->assertRedirect(route('parts.show', $part));
     33▕ 
     34▕         $this->assertSame(20, $part->fresh()->currentStock());
     35▕ 
     36▕         $movement = $part->stockMovements()->first();

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Inventory\StockMovementTest[22m [90m>[39m a stock out decreases derived stock and resolves vehicle and…    
[39;1m  Expected response status code [201, 301, 302, 303, 307, 308] but received 403.
Failed asserting that false is true.[39;22m

  at [32mtests\Feature\Inventory\StockMovementTest.php[39m:[32m56[39m
     52▕             'quantity' => 4,
     53▕             'date' => now()->toDateString(),
     54▕             'vehicle' => $vehicle->ulid,
     55▕             'mechanic' => $mechanic->ulid,
  ➜  56▕         ])->assertRedirect(route('parts.show', $part));
     57▕ 
     58▕         $this->assertSame(6, $part->fresh()->currentStock());
     59▕ 
     60▕         $movement = $part->stockMovements()->where('type', 'out')->first();

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Inventory\StockMovementTest[22m [90m>[39m stock out freezes the parts current unit cost and is immune t…   
[39;1m  Expected response status code [201, 301, 302, 303, 307, 308] but received 403.
Failed asserting that false is true.[39;22m

  at [32mtests\Feature\Inventory\StockMovementTest.php[39m:[32m81[39m
     77▕         $this->actingAs($user)->post(route('stock-movements.store', $part), [
     78▕             'type' => 'out',
     79▕             'quantity' => 4,
     80▕             'date' => now()->toDateString(),
  ➜  81▕         ])->assertRedirect(route('parts.show', $part));
     82▕ 
     83▕         $movement = $part->stockMovements()->where('type', 'out')->first();
     84▕         $this->assertSame('250.00', $movement->unit_cost);
     85▕

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Inventory\StockMovementTest[22m [90m>[39m an adjustment has no cost basis                                  
[39;1m  Expected response status code [201, 301, 302, 303, 307, 308] but received 403.
Failed asserting that false is true.[39;22m

  at [32mtests\Feature\Inventory\StockMovementTest.php[39m:[32m105[39m
    101▕             'quantity' => 1,
    102▕             'direction' => 'decrease',
    103▕             'date' => now()->toDateString(),
    104▕             'notes' => 'Damaged',
  ➜ 105▕         ])->assertRedirect(route('parts.show', $part));
    106▕ 
    107▕         $movement = $part->stockMovements()->where('type', 'adjustment')->first();
    108▕         $this->assertNull($movement->unit_cost);
    109▕     }

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Inventory\StockMovementTest[22m [90m>[39m stock out cannot exceed available stock                          
[39;1m  Session is missing expected key [errors].
Failed asserting that false is true.[39;22m

  at [32mtests\Feature\Inventory\StockMovementTest.php[39m:[32m121[39m
    117▕         $this->actingAs($user)->post(route('stock-movements.store', $part), [
    118▕             'type' => 'out',
    119▕             'quantity' => 6,
    120▕             'date' => now()->toDateString(),
  ➜ 121▕         ])->assertSessionHasErrors('quantity');
    122▕ 
    123▕         // Nothing was recorded and stock is unchanged.
    124▕         $this->assertSame(1, $part->stockMovements()->count());
    125▕         $this->assertSame(5, $part->fresh()->currentStock());

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Inventory\StockMovementTest[22m [90m>[39m adjustment can increase or decrease stock                        
[39;1m  Expected response status code [201, 301, 302, 303, 307, 308] but received 403.
Failed asserting that false is true.[39;22m

  at [32mtests\Feature\Inventory\StockMovementTest.php[39m:[32m140[39m
    136▕             'quantity' => 3,
    137▕             'direction' => 'decrease',
    138▕             'date' => now()->toDateString(),
    139▕             'notes' => 'Damaged in storage',
  ➜ 140▕         ])->assertRedirect(route('parts.show', $part));
    141▕ 
    142▕         $this->assertSame(7, $part->fresh()->currentStock());
    143▕ 
    144▕         $this->actingAs($user)->post(route('stock-movements.store', $part), [

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Inventory\StockMovementTest[22m [90m>[39m parts list and show report low stock and out of stock            
[39;1m  Expected response status code [200] but received 403.
Failed asserting that 403 is identical to 200.[39;22m

  at [32mtests\Feature\Inventory\StockMovementTest.php[39m:[32m167[39m
    163▕         $this->assertTrue($lowStockPart->fresh()->isLowStock());
    164▕         $this->assertTrue($outOfStockPart->fresh()->isOutOfStock());
    165▕ 
    166▕         $this->actingAs($user)->get('/parts')
  ➜ 167▕             ->assertOk()
    168▕             ->assertSee('Low Stock Part')
    169▕             ->assertSee('Zero Stock Part')
    170▕             ->assertSee('Out of stock');
    171▕     }

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Inventory\StockMovementTest[22m [90m>[39m a movement cannot reference another companys supplier vehicle…   
[39;1m  Session is missing expected key [errors].
Failed asserting that false is true.[39;22m

  at [32mtests\Feature\Inventory\StockMovementTest.php[39m:[32m199[39m
    195▕ 
    196▕         $this->actingAs($userA)->post(route('stock-movements.store', $part), [
    197▕             'type' => 'in', 'quantity' => 5, 'unit_cost' => 10, 'date' => now()->toDateString(),
    198▕             'supplier' => $supplierB->ulid,
  ➜ 199▕         ])->assertSessionHasErrors('supplier');
    200▕ 
    201▕         $this->actingAs($userA)->post(route('stock-movements.store', $part), [
    202▕             'type' => 'out', 'quantity' => 1, 'date' => now()->toDateString(),
    203▕             'vehicle' => $vehicleB->ulid,

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Inventory\StockMovementTest[22m [90m>[39m a companys stock ledger and derived stock are isolated           
[39;1m  Expected response status code [200] but received 403.
Failed asserting that 403 is identical to 200.[39;22m

  at [32mtests\Feature\Inventory\StockMovementTest.php[39m:[32m224[39m
    220▕         StockMovement::factory()->create(['company_id' => $companyB->id, 'part_id' => $partB->id, 'type' => 'in', 'quantity' => 999, 'date' => now()]);
    221▕ 
    222▕         // A cannot see B's part at all (existing Part isolation), so its
    223▕         // stock and movements are unreachable through any of A's own views.
  ➜ 224▕         $this->actingAs($userA)->get('/parts')->assertOk()->assertDontSee('RIVAL-PART');
    225▕         $this->actingAs($userA)->get(route('parts.show', $partB))->assertNotFound();
    226▕ 
    227▕         // And the raw ledger table itself never mixes companies.
    228▕         $this->assertSame(999, StockMovement::withoutGlobalScopes()->where('part_id', $partB->id)->sum('quantity'));

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Inventory\SupplierTest[22m [90m>[39m a user can create and see their companys suppliers                    
[39;1m  Failed asserting that null is not null.[39;22m

  at [32mtests\Feature\Inventory\SupplierTest.php[39m:[32m28[39m
     24▕             'address' => 'SITE Area',
     25▕         ]);
     26▕ 
     27▕         $supplier = Supplier::where('name', 'Karachi Auto Parts')->first();
  ➜  28▕         $this->assertNotNull($supplier);
     29▕         $this->assertSame($user->company_id, $supplier->company_id);
     30▕         $this->assertNotEmpty($supplier->ulid);
     31▕ 
     32▕         $response->assertRedirect(route('suppliers.show', $supplier));

  [33m1   [39m[39;1mtests\Feature\Inventory\SupplierTest.php[39;22m:[39;1m28[39;22m

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Inventory\SupplierTest[22m [90m>[39m a user can update and soft delete a supplier                          
[39;1m  Expected response status code [201, 301, 302, 303, 307, 308] but received 403.
Failed asserting that false is true.[39;22m

  at [32mtests\Feature\Inventory\SupplierTest.php[39m:[32m44[39m
     40▕         $supplier = Supplier::factory()->for($user->company)->create();
     41▕ 
     42▕         $this->actingAs($user)
     43▕             ->patch(route('suppliers.update', $supplier), ['name' => 'Renamed Supplier'])
  ➜  44▕             ->assertRedirect(route('suppliers.show', $supplier));
     45▕ 
     46▕         $this->assertSame('Renamed Supplier', $supplier->fresh()->name);
     47▕ 
     48▕         $this->actingAs($user)

  [31m────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[39m  
  [41;1m FAILED [49;22m [1mTests\Feature\Inventory\SupplierTest[22m [90m>[39m a company cannot see or touch another companys suppliers              
[39;1m  Expected response status code [200] but received 403.
Failed asserting that 403 is identical to 200.[39;22m

  at [32mtests\Feature\Inventory\SupplierTest.php[39m:[32m62[39m
     58▕         $companyB = Company::factory()->create();
     59▕         $userA = User::factory()->for($companyA)->create();
     60▕         $supplierB = Supplier::factory()->for($companyB)->create(['name' => 'RIVAL-SUPPLIER']);
     61▕ 
  ➜  62▕         $this->actingAs($userA)->get('/suppliers')->assertOk()->assertDontSee('RIVAL-SUPPLIER');
     63▕         $this->actingAs($userA)->get(route('suppliers.show', $supplierB))->assertNotFound();
     64▕         $this->actingAs($userA)->get(route('suppliers.edit', $supplierB))->assertNotFound();
     65▕         $this->actingAs($userA)
     66▕             ->patch(route('suppliers.update', $supplierB), ['name' => 'HACKED'])


  [90mTests:[39m    [31;1m259 failed[39;22m[90m,[39m[39m [39m[32;1m123 passed[39;22m[90m (633 assertions)[39m
  [90mDuration:[39m [39m39.76s[39m

