1, 'client_id' => 7, 'name' => 'Jane', 'email' => 'jane@example.test', 'is_primary' => true], ['id' => 2, 'client_id' => 7, 'name' => 'John', 'email' => 'john@example.test', 'is_primary' => false], ]; $contactCommand = new ContactEditCommand(); $edit = $contactCommand->validateEdit(2, ['client_id' => 7, 'name' => ' John Smith ', 'is_primary' => 'yes'], $contacts); domain_command_assert_same(true, $edit['valid'], 'A contact edit should validate.'); domain_command_assert_same([1], $edit['replace_primary_contact_ids'], 'Promoting an edited contact should demote the old primary.'); $delete = $contactCommand->validateDelete(1, $contacts); domain_command_assert_same(true, $delete['valid'], 'A primary contact may be deleted when a replacement exists.'); domain_command_assert_same(2, $delete['replacement_primary_contact_id'], 'Deleting the primary should nominate a replacement.'); $last = $contactCommand->validateDelete(2, [['id' => 2, 'client_id' => 7, 'is_primary' => true]]); domain_command_assert_same(false, $last['valid'], 'Deleting the only contact must be rejected.'); $history = new ClientHistoryService(); $rows = [ ['id' => 2, 'client_id' => 7, 'changed_at' => '2026-09-03', 'to_status' => 'closed'], ['id' => 1, 'client_id' => 8, 'changed_at' => '2026-09-02', 'to_status' => 'open'], ['id' => 3, 'client_id' => 7, 'changed_at' => '2026-10-01', 'to_status' => 'open'], ]; domain_command_assert_same([2], array_column($history->filter($rows, ['client_id' => 7, 'date_to' => '2026-09-30']), 'id'), 'History filtering should apply client and date bounds.'); $correction = new TimeEntryCorrectionCommand(); $existing = ['id' => 9, 'jobcard_id' => 12, 'technician_id' => 4, 'work_date' => '2026-09-01', 'hours' => 2, 'notes' => 'old', 'counts_toward_sla' => true, 'voided' => false]; $fixed = $correction->validateCorrection($existing, ['hours' => '3.25', 'notes' => ' corrected ']); domain_command_assert_same(true, $fixed['valid'], 'A time correction should validate against the existing entry.'); domain_command_assert_same(3.25, $fixed['entry']['hours'], 'A time correction should normalize replacement hours.'); domain_command_assert_same(12, $fixed['entry']['jobcard_id'], 'A correction must retain the existing jobcard.'); $void = $correction->validateVoid($existing, ['reason' => 'Duplicate entry']); domain_command_assert_same(true, $void['valid'], 'Voiding should require and retain a reason.'); domain_command_assert_same('void', $void['action'], 'Voiding should be an explicit logical action.'); $voided = $correction->validateVoid([...$existing, 'voided' => true], ['reason' => 'again']); domain_command_assert_same(false, $voided['valid'], 'An already voided entry cannot be voided twice.'); printf("Domain command/service tests: 10 passed\n");