woocommerce-paypal-payments/modules/ppcp-api-client/src/Entity/PurchaseUnit.php
Alex P 770f8e558b Do not send tax in each item
Looks like it is not displayed anyway, and it causes mismatch + cannot send shipping/discount taxes as tax
2022-06-15 10:14:42 +03:00

407 lines
8.2 KiB
PHP

<?php
/**
* The purchase unit object.
*
* @package WooCommerce\PayPalCommerce\ApiClient\Entity
*/
declare(strict_types=1);
namespace WooCommerce\PayPalCommerce\ApiClient\Entity;
/**
* Class PurchaseUnit
*/
class PurchaseUnit {
/**
* The amount.
*
* @var Amount
*/
private $amount;
/**
* The Items.
*
* @var Item[]
*/
private $items;
/**
* The shipping.
*
* @var Shipping|null
*/
private $shipping;
/**
* The reference id.
*
* @var string
*/
private $reference_id;
/**
* The description.
*
* @var string
*/
private $description;
/**
* The Payee.
*
* @var Payee|null
*/
private $payee;
/**
* The custom id.
*
* @var string
*/
private $custom_id;
/**
* The invoice id.
*
* @var string
*/
private $invoice_id;
/**
* The soft descriptor.
*
* @var string
*/
private $soft_descriptor;
/**
* The Payments.
*
* @var Payments|null
*/
private $payments;
/**
* Whether the unit contains physical goods.
*
* @var bool
*/
private $contains_physical_goods = false;
/**
* PurchaseUnit constructor.
*
* @param Amount $amount The Amount.
* @param Item[] $items The Items.
* @param Shipping|null $shipping The Shipping.
* @param string $reference_id The reference ID.
* @param string $description The description.
* @param Payee|null $payee The Payee.
* @param string $custom_id The custom ID.
* @param string $invoice_id The invoice ID.
* @param string $soft_descriptor The soft descriptor.
* @param Payments|null $payments The Payments.
*/
public function __construct(
Amount $amount,
array $items = array(),
Shipping $shipping = null,
string $reference_id = 'default',
string $description = '',
Payee $payee = null,
string $custom_id = '',
string $invoice_id = '',
string $soft_descriptor = '',
Payments $payments = null
) {
$this->amount = $amount;
$this->shipping = $shipping;
$this->reference_id = $reference_id;
$this->description = $description;
//phpcs:disable Inpsyde.CodeQuality.ArgumentTypeDeclaration.NoArgumentType
$this->items = array_values(
array_filter(
$items,
function ( $item ): bool {
$is_item = is_a( $item, Item::class );
/**
* The item.
*
* @var Item $item
*/
if ( $is_item && Item::PHYSICAL_GOODS === $item->category() ) {
$this->contains_physical_goods = true;
}
return $is_item;
}
)
);
$this->payee = $payee;
$this->custom_id = $custom_id;
$this->invoice_id = $invoice_id;
$this->soft_descriptor = $soft_descriptor;
$this->payments = $payments;
}
/**
* Returns the amount.
*
* @return Amount
*/
public function amount(): Amount {
return $this->amount;
}
/**
* Sets the amount.
*
* @param Amount $amount The value to set.
*/
public function set_amount( Amount $amount ): void {
$this->amount = $amount;
}
/**
* Returns the shipping.
*
* @return Shipping|null
*/
public function shipping() {
return $this->shipping;
}
/**
* Sets shipping info.
*
* @param Shipping|null $shipping The value to set.
*/
public function set_shipping( ?Shipping $shipping ): void {
$this->shipping = $shipping;
}
/**
* Returns the reference id.
*
* @return string
*/
public function reference_id(): string {
return $this->reference_id;
}
/**
* Returns the description.
*
* @return string
*/
public function description(): string {
return $this->description;
}
/**
* Returns the custom id.
*
* @return string
*/
public function custom_id(): string {
return $this->custom_id;
}
/**
* Returns the invoice id.
*
* @return string
*/
public function invoice_id(): string {
return $this->invoice_id;
}
/**
* Returns the soft descriptor.
*
* @return string
*/
public function soft_descriptor(): string {
return $this->soft_descriptor;
}
/**
* Returns the Payee.
*
* @return Payee|null
*/
public function payee() {
return $this->payee;
}
/**
* Returns the Payments.
*
* @return Payments|null
*/
public function payments() {
return $this->payments;
}
/**
* Returns the Items.
*
* @return Item[]
*/
public function items(): array {
return $this->items;
}
/**
* Whether the unit contains physical goods.
*
* @return bool
*/
public function contains_physical_goods(): bool {
return $this->contains_physical_goods;
}
/**
* Returns the object as array.
*
* @return array
*/
public function to_array(): array {
$purchase_unit = array(
'reference_id' => $this->reference_id(),
'amount' => $this->amount()->to_array(),
'description' => $this->description(),
'items' => array_map(
static function ( Item $item ): array {
return $item->to_array();
},
$this->items()
),
);
if ( $this->ditch_items_when_mismatch( $this->amount(), ...$this->items() ) ) {
unset( $purchase_unit['items'] );
unset( $purchase_unit['amount']['breakdown'] );
}
if ( $this->payee() ) {
$purchase_unit['payee'] = $this->payee()->to_array();
}
if ( $this->payments() ) {
$purchase_unit['payments'] = $this->payments()->to_array();
}
if ( $this->shipping() ) {
$purchase_unit['shipping'] = $this->shipping()->to_array();
}
if ( $this->custom_id() ) {
$purchase_unit['custom_id'] = $this->custom_id();
}
if ( $this->invoice_id() ) {
$purchase_unit['invoice_id'] = $this->invoice_id();
}
if ( $this->soft_descriptor() ) {
$purchase_unit['soft_descriptor'] = $this->soft_descriptor();
}
return $purchase_unit;
}
/**
* All money values send to PayPal can only have 2 decimal points. WooCommerce internally does
* not have this restriction. Therefore the totals of the cart in WooCommerce and the totals
* of the rounded money values of the items, we send to PayPal, can differ. In those cases,
* we can not send the line items.
*
* @param Amount $amount The amount.
* @param Item ...$items The items.
* @return bool
*/
private function ditch_items_when_mismatch( Amount $amount, Item ...$items ): bool {
$breakdown = $amount->breakdown();
if ( ! $breakdown ) {
return false;
}
$item_total = $breakdown->item_total();
if ( $item_total ) {
$remaining_item_total = array_reduce(
$items,
function ( float $total, Item $item ): float {
return $total - $item->unit_amount()->value() * (float) $item->quantity();
},
$item_total->value()
);
$remaining_item_total = round( $remaining_item_total, 2 );
if ( 0.0 !== $remaining_item_total ) {
return true;
}
}
$tax_total = $breakdown->tax_total();
$items_with_tax = array_filter(
$this->items,
function ( Item $item ): bool {
return null !== $item->tax();
}
);
if ( $tax_total && ! empty( $items_with_tax ) ) {
$remaining_tax_total = array_reduce(
$items,
function ( float $total, Item $item ): float {
$tax = $item->tax();
if ( $tax ) {
$total -= $tax->value() * (float) $item->quantity();
}
return $total;
},
$tax_total->value()
);
$remaining_tax_total = round( $remaining_tax_total, 2 );
if ( 0.0 !== $remaining_tax_total ) {
return true;
}
}
$shipping = $breakdown->shipping();
$discount = $breakdown->discount();
$shipping_discount = $breakdown->shipping_discount();
$handling = $breakdown->handling();
$insurance = $breakdown->insurance();
$amount_total = 0.0;
if ( $shipping ) {
$amount_total += $shipping->value();
}
if ( $item_total ) {
$amount_total += $item_total->value();
}
if ( $discount ) {
$amount_total -= $discount->value();
}
if ( $tax_total ) {
$amount_total += $tax_total->value();
}
if ( $shipping_discount ) {
$amount_total -= $shipping_discount->value();
}
if ( $handling ) {
$amount_total += $handling->value();
}
if ( $insurance ) {
$amount_total += $insurance->value();
}
$amount_str = (string) $amount->to_array()['value'];
$amount_total_str = (string) ( new Money( $amount_total, $amount->currency_code() ) )->to_array()['value'];
$needs_to_ditch = $amount_str !== $amount_total_str;
return $needs_to_ditch;
}
}