commerce_amws-8.x-1.x-dev/modules/shipping/src/EntitySync/FeedPostOrderFulfillmentDataClient.php

modules/shipping/src/EntitySync/FeedPostOrderFulfillmentDataClient.php
<?php

namespace Drupal\commerce_amws_shipping\EntitySync;

use Drupal\commerce_amws\Entity\StoreInterface;
use Drupal\commerce_amws\MachineName\Field\Operation as AmwsOperationField;
use Drupal\commerce_amws_feed\Content\XmlBuilderInterface as FeedContentBuilderInterface;
use Drupal\entity_sync\Client\ClientInterface as EntitySyncClientInterface;
use Drupal\entity_sync\Entity\OperationInterface;
use Drupal\entity_sync\MachineName\Field\Operation as OperationField;

use Drupal\Core\Entity\EntityTypeManagerInterface;

use SellingPartnerApi\Api\FeedsApi;
use SellingPartnerApi\Document;
use SellingPartnerApi\FeedType;
use SellingPartnerApi\Model\Feeds\CreateFeedDocumentSpecification;
use SellingPartnerApi\Model\Feeds\CreateFeedSpecification;
use SellingPartnerApi\Model\Feeds\Feed;

/**
 * Adapter that bridges Entity Sync to the Amazon MWS SPI-API client.
 *
 * phpcs:disable
 * @I Extract common functionality to a base feed client
 *    type     : task
 *    priority : normal
 *    labels   : modularity
 * phpcs:enable
 */
class FeedPostOrderFulfillmentDataClient implements EntitySyncClientInterface {

  /**
   * The entity type manager.
   *
   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
   */
  protected $entityTypeManager;

  /**
   * The XML feed content builder.
   *
   * @var \Drupal\commerce_amws_feed\Content\XmlBuilderInterface
   */
  protected $feedContentBuilder;

  /**
   * The Feeds API.
   *
   * @var \SellingPartnerApi\Api\FeedsApi
   */
  protected $feedsApi;

  /**
   * The Amazon MWS store that the feed will be submitted to.
   *
   * @var \Drupal\commerce_amws\Entity\StoreInterface
   */
  protected $amwsStore;

  /**
   * Constructs a new FeedPostOrderFulfilmentDataClient object.
   *
   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
   *   The entity type manager.
   * @param \Drupal\commerce_amws_feed\Content\XmlBuilderInterface $feed_content_builder
   *   The XML feed content builder.
   * @param \SellingPartnerApi\Api\FeedsApi $feeds_api
   *   The Feeds API client.
   * @param \Drupal\commerce_amws\Entity\StoreInterface $amws_store
   *   The Amazon MWS store that the feed will be submitted to.
   */
  public function __construct(
    EntityTypeManagerInterface $entity_type_manager,
    FeedContentBuilderInterface $feed_content_builder,
    FeedsApi $feeds_api,
    StoreInterface $amws_store
  ) {
    $this->entityTypeManager = $entity_type_manager;
    $this->feedContentBuilder = $feed_content_builder;
    $this->feedsApi = $feeds_api;
    $this->amwsStore = $amws_store;
  }

  /**
   * {@inheritdoc}
   */
  public function importList(array $filters = [], array $options = []) {
    throw new \Exception(
      'Importing a list of Amazon MWS shipments is not supported.'
    );
  }

  /**
   * {@inheritdoc}
   */
  public function importEntity($id) {
    throw new \Exception('Importing an Amazon MWS shipment is not supported.');
  }

  /**
   * {@inheritdoc}
   */
  public function create(array $fields) {
    $feed_type = FeedType::POST_ORDER_FULFILLMENT_DATA;
    $content = $this->feedContentBuilder->build(
      $this->buildContent($fields)
    );

    // First, we create the document that will be submitted with the feed, and
    // upload its content.
    $document_response = $this->feedsApi->createFeedDocument(
      new CreateFeedDocumentSpecification(
        ['content_type' => $feed_type['contentType']]
      )
    );
    $feed_document_id = $document_response->getFeedDocumentId();

    $document = new Document($document_response, $feed_type);
    $document->upload($content);

    // Then, we create and submit the feed.
    $feed_response = $this->feedsApi->createFeed(
      new CreateFeedSpecification(
        [
          'feed_type' => $feed_type['name'],
          'marketplace_ids' => [$this->amwsStore->getMarketplaceId()],
          'input_feed_document_id' => $feed_document_id,
        ]
      )
    );
    $feed_id = $feed_response->getFeedId();

    // We store the feed and document IDs in the response so that even
    // subscribers can store them in the operation entity fields.
    $response = new \stdClass();
    $response->feedId = $feed_id;
    $response->feedDocumentId = $feed_document_id;

    return $response;
  }

  /**
   * {@inheritdoc}
   */
  public function update($id, array $fields) {
    throw new \Exception(
      'Updating an already submitted Feed for an Amazon MWS shipment is not supported. A new Feed should be submitted instead.'
    );
  }

  /**
   * Fetches and responds to updates of the operation in the remote system.
   *
   * @param \Drupal\entity_sync\Entity\OperationInterface $operation
   *   The operation.
   * phpcs:disable
   * @I Considering creating a generic interface method in Entity Sync
   *    type     : task
   *    priority : normal
   *    labels   : modularity
   *
   * @I Design might be more consistent if we only fetch and format results here
   *    type     : task
   *    priority : normal
   *    labels   : code-structure
   * phpcs:enable
   */
  public function fetchFeedUpdates(OperationInterface $operation) {
    $remote_id_field = $operation->get(OperationField::REMOTE_ID);
    // If for whatever reason do no have the feed ID we cannot get the feed
    // updates anymore. Move the operation to the disconnected state so that the
    // update manager does not keep picking it up as pending updates.
    if ($remote_id_field->isEmpty()) {
      $operation->get(OperationField::STATE)
        ->first()
        ->applyTransitionById('disconnect');
      $this->entityTypeManager
        ->getStorage('entity_sync_operation')
        ->save($operation);
      return;
    }

    $feed = $this->feedsApi->getFeed(
      $remote_id_field->first()->getValue()['value']
    );

    $remote_state = $feed->getProcessingStatus();
    $transition_id = $this->getOperationTransitionFromRemoteState(
      $operation,
      $remote_state
    );
    // If we are on the same state i.e. processing has not progressed yet on the
    // remote system there's no updates to make to the operation.
    if (!$transition_id) {
      return;
    }

    $operation->set(
      AmwsOperationField::RESULT_FEED_DOCUMENT_ID,
      $feed->getResultFeedDocumentId()
    );

    $operation->set(OperationField::REMOTE_STATE, $remote_state);
    $operation->get(OperationField::STATE)
      ->first()
      ->applyTransitionById($transition_id);

    $this->entityTypeManager
      ->getStorage('entity_sync_operation')
      ->save($operation);
  }

  /**
   * Builds the content for the feed document.
   *
   * @param array $fields
   *   An associative array containing the values required for building the
   *   feed content.
   *
   * @return array
   *   The content in array format, as required for passing it to the XML
   *   builder.
   */
  protected function buildContent(array $fields) {
    $message = [
      'OrderFulfillment' => [
        'AmazonOrderID' => $fields['AmazonOrderID'],
      ],
    ];

    if ($fields['FulfillmentDate']) {
      $message['OrderFulfillment']['FulfillmentDate'] = $fields['FulfillmentDate'];
    }
    if ($fields['FulfillmentData']) {
      $message['OrderFulfillment']['FulfillmentData'] = $fields['FulfillmentData'];
    }
    if ($fields['Item']) {
      $message['OrderFulfillment']['Item'] = $fields['Item'];
    }

    return [
      'Header' => [
        'MerchantIdentifier' => $fields['MerchantIdentifier'],
      ],
      'MessageType' => 'OrderFulfillment',
      'Message' => [$message],
    ];
  }

  /**
   * Determines the transition that should be applied to the given operation.
   *
   * @param \Drupal\entity_sync\Entity\OperationInterface $operation
   *   The operation.
   * @param string $remote_state
   *   The remote state of the operation.
   *
   * @return string|null
   *   The ID of the transition, of NULL if no transition should be applied to
   *   the operation.
   */
  protected function getOperationTransitionFromRemoteState(
    OperationInterface $operation,
    string $remote_state
  ) {
    // If we are here we must be in either the `submission_completed` or
    // in the `feed_in_progress` state.
    $state = $operation->get(OperationField::STATE)->first()->getId();

    switch ($remote_state) {
      // If the remote state is `IN_QUEUE` the local state must still be
      // `submission_completed`. There's no transition to be made.
      case Feed::PROCESSING_STATUS_IN_QUEUE:
        return;

      // If the remote state is `IN_PROGRESS` the local state must still be
      // `submission_completed` or already `feed_in_progress`. We transition to
      // `feed_in_progress` in the former case.
      case Feed::PROCESSING_STATUS_IN_PROGRESS:
        return $state === 'submission_completed' ? 'process_feed' : NULL;

      // If the remote state is `IN_PROGRESS` the local state must still be
      // `submission_completed`. We transition to `feed_cancelled`.
      case Feed::PROCESSING_STATUS_CANCELLED:
        return 'cancel_feed';

      // In all other cases the local state must be either
      // `submission_completed` or `feed_in_progress`. We transition according
      // to the remote state.
      case Feed::PROCESSING_STATUS_DONE:
        return 'complete_feed';

      case Feed::PROCESSING_STATUS_FATAL:
        return 'fail_feed';
    }
  }

}

Главная | Обратная связь

drupal hosting | друпал хостинг | it patrol .inc