Completed
Pull Request — master (#6)
by Tomáš
03:11
created

Client::trackPackages()   A

Complexity

Conditions 5
Paths 4

Size

Total Lines 24

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 5.3906

Importance

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