From 7b04290eac05b4ed6b028c038d305fb769ecf482 Mon Sep 17 00:00:00 2001
From: Pedro Silva
Date: Fri, 30 Jun 2023 10:00:44 +0100
Subject: [PATCH 1/5] Add support for WooCommerce Bookings on single product
page
---
.../SingleProductActionHandler.js | 84 ++++++++++++-------
.../js/modules/Entity/BookingProduct.js | 18 ++++
.../src/Endpoint/ChangeCartEndpoint.php | 44 +++++++++-
3 files changed, 111 insertions(+), 35 deletions(-)
create mode 100644 modules/ppcp-button/resources/js/modules/Entity/BookingProduct.js
diff --git a/modules/ppcp-button/resources/js/modules/ActionHandler/SingleProductActionHandler.js b/modules/ppcp-button/resources/js/modules/ActionHandler/SingleProductActionHandler.js
index 146fd7e94..a7c8ec8ec 100644
--- a/modules/ppcp-button/resources/js/modules/ActionHandler/SingleProductActionHandler.js
+++ b/modules/ppcp-button/resources/js/modules/ActionHandler/SingleProductActionHandler.js
@@ -1,4 +1,5 @@
import Product from '../Entity/Product';
+import BookingProduct from "../Entity/BookingProduct";
import onApprove from '../OnApproveHandler/onApproveForContinue';
import {payerData} from "../Helper/PayerData";
import {PaymentMethods} from "../Helper/CheckoutMethodState";
@@ -80,33 +81,51 @@ class SingleProductActionHandler {
createOrder()
{
- var getProducts = null;
- if (! this.isGroupedProduct() ) {
- getProducts = () => {
- const id = document.querySelector('[name="add-to-cart"]').value;
- const qty = document.querySelector('[name="quantity"]').value;
- const variations = this.variations();
- return [new Product(id, qty, variations)];
- }
- } else {
- getProducts = () => {
- const products = [];
- this.formElement.querySelectorAll('input[type="number"]').forEach((element) => {
- if (! element.value) {
- return;
+ let getProducts = (() => {
+ if ( this.isBookingProduct() ) {
+ return () => {
+
+ const getPrefixedFields = (formElement, prefix) => {
+ let fields = {};
+ for(const element of formElement.elements) {
+ if( element.name.startsWith(prefix) ) {
+ fields[element.name] = element.value;
+ }
+ }
+ return fields;
}
- const elementName = element.getAttribute('name').match(/quantity\[([\d]*)\]/);
- if (elementName.length !== 2) {
- return;
- }
- const id = parseInt(elementName[1]);
- const quantity = parseInt(element.value);
- products.push(new Product(id, quantity, null));
- })
- return products;
+
+ const id = document.querySelector('[name="add-to-cart"]').value;
+ return [new BookingProduct(id, 1, getPrefixedFields(this.formElement, "wc_bookings_field"))];
+ }
+ } else if ( this.isGroupedProduct() ) {
+ return () => {
+ const products = [];
+ this.formElement.querySelectorAll('input[type="number"]').forEach((element) => {
+ if (! element.value) {
+ return;
+ }
+ const elementName = element.getAttribute('name').match(/quantity\[([\d]*)\]/);
+ if (elementName.length !== 2) {
+ return;
+ }
+ const id = parseInt(elementName[1]);
+ const quantity = parseInt(element.value);
+ products.push(new Product(id, quantity, null));
+ })
+ return products;
+ }
+ } else {
+ return () => {
+ const id = document.querySelector('[name="add-to-cart"]').value;
+ const qty = document.querySelector('[name="quantity"]').value;
+ const variations = this.variations();
+ return [new Product(id, qty, variations)];
+ }
}
- }
- const createOrder = (data, actions) => {
+ })();
+
+ return (data, actions) => {
this.errorHandler.clear();
const onResolve = (purchase_units) => {
@@ -139,19 +158,16 @@ class SingleProductActionHandler {
});
};
- const promise = this.updateCart.update(onResolve, getProducts());
- return promise;
+ return this.updateCart.update(onResolve, getProducts());
};
- return createOrder;
}
variations()
{
-
if (! this.hasVariations()) {
return null;
}
- const attributes = [...this.formElement.querySelectorAll("[name^='attribute_']")].map(
+ return [...this.formElement.querySelectorAll("[name^='attribute_']")].map(
(element) => {
return {
value:element.value,
@@ -159,7 +175,6 @@ class SingleProductActionHandler {
}
}
);
- return attributes;
}
hasVariations()
@@ -171,5 +186,12 @@ class SingleProductActionHandler {
{
return this.formElement.classList.contains('grouped_form');
}
+
+ isBookingProduct()
+ {
+ // detection for "woocommerce-bookings" plugin
+ return !!this.formElement.querySelector('.wc-booking-product-id');
+ }
+
}
export default SingleProductActionHandler;
diff --git a/modules/ppcp-button/resources/js/modules/Entity/BookingProduct.js b/modules/ppcp-button/resources/js/modules/Entity/BookingProduct.js
new file mode 100644
index 000000000..71552f8e2
--- /dev/null
+++ b/modules/ppcp-button/resources/js/modules/Entity/BookingProduct.js
@@ -0,0 +1,18 @@
+import Product from "./Product";
+
+class BookingProduct extends Product {
+
+ constructor(id, quantity, booking) {
+ super(id, quantity, null);
+ this.booking = booking;
+ }
+
+ data() {
+ return {
+ ...super.data(),
+ booking: this.booking
+ }
+ }
+}
+
+export default BookingProduct;
diff --git a/modules/ppcp-button/src/Endpoint/ChangeCartEndpoint.php b/modules/ppcp-button/src/Endpoint/ChangeCartEndpoint.php
index 1c64ddb0d..189099485 100644
--- a/modules/ppcp-button/src/Endpoint/ChangeCartEndpoint.php
+++ b/modules/ppcp-button/src/Endpoint/ChangeCartEndpoint.php
@@ -153,13 +153,23 @@ class ChangeCartEndpoint implements EndpointInterface {
$this->cart->empty_cart( false );
$success = true;
foreach ( $products as $product ) {
- $success = $success && ( ! $product['product']->is_type( 'variable' ) ) ?
- $this->add_product( $product['product'], $product['quantity'] )
- : $this->add_variable_product(
+ if ( $product['product']->is_type( 'booking' ) ) {
+ $success = $success && $this->add_booking_product(
+ $product['product'],
+ $product['booking']
+ );
+ } elseif ( $product['product']->is_type( 'variable' ) ) {
+ $success = $success && $this->add_variable_product(
$product['product'],
$product['quantity'],
$product['variations']
);
+ } else {
+ $success = $success && $this->add_product(
+ $product['product'],
+ $product['quantity']
+ );
+ }
}
if ( ! $success ) {
$this->handle_error();
@@ -234,7 +244,8 @@ class ChangeCartEndpoint implements EndpointInterface {
$products[] = array(
'product' => $wc_product,
'quantity' => (int) $product['quantity'],
- 'variations' => isset( $product['variations'] ) ? $product['variations'] : null,
+ 'variations' => $product['variations'] ?? null,
+ 'booking' => $product['booking'] ?? null,
);
}
return $products;
@@ -286,6 +297,31 @@ class ChangeCartEndpoint implements EndpointInterface {
);
}
+ /**
+ * Adds variations to the cart.
+ *
+ * @param \WC_Product $product The Product.
+ * @param array $data Data used by the booking plugin.
+ *
+ * @return bool
+ * @throws Exception When product could not be added.
+ */
+ private function add_booking_product(
+ \WC_Product $product,
+ array $data
+ ): bool {
+
+ if ( ! is_callable( 'wc_bookings_get_posted_data' ) ) {
+ return false;
+ }
+
+ $cart_item_data = array(
+ 'booking' => wc_bookings_get_posted_data( $data, $product ),
+ );
+
+ return false !== $this->cart->add_to_cart( $product->get_id(), 1, 0, array(), $cart_item_data );
+ }
+
/**
* Based on the cart contents, the purchase units are created.
*
From b9aed09f0f545ed571a42d49a548213c18cd9995 Mon Sep 17 00:00:00 2001
From: Pedro Silva
Date: Fri, 30 Jun 2023 10:58:21 +0100
Subject: [PATCH 2/5] Fix phpunit tests
---
tests/PHPUnit/Button/Endpoint/ChangeCartEndpointTest.php | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/tests/PHPUnit/Button/Endpoint/ChangeCartEndpointTest.php b/tests/PHPUnit/Button/Endpoint/ChangeCartEndpointTest.php
index 9eb6c654b..1949e62b3 100644
--- a/tests/PHPUnit/Button/Endpoint/ChangeCartEndpointTest.php
+++ b/tests/PHPUnit/Button/Endpoint/ChangeCartEndpointTest.php
@@ -88,6 +88,10 @@ class ChangeCartEndpointTest extends TestCase
$defaultProduct
->shouldReceive('get_id')
->andReturn(1);
+ $defaultProduct
+ ->shouldReceive('is_type')
+ ->with('booking')
+ ->andReturn(false);
$defaultProduct
->shouldReceive('is_type')
->with('variable')
@@ -97,6 +101,10 @@ class ChangeCartEndpointTest extends TestCase
$variationProduct
->shouldReceive('get_id')
->andReturn(2);
+ $variationProduct
+ ->shouldReceive('is_type')
+ ->with('booking')
+ ->andReturn(false);
$variationProduct
->shouldReceive('is_type')
->with('variable')
From 28dd3ba0a2a3fad521df255972717f4e71807216 Mon Sep 17 00:00:00 2001
From: Pedro Silva
Date: Fri, 30 Jun 2023 13:56:54 +0100
Subject: [PATCH 3/5] Add booking test case for
ChangeCartEndpointTest::testProducts
---
.../Endpoint/ChangeCartEndpointTest.php | 157 ++++++++++++------
1 file changed, 110 insertions(+), 47 deletions(-)
diff --git a/tests/PHPUnit/Button/Endpoint/ChangeCartEndpointTest.php b/tests/PHPUnit/Button/Endpoint/ChangeCartEndpointTest.php
index 1949e62b3..64b74d91d 100644
--- a/tests/PHPUnit/Button/Endpoint/ChangeCartEndpointTest.php
+++ b/tests/PHPUnit/Button/Endpoint/ChangeCartEndpointTest.php
@@ -26,13 +26,8 @@ class ChangeCartEndpointTest extends TestCase
->once()
->with($singleProductArray['id'])
->andReturn($products[$productKey]);
- if (! $singleProductArray['__test_data_is_variation']) {
- $cart
- ->expects('add_to_cart')
- ->with($singleProductArray['id'], $singleProductArray['quantity'])
- ->andReturnTrue();
- }
- if ($singleProductArray['__test_data_is_variation']) {
+
+ if ($singleProductArray['__test_data_is_variation'] ?? false) {
$dataStore
->expects('find_matching_product_variation')
->with($products[$productKey], $singleProductArray['__test_data_variation_map'])
@@ -47,7 +42,34 @@ class ChangeCartEndpointTest extends TestCase
)
->andReturnTrue();
}
- }
+ elseif ($singleProductArray['__test_data_is_booking'] ?? false) {
+
+ $processedBooking = array();
+ foreach ($singleProductArray['booking'] as $key => $value) {
+ $processedBooking['_processed_' . $key] = $value;
+ }
+
+ expect('wc_bookings_get_posted_data')
+ ->with($singleProductArray['booking'])
+ ->andReturn($processedBooking);
+ $cart
+ ->expects('add_to_cart')
+ ->with(
+ $singleProductArray['id'],
+ $singleProductArray['quantity'],
+ 0,
+ array(),
+ array('booking' => $processedBooking)
+ )
+ ->andReturnTrue();
+ }
+ else {
+ $cart
+ ->expects('add_to_cart')
+ ->with($singleProductArray['id'], $singleProductArray['quantity'])
+ ->andReturnTrue();
+ }
+ }
$cart
->expects('empty_cart')
->with(false);
@@ -110,14 +132,33 @@ class ChangeCartEndpointTest extends TestCase
->with('variable')
->andReturn(true);
- $testData = [
+ $bookingData = [
+ '_duration' => 2,
+ '_start_day' => 12,
+ '_start_month' => 6,
+ '_start_year' => 2023,
+ ];
+
+ $bookingProduct = Mockery::mock(\WC_Product::class);
+ $bookingProduct
+ ->shouldReceive('get_id')
+ ->andReturn(3);
+ $bookingProduct
+ ->shouldReceive('is_type')
+ ->with('booking')
+ ->andReturn(true);
+ $bookingProduct
+ ->shouldReceive('is_type')
+ ->with('variable')
+ ->andReturn(false);
+
+ $testData = [
'default' => [
[
'products' => [
[
'quantity' => 2,
'id' => 1,
- '__test_data_is_variation' => false,
],
]
],
@@ -129,43 +170,65 @@ class ChangeCartEndpointTest extends TestCase
]
],
'variation' => [
- [
- 'products' => [
- [
- 'quantity' => 2,
- 'id' => 1,
- '__test_data_is_variation' => false,
- ],
- [
- 'quantity' => 2,
- 'id' => 2,
- 'variations' => [
- [
- 'name' => 'variation-1',
- 'value' => 'abc',
- ],
- [
- 'name' => 'variation-2',
- 'value' => 'def',
- ],
- ],
- '__test_data_is_variation' => true,
- '__test_data_variation_id' => 123,
- '__test_data_variation_map' => [
- 'variation-1' => 'abc',
- 'variation-2' => 'def',
- ]
- ],
- ]
- ],
- [
- $defaultProduct,
- $variationProduct,
- ],
- [
- [1, 2]
- ]
- ]
+ [
+ 'products' => [
+ [
+ 'quantity' => 2,
+ 'id' => 1,
+ ],
+ [
+ 'quantity' => 2,
+ 'id' => 2,
+ 'variations' => [
+ [
+ 'name' => 'variation-1',
+ 'value' => 'abc',
+ ],
+ [
+ 'name' => 'variation-2',
+ 'value' => 'def',
+ ],
+ ],
+ '__test_data_is_variation' => true,
+ '__test_data_variation_id' => 123,
+ '__test_data_variation_map' => [
+ 'variation-1' => 'abc',
+ 'variation-2' => 'def',
+ ]
+ ],
+ ]
+ ],
+ [
+ $defaultProduct,
+ $variationProduct,
+ ],
+ [
+ [1, 2]
+ ]
+ ],
+ 'booking' => [
+ [
+ 'products' => [
+ [
+ 'quantity' => 2,
+ 'id' => 1,
+ ],
+ [
+ 'quantity' => 1,
+ 'id' => 3,
+ 'booking' => $bookingData,
+ '__test_data_is_booking' => true,
+ ],
+ ]
+ ],
+ [
+ $defaultProduct,
+ $bookingProduct,
+ ],
+ [
+ [1, 3]
+ ]
+ ],
];
return $testData;
From 3b5a4b7f23d7ecc8cc4894df17b73d9f595616e5 Mon Sep 17 00:00:00 2001
From: Pedro Silva
Date: Wed, 5 Jul 2023 17:10:26 +0100
Subject: [PATCH 4/5] Add cart cleanup functionality to single page Bookable
products
---
modules/ppcp-api-client/src/Entity/Item.php | 43 +++++++++---
.../src/Factory/ItemFactory.php | 7 +-
.../SingleProductActionHandler.js | 48 ++++++++++----
.../js/modules/Helper/CartJanitor.js | 65 +++++++++++++++++++
.../resources/js/modules/Helper/FormHelper.js | 17 +++++
5 files changed, 156 insertions(+), 24 deletions(-)
create mode 100644 modules/ppcp-button/resources/js/modules/Helper/CartJanitor.js
create mode 100644 modules/ppcp-button/resources/js/modules/Helper/FormHelper.js
diff --git a/modules/ppcp-api-client/src/Entity/Item.php b/modules/ppcp-api-client/src/Entity/Item.php
index 374c2efcb..0f576f32c 100644
--- a/modules/ppcp-api-client/src/Entity/Item.php
+++ b/modules/ppcp-api-client/src/Entity/Item.php
@@ -73,6 +73,13 @@ class Item {
*/
protected $tax_rate;
+ /**
+ * The tax rate.
+ *
+ * @var string|null
+ */
+ protected $cart_item_key;
+
/**
* Item constructor.
*
@@ -84,6 +91,7 @@ class Item {
* @param string $sku The SKU.
* @param string $category The category.
* @param float $tax_rate The tax rate.
+ * @param ?string $cart_item_key The cart key for this item.
*/
public function __construct(
string $name,
@@ -93,18 +101,20 @@ class Item {
Money $tax = null,
string $sku = '',
string $category = 'PHYSICAL_GOODS',
- float $tax_rate = 0
+ float $tax_rate = 0,
+ string $cart_item_key = null
) {
- $this->name = $name;
- $this->unit_amount = $unit_amount;
- $this->quantity = $quantity;
- $this->description = $description;
- $this->tax = $tax;
- $this->sku = $sku;
- $this->category = ( self::DIGITAL_GOODS === $category ) ? self::DIGITAL_GOODS : self::PHYSICAL_GOODS;
- $this->category = $category;
- $this->tax_rate = $tax_rate;
+ $this->name = $name;
+ $this->unit_amount = $unit_amount;
+ $this->quantity = $quantity;
+ $this->description = $description;
+ $this->tax = $tax;
+ $this->sku = $sku;
+ $this->category = ( self::DIGITAL_GOODS === $category ) ? self::DIGITAL_GOODS : self::PHYSICAL_GOODS;
+ $this->category = $category;
+ $this->tax_rate = $tax_rate;
+ $this->cart_item_key = $cart_item_key;
}
/**
@@ -179,6 +189,15 @@ class Item {
return round( (float) $this->tax_rate, 2 );
}
+ /**
+ * Returns the cart key for this item.
+ *
+ * @return string
+ */
+ public function cart_item_key():?string {
+ return $this->cart_item_key;
+ }
+
/**
* Returns the object as array.
*
@@ -202,6 +221,10 @@ class Item {
$item['tax_rate'] = (string) $this->tax_rate();
}
+ if ( $this->cart_item_key() ) {
+ $item['cart_item_key'] = (string) $this->cart_item_key();
+ }
+
return $item;
}
}
diff --git a/modules/ppcp-api-client/src/Factory/ItemFactory.php b/modules/ppcp-api-client/src/Factory/ItemFactory.php
index 0c5e9b542..ab3f0a95b 100644
--- a/modules/ppcp-api-client/src/Factory/ItemFactory.php
+++ b/modules/ppcp-api-client/src/Factory/ItemFactory.php
@@ -44,7 +44,8 @@ class ItemFactory {
public function from_wc_cart( \WC_Cart $cart ): array {
$items = array_map(
function ( array $item ): Item {
- $product = $item['data'];
+ $product = $item['data'];
+ $cart_item_key = $item['key'] ?? null;
/**
* The WooCommerce product.
@@ -61,7 +62,9 @@ class ItemFactory {
$this->prepare_description( $product->get_description() ),
null,
$product->get_sku(),
- ( $product->is_virtual() ) ? Item::DIGITAL_GOODS : Item::PHYSICAL_GOODS
+ ( $product->is_virtual() ) ? Item::DIGITAL_GOODS : Item::PHYSICAL_GOODS,
+ 0,
+ $cart_item_key
);
},
$cart->get_cart_contents()
diff --git a/modules/ppcp-button/resources/js/modules/ActionHandler/SingleProductActionHandler.js b/modules/ppcp-button/resources/js/modules/ActionHandler/SingleProductActionHandler.js
index a7c8ec8ec..6d2e83706 100644
--- a/modules/ppcp-button/resources/js/modules/ActionHandler/SingleProductActionHandler.js
+++ b/modules/ppcp-button/resources/js/modules/ActionHandler/SingleProductActionHandler.js
@@ -3,6 +3,8 @@ import BookingProduct from "../Entity/BookingProduct";
import onApprove from '../OnApproveHandler/onApproveForContinue';
import {payerData} from "../Helper/PayerData";
import {PaymentMethods} from "../Helper/CheckoutMethodState";
+import CartJanitor from "../Helper/CartJanitor";
+import FormHelper from "../Helper/FormHelper";
class SingleProductActionHandler {
@@ -16,6 +18,7 @@ class SingleProductActionHandler {
this.updateCart = updateCart;
this.formElement = formElement;
this.errorHandler = errorHandler;
+ this.cartJanitor = null;
}
subscriptionsConfiguration() {
@@ -74,29 +77,36 @@ class SingleProductActionHandler {
createOrder: this.createOrder(),
onApprove: onApprove(this, this.errorHandler),
onError: (error) => {
+ this.refreshMiniCart();
+
+ if (this.isBookingProduct() && error.message) {
+ this.errorHandler.clear();
+ this.errorHandler.message(error.message);
+ return;
+ }
this.errorHandler.genericError();
+ },
+ onCancel: () => {
+ // Could be used for every product type,
+ // but only clean the cart for Booking products for now.
+ if (this.isBookingProduct()) {
+ this.cleanCart();
+ } else {
+ this.refreshMiniCart();
+ }
}
}
}
createOrder()
{
+ this.cartJanitor = null;
+
let getProducts = (() => {
if ( this.isBookingProduct() ) {
return () => {
-
- const getPrefixedFields = (formElement, prefix) => {
- let fields = {};
- for(const element of formElement.elements) {
- if( element.name.startsWith(prefix) ) {
- fields[element.name] = element.value;
- }
- }
- return fields;
- }
-
const id = document.querySelector('[name="add-to-cart"]').value;
- return [new BookingProduct(id, 1, getPrefixedFields(this.formElement, "wc_bookings_field"))];
+ return [new BookingProduct(id, 1, FormHelper.getPrefixedFields(this.formElement, "wc_bookings_field"))];
}
} else if ( this.isGroupedProduct() ) {
return () => {
@@ -129,6 +139,8 @@ class SingleProductActionHandler {
this.errorHandler.clear();
const onResolve = (purchase_units) => {
+ this.cartJanitor = (new CartJanitor()).addFromPurchaseUnits(purchase_units);
+
const payer = payerData();
const bnCode = typeof this.config.bn_codes[this.config.context] !== 'undefined' ?
this.config.bn_codes[this.config.context] : '';
@@ -193,5 +205,17 @@ class SingleProductActionHandler {
return !!this.formElement.querySelector('.wc-booking-product-id');
}
+ cleanCart() {
+ this.cartJanitor.removeFromCart().then(() => {
+ this.refreshMiniCart();
+ }).catch(error => {
+ this.refreshMiniCart();
+ });
+ }
+
+ refreshMiniCart() {
+ jQuery(document.body).trigger('wc_fragment_refresh');
+ }
+
}
export default SingleProductActionHandler;
diff --git a/modules/ppcp-button/resources/js/modules/Helper/CartJanitor.js b/modules/ppcp-button/resources/js/modules/Helper/CartJanitor.js
new file mode 100644
index 000000000..aa4c1d839
--- /dev/null
+++ b/modules/ppcp-button/resources/js/modules/Helper/CartJanitor.js
@@ -0,0 +1,65 @@
+class CartJanitor {
+
+ constructor(cartItemKeys = [])
+ {
+ this.endpoint = wc_cart_fragments_params.wc_ajax_url.toString().replace('%%endpoint%%', 'remove_from_cart');
+ this.cartItemKeys = cartItemKeys;
+ }
+
+ addFromPurchaseUnits(purchaseUnits) {
+ for (const purchaseUnit of purchaseUnits || []) {
+ for (const item of purchaseUnit.items || []) {
+ if (!item.cart_item_key) {
+ continue;
+ }
+ this.cartItemKeys.push(item.cart_item_key);
+ }
+ }
+
+ return this;
+ }
+
+ removeFromCart()
+ {
+ return new Promise((resolve, reject) => {
+ if (!this.cartItemKeys || !this.cartItemKeys.length) {
+ resolve();
+ return;
+ }
+
+ const numRequests = this.cartItemKeys.length;
+ let numResponses = 0;
+
+ const tryToResolve = () => {
+ numResponses++;
+ if (numResponses >= numRequests) {
+ resolve();
+ }
+ }
+
+ for (const cartItemKey of this.cartItemKeys) {
+ const params = new URLSearchParams();
+ params.append('cart_item_key', cartItemKey);
+
+ if (!cartItemKey) {
+ tryToResolve();
+ continue;
+ }
+
+ fetch(this.endpoint, {
+ method: 'POST',
+ credentials: 'same-origin',
+ body: params
+ }).then(function (res) {
+ return res.json();
+ }).then(() => {
+ tryToResolve();
+ }).catch(() => {
+ tryToResolve();
+ });
+ }
+ });
+ }
+}
+
+export default CartJanitor;
diff --git a/modules/ppcp-button/resources/js/modules/Helper/FormHelper.js b/modules/ppcp-button/resources/js/modules/Helper/FormHelper.js
new file mode 100644
index 000000000..ff796e3eb
--- /dev/null
+++ b/modules/ppcp-button/resources/js/modules/Helper/FormHelper.js
@@ -0,0 +1,17 @@
+
+/**
+ * Common Form utility methods
+ */
+export default class FormHelper {
+
+ static getPrefixedFields(formElement, prefix) {
+ let fields = {};
+ for(const element of formElement.elements) {
+ if( element.name.startsWith(prefix) ) {
+ fields[element.name] = element.value;
+ }
+ }
+ return fields;
+ }
+
+}
From 25282b9514dc5d85886c15a2c8cb15e0ec6181c1 Mon Sep 17 00:00:00 2001
From: Pedro Silva
Date: Mon, 10 Jul 2023 08:53:05 +0100
Subject: [PATCH 5/5] Fix code review ajustments
---
modules/ppcp-api-client/src/Entity/Item.php | 4 ++--
.../ActionHandler/SingleProductActionHandler.js | 10 +++++-----
.../modules/Helper/{CartJanitor.js => CartHelper.js} | 4 ++--
3 files changed, 9 insertions(+), 9 deletions(-)
rename modules/ppcp-button/resources/js/modules/Helper/{CartJanitor.js => CartHelper.js} (97%)
diff --git a/modules/ppcp-api-client/src/Entity/Item.php b/modules/ppcp-api-client/src/Entity/Item.php
index 0f576f32c..efcaa179d 100644
--- a/modules/ppcp-api-client/src/Entity/Item.php
+++ b/modules/ppcp-api-client/src/Entity/Item.php
@@ -74,7 +74,7 @@ class Item {
protected $tax_rate;
/**
- * The tax rate.
+ * The cart item key.
*
* @var string|null
*/
@@ -192,7 +192,7 @@ class Item {
/**
* Returns the cart key for this item.
*
- * @return string
+ * @return string|null
*/
public function cart_item_key():?string {
return $this->cart_item_key;
diff --git a/modules/ppcp-button/resources/js/modules/ActionHandler/SingleProductActionHandler.js b/modules/ppcp-button/resources/js/modules/ActionHandler/SingleProductActionHandler.js
index 6d2e83706..5cb64febc 100644
--- a/modules/ppcp-button/resources/js/modules/ActionHandler/SingleProductActionHandler.js
+++ b/modules/ppcp-button/resources/js/modules/ActionHandler/SingleProductActionHandler.js
@@ -3,7 +3,7 @@ import BookingProduct from "../Entity/BookingProduct";
import onApprove from '../OnApproveHandler/onApproveForContinue';
import {payerData} from "../Helper/PayerData";
import {PaymentMethods} from "../Helper/CheckoutMethodState";
-import CartJanitor from "../Helper/CartJanitor";
+import CartHelper from "../Helper/CartHelper";
import FormHelper from "../Helper/FormHelper";
class SingleProductActionHandler {
@@ -18,7 +18,7 @@ class SingleProductActionHandler {
this.updateCart = updateCart;
this.formElement = formElement;
this.errorHandler = errorHandler;
- this.cartJanitor = null;
+ this.cartHelper = null;
}
subscriptionsConfiguration() {
@@ -100,7 +100,7 @@ class SingleProductActionHandler {
createOrder()
{
- this.cartJanitor = null;
+ this.cartHelper = null;
let getProducts = (() => {
if ( this.isBookingProduct() ) {
@@ -139,7 +139,7 @@ class SingleProductActionHandler {
this.errorHandler.clear();
const onResolve = (purchase_units) => {
- this.cartJanitor = (new CartJanitor()).addFromPurchaseUnits(purchase_units);
+ this.cartHelper = (new CartHelper()).addFromPurchaseUnits(purchase_units);
const payer = payerData();
const bnCode = typeof this.config.bn_codes[this.config.context] !== 'undefined' ?
@@ -206,7 +206,7 @@ class SingleProductActionHandler {
}
cleanCart() {
- this.cartJanitor.removeFromCart().then(() => {
+ this.cartHelper.removeFromCart().then(() => {
this.refreshMiniCart();
}).catch(error => {
this.refreshMiniCart();
diff --git a/modules/ppcp-button/resources/js/modules/Helper/CartJanitor.js b/modules/ppcp-button/resources/js/modules/Helper/CartHelper.js
similarity index 97%
rename from modules/ppcp-button/resources/js/modules/Helper/CartJanitor.js
rename to modules/ppcp-button/resources/js/modules/Helper/CartHelper.js
index aa4c1d839..8220d7d00 100644
--- a/modules/ppcp-button/resources/js/modules/Helper/CartJanitor.js
+++ b/modules/ppcp-button/resources/js/modules/Helper/CartHelper.js
@@ -1,4 +1,4 @@
-class CartJanitor {
+class CartHelper {
constructor(cartItemKeys = [])
{
@@ -62,4 +62,4 @@ class CartJanitor {
}
}
-export default CartJanitor;
+export default CartHelper;