Closed hatyi closed 1 year ago
Hi @hatyi
Thank you for your message.
Are you able to share your custom shipping method class? If you would rather not post it publicly you can send it over to support@craftcms.com where we can pick it up and take a look.
Thanks!
Hello @nfourtythree, thanks a lot for the quick response.
Sure no problem, we use a base class we are extending by many others:
abstract class BaseNavisionShippingMethod {
private $cachedShippingPrices = [];
private static $cartService;
public $id;
public $name;
public $handle;
public $enabled;
public $isLite;
public $price;
public $dateCreated;
public $dateUpdated;
public function __construct() {
$this->id = $this->getId();
$this->name = $this->getName();
$this->handle = $this->getHandle();
$this->enabled = $this->getIsEnabled();
$this->isLite = false;
if(! self::$cartService ) {
self::$cartService = new Carts();
}
$this->price = $this->getPriceForOrder( self::$cartService->getCart() );
}
public abstract function getHandle(): ?string;
public abstract function getName(): ?string;
public abstract function getType(): ?string;
public abstract function getId(): ?int;
/**
* @param Order $order
* @return float
*/
public function getPriceForOrder(Order $order) : float {
if (isset($this->cachedShippingPrices[$order->getId()])) {
return $this->cachedShippingPrices[$order->getId()];
}
$shippingCost = 0;
if(! is_null( $order->availableNavisionShippingMethods ) || !empty( $order->availableNavisionShippingMethods ) ) {
$savedOptions = json_decode($order->availableNavisionShippingMethods);
foreach ($savedOptions as $so) {
if ($so->ShippingCode === $this->getHandle()) {
$shippingCost = (float) str_replace(',', '.', $so->PriceIncVat);
break;
}
}
}
$this->cachedShippingPrices[$order->getId()] = $shippingCost;
return $shippingCost;
}
/**
* Returns the control panel URL to manage this method and its rules.
* An empty string will result in no link.
*
* @return string
*/
public function getCpEditUrl(): string {
return '';
}
/**
* Returns an array of rules that meet the `ShippingRules` interface.
*
* @return ShippingRuleInterface[] The array of ShippingRules
*/
public function getShippingRules() : array {
$shippingRule = BaseNavisionShippingRule::getInstance();
$shippingRule->setShippingOption($this);
return [$shippingRule];
}
/**
* Returns whether this shipping method is enabled for listing and selection by customers.
*
* @return bool
*/
public function getIsEnabled(): bool {
return true;
}
/**
* The first matching shipping rule for this shipping method
*
* @param Order $order
* @return null|ShippingRuleInterface
*/
public function getMatchingShippingRule(Order $order) :? ShippingRuleInterface {
$rules = $this->getShippingRules();
return $rules[0] ?? null;
}
/**
* Is this shipping method available to the order?
*
* @param Order $order
* @return bool
*/
public function matchOrder(Order $order): bool {
if(! is_null( $order->availableNavisionShippingMethods ) || !empty( $order->availableNavisionShippingMethods ) ) {
$savedOptions = json_decode($order->availableNavisionShippingMethods);
foreach ($savedOptions as $so) {
if ($so->ShippingCode === $this->getHandle()) {
return true;
}
}
}
return false;
}
}
And this is an example of one we actually use:
class BusinessOvernightShippingOption extends BaseNavisionShippingMethod implements ShippingMethodInterface {
/**
* Returns the type of Shipping Method. This might be the name of the plugin or provider.
* The core shipping methods have type: `Custom`. This is shown in the control panel only.
*
* @return string
*/
public function getType(): string {
return 'BNATT';
}
/**
* Returns the ID of this Shipping Method, if it is managed by Craft Commerce.
*
* @return int|null The shipping method ID, or null if it is not managed by Craft Commerce
*/
public function getId() :?int {
return 11;
}
/**
* Returns the name of this Shipping Method as displayed to the customer and in the control panel.
*
* @return string
*/
public function getName(): string {
return 'Bedrift over natten';
}
/**
* Returns the unique handle of this Shipping Method.
*
* @return string
*/
public function getHandle(): string {
return 'BNATT';
}
}
Hi @hatyi
Thank you for raising the issue.
We have pushed fixes for both 3.x
and 4.x
to address the problem. These fixes will be included in the upcoming releases.
To get this early, change your craftcms/commerce
requirement in your project's composer.json
to:
"require": {
"craftcms/commerce": "dev-develop#a2b6413b41abd56cf61a6d8dc578488f2bbf61fc as 4.1.2",
"...": "..."
}
Then run composer update
.
Thanks!
What happened?
Description
Hello, I'm reaching out because I'm trying upgrade a Craft CMS + Commerce website from 3 to 4. Specifically from Craft 3.5.19.1 and Commerce 3.2.7 to the latest available releases.
We have many custom shipping options added by our module using the EVENT_REGISTER_AVAILABLE_SHIPPING_METHODS event.
Everything works as expected until the point where I'm trying to save the selected custom shipping option on a cart using the standard
commerce/cart/update-cart
controller.I can query these options in twig by
cart.getAvailableShippingMethodOptions()
, and I can also see the selected option with the other available options in CP (if I check the cart, it shows up as a select input on the right panel with the "Shipping Method" label), the shippingMethodHandle attribute is also getting saved properly containing the selected options' handle.But it looks like that for some reason the cart itself can not resolve the shipping option by this saved handle. So it's actually not added to the cart, neither increases it's price.
I was trying to trace back the error in the
craftcms/commerce/src/elements/Order.php
class and I've found that inside thegetShippingMethod()
method thePlugin::getInstance()->getShippingMethods()
call returns an empty array and it looks like it runs before our module registers our custom shipping options.This is just absolutely a hunch from my side, maybe I'm doing something wrong here but since I couldn't find any related open issue or conversation about this I thought it's worth creating one to ask for help, report this.
Steps to reproduce
1.Try adding custom shipping options by a module, select it and save it to a cart.
Expected behavior
It's added to the cart, works as it would in Craft Commerce 3.
Actual behavior
Please see description.
Craft CMS version
4.2.2
Craft Commerce version
4.1.2
PHP version
8.1.9
Operating system and version
Ubuntu 20.04
Database type and version
MySQL 8.0.30
Image driver and version
No response
Installed plugins and versions
"ext-json": "*", "aelvan/mailchimp-subscribe": "^4.0.0", "algolia/algoliasearch-client-php": "^2.7", "clubstudioltd/craft-asset-rev": "^7.0", "craftcms/cms": "4.2.2", "craftcms/commerce": "4.1.2", "craftcms/commerce-omnipay": "^4.0", "craftcms/redactor": "^3.0.0", "dolphiq/redirect": "2.0.0", "league/omnipay": "^3", "mailchimp/marketing": "^3.0", "mmikkel/incognito-field": "1.3.0", "monolog/monolog": "^2.8", "nystudio107/craft-seomatic": "^4.0.0", "verbb/navigation": "^2.0.0", "verbb/super-table": "^3.0.0", "vlucas/phpdotenv": "^3.4.0"