Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update Name Your Price compatibility to use new Compatibility methods #7269

Merged
merged 23 commits into from
Oct 4, 2023
Merged
Show file tree
Hide file tree
Changes from 16 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
3ac6459
Add helper function/method for raw amount conversion
jessepearson Sep 13, 2023
cdf7722
Add changelog and tests
jessepearson Sep 13, 2023
f5452dd
Remove external helper function.
jessepearson Sep 14, 2023
44f1a85
Merge branch 'develop' into fix/5524-price-conversion-helper-function
jessepearson Sep 14, 2023
154edc2
Merge branch 'develop' into fix/5524-price-conversion-helper-function
jessepearson Sep 15, 2023
9adfced
Change how the conversion happens and throw exception on invalid curr…
jessepearson Sep 15, 2023
d9545cd
Add check for invalid from currency and throw exception if found
jessepearson Sep 15, 2023
fe10372
Add method for logging and throwing InvalidCurrencyException and upda…
jessepearson Sep 19, 2023
6d47fc1
Merge branch 'develop' into fix/5524-price-conversion-helper-function
jessepearson Sep 19, 2023
9ba6ffe
Merge branch 'develop' into fix/5524-price-conversion-helper-function
jessepearson Sep 21, 2023
200a9f8
Merge branch 'develop' into fix/5524-price-conversion-helper-function
jessepearson Sep 22, 2023
218dfa9
Update Name Your Price compatibility to use new Compatibility methods
jessepearson Sep 22, 2023
59f6568
Merge branch 'develop' into fix/5524-price-conversion-helper-function
jessepearson Sep 26, 2023
fb0cb14
Merge branch 'fix/5524-price-conversion-helper-function' into fix/717…
jessepearson Sep 26, 2023
83b7c3c
Merge branch 'develop' into fix/5524-price-conversion-helper-function
jessepearson Sep 29, 2023
f5ef322
Merge branch 'fix/5524-price-conversion-helper-function' into fix/717…
jessepearson Sep 29, 2023
f6866b8
Merge branch 'develop' into fix/5524-price-conversion-helper-function
jessepearson Oct 2, 2023
1a1079a
Merge branch 'fix/5524-price-conversion-helper-function' into fix/717…
jessepearson Oct 2, 2023
a33f6d7
Merge branch 'develop' into fix/5524-price-conversion-helper-function
jessepearson Oct 3, 2023
80277ef
Merge branch 'fix/5524-price-conversion-helper-function' into fix/717…
jessepearson Oct 3, 2023
4186c57
Merge branch 'develop' into fix/5524-price-conversion-helper-function
jessepearson Oct 4, 2023
5e38cef
Merge branch 'fix/5524-price-conversion-helper-function' into fix/717…
jessepearson Oct 4, 2023
8f96960
Merge branch 'develop' into fix/7172-nyp-cart-editing
jessepearson Oct 4, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions changelog/fix-5524-price-conversion-helper-function
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: patch
Type: add

Add helper function/method for raw currency amount conversion.
4 changes: 4 additions & 0 deletions changelog/fix-7172-nyp-cart-editing
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: minor
Type: update

Update Name Your Price compatibility to use new Compatibility methods.
56 changes: 46 additions & 10 deletions includes/multi-currency/Compatibility/WooCommerceNameYourPrice.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@ protected function init() {
add_action( 'woocommerce_add_cart_item_data', [ $this, 'add_initial_currency' ], 20, 3 );
add_filter( 'woocommerce_get_cart_item_from_session', [ $this, 'convert_cart_currency' ], 20, 2 );
add_filter( MultiCurrency::FILTER_PREFIX . 'should_convert_product_price', [ $this, 'should_convert_product_price' ], 50, 2 );

// Convert cart editing price.
add_filter( 'wc_nyp_edit_in_cart_args', [ $this, 'edit_in_cart_args' ], 10, 2 );
add_filter( 'wc_nyp_get_initial_price', [ $this, 'get_initial_price' ], 10, 3 );
}
}

Expand Down Expand Up @@ -92,20 +96,13 @@ public function convert_cart_currency( $cart_item, $values ) {
$cart_item['nyp'] = $cart_item['nyp_original'];
} else {

$nyp_currency = $this->multi_currency->get_enabled_currencies()[ $cart_item['nyp_currency'] ] ?? null;

// Convert entered price back to default currency.
$converted_price = ( (float) $cart_item['nyp_original'] ) / $nyp_currency->get_rate();
$from_currency = $cart_item['nyp_currency'];
$raw_price = $cart_item['nyp_original'];

if ( ! $selected_currency->get_is_default() ) {
$converted_price = $this->multi_currency->get_price( $converted_price, 'product' );
}

$cart_item['nyp'] = $converted_price;
$cart_item['nyp'] = $this->multi_currency->get_raw_conversion( $raw_price, $selected_currency->get_code(), $from_currency );
}

$cart_item = WC_Name_Your_Price()->cart->set_cart_item( $cart_item );

}

return $cart_item;
Expand Down Expand Up @@ -140,4 +137,43 @@ public function should_convert_product_price( bool $return, $product ): bool {

return $return;
}

/**
* Add currency to cart edit link.
*
* @param array $args The cart args.
* @param array $cart_item The current cart item.
*
* @return array
*/
public function edit_in_cart_args( $args, $cart_item ) {
$args['nyp_currency'] = $this->multi_currency->get_selected_currency()->get_code();
return $args;
}

/**
* Maybe convert any prices being edited from the cart
*
* @param string $initial_price The initial price.
* @param mixed $product The product being queried.
* @param string $suffix The suffix needed for composites and bundles.
*
* @return float|string
*/
public function get_initial_price( $initial_price, $product, $suffix ) {

if ( isset( $_REQUEST[ 'nyp_raw' . $suffix ] ) && isset( $_REQUEST['nyp_currency'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
fashuvo marked this conversation as resolved.
Show resolved Hide resolved
fashuvo marked this conversation as resolved.
Show resolved Hide resolved

$from_currency = wc_clean( wp_unslash( $_REQUEST['nyp_currency'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
$raw_price = (float) wc_clean( wp_unslash( $_REQUEST[ 'nyp_raw' . $suffix ] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended

$selected_currency = $this->multi_currency->get_selected_currency();

if ( $from_currency !== $selected_currency->get_code() ) {
$initial_price = $this->multi_currency->get_raw_conversion( $raw_price, $selected_currency->get_code(), $from_currency );
}
}

return $initial_price;
}
}
73 changes: 64 additions & 9 deletions includes/multi-currency/MultiCurrency.php
Original file line number Diff line number Diff line change
Expand Up @@ -491,9 +491,7 @@ public function get_store_currencies(): array {
public function get_single_currency_settings( string $currency_code ): array {
// Confirm the currency code is valid before trying to get the settings.
if ( ! array_key_exists( strtoupper( $currency_code ), $this->get_available_currencies() ) ) {
$message = 'Invalid currency passed to get_single_currency_settings: ' . $currency_code;
Logger::error( $message );
throw new InvalidCurrencyException( $message, 'wcpay_multi_currency_invalid_currency', 500 );
$this->log_and_throw_invalid_currency_exception( __FUNCTION__, $currency_code );
}

$currency_code = strtolower( $currency_code );
Expand Down Expand Up @@ -522,9 +520,7 @@ public function get_single_currency_settings( string $currency_code ): array {
public function update_single_currency_settings( string $currency_code, string $exchange_rate_type, float $price_rounding, float $price_charm, $manual_rate = null ) {
// Confirm the currency code is valid before trying to update the settings.
if ( ! array_key_exists( strtoupper( $currency_code ), $this->get_available_currencies() ) ) {
$message = 'Invalid currency passed to update_single_currency_settings: ' . $currency_code;
Logger::error( $message );
throw new InvalidCurrencyException( $message, 'wcpay_multi_currency_invalid_currency', 500 );
$this->log_and_throw_invalid_currency_exception( __FUNCTION__, $currency_code );
}

$currency_code = strtolower( $currency_code );
Expand Down Expand Up @@ -690,9 +686,7 @@ public function set_enabled_currencies( $currencies = [] ) {
// Confirm the currencies submitted are available/valid currencies.
$invalid_currencies = array_diff( $currencies, array_keys( $this->get_available_currencies() ) );
if ( 0 < count( $invalid_currencies ) ) {
$message = 'Invalid currency/currencies passed to set_enabled_currencies: ' . implode( ', ', $invalid_currencies );
Logger::error( $message );
throw new InvalidCurrencyException( $message, 'wcpay_multi_currency_invalid_currency', 500 );
$this->log_and_throw_invalid_currency_exception( __FUNCTION__, implode( ', ', $invalid_currencies ) );
}

// Get the currencies that were removed before they are updated.
Expand Down Expand Up @@ -845,6 +839,52 @@ public function get_price( $price, string $type ): float {
return $this->get_adjusted_price( $converted_price, $apply_charm_pricing, $currency );
}

/**
* Gets a raw converted amount based on the amount and currency codes passed.
* This is a helper method for external conversions, if needed.
*
* @param float $amount The amount to be converted.
* @param string $to_currency The 3 letter currency code to convert the amount to.
* @param string $from_currency The 3 letter currency code to convert the amount from.
*
* @return float The converted amount.
*
* @throws InvalidCurrencyException
* @throws InvalidCurrencyRateException
*/
public function get_raw_conversion( float $amount, string $to_currency, string $from_currency = '' ): float {
$enabled_currencies = $this->get_enabled_currencies();

// If the from_currency is not set, use the store currency.
if ( '' === $from_currency ) {
$from_currency = $this->get_default_currency()->get_code();
}

// We throw an exception if either of the currencies are not enabled.
$to_currency = strtoupper( $to_currency );
$from_currency = strtoupper( $from_currency );
foreach ( [ $to_currency, $from_currency ] as $code ) {
if ( ! isset( $enabled_currencies[ $code ] ) ) {
$this->log_and_throw_invalid_currency_exception( __FUNCTION__, $code );
}
}

// Get the rates.
$to_currency_rate = $enabled_currencies[ $to_currency ]->get_rate();
$from_currency_rate = $enabled_currencies[ $from_currency ]->get_rate();

// Throw an exception in case from_currency_rate is less than or equal to 0.
if ( 0 >= $from_currency_rate ) {
$message = 'Invalid rate for from_currency in get_raw_conversion: ' . $from_currency_rate;
Logger::error( $message );
throw new InvalidCurrencyRateException( $message, 'wcpay_multi_currency_invalid_currency_rate', 500 );
}

$amount = $amount * ( $to_currency_rate / $from_currency_rate );

return (float) $amount;
}

/**
* Recalculates WooCommerce cart totals.
*
Expand Down Expand Up @@ -1530,4 +1570,19 @@ public function has_additional_currencies_enabled(): bool {
public static function is_initialized() : bool {
return static::$is_initialized;
}

/**
* Logs a message and throws InvalidCurrencyException.
*
* @param string $method The method that's actually throwing the exception.
* @param string $currency_code The currency code that was invalid.
* @param int $code The exception code.
*
* @throws InvalidCurrencyException
*/
private function log_and_throw_invalid_currency_exception( $method, $currency_code, $code = 500 ) {
$message = 'Invalid currency passed to ' . $method . ': ' . $currency_code;
Logger::error( $message );
throw new InvalidCurrencyException( $message, 'wcpay_multi_currency_invalid_currency', $code );
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -197,13 +197,8 @@ public function test_convert_cart_currency_returns_cart_item_with_converted_valu

$this->mock_multi_currency
->expects( $this->once() )
->method( 'get_enabled_currencies' )
->willReturn( [ $item_currency->get_code() => $item_currency ] );

$this->mock_multi_currency
->expects( $this->once() )
->method( 'get_price' )
->with( $nyp_value / $item_currency->get_rate() )
->method( 'get_raw_conversion' )
->with( $nyp_value, $selected_currency->get_code(), $item_currency->get_code() )
->willReturn( $expected_value );

// Act: Attempt to convert the cart item amount.
Expand Down Expand Up @@ -242,8 +237,9 @@ public function test_convert_cart_currency_returns_cart_item_with_converted_valu

$this->mock_multi_currency
->expects( $this->once() )
->method( 'get_enabled_currencies' )
->willReturn( [ $item_currency->get_code() => $item_currency ] );
->method( 'get_raw_conversion' )
->with( $nyp_value, $selected_currency->get_code(), $item_currency->get_code() )
->willReturn( $expected_value );

// Act: Attempt to convert the cart item amount.
$cart_item = $this->woocommerce_nyp->convert_cart_currency( $cart_item, null );
Expand All @@ -253,7 +249,6 @@ public function test_convert_cart_currency_returns_cart_item_with_converted_valu

// Assert: Confirm the cart_item value matches the expected value.
$this->assertEquals( $expected_value, $cart_item['nyp'] );

}

// If the method is passed false it should return false.
Expand Down Expand Up @@ -322,6 +317,123 @@ public function test_should_convert_product_price_returns_true_when_no_matches()
$this->assertTrue( $this->woocommerce_nyp->should_convert_product_price( true, $product ) );
}

public function test_edit_in_cart_args() {
// Arrange: Set up the currency used for the test.
$selected_currency = new Currency( 'EUR', 2.0 );

// Arrange: Set up the mock_multi_currency method mock.
$this->mock_multi_currency
->expects( $this->once() )
->method( 'get_selected_currency' )
->willReturn( $selected_currency );

// Act: Edit the in cart args.
$result = $this->woocommerce_nyp->edit_in_cart_args( [], [] );

// Assert: Confirm that the currency code was added to the arg array.
$this->assertSame( $selected_currency->get_code(), $result['nyp_currency'] );
}

/**
* Runs through all the checks in the method returning the initial price until the last one passes all the checks.
*
* @dataProvider provider_get_initial_price
*/
public function test_get_initial_price( $initial_price, $suffix, $request, $get_selected_currency, $get_raw_conversion ) {
// Arrange: Set the initial expected price and the currencies that may be used.
$expected_price = $initial_price;
$selected_currency = new Currency( 'EUR', 2.0 );
$store_currency = new Currency( 'USD', 1 );

// Arrange: Set expectations for calls to get_selected_currency method.
if ( $get_selected_currency ) {
$this->mock_multi_currency
->expects( $this->once() )
->method( 'get_selected_currency' )
->willReturn( $selected_currency );
} else {
$this->mock_multi_currency
->expects( $this->never() )
->method( 'get_selected_currency' );
}

// Arrange: Set expectations for calls to get_raw_conversion method and update expected price.
if ( $get_raw_conversion ) {
$expected_price = $initial_price * ( $selected_currency->get_rate() / $store_currency->get_rate() );

$this->mock_multi_currency
->expects( $this->once() )
->method( 'get_raw_conversion' )
->with( $initial_price, $selected_currency->get_code(), $store_currency->get_code() )
->willReturn( $expected_price );
} else {
$this->mock_multi_currency
->expects( $this->never() )
->method( 'get_raw_conversion' );
}

// Arrange: Manually set the request prarameters.
foreach ( $request as $key => $value ) {
$_REQUEST[ $key ] = $value;
}

// Act: Get the initial price.
$result = $this->woocommerce_nyp->get_initial_price( $initial_price, '', $suffix );

// Assert: Confirm the initial price is returned.
$this->assertSame( $expected_price, $result );
}

public function provider_get_initial_price() {
return [
'Both requests false - return initial_price' => [
'initial_price' => 10.00,
'suffix' => '',
'request' => [],
'get_selected_currency' => false,
'get_raw_conversion' => false,
],
'First request true - return initial_price' => [
'initial_price' => 10.00,
'suffix' => '_suffix',
'request' => [
'nyp_raw_suffix' => 'test',
],
'get_selected_currency' => false,
'get_raw_conversion' => false,
],
'Second request true - return initial_price' => [
'initial_price' => 10.00,
'suffix' => '_suffix',
'request' => [
'nyp_currency' => 'test',
],
'get_selected_currency' => false,
'get_raw_conversion' => false,
],
'Both requests true - return initial_price' => [
'initial_price' => 10.00,
'suffix' => '_suffix',
'request' => [
'nyp_raw_suffix' => 10.00,
'nyp_currency' => 'EUR',
],
'get_selected_currency' => true,
'get_raw_conversion' => false,
],
'Both requests true - return converted price' => [
'initial_price' => 10.00,
'suffix' => '_suffix',
'request' => [
'nyp_raw_suffix' => 10.00,
'nyp_currency' => 'USD',
],
'get_selected_currency' => true,
'get_raw_conversion' => true,
],
];
}

/**
* Sets up `is_nyp` to return true or false for a test.
*/
Expand Down
Loading