Refactor PurchaseUnitSanitizer to subtract 0.01 instead of rounding down

Refactor PurchaseUnitSanitizer to use singleton pattern
This commit is contained in:
Pedro Silva 2023-08-09 11:48:49 +01:00
parent dea55e24c4
commit bb3b8553b9
No known key found for this signature in database
GPG key ID: E2EE20C0669D24B3
8 changed files with 70 additions and 61 deletions

View file

@ -0,0 +1,33 @@
<?php
namespace WooCommerce\PayPalCommerce\Vendor\Pattern;
trait SingletonTrait {
/**
* The single instance of the class.
*
* @var self
*/
protected static $instance = null;
/**
* Static method to get the instance of the Singleton class
*
* @return self|null
*/
public static function get_instance(): ?self {
return self::$instance;
}
/**
* Static method to get the instance of the Singleton class
*
* @param self $instance
* @return self
*/
protected static function set_instance( self $instance ): self {
self::$instance = $instance;
return self::$instance;
}
}

View file

@ -824,11 +824,14 @@ return array(
return new OrderTransient( $cache, $purchase_unit_sanitizer );
},
'api.helper.purchase-unit-sanitizer' => static function( ContainerInterface $container ): PurchaseUnitSanitizer {
if ( $instance = PurchaseUnitSanitizer::get_instance() ) {
return $instance;
}
$settings = $container->get( 'wcgateway.settings' );
assert( $settings instanceof Settings );
$behavior = $settings->has( 'subtotal_mismatch_behavior' ) ? $settings->get( 'subtotal_mismatch_behavior' ) : null;
$line_name = $settings->has( 'subtotal_mismatch_line_name' ) ? $settings->get( 'subtotal_mismatch_line_name' ) : null;
return new PurchaseUnitSanitizer( $behavior, $line_name );
return PurchaseUnitSanitizer::singleton( $behavior, $line_name );
},
);

View file

@ -201,13 +201,12 @@ class Item {
/**
* Returns the object as array.
*
* @param bool $round_to_floor If value rounding should be floor.
* @return array
*/
public function to_array( bool $round_to_floor = false ): array {
public function to_array(): array {
$item = array(
'name' => $this->name(),
'unit_amount' => $this->unit_amount()->to_array( $round_to_floor ),
'unit_amount' => $this->unit_amount()->to_array(),
'quantity' => $this->quantity(),
'description' => $this->description(),
'sku' => $this->sku(),

View file

@ -62,11 +62,10 @@ class Money {
/**
* The value formatted as string for API requests.
*
* @param bool $round_to_floor If value rounding should be floor.
* @return string
*/
public function value_str( bool $round_to_floor = false ): string {
return $this->money_formatter->format( $this->value, $this->currency_code, $round_to_floor );
public function value_str(): string {
return $this->money_formatter->format( $this->value, $this->currency_code );
}
/**
@ -81,22 +80,12 @@ class Money {
/**
* Returns the object as array.
*
* @param bool $round_to_floor If value rounding should be floor.
* @return array
*/
public function to_array( bool $round_to_floor = false ): array {
public function to_array(): array {
return array(
'currency_code' => $this->currency_code(),
'value' => $this->value_str( $round_to_floor ),
'value' => $this->value_str(),
);
}
/**
* Indicates if the default rounding for this value is rounding up.
*
* @return bool
*/
public function is_rounding_up(): bool {
return (float) $this->value_str() > $this->value;
}
}

View file

@ -336,7 +336,7 @@ class PurchaseUnit {
}
if ( $sanitize_output && isset( $this->sanitizer ) ) {
$purchase_unit = ( $this->sanitizer->sanitize( $purchase_unit, $this->items(), $allow_ditch_items ) );
$purchase_unit = ( $this->sanitizer->sanitize( $purchase_unit, $allow_ditch_items ) );
}
return $purchase_unit;

View file

@ -25,31 +25,12 @@ class MoneyFormatter {
*
* @param float $value The value.
* @param string $currency The 3-letter currency code.
* @param bool $round_to_floor If value rounding should be floor.
*
* @return string
*/
public function format( float $value, string $currency, bool $round_to_floor = false ): string {
if ( $round_to_floor ) {
return in_array( $currency, $this->currencies_without_decimals, true )
? (string) floor( $value )
: number_format( $this->floor_with_decimals( $value, 2 ), 2, '.', '' );
}
public function format( float $value, string $currency ): string {
return in_array( $currency, $this->currencies_without_decimals, true )
? (string) round( $value, 0 )
: number_format( $value, 2, '.', '' );
}
/**
* Rounds to floor with decimal precision.
*
* @param float $value The value.
* @param int $decimals The number of decimals.
* @return float
*/
private function floor_with_decimals( float $value, int $decimals = 0 ): float {
$adjustment = (float) pow( 10, $decimals );
return floor( $value * $adjustment ) / $adjustment;
}
}

View file

@ -19,11 +19,14 @@ namespace WooCommerce\PayPalCommerce\ApiClient\Helper;
use WooCommerce\PayPalCommerce\ApiClient\Entity\Item;
use WooCommerce\PayPalCommerce\ApiClient\Entity\Money;
use WooCommerce\PayPalCommerce\Vendor\Pattern\SingletonTrait;
/**
* Class PurchaseUnitSanitizer
*/
class PurchaseUnitSanitizer {
use SingletonTrait;
const MODE_DITCH = 'ditch';
const MODE_EXTRA_LINE = 'extra_line';
const VALID_MODES = array(
@ -40,13 +43,6 @@ class PurchaseUnitSanitizer {
*/
private $purchase_unit = array();
/**
* The purchase unit Item objects
*
* @var array|Item[]
*/
private $item_objects = array();
/**
* Whether to allow items to be ditched.
*
@ -95,6 +91,17 @@ class PurchaseUnitSanitizer {
$this->extra_line_name = $extra_line_name;
}
/**
* PurchaseUnitSanitizer singleton.
*
* @param string|null $mode The mismatch handling mode, ditch or extra_line.
* @param string|null $extra_line_name The name of the extra line.
* @return self
*/
public static function singleton( string $mode = null, string $extra_line_name = null ): self {
return self::set_instance( new self( $mode, $extra_line_name ) );
}
/**
* Indicates if mode is ditch.
*
@ -166,13 +173,11 @@ class PurchaseUnitSanitizer {
* The sanitizes the purchase_unit array.
*
* @param array $purchase_unit The purchase_unit array that should be sanitized.
* @param array|Item[] $item_objects The purchase unit Item objects used for recalculations.
* @param bool $allow_ditch_items Whether to allow items to be ditched.
* @return array
*/
public function sanitize( array $purchase_unit, array $item_objects, bool $allow_ditch_items = true ): array {
public function sanitize( array $purchase_unit, bool $allow_ditch_items = true ): array {
$this->purchase_unit = $purchase_unit;
$this->item_objects = $item_objects;
$this->allow_ditch_items = $allow_ditch_items;
$this->sanitize_item_amount_mismatch();
@ -193,10 +198,9 @@ class PurchaseUnitSanitizer {
if ( $item_mismatch < 0 ) {
// Do floors on item amounts so item_mismatch is a positive value.
foreach ( $this->item_objects as $index => $item ) {
$this->purchase_unit['items'][ $index ] = $item->to_array(
$item->unit_amount()->is_rounding_up()
);
foreach ( $this->purchase_unit['items'] as $index => $item ) {
// get a more intelligent adjustment mechanism
$this->purchase_unit['items'][ $index ]['unit_amount']['value'] = ( (float) $this->purchase_unit['items'][ $index ]['unit_amount']['value'] ) - 0.01;
}
}

View file

@ -662,11 +662,11 @@ class PurchaseUnitTest extends TestCase
'insurance' => null,
],
],
'with_rounding_down_only_first_item' => [
'message' => 'Extra line should be added with price 0.01 and lines amount 10.00 and 5.00.',
'with_two_rounding_down' => [
'message' => 'Extra line should be added with price 0.03 and lines amount 10.00 and 4.99.',
'expected' => [
'item_value' => [10.00, 5.00],
'extra_line_value' => 0.01
'item_value' => [10.00, 4.99],
'extra_line_value' => 0.03
],
'items' => [
[
@ -693,11 +693,11 @@ class PurchaseUnitTest extends TestCase
'insurance' => null,
],
],
'with_multiple_roundings_down' => [
'with_many_roundings_down' => [
'message' => 'Extra line should be added with price 0.01 and lines amount 10.00, 5.00 and 6.66.',
'expected' => [
'item_value' => [10.00, 5.00, 6.66],
'extra_line_value' => 0.01
'item_value' => [10.00, 4.99, 6.66],
'extra_line_value' => 0.02
],
'items' => [
[