commerce_xero-8.x-1.x-dev/src/Plugin/CommerceXero/type/Invoice.php
src/Plugin/CommerceXero/type/Invoice.php
<?php
namespace Drupal\commerce_xero\Plugin\CommerceXero\type;
use Drupal\Core\StringTranslation\TranslatableMarkup;
use Drupal\Core\Url;
use Drupal\commerce_order\Entity\OrderItemInterface;
use Drupal\commerce_payment\Entity\PaymentInterface;
use Drupal\commerce_xero\Attribute\CommerceXeroDataType;
use Drupal\commerce_xero\Entity\CommerceXeroStrategyInterface;
use Drupal\commerce_xero\OrderDataTransformationTrait;
use Drupal\commerce_xero\Plugin\CommerceXero\CommerceXeroDataTypePluginBase;
use Drupal\xero\TypedData\XeroItemInterface;
/**
* Provides a commerce xero invoice type.
*
* @CommerceXeroDataType(
* id = "commerce_xero_invoice",
* label = @Translation("Invoice"),
* type = "xero_invoice",
* settings = { }
* )
*/
#[CommerceXeroDataType(
id: 'commerce_xero_invoice',
label: new TranslatableMarkup('Invoice'),
type: 'xero_invoice',
)]
class Invoice extends CommerceXeroDataTypePluginBase {
use OrderDataTransformationTrait;
/**
* {@inheritdoc}
*/
public function make(PaymentInterface $payment, CommerceXeroStrategyInterface $strategy): XeroItemInterface {
$configuration = $this->getConfiguration();
$typedDataManager = $this->getTypedDataManager();
$definition = $this->typedDataManager
->createDataDefinition($configuration['type']);
$order = $payment->getOrder();
$data = [
'Type' => 'ACCREC',
'Contact' => $this->getDefaultContactValues($order),
'Date' => date('Y-m-d', $order->getCreatedTime()),
'DueDate' => date('Y-m-d', $order->getCreatedTime()),
'SubTotal' => $order->getSubtotalPrice()->getNumber(),
'Total' => $order->getTotalPrice()->getNumber(),
'Reference' => $order->id(),
'Status' => 'AUTHORISED',
'CurrencyCode' => strtoupper($order->getBalance()->getCurrencyCode()),
'Url' => Url::fromUri(
'entity:commerce_order/' . $order->id(),
['absolute' => TRUE])->toString(),
'LineAmountTypes' => 'Exclusive',
'LineItems' => [],
];
if ($order->getOrderNumber()) {
$data['InvoiceNumber'] = $order->getOrderNumber();
}
foreach ($order->getItems() as $i => $orderItem) {
$lineItem = [
'Description' => $orderItem->getTitle(),
'Quantity' => $orderItem->getQuantity(),
'AccountCode' => $strategy->getRevenueAccountCode(),
'UnitAmount' => $orderItem->getUnitPrice()->getNumber(),
];
// Including line item inclusive taxes could be problematic here for VAT
// and GST. There's not a good way to get only adjust unit price filtering
// out just tax.
$adjusmentTypes = $this->getNonTaxAdjustmentsForLineItem($orderItem);
$unit_price_compare = $orderItem->getAdjustedUnitPrice($adjusmentTypes)->compareTo($orderItem->getUnitPrice());
if ($unit_price_compare !== 0) {
// Unit price can vary depending on if Commerce includes the discount in
// the unit price or not.
$lineItem['DiscountAmount'] = $orderItem
->getTotalPrice()
->subtract($orderItem->getAdjustedTotalPrice($adjusmentTypes))
->getNumber();
}
$data['LineItems'][] = $lineItem;
}
// Adjustments need to be added as line items unless they're included in
// the base price as long as they are NOT manually added tax-adjustments.
foreach ($order->getAdjustments() as $i => $adjustment) {
if (!$adjustment->isIncluded() && $adjustment->getType() !== 'tax') {
$data['LineItems'][] = [
'Description' => $adjustment->getLabel(),
'Quantity' => 1,
'UnitAmount' => $adjustment->getAmount()->getNumber(),
// Adjustments account code. 260 is a default "Rounding" adjustment
// that Xero classifies as a "Current Liability" Account Type.
'AccountCode' => '260',
// Adjustments are added outside of tax.
'TaxType' => 'NONE',
'TaxAmount' => 0.00,
];
}
}
$item = $typedDataManager->create($definition, $data);
assert($item instanceof XeroItemInterface);
return $item;
}
/**
* Gets the non-tax adjustments for a commerce order item.
*
* @param \Drupal\commerce_order\Entity\OrderItemInterface $item
* The order item.
*
* @return string[]
* The adjustment type strings.
*/
protected function getNonTaxAdjustmentsForLineItem(OrderItemInterface $item): array {
$adjustments = $item->getAdjustments();
$nonTaxAdjustments = array_filter($adjustments, fn ($a) => $a->getType() !== 'tax');
return array_map(fn ($a) => $a->getType(), $nonTaxAdjustments);
}
}
