1066 lines
40 KiB
PHP
1066 lines
40 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
usesApiSuite();
|
|
|
|
function activeWashCertificateAttachmentIdsForOrder(int $orderId): array
|
|
{
|
|
$statement = api_test_runtime()->db()->prepare(
|
|
'SELECT id, content
|
|
FROM object_attachments
|
|
WHERE object_type IN (?, ?)
|
|
AND object_id = ?
|
|
AND deleted_at IS NULL
|
|
ORDER BY id ASC'
|
|
);
|
|
expect($statement)->not->toBeFalse();
|
|
|
|
$objectType = 'orders';
|
|
$backtickedObjectType = '`orders`';
|
|
$statement->bind_param('ssi', $objectType, $backtickedObjectType, $orderId);
|
|
$statement->execute();
|
|
|
|
$result = $statement->get_result();
|
|
$attachmentIds = [];
|
|
while ($row = $result->fetch_assoc()) {
|
|
$content = json_decode((string)($row['content'] ?? ''), true);
|
|
$other = is_array($content) ? ($content['other'] ?? null) : null;
|
|
if (is_string($other) && strtolower($other) === 'wash_certificate') {
|
|
$attachmentIds[] = (int)($row['id'] ?? 0);
|
|
}
|
|
}
|
|
|
|
$result->free();
|
|
$statement->close();
|
|
|
|
return array_values(array_filter($attachmentIds, static fn(int $id): bool => $id > 0));
|
|
}
|
|
|
|
it('lists orders for an admin-scoped user and limits the results to the permitted departments', function (): void {
|
|
api_test_covers('GET /orders', 'happy');
|
|
|
|
$departmentOne = api_fixtures()->createDepartment(['name' => 'Department One']);
|
|
$departmentTwo = api_fixtures()->createDepartment(['name' => 'Department Two']);
|
|
$customerOne = api_fixtures()->createUser(['display_name' => 'Customer One']);
|
|
$customerTwo = api_fixtures()->createUser(['display_name' => 'Customer Two']);
|
|
$cashier = api_fixtures()->createUser(['display_name' => 'Cashier']);
|
|
|
|
$visibleOrder = api_fixtures()->createOrder([
|
|
'customer_id' => $customerOne['customer_number'],
|
|
'cashier_id' => $cashier['id'],
|
|
'department_id' => $departmentOne['id'],
|
|
'reference' => 'VISIBLE',
|
|
'reg_1' => 'VISIBLE1',
|
|
]);
|
|
$hiddenOrder = api_fixtures()->createOrder([
|
|
'customer_id' => $customerTwo['customer_number'],
|
|
'cashier_id' => $cashier['id'],
|
|
'department_id' => $departmentTwo['id'],
|
|
'reference' => 'HIDDEN',
|
|
'reg_1' => 'HIDDEN1',
|
|
]);
|
|
|
|
$session = api_fixtures()->createUserSession([
|
|
'list_orders',
|
|
'department_access_' . $departmentOne['id'],
|
|
]);
|
|
|
|
$response = api_client()->get('/orders', $session['headers']);
|
|
|
|
$response
|
|
->assertStatus(200)
|
|
->assertEnvelope()
|
|
->assertSuccess();
|
|
|
|
$orderIds = array_map(
|
|
static fn(array $order): int => (int)($order['id'] ?? 0),
|
|
is_array($response->data()) ? $response->data() : []
|
|
);
|
|
|
|
expect($orderIds)
|
|
->toContain($visibleOrder['id'])
|
|
->not->toContain($hiddenOrder['id']);
|
|
});
|
|
|
|
it('lists only the targeted customer orders for subuser sessions', function (): void {
|
|
api_test_covers('GET /orders', 'happy');
|
|
|
|
$department = api_fixtures()->createDepartment();
|
|
$targetCustomer = api_fixtures()->createUser(['display_name' => 'Target Customer']);
|
|
$otherCustomer = api_fixtures()->createUser(['display_name' => 'Other Customer']);
|
|
$cashier = api_fixtures()->createUser(['display_name' => 'Subuser Cashier']);
|
|
|
|
$targetOrder = api_fixtures()->createOrder([
|
|
'customer_id' => $targetCustomer['customer_number'],
|
|
'cashier_id' => $cashier['id'],
|
|
'department_id' => $department['id'],
|
|
'reference' => 'TARGET',
|
|
'reg_1' => 'TARGET1',
|
|
]);
|
|
$otherOrder = api_fixtures()->createOrder([
|
|
'customer_id' => $otherCustomer['customer_number'],
|
|
'cashier_id' => $cashier['id'],
|
|
'department_id' => $department['id'],
|
|
'reference' => 'OTHER',
|
|
'reg_1' => 'OTHER1',
|
|
]);
|
|
|
|
$session = api_fixtures()->createSubuserSession(
|
|
(int)$targetCustomer['customer_number'],
|
|
['ORDERS_LIST']
|
|
);
|
|
|
|
$response = api_client()->get('/orders', $session['headers']);
|
|
|
|
$response
|
|
->assertStatus(200)
|
|
->assertEnvelope()
|
|
->assertSuccess();
|
|
|
|
$orderIds = array_map(
|
|
static fn(array $order): int => (int)($order['id'] ?? 0),
|
|
is_array($response->data()) ? $response->data() : []
|
|
);
|
|
|
|
expect($orderIds)
|
|
->toContain($targetOrder['id'])
|
|
->not->toContain($otherOrder['id']);
|
|
|
|
expect($response->meta())->toHaveKey('target_customer_number', $targetCustomer['customer_number']);
|
|
});
|
|
|
|
it('returns the current auth and permission failures when order listing is not allowed', function (): void {
|
|
api_test_covers('GET /orders', 'auth');
|
|
|
|
$missingToken = api_client()->get('/orders');
|
|
|
|
$missingToken
|
|
->assertStatus(400)
|
|
->assertEnvelope()
|
|
->assertSuccess(false)
|
|
->assertMessage('Invalid session');
|
|
|
|
$session = api_fixtures()->createUserSession([]);
|
|
$missingPermissions = api_client()->get('/orders', $session['headers']);
|
|
|
|
$missingPermissions
|
|
->assertStatus(403)
|
|
->assertEnvelope()
|
|
->assertSuccess(false)
|
|
->assertMissingPermissions(['list_own_orders', 'list_orders']);
|
|
});
|
|
|
|
it('creates orders through the real endpoint', function (): void {
|
|
api_test_covers('POST /orders', 'happy');
|
|
|
|
$customer = api_fixtures()->createUser(['display_name' => 'Order Customer']);
|
|
$department = api_fixtures()->createDepartment();
|
|
api_fixtures()->createInvoiceCollection([
|
|
'customer_number' => $customer['customer_number'],
|
|
]);
|
|
$session = api_fixtures()->createUserSession(['add_order']);
|
|
|
|
$response = api_client()->post('/orders', [
|
|
'customer_id' => $customer['customer_number'],
|
|
'department_id' => $department['id'],
|
|
'reference' => 'ORDER-POST',
|
|
'notes' => 'Created through HTTP',
|
|
'safety_seal' => 'SEAL-CREATE',
|
|
'reg_1' => ' post-123 ',
|
|
'reg_2' => ' tr 9-8 ',
|
|
'reg_3' => ' 7z/x ',
|
|
], $session['headers']);
|
|
|
|
$response
|
|
->assertStatus(200)
|
|
->assertEnvelope()
|
|
->assertSuccess();
|
|
|
|
$orderId = (int)($response->data()['id'] ?? 0);
|
|
expect($orderId)->toBeGreaterThan(0);
|
|
|
|
$row = api_fixtures()->fetchRowById('orders', $orderId);
|
|
|
|
expect($row)->not->toBeNull();
|
|
expect($row['reg_1'] ?? null)->toBe('POST123');
|
|
expect($row['reg_2'] ?? null)->toBe('TR98');
|
|
expect($row['reg_3'] ?? null)->toBe('7ZX');
|
|
expect($row['safety_seal'] ?? null)->toBe('SEAL-CREATE');
|
|
|
|
api_fixtures()->cleanupDeleteById('orders', $orderId);
|
|
});
|
|
|
|
it('defaults order PO from a linked booking when creating without an order PO', function (): void {
|
|
api_test_covers('POST /orders', 'happy');
|
|
|
|
$customer = api_fixtures()->createUser(['display_name' => 'Booking PO Customer']);
|
|
$department = api_fixtures()->createDepartment();
|
|
api_fixtures()->createInvoiceCollection([
|
|
'customer_number' => $customer['customer_number'],
|
|
]);
|
|
$booking = api_fixtures()->createOrderBooking([
|
|
'customer_number' => $customer['customer_number'],
|
|
'department' => $department['id'],
|
|
'reference' => 'BOOKING-PO-REF',
|
|
'po' => 'BOOKING-PO-CREATE',
|
|
]);
|
|
$session = api_fixtures()->createUserSession(['add_order']);
|
|
|
|
$orders = [
|
|
[
|
|
'reference' => 'BOOKING-PO-MISSING',
|
|
'reg_1' => 'BKPO001',
|
|
],
|
|
[
|
|
'reference' => 'BOOKING-PO-BLANK',
|
|
'po' => ' ',
|
|
'reg_1' => 'BKPO002',
|
|
],
|
|
];
|
|
|
|
foreach ($orders as $payload) {
|
|
$response = api_client()->post('/orders', [
|
|
'customer_id' => $customer['customer_number'],
|
|
'department_id' => $department['id'],
|
|
'notes' => 'Created with booking PO default',
|
|
'booking_id' => $booking['id'],
|
|
...$payload,
|
|
], $session['headers']);
|
|
|
|
$response
|
|
->assertStatus(200)
|
|
->assertEnvelope()
|
|
->assertSuccess();
|
|
|
|
$orderId = (int)($response->data()['id'] ?? 0);
|
|
expect($orderId)->toBeGreaterThan(0);
|
|
|
|
$row = api_fixtures()->fetchRowById('orders', $orderId);
|
|
expect($row)->not->toBeNull();
|
|
expect((int)($row['booking_id'] ?? 0))->toBe((int)$booking['id']);
|
|
expect($row['po'] ?? null)->toBe('BOOKING-PO-CREATE');
|
|
|
|
api_fixtures()->cleanupDeleteById('orders', $orderId);
|
|
}
|
|
});
|
|
|
|
it('keeps an explicit order PO when creating a linked order', function (): void {
|
|
api_test_covers('POST /orders', 'happy');
|
|
|
|
$customer = api_fixtures()->createUser(['display_name' => 'Explicit PO Customer']);
|
|
$department = api_fixtures()->createDepartment();
|
|
api_fixtures()->createInvoiceCollection([
|
|
'customer_number' => $customer['customer_number'],
|
|
]);
|
|
$booking = api_fixtures()->createOrderBooking([
|
|
'customer_number' => $customer['customer_number'],
|
|
'department' => $department['id'],
|
|
'po' => 'BOOKING-PO-IGNORED',
|
|
]);
|
|
$session = api_fixtures()->createUserSession(['add_order']);
|
|
|
|
$response = api_client()->post('/orders', [
|
|
'customer_id' => $customer['customer_number'],
|
|
'department_id' => $department['id'],
|
|
'reference' => 'EXPLICIT-PO-CREATE',
|
|
'notes' => 'Created with explicit PO',
|
|
'po' => 'ORDER-PO-CREATE',
|
|
'booking_id' => $booking['id'],
|
|
'reg_1' => 'EXPO123',
|
|
], $session['headers']);
|
|
|
|
$response
|
|
->assertStatus(200)
|
|
->assertEnvelope()
|
|
->assertSuccess();
|
|
|
|
$orderId = (int)($response->data()['id'] ?? 0);
|
|
expect($orderId)->toBeGreaterThan(0);
|
|
|
|
$row = api_fixtures()->fetchRowById('orders', $orderId);
|
|
expect($row)->not->toBeNull();
|
|
expect((int)($row['booking_id'] ?? 0))->toBe((int)$booking['id']);
|
|
expect($row['po'] ?? null)->toBe('ORDER-PO-CREATE');
|
|
|
|
api_fixtures()->cleanupDeleteById('orders', $orderId);
|
|
});
|
|
|
|
it('rejects invalid order creation requests', function (): void {
|
|
api_test_covers('POST /orders', 'failure');
|
|
|
|
$customer = api_fixtures()->createUser();
|
|
$department = api_fixtures()->createDepartment();
|
|
$session = api_fixtures()->createUserSession(['add_order']);
|
|
|
|
$missingReference = api_client()->post('/orders', [
|
|
'customer_id' => $customer['customer_number'],
|
|
'department_id' => $department['id'],
|
|
'notes' => 'Missing reference',
|
|
'reg_1' => 'MISSREF',
|
|
], $session['headers']);
|
|
|
|
$missingReference
|
|
->assertStatus(400)
|
|
->assertEnvelope()
|
|
->assertSuccess(false)
|
|
->assertMessage('Reference is required');
|
|
|
|
$invalidDepartment = api_client()->post('/orders', [
|
|
'customer_id' => $customer['customer_number'],
|
|
'department_id' => 999999,
|
|
'reference' => 'BAD-DEPT',
|
|
'notes' => 'Bad department',
|
|
'reg_1' => 'BADDEPT',
|
|
], $session['headers']);
|
|
|
|
$invalidDepartment
|
|
->assertStatus(500)
|
|
->assertEnvelope()
|
|
->assertSuccess(false)
|
|
->assertMessageContains('getDepartmentById()');
|
|
|
|
$invalidCustomer = api_client()->post('/orders', [
|
|
'customer_id' => 999999,
|
|
'department_id' => $department['id'],
|
|
'reference' => 'BAD-CUSTOMER',
|
|
'notes' => 'Bad customer',
|
|
'reg_1' => 'BADCUST',
|
|
], $session['headers']);
|
|
|
|
$invalidCustomer
|
|
->assertStatus(500)
|
|
->assertEnvelope()
|
|
->assertSuccess(false)
|
|
->assertMessageContains('e-conomic request failed with HTTP');
|
|
|
|
api_fixtures()->addCustomerAttribute((int)$customer['id'], 'requiresReferenceNumber');
|
|
$missingRequiredReference = api_client()->post('/orders', [
|
|
'customer_id' => $customer['customer_number'],
|
|
'department_id' => $department['id'],
|
|
'reference' => '',
|
|
'notes' => 'Empty reference',
|
|
'reg_1' => 'REQREF1',
|
|
], $session['headers']);
|
|
|
|
$missingRequiredReference
|
|
->assertStatus(400)
|
|
->assertEnvelope()
|
|
->assertSuccess(false)
|
|
->assertMessage('Reference is required by the customer');
|
|
});
|
|
|
|
it('updates orders through the primary endpoint', function (): void {
|
|
api_test_covers('PUT /orders', 'happy');
|
|
|
|
$department = api_fixtures()->createDepartment(['name' => 'Original Department']);
|
|
$updatedDepartment = api_fixtures()->createDepartment(['name' => 'Updated Department']);
|
|
$customer = api_fixtures()->createUser(['display_name' => 'Original Customer']);
|
|
$updatedCustomer = api_fixtures()->createUser(['display_name' => 'Updated Customer']);
|
|
$cashier = api_fixtures()->createUser(['display_name' => 'Order Editor']);
|
|
$updatedInvoiceCollection = api_fixtures()->createInvoiceCollection([
|
|
'customer_number' => $updatedCustomer['customer_number'],
|
|
]);
|
|
$order = api_fixtures()->createOrder([
|
|
'customer_id' => $customer['customer_number'],
|
|
'cashier_id' => $cashier['id'],
|
|
'department_id' => $department['id'],
|
|
'reference' => 'BEFORE-REF',
|
|
'reg_1' => 'BEFR123',
|
|
'reg_2' => 'OLD-2',
|
|
'reg_3' => 'OLD-3',
|
|
'notes' => 'Before update',
|
|
'po' => 'PO-BEFORE',
|
|
'lane' => 2,
|
|
'wash_id' => 'WASH-BEFORE',
|
|
'booking_id' => 321,
|
|
'safety_seal' => 'SEAL-BEFORE',
|
|
'include_in_invoice' => true,
|
|
'created_at' => '2026-04-08 08:44:07',
|
|
]);
|
|
$session = api_fixtures()->createUserSession(['edit_order']);
|
|
|
|
$response = api_client()->put('/orders', [
|
|
'id' => $order['id'],
|
|
'customer_id' => $updatedCustomer['customer_number'],
|
|
'department_id' => $updatedDepartment['id'],
|
|
'reference' => 'AFTER-REF',
|
|
'reg_1' => ' af-tr 123 ',
|
|
'reg_2' => ' new-2 ',
|
|
'reg_3' => ' new/3 ',
|
|
'notes' => 'After update',
|
|
'po' => 'PO-123',
|
|
'lane' => 7,
|
|
'wash_id' => 'WASH-123',
|
|
'booking_id' => 9876,
|
|
'safety_seal' => 'SEAL-AFTER',
|
|
'invoice_collection_id' => $updatedInvoiceCollection['id'],
|
|
'created_at' => '2026-04-09 13:37:00',
|
|
'include_in_invoice' => false,
|
|
], $session['headers']);
|
|
|
|
$response
|
|
->assertStatus(200)
|
|
->assertEnvelope()
|
|
->assertSuccess()
|
|
->assertMessage('Order updated successfully');
|
|
|
|
$row = api_fixtures()->fetchRowById('orders', (int)$order['id']);
|
|
|
|
expect($row)->not->toBeNull();
|
|
expect((int)($row['customer_id'] ?? 0))->toBe((int)$updatedCustomer['customer_number']);
|
|
expect((int)($row['department_id'] ?? 0))->toBe((int)$updatedDepartment['id']);
|
|
expect($row['reference'] ?? null)->toBe('AFTER-REF');
|
|
expect($row['reg_1'] ?? null)->toBe('AFTR123');
|
|
expect($row['reg_2'] ?? null)->toBe('NEW2');
|
|
expect($row['reg_3'] ?? null)->toBe('NEW3');
|
|
expect($row['notes'] ?? null)->toBe('After update');
|
|
expect($row['po'] ?? null)->toBe('PO-123');
|
|
expect((int)($row['lane'] ?? 0))->toBe(7);
|
|
expect($row['wash_id'] ?? null)->toBe('WASH-123');
|
|
expect((int)($row['booking_id'] ?? 0))->toBe(9876);
|
|
expect($row['safety_seal'] ?? null)->toBe('SEAL-AFTER');
|
|
expect((int)($row['invoice_collection_id'] ?? 0))->toBe((int)$updatedInvoiceCollection['id']);
|
|
expect($row['created_at'] ?? null)->toBe('2026-04-09 13:37:00');
|
|
expect((int)($row['include_in_invoice'] ?? 1))->toBe(0);
|
|
});
|
|
|
|
it('defaults blank order PO from a linked booking on order updates', function (): void {
|
|
api_test_covers('PUT /orders', 'happy');
|
|
api_test_covers('PUT /order', 'happy');
|
|
|
|
$department = api_fixtures()->createDepartment(['name' => 'Booking PO Update Department']);
|
|
$customer = api_fixtures()->createUser(['display_name' => 'Booking PO Update Customer']);
|
|
$cashier = api_fixtures()->createUser(['display_name' => 'Booking PO Update Cashier']);
|
|
$booking = api_fixtures()->createOrderBooking([
|
|
'customer_number' => $customer['customer_number'],
|
|
'department' => $department['id'],
|
|
'po' => 'BOOKING-PO-LINK',
|
|
]);
|
|
$session = api_fixtures()->createUserSession(['edit_order']);
|
|
|
|
foreach (['/orders', '/order'] as $endpoint) {
|
|
$blankOrder = api_fixtures()->createOrder([
|
|
'customer_id' => $customer['customer_number'],
|
|
'cashier_id' => $cashier['id'],
|
|
'department_id' => $department['id'],
|
|
'reference' => 'BLANK-' . trim($endpoint, '/'),
|
|
'reg_1' => 'BLNK123',
|
|
'po' => '',
|
|
]);
|
|
$manualOrder = api_fixtures()->createOrder([
|
|
'customer_id' => $customer['customer_number'],
|
|
'cashier_id' => $cashier['id'],
|
|
'department_id' => $department['id'],
|
|
'reference' => 'MANUAL-' . trim($endpoint, '/'),
|
|
'reg_1' => 'MANU123',
|
|
'po' => 'MANUAL-PO',
|
|
]);
|
|
|
|
api_client()->put($endpoint, [
|
|
'id' => $blankOrder['id'],
|
|
'booking_id' => $booking['id'],
|
|
], $session['headers'])
|
|
->assertStatus(200)
|
|
->assertEnvelope()
|
|
->assertSuccess()
|
|
->assertMessage('Order updated successfully');
|
|
|
|
api_client()->put($endpoint, [
|
|
'id' => $manualOrder['id'],
|
|
'booking_id' => $booking['id'],
|
|
], $session['headers'])
|
|
->assertStatus(200)
|
|
->assertEnvelope()
|
|
->assertSuccess()
|
|
->assertMessage('Order updated successfully');
|
|
|
|
$blankRow = api_fixtures()->fetchRowById('orders', (int)$blankOrder['id']);
|
|
$manualRow = api_fixtures()->fetchRowById('orders', (int)$manualOrder['id']);
|
|
|
|
expect($blankRow)->not->toBeNull();
|
|
expect((int)($blankRow['booking_id'] ?? 0))->toBe((int)$booking['id']);
|
|
expect($blankRow['po'] ?? null)->toBe('BOOKING-PO-LINK');
|
|
|
|
expect($manualRow)->not->toBeNull();
|
|
expect((int)($manualRow['booking_id'] ?? 0))->toBe((int)$booking['id']);
|
|
expect($manualRow['po'] ?? null)->toBe('MANUAL-PO');
|
|
}
|
|
});
|
|
|
|
it('reassigns invoice collections when changing an order across the draft customer boundary', function (): void {
|
|
api_test_covers('PUT /orders', 'happy');
|
|
|
|
$draftCustomer = api_fixtures()->createUser(['display_name' => 'Draft Customer']);
|
|
$regularCustomer = api_fixtures()->createUser(['display_name' => 'Regular Customer']);
|
|
$department = api_fixtures()->createDepartment(['name' => 'Draft Boundary Department']);
|
|
$cashier = api_fixtures()->createUser(['display_name' => 'Draft Boundary Cashier']);
|
|
|
|
api_fixtures()->setModuleConfig('economic', 'transactionDraftCustomerNumber', (string)$draftCustomer['customer_number'], 'int');
|
|
|
|
$order = api_fixtures()->createOrder([
|
|
'customer_id' => $regularCustomer['customer_number'],
|
|
'cashier_id' => $cashier['id'],
|
|
'department_id' => $department['id'],
|
|
'reference' => 'DRAFT-BOUNDARY',
|
|
'reg_1' => 'DRAFT123',
|
|
]);
|
|
$session = api_fixtures()->createUserSession(['edit_order']);
|
|
|
|
api_client()->put('/orders', [
|
|
'id' => $order['id'],
|
|
'customer_id' => $draftCustomer['customer_number'],
|
|
], $session['headers'])
|
|
->assertStatus(200)
|
|
->assertEnvelope()
|
|
->assertSuccess()
|
|
->assertMessage('Order updated successfully');
|
|
|
|
$draftRow = api_fixtures()->fetchRowById('orders', (int)$order['id']);
|
|
expect($draftRow)->not->toBeNull();
|
|
$draftInvoiceCollectionId = (int)($draftRow['invoice_collection_id'] ?? 0);
|
|
expect($draftInvoiceCollectionId)->toBeGreaterThan(0);
|
|
|
|
$draftCollectionRow = api_fixtures()->fetchRowById('collected_order_invoices', $draftInvoiceCollectionId);
|
|
expect($draftCollectionRow)->not->toBeNull();
|
|
expect((int)($draftCollectionRow['customer_number'] ?? 0))->toBe((int)$draftCustomer['customer_number']);
|
|
|
|
api_client()->put('/orders', [
|
|
'id' => $order['id'],
|
|
'customer_id' => $regularCustomer['customer_number'],
|
|
], $session['headers'])
|
|
->assertStatus(200)
|
|
->assertEnvelope()
|
|
->assertSuccess()
|
|
->assertMessage('Order updated successfully');
|
|
|
|
$regularRow = api_fixtures()->fetchRowById('orders', (int)$order['id']);
|
|
expect($regularRow)->not->toBeNull();
|
|
$regularInvoiceCollectionId = (int)($regularRow['invoice_collection_id'] ?? 0);
|
|
expect($regularInvoiceCollectionId)->toBeGreaterThan(0);
|
|
|
|
$regularCollectionRow = api_fixtures()->fetchRowById('collected_order_invoices', $regularInvoiceCollectionId);
|
|
expect($regularCollectionRow)->not->toBeNull();
|
|
expect((int)($regularCollectionRow['customer_number'] ?? 0))->toBe((int)$regularCustomer['customer_number']);
|
|
});
|
|
|
|
it('regenerates attached wash certificates when certificate metadata changes through the primary endpoint', function (): void {
|
|
api_test_covers('PUT /orders', 'happy');
|
|
|
|
$department = api_fixtures()->createDepartment(['name' => 'Wash Certificate Department']);
|
|
$customer = api_fixtures()->createUser(['display_name' => 'Original Certificate Customer']);
|
|
$updatedCustomer = api_fixtures()->createUser(['display_name' => 'Updated Certificate Customer']);
|
|
$cashier = api_fixtures()->createUser(['display_name' => 'Certificate Cashier']);
|
|
$order = api_fixtures()->createOrder([
|
|
'customer_id' => $customer['customer_number'],
|
|
'cashier_id' => $cashier['id'],
|
|
'department_id' => $department['id'],
|
|
'reference' => 'CERT-REF',
|
|
'reg_1' => 'CERT123',
|
|
'reg_2' => 'TRAIL1',
|
|
'safety_seal' => 'SEAL-OLD',
|
|
'created_at' => '2026-04-12 10:15:00',
|
|
]);
|
|
$session = api_fixtures()->createUserSession([
|
|
'edit_order',
|
|
'add_order_attachments',
|
|
'department_access_' . $department['id'],
|
|
]);
|
|
|
|
api_client()->post('/orders/attachments/upload', [
|
|
'order_id' => $order['id'],
|
|
'base64_file' => base64_encode('wash-certificate'),
|
|
'file_name' => 'wash_certificate',
|
|
], $session['headers'])
|
|
->assertStatus(200)
|
|
->assertEnvelope()
|
|
->assertSuccess();
|
|
|
|
$initialAttachmentIds = activeWashCertificateAttachmentIdsForOrder((int)$order['id']);
|
|
expect($initialAttachmentIds)->toHaveCount(1);
|
|
|
|
$initialAttachmentId = $initialAttachmentIds[0];
|
|
$initialAttachmentRow = api_fixtures()->fetchRowById('object_attachments', $initialAttachmentId);
|
|
expect($initialAttachmentRow)->not->toBeNull();
|
|
$initialDocument = (json_decode((string)($initialAttachmentRow['content'] ?? ''), true) ?? [])['document'] ?? null;
|
|
|
|
api_client()->put('/orders', [
|
|
'id' => $order['id'],
|
|
'customer_id' => $updatedCustomer['customer_number'],
|
|
'reg_2' => ' new-trail-55 ',
|
|
'safety_seal' => 'SEAL-NEW',
|
|
], $session['headers'])
|
|
->assertStatus(200)
|
|
->assertEnvelope()
|
|
->assertSuccess()
|
|
->assertMessage('Order updated successfully');
|
|
|
|
$orderRow = api_fixtures()->fetchRowById('orders', (int)$order['id']);
|
|
expect($orderRow)->not->toBeNull();
|
|
expect((int)($orderRow['customer_id'] ?? 0))->toBe((int)$updatedCustomer['customer_number']);
|
|
expect($orderRow['reg_2'] ?? null)->toBe('NEWTRAIL55');
|
|
expect($orderRow['safety_seal'] ?? null)->toBe('SEAL-NEW');
|
|
|
|
$updatedAttachmentIds = activeWashCertificateAttachmentIdsForOrder((int)$order['id']);
|
|
expect($updatedAttachmentIds)->toHaveCount(1);
|
|
expect($updatedAttachmentIds)->not->toContain($initialAttachmentId);
|
|
|
|
$updatedAttachmentId = $updatedAttachmentIds[0];
|
|
$updatedAttachmentRow = api_fixtures()->fetchRowById('object_attachments', $updatedAttachmentId);
|
|
$deletedAttachmentRow = api_fixtures()->fetchRowById('object_attachments', $initialAttachmentId);
|
|
expect($updatedAttachmentRow)->not->toBeNull();
|
|
expect($deletedAttachmentRow)->not->toBeNull();
|
|
expect($deletedAttachmentRow['deleted_at'] ?? null)->not->toBeNull();
|
|
expect((json_decode((string)($updatedAttachmentRow['content'] ?? ''), true) ?? [])['document'] ?? null)->not->toBe($initialDocument);
|
|
});
|
|
|
|
it('regenerates attached wash certificates for legacy field-value updates through the alias endpoint', function (): void {
|
|
api_test_covers('PUT /order', 'happy');
|
|
|
|
$department = api_fixtures()->createDepartment(['name' => 'Alias Wash Certificate Department']);
|
|
$customer = api_fixtures()->createUser(['display_name' => 'Alias Certificate Customer']);
|
|
$cashier = api_fixtures()->createUser(['display_name' => 'Alias Certificate Cashier']);
|
|
$order = api_fixtures()->createOrder([
|
|
'customer_id' => $customer['customer_number'],
|
|
'cashier_id' => $cashier['id'],
|
|
'department_id' => $department['id'],
|
|
'reference' => 'ALIAS-CERT',
|
|
'reg_1' => 'ALIAS123',
|
|
'safety_seal' => 'ALIAS-SEAL',
|
|
]);
|
|
$session = api_fixtures()->createUserSession([
|
|
'edit_order',
|
|
'add_order_attachments',
|
|
'department_access_' . $department['id'],
|
|
]);
|
|
|
|
api_client()->post('/orders/attachments/upload', [
|
|
'order_id' => $order['id'],
|
|
'base64_file' => base64_encode('wash-certificate'),
|
|
'file_name' => 'wash_certificate',
|
|
], $session['headers'])
|
|
->assertStatus(200)
|
|
->assertEnvelope()
|
|
->assertSuccess();
|
|
|
|
$initialAttachmentIds = activeWashCertificateAttachmentIdsForOrder((int)$order['id']);
|
|
expect($initialAttachmentIds)->toHaveCount(1);
|
|
|
|
$initialAttachmentId = $initialAttachmentIds[0];
|
|
|
|
api_client()->put('/order', [
|
|
'id' => $order['id'],
|
|
'field' => 'reg_1',
|
|
'value' => ' zz-88 11 ',
|
|
], $session['headers'])
|
|
->assertStatus(200)
|
|
->assertEnvelope()
|
|
->assertSuccess()
|
|
->assertMessage('Order updated successfully');
|
|
|
|
$orderRow = api_fixtures()->fetchRowById('orders', (int)$order['id']);
|
|
expect($orderRow)->not->toBeNull();
|
|
expect($orderRow['reg_1'] ?? null)->toBe('ZZ8811');
|
|
|
|
$updatedAttachmentIds = activeWashCertificateAttachmentIdsForOrder((int)$order['id']);
|
|
expect($updatedAttachmentIds)->toHaveCount(1);
|
|
expect($updatedAttachmentIds)->not->toContain($initialAttachmentId);
|
|
|
|
$deletedAttachmentRow = api_fixtures()->fetchRowById('object_attachments', $initialAttachmentId);
|
|
|
|
expect($deletedAttachmentRow)->not->toBeNull();
|
|
expect($deletedAttachmentRow['deleted_at'] ?? null)->not->toBeNull();
|
|
});
|
|
|
|
it('supports legacy field-value metadata updates through the primary endpoint', function (): void {
|
|
api_test_covers('PUT /orders', 'happy');
|
|
|
|
$department = api_fixtures()->createDepartment(['name' => 'Legacy Field Department']);
|
|
$customer = api_fixtures()->createUser(['display_name' => 'Legacy Field Customer']);
|
|
$cashier = api_fixtures()->createUser(['display_name' => 'Legacy Field Cashier']);
|
|
$order = api_fixtures()->createOrder([
|
|
'customer_id' => $customer['customer_number'],
|
|
'cashier_id' => $cashier['id'],
|
|
'department_id' => $department['id'],
|
|
'reference' => 'START-REF',
|
|
'reg_1' => 'START123',
|
|
'reg_2' => 'START2',
|
|
'reg_3' => 'START3',
|
|
'notes' => 'Start note',
|
|
]);
|
|
$session = api_fixtures()->createUserSession(['edit_order']);
|
|
|
|
$payloads = [
|
|
['field' => 'reference', 'value' => 'LEGACY-REF'],
|
|
['field' => 'notes', 'value' => 'Legacy note'],
|
|
['field' => 'safety_seal', 'value' => 'LEGACY-SEAL'],
|
|
['field' => 'reg_1', 'value' => ' ab-12 34 '],
|
|
['field' => 'reg_2', 'value' => ' cd/56 78 '],
|
|
['field' => 'reg_3', 'value' => ' ef_90 12 '],
|
|
];
|
|
|
|
foreach ($payloads as $payload) {
|
|
api_client()->put('/orders', [
|
|
'id' => $order['id'],
|
|
...$payload,
|
|
], $session['headers'])
|
|
->assertStatus(200)
|
|
->assertEnvelope()
|
|
->assertSuccess()
|
|
->assertMessage('Order updated successfully');
|
|
}
|
|
|
|
$row = api_fixtures()->fetchRowById('orders', (int)$order['id']);
|
|
|
|
expect($row)->not->toBeNull();
|
|
expect($row['reference'] ?? null)->toBe('LEGACY-REF');
|
|
expect($row['notes'] ?? null)->toBe('Legacy note');
|
|
expect($row['safety_seal'] ?? null)->toBe('LEGACY-SEAL');
|
|
expect($row['reg_1'] ?? null)->toBe('AB1234');
|
|
expect($row['reg_2'] ?? null)->toBe('CD5678');
|
|
expect($row['reg_3'] ?? null)->toBe('EF9012');
|
|
});
|
|
|
|
it('rejects invalid updates through the primary order endpoint', function (): void {
|
|
api_test_covers('PUT /orders', 'failure');
|
|
|
|
$session = api_fixtures()->createUserSession(['edit_order']);
|
|
|
|
$missingId = api_client()->put('/orders', [
|
|
'notes' => 'No id',
|
|
], $session['headers']);
|
|
|
|
$missingId
|
|
->assertStatus(400)
|
|
->assertEnvelope()
|
|
->assertSuccess(false)
|
|
->assertMessage('ID is required');
|
|
|
|
$missingOrder = api_client()->put('/orders', [
|
|
'id' => 999999,
|
|
'notes' => 'Missing order',
|
|
], $session['headers']);
|
|
|
|
$missingOrder
|
|
->assertStatus(500)
|
|
->assertEnvelope()
|
|
->assertSuccess(false)
|
|
->assertMessageContains('must not be accessed before initialization');
|
|
});
|
|
|
|
it('updates orders through the legacy alias endpoint', function (): void {
|
|
api_test_covers('PUT /order', 'happy');
|
|
|
|
$department = api_fixtures()->createDepartment(['name' => 'Legacy Department']);
|
|
$updatedDepartment = api_fixtures()->createDepartment(['name' => 'Legacy Updated Department']);
|
|
$customer = api_fixtures()->createUser(['display_name' => 'Legacy Original Customer']);
|
|
$updatedCustomer = api_fixtures()->createUser(['display_name' => 'Legacy Updated Customer']);
|
|
$cashier = api_fixtures()->createUser(['display_name' => 'Legacy Editor']);
|
|
$updatedInvoiceCollection = api_fixtures()->createInvoiceCollection([
|
|
'customer_number' => $updatedCustomer['customer_number'],
|
|
]);
|
|
$order = api_fixtures()->createOrder([
|
|
'customer_id' => $customer['customer_number'],
|
|
'cashier_id' => $cashier['id'],
|
|
'department_id' => $department['id'],
|
|
'reference' => 'LEGACY-BEFORE',
|
|
'reg_1' => 'LGCY123',
|
|
'reg_2' => 'LGCY-2',
|
|
'reg_3' => 'LGCY-3',
|
|
'notes' => 'Legacy before',
|
|
'po' => 'LEGACY-PO',
|
|
'lane' => 4,
|
|
'wash_id' => 'LEGACY-WASH',
|
|
'booking_id' => 654,
|
|
'safety_seal' => 'LEGACY-SEAL-BEFORE',
|
|
'include_in_invoice' => false,
|
|
'created_at' => '2026-04-10 08:15:00',
|
|
]);
|
|
$session = api_fixtures()->createUserSession(['edit_order']);
|
|
|
|
$response = api_client()->put('/order', [
|
|
'id' => $order['id'],
|
|
'customer_id' => $updatedCustomer['customer_number'],
|
|
'department_id' => $updatedDepartment['id'],
|
|
'reference' => 'LEGACY-AFTER',
|
|
'reg_1' => ' lgcy-999 ',
|
|
'reg_2' => ' leg-2 ',
|
|
'reg_3' => ' leg/3 ',
|
|
'notes' => 'Legacy after',
|
|
'po' => 'LEGACY-PO-NEW',
|
|
'lane' => 9,
|
|
'wash_id' => 'LEGACY-WASH-NEW',
|
|
'booking_id' => 7654,
|
|
'safety_seal' => 'LEGACY-SEAL-AFTER',
|
|
'invoice_collection_id' => $updatedInvoiceCollection['id'],
|
|
'created_at' => '2026-04-11 11:22:33',
|
|
'include_in_invoice' => true,
|
|
], $session['headers']);
|
|
|
|
$response
|
|
->assertStatus(200)
|
|
->assertEnvelope()
|
|
->assertSuccess()
|
|
->assertMessage('Order updated successfully');
|
|
|
|
$row = api_fixtures()->fetchRowById('orders', (int)$order['id']);
|
|
|
|
expect($row)->not->toBeNull();
|
|
expect((int)($row['customer_id'] ?? 0))->toBe((int)$updatedCustomer['customer_number']);
|
|
expect((int)($row['department_id'] ?? 0))->toBe((int)$updatedDepartment['id']);
|
|
expect($row['reference'] ?? null)->toBe('LEGACY-AFTER');
|
|
expect($row['reg_1'] ?? null)->toBe('LGCY999');
|
|
expect($row['reg_2'] ?? null)->toBe('LEG2');
|
|
expect($row['reg_3'] ?? null)->toBe('LEG3');
|
|
expect($row['notes'] ?? null)->toBe('Legacy after');
|
|
expect($row['po'] ?? null)->toBe('LEGACY-PO-NEW');
|
|
expect((int)($row['lane'] ?? 0))->toBe(9);
|
|
expect($row['wash_id'] ?? null)->toBe('LEGACY-WASH-NEW');
|
|
expect((int)($row['booking_id'] ?? 0))->toBe(7654);
|
|
expect($row['safety_seal'] ?? null)->toBe('LEGACY-SEAL-AFTER');
|
|
expect((int)($row['invoice_collection_id'] ?? 0))->toBe((int)$updatedInvoiceCollection['id']);
|
|
expect($row['created_at'] ?? null)->toBe('2026-04-11 11:22:33');
|
|
expect((int)($row['include_in_invoice'] ?? 0))->toBe(1);
|
|
});
|
|
|
|
it('supports legacy field-value metadata updates through the alias endpoint', function (): void {
|
|
api_test_covers('PUT /order', 'happy');
|
|
|
|
$department = api_fixtures()->createDepartment(['name' => 'Alias Field Department']);
|
|
$customer = api_fixtures()->createUser(['display_name' => 'Alias Field Customer']);
|
|
$cashier = api_fixtures()->createUser(['display_name' => 'Alias Field Cashier']);
|
|
$order = api_fixtures()->createOrder([
|
|
'customer_id' => $customer['customer_number'],
|
|
'cashier_id' => $cashier['id'],
|
|
'department_id' => $department['id'],
|
|
'reference' => 'ALIAS-START',
|
|
'reg_1' => 'ALIAS123',
|
|
'reg_2' => 'ALIAS2',
|
|
'reg_3' => 'ALIAS3',
|
|
'notes' => 'Alias note',
|
|
]);
|
|
$session = api_fixtures()->createUserSession(['edit_order']);
|
|
|
|
$payloads = [
|
|
['field' => 'reference', 'value' => 'ALIAS-REF'],
|
|
['field' => 'notes', 'value' => 'Alias updated note'],
|
|
['field' => 'safety_seal', 'value' => 'ALIAS-SEAL'],
|
|
['field' => 'reg_1', 'value' => ' gh-12 34 '],
|
|
['field' => 'reg_2', 'value' => ' ij/56 78 '],
|
|
['field' => 'reg_3', 'value' => ' kl_90 12 '],
|
|
];
|
|
|
|
foreach ($payloads as $payload) {
|
|
api_client()->put('/order', [
|
|
'id' => $order['id'],
|
|
...$payload,
|
|
], $session['headers'])
|
|
->assertStatus(200)
|
|
->assertEnvelope()
|
|
->assertSuccess()
|
|
->assertMessage('Order updated successfully');
|
|
}
|
|
|
|
$row = api_fixtures()->fetchRowById('orders', (int)$order['id']);
|
|
|
|
expect($row)->not->toBeNull();
|
|
expect($row['reference'] ?? null)->toBe('ALIAS-REF');
|
|
expect($row['notes'] ?? null)->toBe('Alias updated note');
|
|
expect($row['safety_seal'] ?? null)->toBe('ALIAS-SEAL');
|
|
expect($row['reg_1'] ?? null)->toBe('GH1234');
|
|
expect($row['reg_2'] ?? null)->toBe('IJ5678');
|
|
expect($row['reg_3'] ?? null)->toBe('KL9012');
|
|
});
|
|
|
|
it('rejects invalid updates through the legacy alias endpoint', function (): void {
|
|
api_test_covers('PUT /order', 'failure');
|
|
|
|
$session = api_fixtures()->createUserSession(['edit_order']);
|
|
|
|
$response = api_client()->put('/order', [
|
|
'id' => 999999,
|
|
'notes' => 'Missing alias order',
|
|
], $session['headers']);
|
|
|
|
$response
|
|
->assertStatus(500)
|
|
->assertEnvelope()
|
|
->assertSuccess(false)
|
|
->assertMessageContains('must not be accessed before initialization');
|
|
});
|
|
|
|
it('rejects unsupported legacy field-value updates on both update endpoints', function (): void {
|
|
api_test_covers('PUT /orders', 'failure');
|
|
|
|
$department = api_fixtures()->createDepartment(['name' => 'Unsupported Field Department']);
|
|
$customer = api_fixtures()->createUser(['display_name' => 'Unsupported Field Customer']);
|
|
$cashier = api_fixtures()->createUser(['display_name' => 'Unsupported Field Cashier']);
|
|
$order = api_fixtures()->createOrder([
|
|
'customer_id' => $customer['customer_number'],
|
|
'cashier_id' => $cashier['id'],
|
|
'department_id' => $department['id'],
|
|
'reference' => 'UNCHANGED-REF',
|
|
'notes' => 'Unchanged note',
|
|
'reg_1' => 'UNCH123',
|
|
]);
|
|
$session = api_fixtures()->createUserSession(['edit_order']);
|
|
|
|
foreach (['/orders', '/order'] as $endpoint) {
|
|
api_client()->put($endpoint, [
|
|
'id' => $order['id'],
|
|
'field' => 'cashier_id',
|
|
'value' => 999999,
|
|
], $session['headers'])
|
|
->assertStatus(400)
|
|
->assertEnvelope()
|
|
->assertSuccess(false)
|
|
->assertMessage('Unsupported legacy order field: cashier_id');
|
|
}
|
|
|
|
$row = api_fixtures()->fetchRowById('orders', (int)$order['id']);
|
|
|
|
expect($row)->not->toBeNull();
|
|
expect($row['reference'] ?? null)->toBe('UNCHANGED-REF');
|
|
expect($row['notes'] ?? null)->toBe('Unchanged note');
|
|
expect($row['reg_1'] ?? null)->toBe('UNCH123');
|
|
});
|
|
|
|
it('deletes orders through the real endpoint', function (): void {
|
|
api_test_covers('DELETE /orders', 'happy');
|
|
|
|
$department = api_fixtures()->createDepartment();
|
|
$customer = api_fixtures()->createUser();
|
|
$cashier = api_fixtures()->createUser(['display_name' => 'Delete Cashier']);
|
|
$order = api_fixtures()->createOrder([
|
|
'customer_id' => $customer['customer_number'],
|
|
'cashier_id' => $cashier['id'],
|
|
'department_id' => $department['id'],
|
|
]);
|
|
$session = api_fixtures()->createUserSession([
|
|
'delete_order',
|
|
'department_access_' . $department['id'],
|
|
]);
|
|
|
|
$response = api_client()->delete('/orders', [
|
|
'id' => $order['id'],
|
|
], $session['headers']);
|
|
|
|
$response
|
|
->assertStatus(200)
|
|
->assertEnvelope()
|
|
->assertSuccess()
|
|
->assertMessage('Order deleted successfully');
|
|
|
|
$row = api_fixtures()->fetchRowById('orders', (int)$order['id']);
|
|
expect($row)->not->toBeNull();
|
|
expect($row['deleted_at'] ?? null)->not->toBeNull();
|
|
});
|
|
|
|
it('requires explicit confirmation before deleting protected orders', function (): void {
|
|
api_test_covers('DELETE /orders', 'failure');
|
|
api_test_covers('DELETE /orders', 'happy');
|
|
|
|
$department = api_fixtures()->createDepartment();
|
|
$customer = api_fixtures()->createUser();
|
|
$cashier = api_fixtures()->createUser(['display_name' => 'Protected Delete Cashier']);
|
|
$session = api_fixtures()->createUserSession([
|
|
'delete_order',
|
|
'department_access_' . $department['id'],
|
|
]);
|
|
|
|
$cases = [
|
|
'completed' => function () use ($department, $customer, $cashier): array {
|
|
return api_fixtures()->createOrder([
|
|
'customer_id' => $customer['customer_number'],
|
|
'cashier_id' => $cashier['id'],
|
|
'department_id' => $department['id'],
|
|
'completed_at' => date('Y-m-d H:i:s'),
|
|
]);
|
|
},
|
|
'order_items' => function () use ($department, $customer, $cashier): array {
|
|
$order = api_fixtures()->createOrder([
|
|
'customer_id' => $customer['customer_number'],
|
|
'cashier_id' => $cashier['id'],
|
|
'department_id' => $department['id'],
|
|
]);
|
|
api_fixtures()->createOrderItem([
|
|
'order_id' => $order['id'],
|
|
'product_id' => 53,
|
|
'cashier_id' => $cashier['id'],
|
|
'quantity' => 1,
|
|
]);
|
|
return $order;
|
|
},
|
|
'attachments' => function () use ($department, $customer, $cashier): array {
|
|
$order = api_fixtures()->createOrder([
|
|
'customer_id' => $customer['customer_number'],
|
|
'cashier_id' => $cashier['id'],
|
|
'department_id' => $department['id'],
|
|
]);
|
|
api_fixtures()->createOrderAttachment([
|
|
'order_id' => $order['id'],
|
|
]);
|
|
return $order;
|
|
},
|
|
];
|
|
|
|
foreach ($cases as $expectedReason => $createProtectedOrder) {
|
|
$order = $createProtectedOrder();
|
|
|
|
$unconfirmed = api_client()->delete('/orders', [
|
|
'id' => $order['id'],
|
|
], $session['headers']);
|
|
|
|
$unconfirmed
|
|
->assertStatus(409)
|
|
->assertEnvelope()
|
|
->assertSuccess(false)
|
|
->assertMessage('Order deletion requires confirmation');
|
|
|
|
expect($unconfirmed->data()['requires_confirmation'] ?? null)->toBeTrue();
|
|
expect($unconfirmed->data()['protected_reasons'] ?? [])->toContain($expectedReason);
|
|
expect(api_fixtures()->fetchRowById('orders', (int)$order['id'])['deleted_at'] ?? null)->toBeNull();
|
|
|
|
$confirmed = api_client()->delete('/orders', [
|
|
'id' => $order['id'],
|
|
'confirmed' => 'true',
|
|
], $session['headers']);
|
|
|
|
$confirmed
|
|
->assertStatus(200)
|
|
->assertEnvelope()
|
|
->assertSuccess()
|
|
->assertMessage('Order deleted successfully');
|
|
|
|
expect(api_fixtures()->fetchRowById('orders', (int)$order['id'])['deleted_at'] ?? null)->not->toBeNull();
|
|
}
|
|
});
|
|
|
|
it('rejects invalid order delete requests', function (): void {
|
|
api_test_covers('DELETE /orders', 'failure');
|
|
|
|
$department = api_fixtures()->createDepartment();
|
|
$session = api_fixtures()->createUserSession([
|
|
'delete_order',
|
|
'department_access_' . $department['id'],
|
|
]);
|
|
|
|
$missingId = api_client()->delete('/orders', [], $session['headers']);
|
|
|
|
$missingId
|
|
->assertStatus(400)
|
|
->assertEnvelope()
|
|
->assertSuccess(false)
|
|
->assertMessage('ID is required');
|
|
|
|
$missingOrder = api_client()->delete('/orders', [
|
|
'id' => 999999,
|
|
], $session['headers']);
|
|
|
|
$missingOrder
|
|
->assertStatus(400)
|
|
->assertEnvelope()
|
|
->assertSuccess(false)
|
|
->assertMessage('Order not found');
|
|
});
|