Skip to content

Commit

Permalink
Merge pull request #224 from MyParcelCOM/feature/multi-colli
Browse files Browse the repository at this point in the history
🗂️ Support for multi-colli shipments
  • Loading branch information
yoerriwalstra authored Dec 6, 2024
2 parents 9dda3e6 + e5a3369 commit 4361a0e
Show file tree
Hide file tree
Showing 10 changed files with 624 additions and 18 deletions.
113 changes: 95 additions & 18 deletions src/MyParcelComApi.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
use MyParcelCom\ApiSdk\Resources\Interfaces\ManifestInterface;
use MyParcelCom\ApiSdk\Resources\Interfaces\ResourceFactoryInterface;
use MyParcelCom\ApiSdk\Resources\Interfaces\ResourceInterface;
use MyParcelCom\ApiSdk\Resources\Interfaces\ResourceProxyInterface;
use MyParcelCom\ApiSdk\Resources\Interfaces\ServiceInterface;
use MyParcelCom\ApiSdk\Resources\Interfaces\ServiceOptionInterface;
use MyParcelCom\ApiSdk\Resources\Interfaces\ServiceRateInterface;
Expand All @@ -34,7 +35,6 @@
use MyParcelCom\ApiSdk\Resources\Service;
use MyParcelCom\ApiSdk\Resources\ServiceRate;
use MyParcelCom\ApiSdk\Resources\Shipment;
use MyParcelCom\ApiSdk\Resources\ShipmentSurcharge;
use MyParcelCom\ApiSdk\Shipments\ServiceMatcher;
use MyParcelCom\ApiSdk\Utils\UrlBuilder;
use MyParcelCom\ApiSdk\Validators\CollectionValidator;
Expand Down Expand Up @@ -556,7 +556,7 @@ public function createAndRegisterShipment(
}

$response = $this->doRequest(
'/registered-shipments?' . http_build_query(['include' => 'files']),
'/registered-shipments?' . http_build_query(['include' => Shipment::RELATIONSHIP_FILES]),
'post',
[
'data' => $shipment,
Expand All @@ -571,22 +571,99 @@ public function createAndRegisterShipment(

/** @var Shipment $registeredShipment */
$registeredShipment = $this->resourceFactory->create('shipments', $json['data']);
$included = isset($json['included']) ? $json['included'] : [];
$metaFiles = isset($json['meta']['files']) ? $json['meta']['files'] : [];

if (!empty($included)) {
$includedResources = $this->jsonToResources($included);
$registeredShipment->processIncludedResources($includedResources);

foreach ($registeredShipment->getFiles() as $file) {
$format = $file->getFormats()[0];

foreach ($metaFiles as $metaFile) {
if ($metaFile['document_type'] === $file->getDocumentType()
&& $metaFile['mime_type'] === $format[FileInterface::FORMAT_MIME_TYPE]
&& $metaFile['extension'] === $format[FileInterface::FORMAT_EXTENSION]
) {
$file->setBase64Data($metaFile['contents'], $metaFile['mime_type']);
$included = $json['included'] ?? [];
$metaFiles = $json['meta']['files'] ?? [];

if (empty($included)) {
return $registeredShipment;
}

$includedResources = $this->jsonToResources($included);
$registeredShipment->processIncludedResources($includedResources);

// After the included file models have been populated, we hydrate them with the base64 data from the meta.
foreach ($registeredShipment->getFiles() as $file) {
$file->setBase64DataFromResponseMeta($metaFiles);
}

return $registeredShipment;
}

/**
* This function is similar to createAndRegisterShipment() and immediately communicates the shipment to the carrier.
* The carrier response is processed before your request is completed, so files and base64 data will be available.
* Note that files will not be set on the `master` shipment, but each individual `collo` shipment will have a file.
*/
public function createAndRegisterMultiColliShipment(
ShipmentInterface $shipment,
?string $idempotencyKey = null,
): ShipmentInterface {
$this->populateShipmentWithDefaultsFromShop($shipment);
$this->validateShipment($shipment);

if (count($shipment->getColli()) < 1) {
throw new InvalidResourceException(
'Could not create multi-colli shipment without any colli. Please add one or more collo shipments to this master shipment.',
);
}

$headers = [];

if ($idempotencyKey) {
$headers[self::HEADER_IDEMPOTENCY_KEY] = $idempotencyKey;
}

$response = $this->doRequest(
'/multi-colli-shipments?' . http_build_query([
'include' => implode(',', [
Shipment::RELATIONSHIP_COLLI,
Shipment::RELATIONSHIP_COLLI . '.' . Shipment::RELATIONSHIP_FILES,
]),
]),
'post',
[
'data' => $shipment,
'meta' => array_merge(
[
'colli' => array_map(
fn (ShipmentInterface $collo) => $collo->jsonSerialize()['attributes'] ?? null,
$shipment->getColli(),
),
],
array_filter($shipment->getMeta()),
),
],
$this->authenticator->getAuthorizationHeader() + [
AuthenticatorInterface::HEADER_ACCEPT => AuthenticatorInterface::MIME_TYPE_JSONAPI,
] + $headers,
);

$json = json_decode((string) $response->getBody(), true);

/** @var Shipment $registeredShipment */
$registeredShipment = $this->resourceFactory->create('shipments', $json['data']);
$included = $json['included'] ?? [];
$relationshipColli = $json['data']['relationships']['colli']['data'] ?? [];

if (empty($included)) {
return $registeredShipment;
}

$includedResources = $this->jsonToResources($included);
$registeredShipment->processIncludedResources($includedResources);

// After the included colli models have been populated, we hydrate them with the base64 data from the meta.
foreach ($registeredShipment->getColli() as $collo) {
foreach ($collo->getFiles() as $file) {
if ($file instanceof ResourceProxyInterface) {
$file->setResourceFromIncludes($includedResources);
}

foreach ($relationshipColli as $relationshipCollo) {
if ($relationshipCollo['meta']['collo_number'] === $collo->getColloNumber()) {
$metaFiles = $relationshipCollo['meta']['files'] ?? [];

$file->setBase64DataFromResponseMeta($metaFiles);
}
}
}
Expand Down
16 changes: 16 additions & 0 deletions src/Resources/File.php
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,22 @@ public function getStream(?string $mimeType = null): ?StreamInterface
return null;
}

public function setBase64DataFromResponseMeta(array $metaFiles): self
{
$format = $this->getFormats()[0];

foreach ($metaFiles as $metaFile) {
if ($metaFile['document_type'] === $this->getDocumentType()
&& $metaFile['mime_type'] === $format[FileInterface::FORMAT_MIME_TYPE]
&& $metaFile['extension'] === $format[FileInterface::FORMAT_EXTENSION]
) {
$this->setBase64Data($metaFile['contents'], $metaFile['mime_type']);
}
}

return $this;
}

public function setBase64Data(string $data, string $mimeType): self
{
$this->base64Data[$mimeType] = $data;
Expand Down
5 changes: 5 additions & 0 deletions src/Resources/Interfaces/FileInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,11 @@ public function setStream(StreamInterface $stream, string $mimeType): self;
*/
public function getStream(?string $mimeType = null): ?StreamInterface;

/**
* Set the base64 encoded data of the first format of this file, using the meta.files JSON of a response.
*/
public function setBase64DataFromResponseMeta(array $metaFiles): self;

/**
* Set the base64 encoded data of this file for given mime type.
*/
Expand Down
19 changes: 19 additions & 0 deletions src/Resources/Interfaces/ShipmentInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,9 @@ public function setItems(?array $items): self;

public function addItem(ShipmentItemInterface $item): self;

/**
* @return ShipmentItemInterface[]|null
*/
public function getItems(): ?array;

/**
Expand Down Expand Up @@ -230,4 +233,20 @@ public function setShipmentSurcharges(array $surcharges): self;
public function addShipmentSurcharge(ShipmentSurchargeInterface $surcharge): self;

public function getShipmentSurcharges(): array;

/**
* @param ShipmentInterface[] $colli
*/
public function setColli(array $colli): self;

public function addCollo(ShipmentInterface $collo): self;

/**
* @return ShipmentInterface[]
*/
public function getColli(): array;

public function setColloNumber(?int $colloNumber): self;

public function getColloNumber(): ?int;
}
7 changes: 7 additions & 0 deletions src/Resources/Proxy/FileProxy.php
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,13 @@ public function getStream(?string $mimeType = null): ?StreamInterface
return $this->getResource()->getStream($mimeType);
}

public function setBase64DataFromResponseMeta(array $metaFiles): self
{
$this->getResource()->setBase64DataFromResponseMeta($metaFiles);

return $this;
}

public function setBase64Data(string $data, string $mimeType): self
{
$this->getResource()->setBase64Data($data, $mimeType);
Expand Down
31 changes: 31 additions & 0 deletions src/Resources/Proxy/ShipmentProxy.php
Original file line number Diff line number Diff line change
Expand Up @@ -581,6 +581,37 @@ public function getShipmentSurcharges(): array
return $this->getResource()->getShipmentSurcharges();
}

public function setColli(array $colli): self
{
$this->getResource()->setColli($colli);

return $this;
}

public function addCollo(ShipmentInterface $collo): self
{
$this->getResource()->addCollo($collo);

return $this;
}

public function getColli(): array
{
return $this->getResource()->getColli();
}

public function setColloNumber(?int $colloNumber): self
{
$this->getResource()->setColloNumber($colloNumber);

return $this;
}

public function getColloNumber(): ?int
{
return $this->getResource()->getColloNumber();
}

/**
* This function puts all object properties in an array and returns it.
*/
Expand Down
55 changes: 55 additions & 0 deletions src/Resources/Shipment.php
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,9 @@ class Shipment implements ShipmentInterface
const ATTRIBUTE_REGISTER_AT = 'register_at';
const ATTRIBUTE_TOTAL_VALUE = 'total_value';
const ATTRIBUTE_TAGS = 'tags';
const ATTRIBUTE_COLLO_NUMBER = 'collo_number';

const RELATIONSHIP_COLLI = 'colli';
const RELATIONSHIP_CONTRACT = 'contract';
const RELATIONSHIP_FILES = 'files';
const RELATIONSHIP_MANIFEST = 'manifest';
Expand All @@ -77,6 +79,7 @@ class Shipment implements ShipmentInterface
ResourceInterface::TYPE_FILE => self::RELATIONSHIP_FILES,
ResourceInterface::TYPE_SERVICE => self::RELATIONSHIP_SERVICE,
ResourceInterface::TYPE_SERVICE_OPTION => self::RELATIONSHIP_SERVICE_OPTIONS,
ResourceInterface::TYPE_SHIPMENT => self::RELATIONSHIP_COLLI,
ResourceInterface::TYPE_SHIPMENT_STATUS => self::RELATIONSHIP_STATUS,
ResourceInterface::TYPE_SHOP => self::RELATIONSHIP_SHOP,
ResourceInterface::TYPE_COLLECTION => self::RELATIONSHIP_COLLECTION,
Expand Down Expand Up @@ -113,6 +116,7 @@ class Shipment implements ShipmentInterface
'currency' => null,
],
self::ATTRIBUTE_TAGS => null,
self::ATTRIBUTE_COLLO_NUMBER => null,
];

private array $relationships = [
Expand Down Expand Up @@ -143,6 +147,9 @@ class Shipment implements ShipmentInterface
self::RELATIONSHIP_SHIPMENT_SURCHARGES => [
'data' => [],
],
self::RELATIONSHIP_COLLI => [
'data' => [],
],
];

private array $meta = [
Expand Down Expand Up @@ -570,6 +577,16 @@ public function addFile(FileInterface $file): self

public function getFiles(string $type = null): array
{
// For multi-colli `master` shipments we make this function return all files from the related `colli` shipments.
if (!empty($this->getColli())) {
$colliFiles = array_map(
fn (ShipmentInterface $collo) => $collo->getFiles($type),
$this->getColli(),
);

return array_merge(...$colliFiles);
}

if ($type === null) {
return $this->relationships[self::RELATIONSHIP_FILES]['data'];
}
Expand Down Expand Up @@ -821,4 +838,42 @@ public function getShipmentSurcharges(): array
{
return $this->relationships[self::RELATIONSHIP_SHIPMENT_SURCHARGES]['data'];
}

public function setColli(array $colli): self
{
$this->relationships[self::RELATIONSHIP_COLLI]['data'] = [];

array_walk($colli, function ($collo) {
$this->addCollo($collo);
});

return $this;
}

public function addCollo(ShipmentInterface $collo): self
{
$this->relationships[self::RELATIONSHIP_COLLI]['data'][] = $collo;

return $this;
}

public function getColli(): array
{
return $this->relationships[self::RELATIONSHIP_COLLI]['data'];
}

/**
* @internal Method to process our API response. You should not set your own collo number on a shipment.
*/
public function setColloNumber(?int $colloNumber): self
{
$this->attributes[self::ATTRIBUTE_COLLO_NUMBER] = $colloNumber;

return $this;
}

public function getColloNumber(): ?int
{
return $this->attributes[self::ATTRIBUTE_COLLO_NUMBER];
}
}
12 changes: 12 additions & 0 deletions src/Resources/Traits/ProxiesResource.php
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,18 @@ public function setResource(ResourceInterface $resource): self
return $this;
}

public function setResourceFromIncludes(array $includedResources): self
{
foreach ($includedResources as $resource) {
if ($this->getType() === $resource->getType() && $this->getId() === $resource->getId()) {
$this->resource = $resource;
break;
}
}

return $this;
}

/**
* Get the resource that this instance is a proxy for.
*/
Expand Down
Loading

0 comments on commit 4361a0e

Please sign in to comment.