I know this post is old, but may have a short term solution for M1 and could possibly be very similar for M2. I hate copying core code to the local scope but since this is a short term solution on M1, I think it should be perfectly suitable:
Copy app/core/Mage/SalesRule/Model/Validator.php
to app/local/Mage/SalesRule/Model/Validator.php
Update the method _canProcessRule($rule, $address)
(see comment: Modified - coupon expiry check start):
/**
* Check if rule can be applied for specific address/quote/customer
*
* @param Mage_SalesRule_Model_Rule $rule
* @param Mage_Sales_Model_Quote_Address $address
* @return bool
*/
protected function _canProcessRule($rule, $address)
{
if ($rule->hasIsValidForAddress($address) && !$address->isObjectNew()) {
return $rule->getIsValidForAddress($address);
}
/**
* check per coupon usage limit
*/
if ($rule->getCouponType() != Mage_SalesRule_Model_Rule::COUPON_TYPE_NO_COUPON) {
$couponCode = $address->getQuote()->getCouponCode();
if (strlen($couponCode)) {
$coupon = Mage::getModel('salesrule/coupon');
$coupon->load($couponCode, 'code');
if ($coupon->getId()) {
// check entire usage limit
if ($coupon->getUsageLimit() && $coupon->getTimesUsed() >= $coupon->getUsageLimit()) {
$rule->setIsValidForAddress($address, false);
return false;
}
// check per customer usage limit
$customerId = $address->getQuote()->getCustomerId();
if ($customerId && $coupon->getUsagePerCustomer()) {
$couponUsage = new Varien_Object();
Mage::getResourceModel('salesrule/coupon_usage')->loadByCustomerCoupon(
$couponUsage,
$customerId,
$coupon->getId()
);
if ($couponUsage->getCouponId() &&
$couponUsage->getTimesUsed() >= $coupon->getUsagePerCustomer()
) {
$rule->setIsValidForAddress($address, false);
return false;
}
}
/**
* Modified - coupon expiry check start
*/
// Validate individual coupon expiry date
// Currently does not exist in M1 or M2
$tz = Mage::getStoreConfig('general/locale/timezone');
$dateNow = new DateTime('now', new DateTimeZone($tz));
$dateCouponExpiry = new DateTime($coupon->getExpirationDate(), new DateTimeZone($tz));
if ($dateCouponExpiry <= $dateNow) {
$rule->setIsValidForAddress($address, false);
return false;
}
/**
* Modified - coupon expiry check end
*/
}
}
}
/**
* check per rule usage limit
*/
$ruleId = $rule->getId();
if ($ruleId && $rule->getUsesPerCustomer()) {
$customerId = $address->getQuote()->getCustomerId();
$ruleCustomer = Mage::getModel('salesrule/rule_customer');
$ruleCustomer->loadByCustomerRule($customerId, $ruleId);
if ($ruleCustomer->getId()) {
if ($ruleCustomer->getTimesUsed() >= $rule->getUsesPerCustomer()) {
$rule->setIsValidForAddress($address, false);
return false;
}
}
}
$rule->afterLoad();
/**
* quote does not meet rule's conditions
*/
if (!$rule->validate($address)) {
$rule->setIsValidForAddress($address, false);
return false;
}
/**
* passed all validations, remember to be valid
*/
$rule->setIsValidForAddress($address, true);
return true;
}
I will eventually be doing the same on M2 but if anyone wants to attempt, it should be as simple as creating a DI for <preference for="Magento\SalesRule\Model\Utility" type="Your\Module\Model\SalesRule\Utility" />
and implementing the similar logic in the method canProcessRule($rule, $address)
vendor/magento/module-reminder
.