| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848 |
- <?php
- namespace Webkul\BagistoApi\Tests\Feature\GraphQL;
- use Webkul\BagistoApi\Tests\GraphQLTestCase;
- use Webkul\Core\Models\Channel;
- use Webkul\Customer\Models\Customer;
- use Webkul\Product\Models\Product;
- use Webkul\Sales\Models\Order;
- use Webkul\Sales\Models\OrderAddress;
- use Webkul\Sales\Models\OrderItem;
- use Webkul\Sales\Models\OrderPayment;
- use Webkul\Sales\Models\Shipment;
- use Webkul\Sales\Models\ShipmentItem;
- class CustomerOrderShipmentTest extends GraphQLTestCase
- {
- /**
- * Create test data — customer with order and shipments
- */
- private function createTestData(): array
- {
- $this->seedRequiredData();
- $customer = $this->createCustomer();
- $channel = Channel::first();
- $product = Product::factory()->create();
- $order = Order::factory()->create([
- 'customer_id' => $customer->id,
- 'customer_email' => $customer->email,
- 'customer_first_name' => $customer->first_name,
- 'customer_last_name' => $customer->last_name,
- 'channel_id' => $channel->id,
- 'status' => 'completed',
- 'shipping_title' => 'Flat Rate - Flat Rate',
- ]);
- $orderItem = OrderItem::factory()->create([
- 'order_id' => $order->id,
- 'product_id' => $product->id,
- 'sku' => 'TEST-SHIP-SKU-001',
- 'type' => 'simple',
- 'name' => 'Test Shipment Product',
- 'qty_ordered' => 3,
- ]);
- OrderPayment::factory()->create([
- 'order_id' => $order->id,
- 'method' => 'money_transfer',
- 'method_title' => 'Money Transfer',
- ]);
- /** Create shipping address */
- $shippingAddress = OrderAddress::factory()->create([
- 'order_id' => $order->id,
- 'address_type' => 'shipping',
- 'first_name' => 'John',
- 'last_name' => 'Doe',
- 'email' => $customer->email,
- 'phone' => '+1-555-0123',
- 'address' => '123 Main St',
- 'city' => 'Springfield',
- 'state' => 'IL',
- 'country' => 'US',
- 'postcode' => '62701',
- ]);
- /** Create first shipment (partial) */
- $shipment1 = Shipment::factory()->create([
- 'order_id' => $order->id,
- 'order_address_id' => $shippingAddress->id,
- 'customer_id' => $customer->id,
- 'customer_type' => Customer::class,
- 'status' => 'shipped',
- 'total_qty' => 2,
- 'total_weight' => 10.5,
- 'carrier_code' => 'flat_rate',
- 'carrier_title' => 'Flat Rate',
- 'track_number' => 'TRACK123456789',
- 'email_sent' => true,
- ]);
- ShipmentItem::create([
- 'shipment_id' => $shipment1->id,
- 'order_item_id' => $orderItem->id,
- 'name' => 'Test Shipment Product',
- 'sku' => 'TEST-SHIP-SKU-001',
- 'qty' => 2,
- 'weight' => 10.5,
- ]);
- /** Create second shipment (remainder) */
- $shipment2 = Shipment::factory()->create([
- 'order_id' => $order->id,
- 'order_address_id' => $shippingAddress->id,
- 'customer_id' => $customer->id,
- 'customer_type' => Customer::class,
- 'status' => 'pending',
- 'total_qty' => 1,
- 'total_weight' => 5.25,
- 'carrier_code' => 'flat_rate',
- 'carrier_title' => 'Flat Rate',
- 'track_number' => null,
- 'email_sent' => false,
- ]);
- ShipmentItem::create([
- 'shipment_id' => $shipment2->id,
- 'order_item_id' => $orderItem->id,
- 'name' => 'Test Shipment Product',
- 'sku' => 'TEST-SHIP-SKU-001',
- 'qty' => 1,
- 'weight' => 5.25,
- ]);
- return compact(
- 'customer',
- 'channel',
- 'product',
- 'order',
- 'orderItem',
- 'shippingAddress',
- 'shipment1',
- 'shipment2'
- );
- }
- // ── Shipments in Order Query ──────────────────────────────
- /**
- * Test: Query order with shipments collection
- */
- public function test_query_order_includes_shipments(): void
- {
- $testData = $this->createTestData();
- $orderId = "/api/shop/customer-orders/{$testData['order']->id}";
- dump($testData);
- $query = <<<GQL
- query getCustomerOrder {
- customerOrder(id: "{$orderId}") {
- _id
- incrementId
- shipments {
- edges {
- node {
- _id
- status
- totalQty
- carrierTitle
- trackNumber
- }
- }
- }
- }
- }
- GQL;
- $response = $this->graphQL($query);
- $response->assertOk();
- $data = $response->json('data.customerOrder');
- $errors = $response->json('errors');
- // For debugging: print any errors
- if ($errors) {
- dump($errors);
- }
- expect($data['incrementId'])->toBeTruthy();
- expect($data['shipments']['edges'])->toHaveCount(2);
- $edges = $data['shipments']['edges'];
-
- /** First shipment should be shipped */
- expect($edges[0]['node']['status'])->toBe('shipped');
- expect($edges[0]['node']['totalQty'])->toBe(2);
- expect($edges[0]['node']['carrierTitle'])->toBe('Flat Rate');
- expect($edges[0]['node']['trackNumber'])->toBe('TRACK123456789');
- /** Second shipment should be pending */
- expect($edges[1]['node']['status'])->toBe('pending');
- expect($edges[1]['node']['totalQty'])->toBe(1);
- expect($edges[1]['node']['trackNumber'])->toBeNull();
- }
- /**
- * Test: Query order shipments includes items
- */
- public function test_query_shipments_includes_items(): void
- {
- $testData = $this->createTestData();
- $orderId = "/api/shop/customer-orders/{$testData['order']->id}";
- $query = <<<GQL
- query getCustomerOrder {
- customerOrder(id: "{$orderId}") {
- _id
- shipments {
- _id
- status
- items {
- _id
- sku
- name
- qty
- weight
- }
- }
- }
- }
- GQL;
- $response = $this->graphQL($query);
- $response->assertOk();
- $data = $response->json('data.customerOrder');
- expect($data['shipments'])->toHaveCount(2);
- /** First shipment items */
- expect($data['shipments'][0]['items'])->toHaveCount(1);
- $item1 = $data['shipments'][0]['items'][0];
- expect($item1['sku'])->toBe('TEST-SHIP-SKU-001');
- expect($item1['name'])->toBe('Test Shipment Product');
- expect($item1['qty'])->toBe(2);
- expect($item1['weight'])->toBe(10.5);
- /** Second shipment items */
- expect($data['shipments'][1]['items'])->toHaveCount(1);
- $item2 = $data['shipments'][1]['items'][0];
- expect($item2['sku'])->toBe('TEST-SHIP-SKU-001');
- expect($item2['qty'])->toBe(1);
- expect($item2['weight'])->toBe(5.25);
- }
- /**
- * Test: Query shipments includes shipping address
- */
- public function test_query_shipments_includes_shipping_address(): void
- {
- $testData = $this->createTestData();
- $orderId = "/api/shop/customer-orders/{$testData['order']->id}";
- $query = <<<GQL
- query getCustomerOrder {
- customerOrder(id: "{$orderId}") {
- _id
- shipments {
- _id
- shippingAddress {
- _id
- firstName
- lastName
- email
- street
- city
- state
- postcode
- phone
- }
- }
- }
- }
- GQL;
- $response = $this->graphQL($query);
- $response->assertOk();
- $data = $response->json('data.customerOrder');
- expect($data['shipments'])->toHaveCount(2);
- /** First shipment address */
- $address = $data['shipments'][0]['shippingAddress'];
- if ($address) {
- expect($address['firstName'])->toBe('John');
- expect($address['lastName'])->toBe('Doe');
- expect($address['city'])->toBe('Springfield');
- expect($address['phone'])->toBe('+1-555-0123');
- }
- }
- /**
- * Test: Query shipments includes payment method
- */
- public function test_query_shipments_includes_payment_method(): void
- {
- $testData = $this->createTestData();
- $orderId = "/api/shop/customer-orders/{$testData['order']->id}";
- $query = <<<GQL
- query getCustomerOrder {
- customerOrder(id: "{$orderId}") {
- _id
- shipments {
- _id
- paymentMethodTitle
- shippingMethodTitle
- }
- }
- }
- GQL;
- $response = $this->graphQL($query);
- $response->assertOk();
- $data = $response->json('data.customerOrder');
- expect($data['shipments'])->toHaveCount(2);
- /** Both shipments should have payment and shipping method */
- expect($data['shipments'][0]['paymentMethodTitle'])->toBe('Money Transfer');
- expect($data['shipments'][0]['shippingMethodTitle'])->toBe('Flat Rate - Flat Rate');
- expect($data['shipments'][1]['paymentMethodTitle'])->toBe('Money Transfer');
- expect($data['shipments'][1]['shippingMethodTitle'])->toBe('Flat Rate - Flat Rate');
- }
- /**
- * Test: Query shipment computed fields (shippingNumber)
- */
- public function test_query_shipment_computed_fields(): void
- {
- $testData = $this->createTestData();
- $orderId = "/api/shop/customer-orders/{$testData['order']->id}";
- $query = <<<GQL
- query getCustomerOrder {
- customerOrder(id: "{$orderId}") {
- _id
- shipments {
- _id
- shippingNumber
- }
- }
- }
- GQL;
- $response = $this->graphQL($query);
- $response->assertOk();
- $data = $response->json('data.customerOrder');
- expect($data['shipments'])->toHaveCount(2);
- /** Shipping numbers should be formatted as #ID */
- $shipment1 = $data['shipments'][0];
- expect($shipment1['shippingNumber'])->toMatch('/^#\d+$/');
- }
- /**
- * Test: Query all shipment fields
- */
- public function test_query_all_shipment_fields(): void
- {
- $testData = $this->createTestData();
- $orderId = "/api/shop/customer-orders/{$testData['order']->id}";
- $query = <<<GQL
- query getCustomerOrder {
- customerOrder(id: "{$orderId}") {
- _id
- shipments {
- _id
- status
- totalQty
- totalWeight
- carrierCode
- carrierTitle
- trackNumber
- emailSent
- shippingNumber
- paymentMethodTitle
- shippingMethodTitle
- createdAt
- }
- }
- }
- GQL;
- $response = $this->graphQL($query);
- $response->assertOk();
- $data = $response->json('data.customerOrder');
- expect($data['shipments'])->toHaveCount(2);
- $shipment = $data['shipments'][0];
- expect($shipment)->toHaveKeys([
- '_id',
- 'status',
- 'totalQty',
- 'totalWeight',
- 'carrierCode',
- 'carrierTitle',
- 'trackNumber',
- 'emailSent',
- 'shippingNumber',
- 'paymentMethodTitle',
- 'shippingMethodTitle',
- 'createdAt',
- ]);
- }
- // ── Access Control ────────────────────────────────────────
- /**
- * Test: Customer only sees their own shipments
- */
- public function test_customer_only_sees_own_shipments(): void
- {
- $testData = $this->createTestData();
- /** Create another customer with their own order/shipment */
- $otherCustomer = $this->createCustomer();
- $channel = Channel::first();
- $product = Product::factory()->create();
- $otherOrder = Order::factory()->create([
- 'customer_id' => $otherCustomer->id,
- 'customer_email' => $otherCustomer->email,
- 'customer_first_name' => $otherCustomer->first_name,
- 'customer_last_name' => $otherCustomer->last_name,
- 'channel_id' => $channel->id,
- 'status' => 'completed',
- ]);
- $otherOrderItem = OrderItem::factory()->create([
- 'order_id' => $otherOrder->id,
- 'product_id' => $product->id,
- 'sku' => 'OTHER-SKU',
- 'type' => 'simple',
- 'name' => 'Other Product',
- ]);
- $otherAddress = OrderAddress::factory()->create([
- 'order_id' => $otherOrder->id,
- 'address_type' => 'shipping',
- ]);
- Shipment::factory()->create([
- 'order_id' => $otherOrder->id,
- 'order_address_id' => $otherAddress->id,
- 'customer_id' => $otherCustomer->id,
- 'customer_type' => Customer::class,
- 'status' => 'shipped',
- 'total_qty' => 1,
- ]);
- ShipmentItem::create([
- 'shipment_id' => Shipment::where('order_id', $otherOrder->id)->first()->id,
- 'order_item_id' => $otherOrderItem->id,
- ]);
- $orderId = "/api/shop/customer-orders/{$testData['order']->id}";
- $query = <<<GQL
- query getCustomerOrder {
- customerOrder(id: "{$orderId}") {
- _id
- shipments {
- _id
- }
- }
- }
- GQL;
- $response = $this->authenticatedGraphQL($testData['customer'], $query);
- $response->assertOk();
- $data = $response->json('data.customerOrder');
- /** Should only see 2 shipments from own order */
- expect($data['shipments'])->toHaveCount(2);
- }
- /**
- * Test: Unauthenticated request cannot access shipments
- */
- public function test_unauthenticated_cannot_access_shipments(): void
- {
- $testData = $this->createTestData();
- $orderId = "/api/shop/customer-orders/{$testData['order']->id}";
- $query = <<<GQL
- query getCustomerOrder {
- customerOrder(id: "{$orderId}") {
- _id
- shipments {
- _id
- }
- }
- }
- GQL;
- $response = $this->graphQL($query);
- $response->assertOk();
- $errors = $response->json('errors');
- expect($errors)->not()->toBeEmpty();
- }
- // ── Shipment Status Filtering ─────────────────────────────
- /**
- * Test: Filter shipments by status (archived)
- */
- public function test_order_with_shipped_and_pending_shipments(): void
- {
- $testData = $this->createTestData();
- $orderId = "/api/shop/customer-orders/{$testData['order']->id}";
- $query = <<<GQL
- query getCustomerOrder {
- customerOrder(id: "{$orderId}") {
- _id
- shipments {
- _id
- status
- }
- }
- }
- GQL;
- $response = $this->graphQL($query);
- $response->assertOk();
- $data = $response->json('data.customerOrder');
- expect($data['shipments'])->toHaveCount(2);
- $statuses = array_column($data['shipments'], 'status');
- expect($statuses)->toContain('shipped');
- expect($statuses)->toContain('pending');
- }
- // ── Shipment Item Details ────────────────────────────────
- /**
- * Test: Query shipment items with full details
- */
- public function test_query_shipment_items_full_details(): void
- {
- $testData = $this->createTestData();
- $orderId = "/api/shop/customer-orders/{$testData['order']->id}";
- $query = <<<GQL
- query getCustomerOrder {
- customerOrder(id: "{$orderId}") {
- _id
- shipments {
- _id
- items {
- _id
- sku
- name
- qty
- weight
- }
- }
- }
- }
- GQL;
- $response = $this->graphQL($query);
- $response->assertOk();
- $data = $response->json('data.customerOrder');
- expect($data['shipments'])->toHaveCount(2);
- /** Each shipment has items */
- foreach ($data['shipments'] as $shipment) {
- expect($shipment['items'])->not()->toBeEmpty();
- foreach ($shipment['items'] as $item) {
- expect($item)->toHaveKeys(['_id', 'sku', 'name', 'qty', 'weight']);
- expect($item['sku'])->toBeTruthy();
- expect($item['qty'])->toBeGreaterThan(0);
- }
- }
- }
- /**
- * Test: Shipment item preserves order item details
- */
- public function test_shipment_item_preserves_order_item_details(): void
- {
- $testData = $this->createTestData();
- $orderId = "/api/shop/customer-orders/{$testData['order']->id}";
- $query = <<<GQL
- query getCustomerOrder {
- customerOrder(id: "{$orderId}") {
- _id
- shipments {
- items {
- sku
- name
- }
- }
- }
- }
- GQL;
- $response = $this->graphQL($query);
- $response->assertOk();
- $data = $response->json('data.customerOrder');
- /** Verify SKU and name match original order item */
- $shipment = $data['shipments'][0];
- $item = $shipment['items'][0];
- expect($item['sku'])->toBe('TEST-SHIP-SKU-001');
- expect($item['name'])->toBe('Test Shipment Product');
- }
- // ── Tracking Information ──────────────────────────────────
- /**
- * Test: Shipment with tracking number
- */
- public function test_shipment_with_tracking_number(): void
- {
- $testData = $this->createTestData();
- $orderId = "/api/shop/customer-orders/{$testData['order']->id}";
- $query = <<<GQL
- query getCustomerOrder {
- customerOrder(id: "{$orderId}") {
- _id
- shipments(filter: {status: ["shipped"]}) {
- _id
- trackNumber
- carrierCode
- carrierTitle
- emailSent
- }
- }
- }
- GQL;
- $response = $this->graphQL($query);
- $response->assertOk();
- $data = $response->json('data.customerOrder');
- expect($data['shipments'])->not()->toBeEmpty();
- $shipment = $data['shipments'][0];
- expect($shipment['trackNumber'])->toBe('TRACK123456789');
- expect($shipment['carriercode'])->toBe('flat_rate');
- expect($shipment['emailSent'])->toBeTrue();
- }
- /**
- * Test: Shipment without tracking number (pending)
- */
- public function test_shipment_without_tracking_number(): void
- {
- $testData = $this->createTestData();
- $orderId = "/api/shop/customer-orders/{$testData['order']->id}";
- $query = <<<GQL
- query getCustomerOrder {
- customerOrder(id: "{$orderId}") {
- _id
- shipments {
- _id
- status
- trackNumber
- emailSent
- }
- }
- }
- GQL;
- $response = $this->graphQL($query);
- $response->assertOk();
- $data = $response->json('data.customerOrder');
- $pendingShipment = collect($data['shipments'])
- ->firstWhere('status', 'pending');
- expect($pendingShipment)->not()->toBeNull();
- expect($pendingShipment['trackNumber'])->toBeNull();
- expect($pendingShipment['emailSent'])->toBeFalse();
- }
- // ── Schema Introspection ──────────────────────────────────
- /**
- * Test: CustomerOrderShipment type has expected fields in schema
- */
- public function test_customer_order_shipment_schema_has_expected_fields(): void
- {
- $query = <<<'GQL'
- {
- __type(name: "CustomerOrderShipment") {
- name
- fields {
- name
- }
- }
- }
- GQL;
- $response = $this->graphQL($query);
- $response->assertSuccessful();
- $type = $response->json('data.__type');
- expect($type)->not->toBeNull()
- ->and($type['name'])->toBe('CustomerOrderShipment');
- $fieldNames = array_column($type['fields'], 'name');
- expect($fieldNames)
- ->toContain('_id')
- ->toContain('status')
- ->toContain('totalQty')
- ->toContain('totalWeight')
- ->toContain('carrierCode')
- ->toContain('carrierTitle')
- ->toContain('trackNumber')
- ->toContain('emailSent')
- ->toContain('shippingNumber')
- ->toContain('items')
- ->toContain('shippingAddress')
- ->toContain('createdAt');
- }
- /**
- * Test: CustomerOrderShipmentItem type has expected fields in schema
- */
- public function test_customer_order_shipment_item_schema_has_expected_fields(): void
- {
- $query = <<<'GQL'
- {
- __type(name: "CustomerOrderShipmentItem") {
- name
- fields {
- name
- }
- }
- }
- GQL;
- $response = $this->graphQL($query);
- $response->assertSuccessful();
- $type = $response->json('data.__type');
- expect($type)->not->toBeNull()
- ->and($type['name'])->toBe('CustomerOrderShipmentItem');
- $fieldNames = array_column($type['fields'], 'name');
- expect($fieldNames)
- ->toContain('_id')
- ->toContain('sku')
- ->toContain('name')
- ->toContain('qty')
- ->toContain('weight');
- }
- // ── Weight and Quantity ───────────────────────────────────
- /**
- * Test: Shipment total qty and weight calculations
- */
- public function test_shipment_total_qty_and_weight(): void
- {
- $testData = $this->createTestData();
- $orderId = "/api/shop/customer-orders/{$testData['order']->id}";
- $query = <<<GQL
- query getCustomerOrder {
- customerOrder(id: "{$orderId}") {
- _id
- shipments {
- _id
- totalQty
- totalWeight
- }
- }
- }
- GQL;
- $response = $this->graphQL($query);
- $response->assertOk();
- $data = $response->json('data.customerOrder');
- expect($data['shipments'])->toHaveCount(2);
- /** First shipment */
- expect($data['shipments'][0]['totalQty'])->toBe(2);
- expect($data['shipments'][0]['totalWeight'])->toBe(10.5);
- /** Second shipment */
- expect($data['shipments'][1]['totalQty'])->toBe(1);
- expect($data['shipments'][1]['totalWeight'])->toBe(5.25);
- }
- /**
- * Test: Individual item qty and weight
- */
- public function test_shipment_item_qty_and_weight(): void
- {
- $testData = $this->createTestData();
- $orderId = "/api/shop/customer-orders/{$testData['order']->id}";
- $query = <<<GQL
- query getCustomerOrder {
- customerOrder(id: "{$orderId}") {
- shipments {
- items {
- qty
- weight
- }
- }
- }
- }
- GQL;
- $response = $this->graphQL($query);
- $response->assertOk();
- $data = $response->json('data.customerOrder');
- $items = collect($data['shipments'])
- ->pluck('items')
- ->flatten(1);
- expect($items)->not()->toBeEmpty();
- foreach ($items as $item) {
- expect($item['qty'])->toBeGreaterThan(0);
- expect($item['weight'])->toBeGreaterThan(0);
- }
- }
- }
|