diff --git a/src/MyParcelComApi.php b/src/MyParcelComApi.php index 88f9e95..409edb8 100644 --- a/src/MyParcelComApi.php +++ b/src/MyParcelComApi.php @@ -4,9 +4,13 @@ namespace MyParcelCom\ApiSdk; +use DateMalformedStringException; +use DateTimeImmutable; +use DateTimeInterface; use GuzzleHttp\Psr7\Message; use GuzzleHttp\Psr7\Request; use Http\Discovery\HttpClientDiscovery; +use JsonException; use MyParcelCom\ApiSdk\Authentication\AuthenticatorInterface; use MyParcelCom\ApiSdk\Collection\ArrayCollection; use MyParcelCom\ApiSdk\Collection\CollectionInterface as ResourceCollectionInterface; @@ -18,6 +22,7 @@ use MyParcelCom\ApiSdk\Http\Exceptions\RequestException; use MyParcelCom\ApiSdk\Resources\Collection; use MyParcelCom\ApiSdk\Resources\File; +use MyParcelCom\ApiSdk\Resources\Interfaces\AddressInterface; use MyParcelCom\ApiSdk\Resources\Interfaces\CarrierInterface; use MyParcelCom\ApiSdk\Resources\Interfaces\CollectionInterface; use MyParcelCom\ApiSdk\Resources\Interfaces\FileInterface; @@ -989,6 +994,48 @@ public function deleteShipmentSurcharge(ShipmentSurchargeInterface $shipmentSurc return true; } + /** + * @return list + * @throws RequestException + * @throws JsonException + * @throws DateMalformedStringException + */ + public function getDeliveryDates( + string $serviceCode, + AddressInterface $address, + DatetimeInterface $startDate, + DatetimeInterface $endDate, + array $serviceOptionCodes = [], + $ttl = self::TTL_10MIN, + ): array { + $response = $this->doRequest( + self::PATH_DELIVERY_DATES, + 'post', + [ + 'service_code' => $serviceCode, + 'service_option_codes' => $serviceOptionCodes, + 'address' => [ + 'country_code' => $address->getCountryCode(), + 'postal_code' => $address->getPostalCode(), + 'street_number' => $address->getStreetNumber(), + ], + 'start_date' => $startDate->format('c'), // ISO 8601 date + 'end_date' => $endDate->format('c'), // ISO 8601 date + ], + ttl: $ttl, + ); + + $json = json_decode((string) $response->getBody(), true, 512, JSON_THROW_ON_ERROR); + + return array_map( + static fn ($deliveryWindow) => [ + 'date_from' => new DateTimeImmutable($deliveryWindow['date_from']), + 'date_to' => new DateTimeImmutable($deliveryWindow['date_to']), + ], + $json['data'], + ); + } + /** * Set the URI of the MyParcel.com API. */ diff --git a/src/MyParcelComApiInterface.php b/src/MyParcelComApiInterface.php index 3b314f9..1949c59 100644 --- a/src/MyParcelComApiInterface.php +++ b/src/MyParcelComApiInterface.php @@ -4,10 +4,15 @@ namespace MyParcelCom\ApiSdk; +use DateMalformedStringException; +use DateTimeImmutable; +use DateTimeInterface; +use JsonException; use MyParcelCom\ApiSdk\Authentication\AuthenticatorInterface; use MyParcelCom\ApiSdk\Collection\CollectionInterface as ResourceCollectionInterface; use MyParcelCom\ApiSdk\Exceptions\MyParcelComException; use MyParcelCom\ApiSdk\Http\Exceptions\RequestException; +use MyParcelCom\ApiSdk\Resources\Interfaces\AddressInterface; use MyParcelCom\ApiSdk\Resources\Interfaces\CarrierInterface; use MyParcelCom\ApiSdk\Resources\Interfaces\CollectionInterface; use MyParcelCom\ApiSdk\Resources\Interfaces\FileInterface; @@ -17,7 +22,6 @@ use MyParcelCom\ApiSdk\Resources\Interfaces\ServiceRateInterface; use MyParcelCom\ApiSdk\Resources\Interfaces\ShipmentInterface; use MyParcelCom\ApiSdk\Resources\Interfaces\ShopInterface; -use MyParcelCom\ApiSdk\Resources\Shipment; use Psr\Http\Client\ClientInterface; use Psr\Http\Message\ResponseInterface; use Psr\SimpleCache\CacheInterface; @@ -26,6 +30,7 @@ interface MyParcelComApiInterface { const PATH_CARRIERS = '/carriers'; const PATH_COLLECTIONS = '/collections'; + const PATH_DELIVERY_DATES = '/shipping/v2/available-delivery-dates'; const PATH_FILES_ID = '/files/{file_id}'; const PATH_MANIFESTS = '/manifests'; const PATH_MANIFESTS_ID_FILES_ID = '/manifests/{manifest_id}/files/{file_id}'; @@ -111,7 +116,10 @@ public function getServices( * * @throws MyParcelComException */ - public function getServicesForCarrier(CarrierInterface $carrier, int $ttl = self::TTL_10MIN): ResourceCollectionInterface; + public function getServicesForCarrier( + CarrierInterface $carrier, + int $ttl = self::TTL_10MIN, + ): ResourceCollectionInterface; /** * Retrieves service rates based on the set filters. Available filters are: service, contract and weight. Note that @@ -149,7 +157,10 @@ public function resolveDynamicServiceRates( * * @throws MyParcelComException */ - public function getShipments(ShopInterface $shop = null, int $ttl = self::TTL_NO_CACHE): ResourceCollectionInterface; + public function getShipments( + ShopInterface $shop = null, + int $ttl = self::TTL_NO_CACHE, + ): ResourceCollectionInterface; /** * Get a specific shipment from the API. @@ -242,13 +253,41 @@ public function registerCollection(CollectionInterface|string $collectionId): Co public function deleteCollection(CollectionInterface $collection): bool; /** - * @param CollectionInterface $collection + * @param CollectionInterface $collection * @param array $shipments Either an array of strings or an array of ShipmentInterface objects. * @return CollectionInterface */ public function addShipmentsToCollection(CollectionInterface $collection, array $shipments): CollectionInterface; public function generateManifestForCollection(CollectionInterface $collection): ManifestInterface; + + /** + * Get delivery date time windows from a carrier for the provided service. + * Accepts an optional array of `delivery-window` service option codes + * + * @param string $serviceCode + * @param AddressInterface $address + * @param DateTimeInterface $startDate + * @param DateTimeInterface $endDate + * @param array $serviceOptionCodes + * @param int $ttl + * @return list + * @throws RequestException + * @throws JsonException + * @throws DateMalformedStringException + */ + public function getDeliveryDates( + string $serviceCode, + AddressInterface $address, + DatetimeInterface $startDate, + DatetimeInterface $endDate, + array $serviceOptionCodes = [], + $ttl = self::TTL_10MIN, + ): array; + /** * Set the URI of the MyParcel.com API. */ diff --git a/tests/Feature/MyParcelComApi/DeliveryDatesTest.php b/tests/Feature/MyParcelComApi/DeliveryDatesTest.php new file mode 100644 index 0000000..27f3367 --- /dev/null +++ b/tests/Feature/MyParcelComApi/DeliveryDatesTest.php @@ -0,0 +1,43 @@ +setCountryCode('DE') + ->setPostalCode('12345') + ->setStreetNumber(221); + + $deliveryDates = $this->api->getDeliveryDates( + 'service-code', + $address, + new DateTime(), + new DateTime(), + [ + 'service-option-code-1', + 'service-option-code-2', + ], + ); + + $this->assertCount(3, $deliveryDates); + + foreach ($deliveryDates as $deliveryDate) { + $this->assertInstanceOf(DateTimeImmutable::class, $deliveryDate['date_from']); + $this->assertInstanceOf(DateTimeImmutable::class, $deliveryDate['date_to']); + } + } +} diff --git a/tests/Stubs/post/https---api-shipping-v2-available-delivery-dates.json b/tests/Stubs/post/https---api-shipping-v2-available-delivery-dates.json new file mode 100644 index 0000000..2d29434 --- /dev/null +++ b/tests/Stubs/post/https---api-shipping-v2-available-delivery-dates.json @@ -0,0 +1,16 @@ +{ + "data": [ + { + "date_from": "2025-05-01T09:30:00+01:00", + "date_to": "2025-05-01T11:30:00+01:00" + }, + { + "date_from": "2025-05-01T12:30:00+01:00", + "date_to": "2025-05-01T14:30:00+01:00" + }, + { + "date_from": "2025-05-01T15:30:00+01:00", + "date_to": "2025-05-01T17:30:00+01:00" + } + ] +}