You are here

public function AvataxLib::prepareTransactionsCreate in Drupal Commerce Connector for AvaTax 8

Prepares the transaction request body. (This method should not be public but that makes the tests easier).

Parameters

\Drupal\commerce_order\Entity\OrderInterface $order: The order.

string $type: The transactions type (e.g SalesOrder|SalesInvoice).

Return value

array The request parameters array.

Overrides AvataxLibInterface::prepareTransactionsCreate

1 call to AvataxLib::prepareTransactionsCreate()
AvataxLib::transactionsCreate in src/AvataxLib.php
Creates a new transaction (/api/v2/transactions/create).

File

src/AvataxLib.php, line 178

Class

AvataxLib
The AvaTax integration library.

Namespace

Drupal\commerce_avatax

Code

public function prepareTransactionsCreate(OrderInterface $order, $type = 'SalesOrder') {
  $store = $order
    ->getStore();

  // Attempt to get company code for specific store, otherwise, fallback to
  // the company code configured in the settings.
  if ($store
    ->get('avatax_company_code')
    ->isEmpty()) {
    $company_code = $this->config
      ->get('company_code');
  }
  else {
    $company_code = $store
      ->get('avatax_company_code')->value;
  }
  $date = new DrupalDateTime();

  // Gather all the adjustment types.
  $adjustment_types = array_keys($this->adjustmentTypeManager
    ->getDefinitions());
  $customer = $order
    ->getCustomer();
  $currency_code = $order
    ->getTotalPrice() ? $order
    ->getTotalPrice()
    ->getCurrencyCode() : $store
    ->getDefaultCurrencyCode();
  $request_body = [
    'type' => $type,
    'companyCode' => $company_code,
    'date' => $date
      ->format('c'),
    'code' => 'DC-' . $order
      ->uuid(),
    'currencyCode' => $currency_code,
    'lines' => [],
  ];

  // Pass the tax exemption number|type if not empty.
  if (!$customer
    ->isAnonymous()) {
    if ($customer
      ->hasField('avatax_tax_exemption_number') && !$customer
      ->get('avatax_tax_exemption_number')
      ->isEmpty()) {
      $request_body['ExemptionNo'] = $customer
        ->get('avatax_tax_exemption_number')->value;
    }
    if ($customer
      ->hasField('avatax_tax_exemption_type') && !$customer
      ->get('avatax_tax_exemption_type')
      ->isEmpty()) {
      $request_body['CustomerUsageType'] = $customer
        ->get('avatax_tax_exemption_type')->value;
    }
    if ($customer
      ->hasField('avatax_customer_code') && !$customer
      ->get('avatax_customer_code')
      ->isEmpty()) {
      $request_body['customerCode'] = $customer
        ->get('avatax_customer_code')->value;
    }
    else {
      $customer_code_field = $this->config
        ->get('customer_code_field');

      // For authenticated users, if the avatax_customer_code field is empty,
      // use the field configured in config (mail|uid).
      if ($order
        ->hasField($customer_code_field) && !$order
        ->get($customer_code_field)
        ->isEmpty()) {
        $customer_code = $customer_code_field === 'mail' ? $order
          ->getEmail() : $order
          ->getCustomerId();
        $request_body['customerCode'] = $customer_code;
      }
    }
  }

  // If the customer code could not be determined (either because the customer
  // is anonymous or the mail is empty, fallback to the logic below).
  if (!isset($request_body['customerCode'])) {
    $request_body['customerCode'] = $order
      ->getEmail() ?: 'anonymous-' . $order
      ->id();
  }
  $has_shipments = $order
    ->hasField('shipments') && !$order
    ->get('shipments')
    ->isEmpty();
  foreach ($order
    ->getItems() as $order_item) {
    $profile = $this
      ->resolveCustomerProfile($order_item);

    // If we could not resolve a profile for the order item, do not add it
    // to the API request. There may not be an address available yet, or the
    // item may not be shippable and not attached to a shipment.
    if (!$profile) {
      continue;
    }
    $purchased_entity = $order_item
      ->getPurchasedEntity();

    /** @var \Drupal\address\Plugin\Field\FieldType\AddressItem $address */
    $address = $profile
      ->get('address')
      ->first();
    $line_item = [
      'number' => $order_item
        ->uuid(),
      'quantity' => $order_item
        ->getQuantity(),
      // When the transaction request is performed when an order is placed,
      // the order item already has a tax adjustment that we shouldn't send
      // to AvaTax.
      'amount' => $order_item
        ->getAdjustedTotalPrice(array_diff($adjustment_types, [
        'tax',
      ]))
        ->getNumber(),
    ];

    // Send the "SKU" as the "itemCode".
    if ($purchased_entity instanceof ProductVariationInterface) {
      $line_item['itemCode'] = $purchased_entity
        ->getSku();
    }
    if ($has_shipments) {
      $line_item['addresses'] = [
        'shipFrom' => self::formatAddress($store
          ->getAddress()),
        'shipTo' => self::formatAddress($address),
      ];
    }
    else {
      $line_item['addresses']['singleLocation'] = self::formatAddress($address);
    }
    $line_item['taxCode'] = $this->chainTaxCodeResolver
      ->resolve($order_item);
    $request_body['lines'][] = $line_item;
  }
  if ($has_shipments) {

    /** @var \Drupal\commerce_shipping\Entity\ShipmentInterface $shipment */
    foreach ($order
      ->get('shipments')
      ->referencedEntities() as $shipment) {
      if (is_null($shipment
        ->getAmount())) {
        continue;
      }
      $request_body['lines'][] = [
        'taxCode' => $this->config
          ->get('shipping_tax_code'),
        'number' => $shipment
          ->uuid(),
        'description' => $shipment
          ->label(),
        'amount' => $shipment
          ->getAmount()
          ->getNumber(),
        'quantity' => 1,
        'addresses' => [
          'shipFrom' => self::formatAddress($store
            ->getAddress()),
          'shipTo' => self::formatAddress($shipment
            ->getShippingProfile()
            ->get('address')
            ->first()),
        ],
      ];
    }
  }

  // Send additional order adjustments as separate lines.
  foreach ($order
    ->getAdjustments() as $adjustment) {

    // Skip shipping, fees and tax adjustments.
    if (in_array($adjustment
      ->getType(), [
      'shipping',
      'fee',
      'tax',
    ])) {
      continue;
    }
    $line_item = [
      // @todo: Figure out which taxCode to use here.
      'taxCode' => 'P0000000',
      'description' => $adjustment
        ->getLabel(),
      'amount' => $adjustment
        ->getAmount()
        ->getNumber(),
      'quantity' => 1,
      'addresses' => [
        'shipFrom' => self::formatAddress($store
          ->getAddress()),
      ],
    ];

    // Take the "shipTo" from the first line if present, otherwise just ignore
    // the adjustment, because sending lines without an "addresses" key
    // is only possible when a global "addresses" is specified at the
    // document level, which isn't the case here.
    if (isset($request_body['lines'][0]['addresses']['shipTo'])) {
      $line_item['addresses']['shipTo'] = $request_body['lines'][0]['addresses']['shipTo'];
      $request_body['lines'][] = $line_item;
    }
  }
  if ($request_body['type'] === 'SalesInvoice') {
    $request_body['commit'] = TRUE;
  }
  $this->moduleHandler
    ->alter('commerce_avatax_order_request', $request_body, $order);
  return $request_body;
}