Completed
Branch master (890fb6)
by Tomáš
01:29
created

Client::trackPackages()   A

Complexity

Conditions 4
Paths 2

Size

Total Lines 20

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 10
CRAP Score 4

Importance

Changes 0
Metric Value
dl 0
loc 20
ccs 10
cts 10
cp 1
rs 9.6
c 0
b 0
f 0
cc 4
nc 2
nop 2
crap 4
1
<?php
2
3
namespace Inspirum\Balikobot\Services;
4
5
use DateTime;
6
use Inspirum\Balikobot\Contracts\RequesterInterface;
7
use Inspirum\Balikobot\Definitions\API;
8
use Inspirum\Balikobot\Definitions\Request;
9
use Inspirum\Balikobot\Definitions\Shipper;
10
use Inspirum\Balikobot\Exceptions\BadRequestException;
11
12
class Client
13
{
14
    /**
15
     * API requester
16
     *
17
     * @var \Inspirum\Balikobot\Contracts\RequesterInterface
18
     */
19
    private $requester;
20
21
    /**
22
     * Balikobot API client
23
     *
24
     * @param \Inspirum\Balikobot\Contracts\RequesterInterface $requester
25
     */
26 248
    public function __construct(RequesterInterface $requester)
27
    {
28 248
        $this->requester = $requester;
29 248
    }
30
31
    /**
32
     * Add package(s) to the Balikobot
33
     *
34
     * @param string                     $shipper
35
     * @param array<array<string,mixed>> $packages
36
     * @param string|null                $version
37
     * @param mixed|null                 $labelsUrl
38
     *
39
     * @return array<array<string,mixed>>
40
     *
41
     * @throws \Inspirum\Balikobot\Contracts\ExceptionInterface
42
     */
43 24
    public function addPackages(string $shipper, array $packages, string $version = null, &$labelsUrl = null): array
44
    {
45 24
        $response = $this->requester->call($version ?: API::V1, $shipper, Request::ADD, $packages);
46
47 15
        if (isset($response[0]['package_id']) === false) {
48 1
            throw new BadRequestException($response);
49
        }
50
51 14
        if (isset($response['labels_url'])) {
52 5
            $labelsUrl = $response['labels_url'];
53
        }
54
55 14
        unset($response['labels_url']);
56 14
        unset($response['status']);
57
58 14
        $this->validateIndexes($response, $packages);
59
60 13
        return $response;
61
    }
62
63
    /**
64
     * Drops a package from the Balikobot – the package must be not ordered
65
     *
66
     * @param string $shipper
67
     * @param int    $packageId
68
     *
69
     * @return void
70
     *
71
     * @throws \Inspirum\Balikobot\Contracts\ExceptionInterface
72
     */
73 2
    public function dropPackage(string $shipper, int $packageId): void
74
    {
75 2
        $this->dropPackages($shipper, [$packageId]);
76 2
    }
77
78
    /**
79
     * Drops a package from the Balikobot – the package must be not ordered
80
     *
81
     * @param string     $shipper
82
     * @param array<int> $packageIds
83
     *
84
     * @return void
85
     *
86
     * @throws \Inspirum\Balikobot\Contracts\ExceptionInterface
87
     */
88 7
    public function dropPackages(string $shipper, array $packageIds): void
89
    {
90 7
        $data = $this->encapsulateIds($packageIds);
91
92 7
        if (count($data) === 0) {
93 1
            return;
94
        }
95
96 6
        $this->requester->call(API::V1, $shipper, Request::DROP, $data);
97 3
    }
98
99
    /**
100
     * Tracks a package
101
     *
102
     * @param string $shipper
103
     * @param string $carrierId
104
     *
105
     * @return array<array<string,int|string>>
106
     *
107
     * @throws \Inspirum\Balikobot\Contracts\ExceptionInterface
108
     */
109 6
    public function trackPackage(string $shipper, string $carrierId): array
110
    {
111 6
        $response = $this->trackPackages($shipper, [$carrierId]);
112
113 3
        return $response[0];
114
    }
115
116
    /**
117
     * Tracks a packages
118
     *
119
     * @param string        $shipper
120
     * @param array<string> $carrierIds
121
     *
122
     * @return array<array<array<string,int|string>>>
123
     *
124
     * @throws \Inspirum\Balikobot\Contracts\ExceptionInterface
125
     */
126 18
    public function trackPackages(string $shipper, array $carrierIds): array
127
    {
128 18
        $data = $this->encapsulateIds($carrierIds);
129
130 18
        $response = $this->requester->call(API::V2, $shipper, Request::TRACK, $data, false);
131
132 14
        unset($response['status']);
133
134
        // fixes that API return only last package statuses for GLS shipper
135 14
        if ($shipper === Shipper::GLS && count($carrierIds) !== count($response)) {
136 1
            for ($i = 0; $i < count($carrierIds) - 1; $i++) {
137 1
                $response[$i] = $response[$i] ?? [];
138
            }
139 1
            sort($response);
140
        }
141
142 14
        $this->validateIndexes($response, $carrierIds);
143
144 10
        return $response;
145
    }
146
147
    /**
148
     * Tracks a package, get the last info
149
     *
150
     * @param string $shipper
151
     * @param string $carrierId
152
     *
153
     * @return array<string,int|string>
154
     *
155
     * @throws \Inspirum\Balikobot\Contracts\ExceptionInterface
156
     */
157 7
    public function trackPackageLastStatus(string $shipper, string $carrierId): array
158
    {
159 7
        $response = $this->trackPackagesLastStatus($shipper, [$carrierId]);
160
161 3
        return $response[0];
162
    }
163
164
    /**
165
     * Tracks a package, get the last info
166
     *
167
     * @param string        $shipper
168
     * @param array<string> $carrierIds
169
     *
170
     * @return array<array<string,int|string|null>>
171
     *
172
     * @throws \Inspirum\Balikobot\Contracts\ExceptionInterface
173
     */
174 19
    public function trackPackagesLastStatus(string $shipper, array $carrierIds): array
175
    {
176 19
        $data = $this->encapsulateIds($carrierIds);
177
178 19
        $response = $this->requester->call(API::V1, $shipper, Request::TRACK_STATUS, $data, false);
179
180 15
        unset($response['status']);
181
182 15
        $this->validateIndexes($response, $carrierIds);
183
184 12
        $formatedStatuses = [];
185
186 12
        foreach ($response as $responseItem) {
187 12
            $this->validateStatus($responseItem, $response);
188
189 11
            $formatedStatuses[] = [
190 11
                'name'      => $responseItem['status_text'],
191 11
                'status_id' => $responseItem['status_id'],
192
                'date'      => null,
193
            ];
194
        }
195
196 10
        return $formatedStatuses;
197
    }
198
199
    /**
200
     * Returns packages from the front (not ordered) for given shipper
201
     *
202
     * @param string $shipper
203
     *
204
     * @return array<array<string,int|string>>
205
     *
206
     * @throws \Inspirum\Balikobot\Contracts\ExceptionInterface
207
     */
208 6
    public function getOverview(string $shipper): array
209
    {
210 6
        $response = $this->requester->call(API::V1, $shipper, Request::OVERVIEW, [], false);
211
212 4
        return $response;
213
    }
214
215
    /**
216
     * Gets labels
217
     *
218
     * @param string     $shipper
219
     * @param array<int> $packageIds
220
     *
221
     * @return string
222
     *
223
     * @throws \Inspirum\Balikobot\Contracts\ExceptionInterface
224
     */
225 7
    public function getLabels(string $shipper, array $packageIds): string
226
    {
227
        $data = [
228 7
            'package_ids' => $packageIds,
229
        ];
230
231 7
        $response = $this->requester->call(API::V1, $shipper, Request::LABELS, $data);
232
233 4
        $formattedResponse = $response['labels_url'];
234
235 4
        return $formattedResponse;
236
    }
237
238
    /**
239
     * Gets complete information about a package
240
     *
241
     * @param string $shipper
242
     * @param int    $packageId
243
     *
244
     * @return array<string,int|string>
245
     *
246
     * @throws \Inspirum\Balikobot\Contracts\ExceptionInterface
247
     */
248 6
    public function getPackageInfo(string $shipper, int $packageId): array
249
    {
250 6
        $response = $this->requester->call(API::V1, $shipper, Request::PACKAGE . '/' . $packageId, [], false);
251
252 4
        return $response;
253
    }
254
255
    /**
256
     * Order shipment for packages
257
     *
258
     * @param string     $shipper
259
     * @param array<int> $packageIds
260
     *
261
     * @return array<string,int|string>
262
     *
263
     * @throws \Inspirum\Balikobot\Contracts\ExceptionInterface
264
     */
265 7
    public function orderShipment(string $shipper, array $packageIds): array
266
    {
267
        $data = [
268 7
            'package_ids' => $packageIds,
269
        ];
270
271 7
        $response = $this->requester->call(API::V1, $shipper, Request::ORDER, $data);
272
273 4
        unset($response['status']);
274
275 4
        return $response;
276
    }
277
278
    /**
279
     * Get order details
280
     *
281
     * @param string $shipper
282
     * @param int    $orderId
283
     *
284
     * @return array<string,int|string|array>
285
     *
286
     * @throws \Inspirum\Balikobot\Contracts\ExceptionInterface
287
     */
288 7
    public function getOrder(string $shipper, int $orderId): array
289
    {
290 7
        $response = $this->requester->call(API::V1, $shipper, Request::ORDER_VIEW . '/' . $orderId, [], false);
291
292 5
        unset($response['status']);
293
294 5
        return $response;
295
    }
296
297
    /**
298
     * Order pickup for packages
299
     *
300
     * @param string      $shipper
301
     * @param \DateTime   $dateFrom
302
     * @param \DateTime   $dateTo
303
     * @param float       $weight
304
     * @param int         $packageCount
305
     * @param string|null $message
306
     *
307
     * @return void
308
     *
309
     * @throws \Inspirum\Balikobot\Contracts\ExceptionInterface
310
     */
311 4
    public function orderPickup(
312
        string $shipper,
313
        DateTime $dateFrom,
314
        DateTime $dateTo,
315
        float $weight,
316
        int $packageCount,
317
        string $message = null
318
    ): void {
319
        $data = [
320 4
            'date'          => $dateFrom->format('Y-m-d'),
321 4
            'time_from'     => $dateFrom->format('H:s'),
322 4
            'time_to'       => $dateTo->format('H:s'),
323 4
            'weight'        => $weight,
324 4
            'package_count' => $packageCount,
325 4
            'message'       => $message,
326
        ];
327
328 4
        $this->requester->call(API::V1, $shipper, Request::ORDER_PICKUP, $data);
329 1
    }
330
331
    /**
332
     * Returns available services for the given shipper
333
     *
334
     * @param string      $shipper
335
     * @param string|null $country
336
     * @param string|null $version
337
     *
338
     * @return array<string,string>
339
     *
340
     * @throws \Inspirum\Balikobot\Contracts\ExceptionInterface
341
     */
342 12
    public function getServices(string $shipper, string $country = null, string $version = null): array
343
    {
344 12
        $response = $this->requester->call($version ?: API::V1, $shipper, Request::SERVICES . '/' . $country);
345
346 8
        $formattedResponse = $response['service_types'] ?? [];
347
348 8
        return $formattedResponse;
349
    }
350
351
    /**
352
     * Returns available B2A services for the given shipper
353
     *
354
     * @param string $shipper
355
     *
356
     * @return array<string,string>
357
     *
358
     * @throws \Inspirum\Balikobot\Contracts\ExceptionInterface
359
     */
360 8
    public function getB2AServices(string $shipper): array
361
    {
362 8
        $response = $this->requester->call(API::V1, $shipper, Request::B2A . '/' . Request::SERVICES);
363
364 5
        $formattedResponse = $response['service_types'] ?? [];
365
366 5
        return $formattedResponse;
367
    }
368
369
    /**
370
     * Returns all manipulation units for the given shipper
371
     *
372
     * @param string $shipper
373
     *
374
     * @return array<string,string>
375
     *
376
     * @throws \Inspirum\Balikobot\Contracts\ExceptionInterface
377
     */
378 8
    public function getManipulationUnits(string $shipper): array
379
    {
380 8
        $response = $this->requester->call(API::V1, $shipper, Request::MANIPULATION_UNITS);
381
382 5
        $formattedResponse = $this->normalizeResponseItems($response['units'] ?? [], 'code', 'name');
383
384 5
        return $formattedResponse;
385
    }
386
387
    /**
388
     * Returns available manipulation units for the given shipper
389
     *
390
     * @param string $shipper
391
     *
392
     * @return array<string,string>
393
     *
394
     * @throws \Inspirum\Balikobot\Contracts\ExceptionInterface
395
     */
396 8
    public function getActivatedManipulationUnits(string $shipper): array
397
    {
398 8
        $response = $this->requester->call(API::V1, $shipper, Request::ACTIVATED_MANIPULATION_UNITS);
399
400 5
        $formattedResponse = $this->normalizeResponseItems($response['units'] ?? [], 'code', 'name');
401
402 5
        return $formattedResponse;
403
    }
404
405
    /**
406
     * Returns available branches for the given shipper and its service
407
     * Full branches instead branches request
408
     *
409
     * @param string      $shipper
410
     * @param string|null $service
411
     * @param bool        $fullBranchRequest
412
     * @param string|null $country
413
     * @param string|null $version
414
     *
415
     * @return array<array<string,mixed>>
416
     *
417
     * @throws \Inspirum\Balikobot\Contracts\ExceptionInterface
418
     */
419 15
    public function getBranches(
420
        string $shipper,
421
        ?string $service,
422
        bool $fullBranchRequest = false,
423
        string $country = null,
424
        string $version = null
425
    ): array {
426 15
        $usedRequest = $fullBranchRequest ? Request::FULL_BRANCHES : Request::BRANCHES;
427
428 15
        $response = $this->requester->call(
429 15
            $version ?: API::V1,
430
            $shipper,
431 15
            $usedRequest . '/' . $service . '/' . $country
432
        );
433
434 12
        $formattedResponse = $response['branches'] ?? [];
435
436 12
        return $formattedResponse;
437
    }
438
439
    /**
440
     * Returns available branches for the given shipper in given location
441
     *
442
     * @param string      $shipper
443
     * @param string      $country
444
     * @param string      $city
445
     * @param string|null $postcode
446
     * @param string|null $street
447
     * @param int|null    $maxResults
448
     * @param float|null  $radius
449
     * @param string|null $type
450
     *
451
     * @return array<array<string,mixed>>
452
     *
453
     * @throws \Inspirum\Balikobot\Contracts\ExceptionInterface
454
     */
455 9
    public function getBranchesForLocation(
456
        string $shipper,
457
        string $country,
458
        string $city,
459
        string $postcode = null,
460
        string $street = null,
461
        int $maxResults = null,
462
        float $radius = null,
463
        string $type = null
464
    ): array {
465
        $data = [
466 9
            'country'     => $country,
467 9
            'city'        => $city,
468 9
            'zip'         => $postcode,
469 9
            'street'      => $street,
470 9
            'max_results' => $maxResults,
471 9
            'radius'      => $radius,
472 9
            'type'        => $type,
473
        ];
474
475 9
        $response = $this->requester->call(API::V1, $shipper, Request::BRANCH_LOCATOR, array_filter($data));
476
477 6
        $formattedResponse = $response['branches'] ?? [];
478
479 6
        return $formattedResponse;
480
    }
481
482
    /**
483
     * Returns list of countries where service with cash-on-delivery payment type is available in
484
     *
485
     * @param string $shipper
486
     *
487
     * @return array<array<int|string,array<string,array>>>
488
     *
489
     * @throws \Inspirum\Balikobot\Contracts\ExceptionInterface
490
     */
491 8
    public function getCodCountries(string $shipper): array
492
    {
493 8
        $response = $this->requester->call(API::V1, $shipper, Request::CASH_ON_DELIVERY_COUNTRIES);
494
495 5
        $formattedResponse = $this->normalizeResponseItems(
496 5
            $response['service_types'] ?? [],
497 5
            'service_type',
498 5
            'cod_countries'
499
        );
500
501 5
        return $formattedResponse;
502
    }
503
504
    /**
505
     * Returns list of countries where service is available in
506
     *
507
     * @param string $shipper
508
     *
509
     * @return array<array<int|string,string>>
510
     *
511
     * @throws \Inspirum\Balikobot\Contracts\ExceptionInterface
512
     */
513 8
    public function getCountries(string $shipper): array
514
    {
515 8
        $response = $this->requester->call(API::V1, $shipper, Request::COUNTRIES);
516
517 5
        $formattedResponse = $this->normalizeResponseItems(
518 5
            $response['service_types'] ?? [],
519 5
            'service_type',
520 5
            'countries'
521
        );
522
523 5
        return $formattedResponse;
524
    }
525
526
    /**
527
     * Returns available branches for the given shipper and its service
528
     *
529
     * @param string      $shipper
530
     * @param string      $service
531
     * @param string|null $country
532
     *
533
     * @return array<array<string,mixed>>
534
     *
535
     * @throws \Inspirum\Balikobot\Contracts\ExceptionInterface
536
     */
537 12
    public function getPostCodes(string $shipper, string $service, string $country = null): array
538
    {
539 12
        $response = $this->requester->call(API::V1, $shipper, Request::ZIP_CODES . '/' . $service . '/' . $country);
540
541 9
        $country = $response['country'] ?? $country;
542
543 9
        $formattedResponse = [];
544
545 9
        foreach ($response['zip_codes'] ?? [] as $responseItem) {
546 5
            $formattedResponse[] = [
547 5
                'postcode'     => $responseItem['zip'] ?? ($responseItem['zip_start'] ?? null),
548 5
                'postcode_end' => $responseItem['zip_end'] ?? null,
549 5
                'city'         => $responseItem['city'] ?? null,
550 5
                'country'      => $responseItem['country'] ?? $country,
551 5
                '1B'           => (bool) ($responseItem['1B'] ?? false),
552
            ];
553
        }
554
555 9
        return $formattedResponse;
556
    }
557
558
    /**
559
     * Check package(s) data
560
     *
561
     * @param string               $shipper
562
     * @param array<array<string>> $packages
563
     *
564
     * @return void
565
     *
566
     * @throws \Inspirum\Balikobot\Contracts\ExceptionInterface
567
     */
568 5
    public function checkPackages(string $shipper, array $packages): void
569
    {
570 5
        $this->requester->call(API::V1, $shipper, Request::CHECK, $packages);
571 2
    }
572
573
    /**
574
     * Returns available manipulation units for the given shipper
575
     *
576
     * @param string $shipper
577
     *
578
     * @return array<string>
579
     *
580
     * @throws \Inspirum\Balikobot\Contracts\ExceptionInterface
581
     */
582 8
    public function getAdrUnits(string $shipper): array
583
    {
584 8
        $response = $this->requester->call(API::V1, $shipper, Request::ADR_UNITS);
585
586 5
        $formattedResponse = $this->normalizeResponseItems($response['units'] ?? [], 'code', 'name');
587
588 5
        return $formattedResponse;
589
    }
590
591
    /**
592
     * Returns available activated services for the given shipper
593
     *
594
     * @param string $shipper
595
     *
596
     * @return array<string,mixed>
597
     *
598
     * @throws \Inspirum\Balikobot\Contracts\ExceptionInterface
599
     */
600 6
    public function getActivatedServices(string $shipper): array
601
    {
602 6
        $response = $this->requester->call(API::V1, $shipper, Request::ACTIVATED_SERVICES);
603
604 4
        unset($response['status']);
605
606 4
        return $response;
607
    }
608
609
    /**
610
     * Order shipments from place B (typically supplier / previous consignee) to place A (shipping point)
611
     *
612
     * @param string                     $shipper
613
     * @param array<array<string,mixed>> $packages
614
     *
615
     * @return array<array<string,mixed>>
616
     *
617
     * @throws \Inspirum\Balikobot\Contracts\ExceptionInterface
618
     */
619 10
    public function orderB2AShipment(string $shipper, array $packages): array
620
    {
621 10
        $response = $this->requester->call(API::V1, $shipper, Request::B2A, $packages);
622
623 7
        if (isset($response[0]['package_id']) === false) {
624 1
            throw new BadRequestException($response);
625
        }
626
627 6
        unset($response['status']);
628
629 6
        $this->validateIndexes($response, $packages);
630
631 5
        return $response;
632
    }
633
634
    /**
635
     * Get PDF link with signed consignment delivery document by the recipient
636
     *
637
     * @param string $shipper
638
     * @param string $carrierId
639
     *
640
     * @return string
641
     *
642
     * @throws \Inspirum\Balikobot\Contracts\ExceptionInterface
643
     */
644 7
    public function getProofOfDelivery(string $shipper, string $carrierId): string
645
    {
646 7
        $response = $this->getProofOfDeliveries($shipper, [$carrierId]);
647
648 3
        return $response[0];
649
    }
650
651
    /**
652
     * Get array of PDF links with signed consignment delivery document by the recipient
653
     *
654
     * @param string        $shipper
655
     * @param array<string> $carrierIds
656
     *
657
     * @return array<string>
658
     *
659
     * @throws \Inspirum\Balikobot\Contracts\ExceptionInterface
660
     */
661 19
    public function getProofOfDeliveries(string $shipper, array $carrierIds): array
662
    {
663 19
        $data = $this->encapsulateIds($carrierIds);
664
665 19
        $response = $this->requester->call(API::V1, $shipper, Request::PROOF_OF_DELIVERY, $data, false);
666
667 15
        unset($response['status']);
668
669 15
        $this->validateIndexes($response, $carrierIds);
670
671 11
        $formatedLinks = [];
672
673 11
        foreach ($response as $responseItem) {
674 11
            $this->validateStatus($responseItem, $response);
675
676 11
            $formatedLinks[] = $responseItem['file_url'];
677
        }
678
679 10
        return $formatedLinks;
680
    }
681
682
    /**
683
     * Validate response item status
684
     *
685
     * @param array<mixed,mixed> $responseItem
686
     * @param array<mixed,mixed> $response
687
     *
688
     * @return void
689
     */
690 23
    private function validateStatus(array $responseItem, array $response): void
691
    {
692 23
        if (isset($responseItem['status']) && ((int) $responseItem['status']) !== 200) {
693 3
            throw new BadRequestException($response);
694
        }
695 22
    }
696
697
    /**
698
     * Validate indexes
699
     *
700
     * @param array<mixed,mixed> $response
701
     * @param array<mixed,mixed> $request
702
     *
703
     * @return void
704
     *
705
     * @throws \Inspirum\Balikobot\Exceptions\BadRequestException
706
     */
707 64
    private function validateIndexes(array $response, array $request): void
708
    {
709 64
        if (array_keys($response) !== range(0, count($request) - 1)) {
710 13
            throw new BadRequestException($response);
711
        }
712 51
    }
713
714
    /**
715
     * Normalize response items
716
     *
717
     * @param array<array<string,string>> $items
718
     * @param string                      $keyName
719
     * @param string                      $valueName
720
     *
721
     * @return array<string,mixed>
722
     */
723 25
    private function normalizeResponseItems(array $items, string $keyName, string $valueName): array
724
    {
725 25
        $formattedResponse = [];
726
727 25
        foreach ($items as $item) {
728 10
            $formattedResponse[$item[$keyName]] = $item[$valueName];
729
        }
730
731 25
        return $formattedResponse;
732
    }
733
734
    /**
735
     * Encapsulate ids
736
     *
737
     * @param array<int|string> $ids
738
     *
739
     * @return array<array<int|string>>
740
     */
741 63
    private function encapsulateIds(array $ids): array
742
    {
743
        return array_map(function ($carrierId) {
744
            return [
745 62
                'id' => $carrierId,
746
            ];
747 63
        }, $ids);
748
    }
749
}
750