Closed srosenbrand closed 5 years ago
Yep I also preferred the Commerce 1 behaviour here - I (and, it seems, our customers) like carts to survive pretty much no matter what. That said, for privacy reasons, it might be worth ditching any address info on the cart...I think that is folks' fears with persistent carts maybe? I don't know, TBH - I like persistent ones and figure people should use incognito or whatevs if they are on public PCs...
I agree with this request. If we take your example, a lot of people that were on the fence won't go through step 7 again, they will just decide not to buy. This really needs to happen.
Looks like we will need to move back to using an independent cookie.
@lukeholder Great news!!
I'm sure you thought of these scenarios, but I'm gonna put them here just in case:
This is great!
In addition to what Eric mentioned above, could you consider storing cart data in a user/consumer account for ultimate UX when switching devices. It’s really not that uncomen for users to browse on mobile and really convert and checkout on desktop for example.
I agree there too. I kind of assumed that if we have persistant carts, it would check if a cart exists when you log in.
Just chiming in - yep, the best implementations are where if they are a user, and logged in, the cart is saved persistently on that user (for some months at least - via Google we know folks will often convert anywhere up to 90 days or so from initial contact, and sometimes even longer).
...And I know, literally from seconds ago, that my wife gets annoyed even if, as just a guest browsing a site, her cart from a few days ago has now disappeared. Basically, people like to keep their behaviour DRY ;)
This will be a breaking change for 3rd party plugins like this: https://github.com/mediabeastnz/craft-commerce-abandoned-cart/blob/master/src/controllers/BaseController.php#L122
So need to make the change on a branch and notify 3rd party plugins for testing before releasing.
@lukeholder I'm guessing that a plugin like that actually introduces the Core functionality that was missing. It's ok of course to get them to test, but I really hope it won't be long to get to the release of the functionality.
Thanks Luke
@lukeholder Hi Luke! Any update on the state of this feature? Thanks
Been playing around with this a bit on a dev install.
For authenticated users, I think it could be as simple as something like:
Event::on(
\yii\web\User::class,
\yii\web\User::EVENT_AFTER_LOGIN,
function (\yii\web\UserEvent $event) {
$commerce = craft\commerce\Plugin::getInstance();
$num = $commerce->getCarts()->getCart()->id;
// only do something if the cart is empty; if cart is empty, id will be null
if (!$num) {
$userEmail = ($event->identity->email);
$query = Order::find();
$query->email($userEmail);
$query->isCompleted(false);
$query->orderBy('dateCreated DESC');
$order = $query->one();
// set the new cart to the last one the user updated
if ($order) {
$commerce->carts->forgetCart();
$cartNumber = $order->number;
$session = Craft::$app->getSession();
$session->set('commerce_cart', $cartNumber);
}
} else {
// cart has items in it, Craft will use it instead
// might want to do something there like show a flash, etc.
}
});
Basically on login, if there's an active cart, Craft will use that cart instead but if the cart is empty (eg maybe the user is logging in to revisit their prior session), it'll grab the most recent (inactive) cart they had and make that the most recent cart. (Much like how an abandoned cart notification works.)
I would highly recommend that Commerce not merge cart items. This is actually the default behavior in Magento and other platforms and a lot of devs turn it off/work around it because what they found was users forget they had items in their authenticated cart prior to logging in. You end up creating more work for the user. Ryan Szrama of Drupal Commerce/Ubercart talks about this and his reasoning is correct.
However, since Craft does save every inactive cart, there's nothing stopping us from spitting those old items out on the cart page (or some other page as well). "Hey we noticed you may have left some items behind from earlier...." Loop through the inactive carts and spit out the products the user hasn't already added and offer to add them (similar to how you'd do an upsell).
@RitterKnightCreative I understand your reasoning behind this and I can see that it might be an option to have for some use cases, but in the case of the site I'm taking care of, people want to have the carts merged. Too often people told us that they added a few items to their carts, thinking they were logged in. Then, went and logged in to checkout to notice that not all of their items were in the cart.
Maybe a simple toggle in the backend to merge or not merge carts on login would take care of all use cases? I think it would be better to have both option in the core to get rid of any unnecessary plugins just to deal with this.
Personally I am with @RitterKnightCreative here - I think only restore an account's cart if the current cart is empty...I consider the current cart as the newest, therefore the best expression of the user's current intenstions. That said, I think some folks do log in looking for their 'saved cart from last time' so I do imn general like that functionality. But I do think you have to detect it happening, and show the user an explicit message and take them to their cart page so that it is very clear/explicit to the user what has been happening....because I definitely agree that items spontaneously being added to a cart without any UI feedback is a big no no with customers.
This would be great to have. @lukeholder Is there any update on timeline for this feature? Thanks.
Update.
On the develop branch right now, when a user logs in, all previous orders are merged (optionally) with the current order (line items are copied into the current order). Next up will be switching back to cookies for the cart.
Change: 4d719296e14077eb5f9ae83d9257803fba09d610
I'll be testing this today @lukeholder . Thank you
@lukeholder I tested, and I'm sorry to say that this is not working correctly on my end.
Also, please note that there's also something else wrong on develop: #843
Steps:
1- 'mergePreviousCartsOnCustomerLogin' => true
in my commerce config file (I'm assuming this will be a backend setting later?
2- Login and add items to the cart
3- Open an incognito window, login and this error comes up (I didn't add anything to the cart before loging in)
Stack trace:
PDOException: SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'orderId' cannot be null in C:\wamp64\www\my-site\vendor\yiisoft\yii2\db\Command.php:1290
Stack trace:
#0 C:\wamp64\www\my-site\vendor\yiisoft\yii2\db\Command.php(1290): PDOStatement->execute()
#1 C:\wamp64\www\my-site\vendor\yiisoft\yii2\db\Command.php(1091): yii\db\Command->internalExecute('INSERT INTO `co...')
#2 C:\wamp64\www\my-site\vendor\yiisoft\yii2\db\Schema.php(433): yii\db\Command->execute()
#3 C:\wamp64\www\my-site\vendor\yiisoft\yii2\db\ActiveRecord.php(600): yii\db\Schema->insert('{{%commerce_lin...', Array)
#4 C:\wamp64\www\my-site\vendor\yiisoft\yii2\db\ActiveRecord.php(566): yii\db\ActiveRecord->insertInternal(NULL)
#5 C:\wamp64\www\my-site\vendor\yiisoft\yii2\db\BaseActiveRecord.php(678): yii\db\ActiveRecord->insert(false, NULL)
#6 C:\wamp64\www\my-site\vendor\craftcms\commerce\src\services\LineItems.php(211): yii\db\BaseActiveRecord->save(false)
#7 C:\wamp64\www\my-site\vendor\craftcms\commerce\src\elements\Order.php(2133): craft\commerce\services\LineItems->saveLineItem(Object(craft\commerce\models\LineItem), false)
#8 C:\wamp64\www\my-site\vendor\craftcms\commerce\src\elements\Order.php(974): craft\commerce\elements\Order->_saveLineItems()
#9 C:\wamp64\www\my-site\vendor\craftcms\cms\src\services\Elements.php(515): craft\commerce\elements\Order->afterSave(true)
#10 C:\wamp64\www\my-site\vendor\craftcms\commerce\src\helpers\Order.php(81): craft\services\Elements->saveElement(Object(craft\commerce\elements\Order))
#11 C:\wamp64\www\my-site\vendor\craftcms\commerce\src\services\Customers.php(271): craft\commerce\helpers\Order::mergeOrders(Object(craft\commerce\elements\Order), Object(craft\commerce\elements\Order))
#12 [internal function]: craft\commerce\services\Customers->loginHandler(Object(yii\web\UserEvent))
#13 C:\wamp64\www\my-site\vendor\yiisoft\yii2\base\Event.php(312): call_user_func(Array, Object(yii\web\UserEvent))
#14 C:\wamp64\www\my-site\vendor\yiisoft\yii2\base\Component.php(636): yii\base\Event::trigger('yii\\web\\User', 'afterLogin', Object(yii\web\UserEvent))
#15 C:\wamp64\www\my-site\vendor\yiisoft\yii2\web\User.php(495): yii\base\Component->trigger('afterLogin', Object(yii\web\UserEvent))
#16 C:\wamp64\www\my-site\vendor\craftcms\cms\src\web\User.php(423): yii\web\User->afterLogin(Object(craft\elements\User), false, 0)
#17 C:\wamp64\www\my-site\vendor\yiisoft\yii2\web\User.php(264): craft\web\User->afterLogin(Object(craft\elements\User), false, 0)
#18 C:\wamp64\www\my-site\vendor\craftcms\cms\src\controllers\UsersController.php(168): yii\web\User->login(Object(craft\elements\User), 0)
#19 [internal function]: craft\controllers\UsersController->actionLogin()
#20 C:\wamp64\www\my-site\vendor\yiisoft\yii2\base\InlineAction.php(57): call_user_func_array(Array, Array)
#21 C:\wamp64\www\my-site\vendor\yiisoft\yii2\base\Controller.php(157): yii\base\InlineAction->runWithParams(Array)
#22 C:\wamp64\www\my-site\vendor\craftcms\cms\src\web\Controller.php(109): yii\base\Controller->runAction('login', Array)
#23 C:\wamp64\www\my-site\vendor\yiisoft\yii2\base\Module.php(528): craft\web\Controller->runAction('login', Array)
#24 C:\wamp64\www\my-site\vendor\craftcms\cms\src\web\Application.php(297): yii\base\Module->runAction('users/login', Array)
#25 C:\wamp64\www\my-site\vendor\craftcms\cms\src\web\Application.php(565): craft\web\Application->runAction('users/login', Array)
#26 C:\wamp64\www\my-site\vendor\craftcms\cms\src\web\Application.php(281): craft\web\Application->_processActionRequest(Object(craft\web\Request))
#27 C:\wamp64\www\my-site\vendor\yiisoft\yii2\base\Application.php(386): craft\web\Application->handleRequest(Object(craft\web\Request))
#28 C:\wamp64\www\my-site\public_html\index.php(21): yii\base\Application->run()
#29 {main}
Next yii\db\IntegrityException: SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'orderId' cannot be null
The SQL being executed was: INSERT INTO `commerce_lineitems` (`purchasableId`, `orderId`, `taxCategoryId`, `shippingCategoryId`, `options`, `optionsSignature`, `qty`, `price`, `weight`, `width`, `length`, `height`, `snapshot`, `note`, `saleAmount`, `salePrice`, `total`, `subtotal`, `dateCreated`, `uid`, `dateUpdated`) VALUES (528, NULL, 1, 1, '[]', 'd751713988987e9331980363e24189ce', 1, '49.95', '0', '0', '0', '0', '{\"onSale\":false,\"cpEditUrl\":\"#\",\"productFields\":[],\"fields\":[],\"price\":49.95,\"sku\":\"RIO281\",\"description\":\"Caylus\",\"purchasableId\":\"528\",\"options\":[],\"sales\":[]}', '', '0', '49.95', '49.95', '49.95', '2019-05-06 15:48:20', '9d7dee1b-3e89-42a3-a666-4599218c51d7', '2019-05-06 15:48:20') in C:\wamp64\www\my-site\vendor\yiisoft\yii2\db\Schema.php:664
Stack trace:
#0 C:\wamp64\www\my-site\vendor\yiisoft\yii2\db\Command.php(1295): yii\db\Schema->convertException(Object(PDOException), 'INSERT INTO `co...')
#1 C:\wamp64\www\my-site\vendor\yiisoft\yii2\db\Command.php(1091): yii\db\Command->internalExecute('INSERT INTO `co...')
#2 C:\wamp64\www\my-site\vendor\yiisoft\yii2\db\Schema.php(433): yii\db\Command->execute()
#3 C:\wamp64\www\my-site\vendor\yiisoft\yii2\db\ActiveRecord.php(600): yii\db\Schema->insert('{{%commerce_lin...', Array)
#4 C:\wamp64\www\my-site\vendor\yiisoft\yii2\db\ActiveRecord.php(566): yii\db\ActiveRecord->insertInternal(NULL)
#5 C:\wamp64\www\my-site\vendor\yiisoft\yii2\db\BaseActiveRecord.php(678): yii\db\ActiveRecord->insert(false, NULL)
#6 C:\wamp64\www\my-site\vendor\craftcms\commerce\src\services\LineItems.php(211): yii\db\BaseActiveRecord->save(false)
#7 C:\wamp64\www\my-site\vendor\craftcms\commerce\src\elements\Order.php(2133): craft\commerce\services\LineItems->saveLineItem(Object(craft\commerce\models\LineItem), false)
#8 C:\wamp64\www\my-site\vendor\craftcms\commerce\src\elements\Order.php(974): craft\commerce\elements\Order->_saveLineItems()
#9 C:\wamp64\www\my-site\vendor\craftcms\cms\src\services\Elements.php(515): craft\commerce\elements\Order->afterSave(true)
#10 C:\wamp64\www\my-site\vendor\craftcms\commerce\src\helpers\Order.php(81): craft\services\Elements->saveElement(Object(craft\commerce\elements\Order))
#11 C:\wamp64\www\my-site\vendor\craftcms\commerce\src\services\Customers.php(271): craft\commerce\helpers\Order::mergeOrders(Object(craft\commerce\elements\Order), Object(craft\commerce\elements\Order))
#12 [internal function]: craft\commerce\services\Customers->loginHandler(Object(yii\web\UserEvent))
#13 C:\wamp64\www\my-site\vendor\yiisoft\yii2\base\Event.php(312): call_user_func(Array, Object(yii\web\UserEvent))
#14 C:\wamp64\www\my-site\vendor\yiisoft\yii2\base\Component.php(636): yii\base\Event::trigger('yii\\web\\User', 'afterLogin', Object(yii\web\UserEvent))
#15 C:\wamp64\www\my-site\vendor\yiisoft\yii2\web\User.php(495): yii\base\Component->trigger('afterLogin', Object(yii\web\UserEvent))
#16 C:\wamp64\www\my-site\vendor\craftcms\cms\src\web\User.php(423): yii\web\User->afterLogin(Object(craft\elements\User), false, 0)
#17 C:\wamp64\www\my-site\vendor\yiisoft\yii2\web\User.php(264): craft\web\User->afterLogin(Object(craft\elements\User), false, 0)
#18 C:\wamp64\www\my-site\vendor\craftcms\cms\src\controllers\UsersController.php(168): yii\web\User->login(Object(craft\elements\User), 0)
#19 [internal function]: craft\controllers\UsersController->actionLogin()
#20 C:\wamp64\www\my-site\vendor\yiisoft\yii2\base\InlineAction.php(57): call_user_func_array(Array, Array)
#21 C:\wamp64\www\my-site\vendor\yiisoft\yii2\base\Controller.php(157): yii\base\InlineAction->runWithParams(Array)
#22 C:\wamp64\www\my-site\vendor\craftcms\cms\src\web\Controller.php(109): yii\base\Controller->runAction('login', Array)
#23 C:\wamp64\www\my-site\vendor\yiisoft\yii2\base\Module.php(528): craft\web\Controller->runAction('login', Array)
#24 C:\wamp64\www\my-site\vendor\craftcms\cms\src\web\Application.php(297): yii\base\Module->runAction('users/login', Array)
#25 C:\wamp64\www\my-site\vendor\craftcms\cms\src\web\Application.php(565): craft\web\Application->runAction('users/login', Array)
#26 C:\wamp64\www\my-site\vendor\craftcms\cms\src\web\Application.php(281): craft\web\Application->_processActionRequest(Object(craft\web\Request))
#27 C:\wamp64\www\my-site\vendor\yiisoft\yii2\base\Application.php(386): craft\web\Application->handleRequest(Object(craft\web\Request))
#28 C:\wamp64\www\my-site\public_html\index.php(21): yii\base\Application->run()
#29 {main}
Additional Information:
Array
(
[0] => 23000
[1] => 1048
[2] => Column 'orderId' cannot be null
)
@brandonkelly @lukeholder This issue I mentioned just above is still true on 2.1.5.
And just to add more info, this happens only if you start with an empty cart. If there's items in the "guest" cart and you then login, it works fine.
Pull request added to fix the above. #847
@lukeholder There's something working weird here and I'm not sure how it could be fixed.
The issue is that it fetches the old carts “On Login”. So, if you do this:
1- Create your cart at home. Stay logged in 2- Login at work and add some stuff to it 3- Come back home and check the cart (when still logged in). Stuff from #2 is not in there.
The only solution I found with the current implementation is to have the user log out and log back in, which triggers the merge of all of them.
Would there be any way to check for "new session of a logged in user" instead of login? Might not even be a solution if the user doesn't even close it's browser!?
Was this fixed or just closed?
Well, not exactly fixed as such - the behaviour that resulted is here - https://docs.craftcms.com/commerce/v2/adding-to-and-updating-the-cart.html#cart-merging
@aNickzz In Commerce 3 we removed cart merging and replaced it with: https://docs.craftcms.com/commerce/v3/adding-to-and-updating-the-cart.html#restoring-previous-cart-contents
An upgrade explanation is her:
https://docs.craftcms.com/commerce/v3/upgrading.html#cart-merging
The docs that @bossanova808 linked are for Commerce 2.1 and onwards, but the change happened in Commerce 3.
Let me know what led you here and what you are trying to accomplish and I am happy to help and hear feedback.
Thanks for the heads up, at this stage I just want to retain the previous cart. The points against doing this in the doc's are good ones, but not applicable in my case. Looks like i'll have to do this in a login event.
@aNickzz if the user logs in they will retain their current cart before they logged in as long as something is in the cart, what is the workflow in your case?
The goal is to retain items added before they logged out
I tested this with and without incognito (aka. "different device") and the cart was empty after logging back in.
@aNickzz it should work. Are you on the latest commerce release?
No, currently running v3.0.5, but i'll update that soon, (currently setting up a new dev workspace)
Hmm, 3.0.5 should work I think. I will see if I can reproduce.
This kinda works, it's just not restoring the previous cart on login, is there a setting I need to enable for this?
It does restore the previous cart when you delete the last item in your current cart though, which makes things a little confusing;
Given that I'm spending a lot of time building and testing this site, I end up with a stack of 5 - 10 carts at one time and have to go through removing all the items from each one as it gets restored after the last to fully empty the cart.
In my case, users cannot add items to the cart unless they are logged in, so I've implemented @RitterKnightCreative's code above, which effectively solves my use case.
@aNickzz can you confirm you are on the latest release 3.0.12?
3.0.11, updates are released more frequently than I check for them.
Feature request.
When a user adds products to their cart, then logs-out or closes their browser and returns later the cart is empty. In my opinion that's undesirable from an UX and revenue point of view. Of course after a cart is completed and became an order the cart should be empty.
Carts are saved in the CP as inactive carts, so what is the use for this when users can not acces this data?
Steps to reproduce
Additional info