diff --git a/config/schema/commerce_shipping.schema.yml b/config/schema/commerce_shipping.schema.yml index e2c7e5d..70ad4d7 100644 --- a/config/schema/commerce_shipping.schema.yml +++ b/config/schema/commerce_shipping.schema.yml @@ -80,6 +80,22 @@ commerce_shipping.commerce_shipping_method.plugin.flat_rate_per_item: type: field.value.commerce_price label: 'Rate amount' +commerce_shipping.commerce_shipping_method.plugin.flat_rate_plus_per_item: + type: commerce_shipping_method_configuration + mapping: + rate_label: + type: label + label: 'Rate label' + rate_description: + type: label + label: 'Rate description' + rate_amount: + type: field.value.commerce_price + label: 'Rate amount' + per_item_amount: + type: field.value.commerce_price + label: 'Per item amount' + commerce_shipping_method_configuration: type: mapping mapping: diff --git a/src/Plugin/Commerce/ShippingMethod/FlatRatePlusPerItem.php b/src/Plugin/Commerce/ShippingMethod/FlatRatePlusPerItem.php new file mode 100644 index 0000000..8bc40ea --- /dev/null +++ b/src/Plugin/Commerce/ShippingMethod/FlatRatePlusPerItem.php @@ -0,0 +1,81 @@ + '', + ] + parent::defaultConfiguration(); + } + + /** + * {@inheritdoc} + */ + public function buildConfigurationForm(array $form, FormStateInterface $form_state) { + $form = parent::buildConfigurationForm($form, $form_state); + + $per_item_amount = $this->configuration['per_item_amount'] ?: NULL; + // A bug in the plugin_select form element causes amount to be incomplete. + if (isset($per_item_amount) && !isset($per_item_amount['number'], $per_item_amount['currency_code'])) { + $per_item_amount = NULL; + } + + $form['per_item_amount'] = [ + '#type' => 'commerce_price', + '#title' => $this->t('Per item amount'), + '#description' => $this->t('Charged for each quantity of each shipment item.'), + '#default_value' => $per_item_amount, + '#required' => TRUE, + ]; + + return $form; + } + + /** + * {@inheritdoc} + */ + public function submitConfigurationForm(array &$form, FormStateInterface $form_state) { + parent::submitConfigurationForm($form, $form_state); + if (!$form_state->getErrors()) { + $values = $form_state->getValue($form['#parents']); + $this->configuration['per_item_amount'] = $values['per_item_amount']; + } + } + + /** + * {@inheritdoc} + */ + public function calculateRates(ShipmentInterface $shipment) { + $amount = Price::fromArray($this->configuration['rate_amount']); + $per_item_amount = Price::fromArray($this->configuration['per_item_amount']); + $quantity = $shipment->getTotalQuantity(); + + $rates = []; + $rates[] = new ShippingRate([ + 'shipping_method_id' => $this->parentEntity->id(), + 'service' => $this->services['default'], + 'amount' => $amount->add($per_item_amount->multiply($quantity)), + ]); + + return $rates; + } + +} diff --git a/tests/src/FunctionalJavascript/CartIntegrationTest.php b/tests/src/FunctionalJavascript/CartIntegrationTest.php index 08213f8..e2a36da 100644 --- a/tests/src/FunctionalJavascript/CartIntegrationTest.php +++ b/tests/src/FunctionalJavascript/CartIntegrationTest.php @@ -217,6 +217,97 @@ class CartIntegrationTest extends CommerceWebDriverTestBase { } + /** + * Test for Flat Rate + Per Item shipping cost updates. + */ + public function testRecalculatePlusPerItem() { + // Create a flat rate + per item. + $this->createEntity('commerce_shipping_method', [ + 'name' => 'Flat Rate + Per Item', + 'stores' => [$this->store->id()], + 'plugin' => [ + 'target_plugin_id' => 'flat_rate_plus_per_item', + 'target_plugin_configuration' => [ + 'rate_label' => 'Flat Rate + Per Item', + 'rate_amount' => [ + 'number' => '9.00', + 'currency_code' => 'USD', + ], + 'per_item_amount' => [ + 'number' => '3.00', + 'currency_code' => 'USD', + ], + ], + ], + 'conditions' => [ + [ + 'target_plugin_id' => 'shipment_quantity', + 'target_plugin_configuration' => [ + 'operator' => '>', + 'quantity' => '1', + ], + ], + ], + ]); + + // Add product to order and calculate shipping. + $this->drupalGet($this->firstProduct->toUrl()->toString()); + $this->submitForm([], 'Add to cart'); + $this->drupalGet('checkout/1'); + $address = [ + 'given_name' => 'John', + 'family_name' => 'Smith', + 'address_line1' => '1098 Alta Ave', + 'locality' => 'Mountain View', + 'administrative_area' => 'CA', + 'postal_code' => '94043', + ]; + $address_prefix = 'shipping_information[shipping_profile][address][0][address]'; + $this->getSession()->getPage()->fillField($address_prefix. '[country_code]', 'US'); + foreach ($address as $property => $value) { + $this->getSession()->getPage()->fillField($address_prefix . '[' . $property . ']', $value); + } + + $this->getSession()->getPage()->findButton('Recalculate shipping')->click(); + $this->assertSession()->assertWaitOnAjaxRequest(); + $this->getSession()->getPage()->uncheckField('payment_information[add_payment_method][billing_information][copy_fields][enable]'); + $this->assertSession()->assertWaitOnAjaxRequest(); + $this->submitForm([ + 'payment_information[add_payment_method][payment_details][number]' => '4111111111111111', + 'payment_information[add_payment_method][payment_details][expiration][month]' => '02', + 'payment_information[add_payment_method][payment_details][expiration][year]' => $this->getCardExpirationYear(), + 'payment_information[add_payment_method][payment_details][security_code]' => '123', + 'payment_information[add_payment_method][billing_information][address][0][address][given_name]' => 'Johnny', + 'payment_information[add_payment_method][billing_information][address][0][address][family_name]' => 'Appleseed', + 'payment_information[add_payment_method][billing_information][address][0][address][address_line1]' => '123 New York Drive', + 'payment_information[add_payment_method][billing_information][address][0][address][locality]' => 'New York City', + 'payment_information[add_payment_method][billing_information][address][0][address][administrative_area]' => 'NY', + 'payment_information[add_payment_method][billing_information][address][0][address][postal_code]' => '10001', + ], 'Continue to review'); + + $this->assertSession()->pageTextContains('Shipping $10.00'); + + // Test whether the shipping amount gets updated. + $this->drupalGet('/cart'); + $this->getSession()->getPage()->fillField('edit_quantity[0]', '3'); + $this->getSession()->getPage()->findButton('Update cart')->click(); + $this->assertSession()->pageTextContains('Shipping $30.00'); + + // Switch to "Flat Rate + Per Item" and recalculate shipping. + $this->drupalGet('checkout/1'); + $this->getSession()->getPage()->findButton('Recalculate shipping')->click(); + $this->assertSession()->assertWaitOnAjaxRequest(); + $this->getSession()->getPage()->findField('Flat Rate + Per Item: $18.00')->click(); + $this->assertSession()->assertWaitOnAjaxRequest(); + $this->assertTrue($this->getSession()->getPage()->findField('Flat Rate + Per Item: $18.00')->isChecked()); + + $this->submitForm([], 'Continue to review'); + $this->assertSession()->pageTextContains('Shipping method'); + $this->assertSession()->pageTextNotContains('Flat Rate Per Item'); + $this->assertSession()->pageTextContains('Flat Rate + Per Item'); + $this->assertSession()->pageTextContains('Shipping $18.00'); + } + /** * Test for recalculating shipping trough cart/checkout steps. */