Magento2: Regression: Cart Price Rules: Empty "From" and "To" values are replaced with todays date

Created on 12 Aug 2016  路  20Comments  路  Source: magento/magento2

In Magento 1 you could have Cart Sales Rules with no start and/or end date, i.e. codes with unlimited validity. In Magento 2, saving a rule with an empty start and/or end date will automatically fill the current date as value.

Preconditions

  1. Magento 2.1.0

    Steps to reproduce

  2. Create new "Cart Price Rule"

  3. Do not fill a value in "From" and "To"
  4. Save & continue editing

    Expected result

  5. "From" and "To" fields should be empty

  6. The rule should be saved without date and be always valid

    Actual result

  7. Todays date is filled into "From" and "To"

  8. The rule is date restricted
Tax Fixed in 2.2.x Clear Description Format is valid Ready for Work Reproduced on 2.1.x bug report

Most helpful comment

This issue causes further problems when you have content staging on a site. As it seems content staging is removing the from_date & to_date from the cart rules form.

Fields are marked as invisible in vendor/magento/module-sales-rule-staging/view/adminhtml/ui_component/sales_rule_form.xml.

The underlying problem here seems to be that Magento\Framework\Stdlib\DateTime\Filter\Date is always returning a DateTime instance, without first validating if the value has any data. This could be expected functionality, but I can imagine causes other problems throughout the admin.

It seems there are a number of issues in conjunction with content staging. Content staging is forcing the to_date for rules to always be the current date when a coupon is updated causing all coupon codes to have their expiration_date set to the current date. Resulting in them expiring daily and requires to be saved each day.

All 20 comments

the post fields from_date and to_date are filtered in \Magento\SalesRule\Controller\Adminhtml\Promo\Quote\Save::execute():

                $data = $this->getRequest()->getPostValue();
                $inputFilter = new \Zend_Filter_Input(
                    ['from_date' => $this->_dateFilter, 'to_date' => $this->_dateFilter],
                    [],
                    $data
                );
                $data = $inputFilter->getUnescaped();

unfortunately \Magento\Framework\Stdlib\DateTime\Filter\Date does not handle empty strings:

    public function filter($value)
    {
        try {
            $value = new \DateTime($value);
            return $value->format('Y-m-d');
        } catch (\Exception $e) {
            throw new \Exception("Invalid input date format '$value'");
        }
    }

initializing a \DateTime object with an empty string will automatically use todays date.

here's a quick (and dirty) workaround: in Magento\SalesRule\Controller\Adminhtml\Promo\Quote\Save::execute() replace

                $data = $this->getRequest()->getPostValue();
                $inputFilter = new \Zend_Filter_Input(
                    ['from_date' => $this->_dateFilter, 'to_date' => $this->_dateFilter],
                    [],
                    $data
                );
                $data = $inputFilter->getUnescaped();
                $id = $this->getRequest()->getParam('rule_id');

with

                $data = $this->getRequest()->getPostValue();
                $inputFilter = new \Zend_Filter_Input(
                    ['from_date' => $this->_dateFilter, 'to_date' => $this->_dateFilter],
                    [],
                    $data
                );
                if (array_key_exists('from_date', $data)) {
                    $data['from_date'] = trim($data['from_date']) ? $inputFilter->getUnescaped('from_date') : '';
                }
                if (array_key_exists('to_date', $data)) {
                    $data['to_date'] = trim($data['to_date']) ? $inputFilter->getUnescaped('to_date') : '';
                }
                $id = $this->getRequest()->getParam('rule_id');

still present in 2.1.1

Can't reproduce on clean install of 2.1.1. Have you upgraded from previous versions?

yes, 2.0.4 was our starting point. our 2.1.1 store still shows this behaviour and the above mentioned patch fixes the problem. will retry on a clean 2.1.1 and report back.

sorry for the delay, the post shop launches prevented me from retesting. will be done ASAP.

it seems that your team was able to reproduce it after all: #6762 - MAGETWO-59047

Hi @heldchen , thank for report, yes we've created internal ticket MAGETWO-59047

This issue causes further problems when you have content staging on a site. As it seems content staging is removing the from_date & to_date from the cart rules form.

Fields are marked as invisible in vendor/magento/module-sales-rule-staging/view/adminhtml/ui_component/sales_rule_form.xml.

The underlying problem here seems to be that Magento\Framework\Stdlib\DateTime\Filter\Date is always returning a DateTime instance, without first validating if the value has any data. This could be expected functionality, but I can imagine causes other problems throughout the admin.

It seems there are a number of issues in conjunction with content staging. Content staging is forcing the to_date for rules to always be the current date when a coupon is updated causing all coupon codes to have their expiration_date set to the current date. Resulting in them expiring daily and requires to be saved each day.

I can confirm that this issue is still occurring in Magento 2.1.3. As these fields are currently not flagged as mandatory and are optional, this definitely appears to be a bug.

the issue is still present in 2.1.4, the commit https://github.com/magento/magento2/commit/e200425c1ecec910d50660fed8c422caa8d0971b did not yet land in 2.1.4

This issue is still happening in 2.1.5

I found that MagentoSalesRuleStagingModelPluginDateResolverPlugin::beforeGetToDate() is forcing toDate on the cart rule when the rule save. The coupon picks it up as expiration date. Hence the coupon code breaks each day.

I wrote a plugin to reset the coupon expiration date to null.

What is Date Resolver suppose to do?

This plugin is indeed what cause the to_date to be updated with today's date instead of null

It looks like any time after 03:14:07 UTC on Tuesday, 19 January 2038 is converted to null in the database. This is because Unix timestamp runs out of int to convert to.

Im not sure if my solution is correct. I force the expiration_date to null

Modified file: vendor/magento/module-sales-rule/Model/Rule.php

Before

public function afterSave()
...
)->setExpirationDate(
$this->getToDate()
)->save();

After

public function afterSave()
...
)->setExpirationDate(
null
)->save();

The commit referenced above on Oct 27 does not appear to fix this issue, at least on M2.1.5 EE.

I'm going ahead with the workaround from @nphp101 on Apr 25, which appears to be effective.

I'm not sure if forcing to null in all cases is really the best for everyone. But on EE, if the content staging system is supposed to handle rule expiration, it seems safe to me.

Hello Magento-Team,

We still can reproduce this issue in 2.1.7. I had a look at Release 2.1.8, and in Branch 2.1-Dev. Its still not merged there.

Could you please merge it in 2.1-Dev? In 2.2 this fix is already avaible, but 2.2 is not released yet.

Greetings
Elias

Additional Info:
There is another issue which have the same effect, with a the EE-Sales-Rule-Staging-Module.

It can be found here:
File: magento/vendor/magento/module-sales-rule-staging/Model/Plugin/DateResolverPlugin.php
Method: beforeGetToDate

This Plugin create the same behaviour (sets the date incorrect if its "null"). I am aware that this issue is EE-only and here is not a support area for such issues. But i hope it will help someone, who is searching for a solution.

Greetings
Elias

@heldchen, thank you for your report.
The issue is already fixed in 2.2.0

This issue is still open in 2.1.12

Was this page helpful?
0 / 5 - 0 ratings