9
9
use Magento \Framework \DB \Select ;
10
10
use Magento \Framework \Serialize \Serializer \Json ;
11
11
use Magento \Quote \Model \Quote \Address ;
12
+ use Magento \SalesRule \Api \Data \CouponInterface ;
13
+ use Magento \SalesRule \Model \Coupon ;
14
+ use Magento \SalesRule \Model \Rule ;
12
15
13
16
/**
14
17
* Sales Rules resource collection model.
@@ -105,12 +108,15 @@ protected function mapAssociatedEntities($entityType, $objectField)
105
108
106
109
$ associatedEntities = $ this ->getConnection ()->fetchAll ($ select );
107
110
108
- array_map (function ($ associatedEntity ) use ($ entityInfo , $ ruleIdField , $ objectField ) {
109
- $ item = $ this ->getItemByColumnValue ($ ruleIdField , $ associatedEntity [$ ruleIdField ]);
110
- $ itemAssociatedValue = $ item ->getData ($ objectField ) === null ? [] : $ item ->getData ($ objectField );
111
- $ itemAssociatedValue [] = $ associatedEntity [$ entityInfo ['entity_id_field ' ]];
112
- $ item ->setData ($ objectField , $ itemAssociatedValue );
113
- }, $ associatedEntities );
111
+ array_map (
112
+ function ($ associatedEntity ) use ($ entityInfo , $ ruleIdField , $ objectField ) {
113
+ $ item = $ this ->getItemByColumnValue ($ ruleIdField , $ associatedEntity [$ ruleIdField ]);
114
+ $ itemAssociatedValue = $ item ->getData ($ objectField ) ?? [];
115
+ $ itemAssociatedValue [] = $ associatedEntity [$ entityInfo ['entity_id_field ' ]];
116
+ $ item ->setData ($ objectField , $ itemAssociatedValue );
117
+ },
118
+ $ associatedEntities
119
+ );
114
120
}
115
121
116
122
/**
@@ -137,6 +143,7 @@ protected function _afterLoad()
137
143
* @param string $couponCode
138
144
* @param string|null $now
139
145
* @param Address $address allow extensions to further filter out rules based on quote address
146
+ * @throws \Zend_Db_Select_Exception
140
147
* @use $this->addWebsiteGroupDateFilter()
141
148
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
142
149
* @return $this
@@ -149,84 +156,122 @@ public function setValidationFilter(
149
156
Address $ address = null
150
157
) {
151
158
if (!$ this ->getFlag ('validation_filter ' )) {
152
- /* We need to overwrite joinLeft if coupon is applied */
153
- $ this ->getSelect ()->reset ();
154
- parent ::_initSelect ();
155
159
156
- $ this ->addWebsiteGroupDateFilter ($ websiteId , $ customerGroupId , $ now );
157
- $ select = $ this ->getSelect ();
160
+ $ this ->prepareSelect ($ websiteId , $ customerGroupId , $ now );
158
161
159
- $ connection = $ this ->getConnection ();
160
- if (strlen ($ couponCode )) {
161
- $ select ->joinLeft (
162
- ['rule_coupons ' => $ this ->getTable ('salesrule_coupon ' )],
163
- $ connection ->quoteInto (
164
- 'main_table.rule_id = rule_coupons.rule_id AND main_table.coupon_type != ? ' ,
165
- \Magento \SalesRule \Model \Rule::COUPON_TYPE_NO_COUPON
166
- ),
167
- ['code ' ]
168
- );
169
-
170
- $ noCouponWhereCondition = $ connection ->quoteInto (
171
- 'main_table.coupon_type = ? ' ,
172
- \Magento \SalesRule \Model \Rule::COUPON_TYPE_NO_COUPON
173
- );
174
-
175
- $ autoGeneratedCouponCondition = [
176
- $ connection ->quoteInto (
177
- "main_table.coupon_type = ? " ,
178
- \Magento \SalesRule \Model \Rule::COUPON_TYPE_AUTO
179
- ),
180
- $ connection ->quoteInto (
181
- "rule_coupons.type = ? " ,
182
- \Magento \SalesRule \Api \Data \CouponInterface::TYPE_GENERATED
183
- ),
184
- ];
185
-
186
- $ orWhereConditions = [
187
- "( " . implode ($ autoGeneratedCouponCondition , " AND " ) . ") " ,
188
- $ connection ->quoteInto (
189
- '(main_table.coupon_type = ? AND main_table.use_auto_generation = 1 AND rule_coupons.type = 1) ' ,
190
- \Magento \SalesRule \Model \Rule::COUPON_TYPE_SPECIFIC
191
- ),
192
- $ connection ->quoteInto (
193
- '(main_table.coupon_type = ? AND main_table.use_auto_generation = 0 AND rule_coupons.type = 0) ' ,
194
- \Magento \SalesRule \Model \Rule::COUPON_TYPE_SPECIFIC
195
- ),
196
- ];
197
-
198
- $ andWhereConditions = [
199
- $ connection ->quoteInto (
200
- 'rule_coupons.code = ? ' ,
201
- $ couponCode
202
- ),
203
- $ connection ->quoteInto (
204
- '(rule_coupons.expiration_date IS NULL OR rule_coupons.expiration_date >= ?) ' ,
205
- $ this ->_date ->date ()->format ('Y-m-d ' )
206
- ),
207
- ];
208
-
209
- $ orWhereCondition = implode (' OR ' , $ orWhereConditions );
210
- $ andWhereCondition = implode (' AND ' , $ andWhereConditions );
211
-
212
- $ select ->where (
213
- $ noCouponWhereCondition . ' OR (( ' . $ orWhereCondition . ') AND ' . $ andWhereCondition . ') ' ,
214
- null ,
215
- Select::TYPE_CONDITION
216
- );
162
+ $ noCouponRules = $ this ->getNoCouponCodeSelect ();
163
+
164
+ if ($ couponCode ) {
165
+ $ couponRules = $ this ->getCouponCodeSelect ($ couponCode );
166
+
167
+ $ allAllowedRules = $ this ->getConnection ()->select ();
168
+ $ allAllowedRules ->union ([$ noCouponRules , $ couponRules ], \Zend_Db_Select::SQL_UNION_ALL );
169
+
170
+ $ this ->_select = $ allAllowedRules ;
217
171
} else {
218
- $ this ->addFieldToFilter (
219
- 'main_table.coupon_type ' ,
220
- \Magento \SalesRule \Model \Rule::COUPON_TYPE_NO_COUPON
221
- );
172
+ $ this ->_select = $ noCouponRules ;
222
173
}
174
+
223
175
$ this ->setOrder ('sort_order ' , self ::SORT_ORDER_ASC );
224
176
$ this ->setFlag ('validation_filter ' , true );
225
177
}
226
178
227
179
return $ this ;
228
180
}
229
181
182
+ /**
183
+ * Recreate the default select object for specific needs of salesrule evaluation with coupon codes.
184
+ *
185
+ * @param $websiteId
186
+ * @param $customerGroupId
187
+ * @param $now
188
+ */
189
+ private function prepareSelect ($ websiteId , $ customerGroupId , $ now )
190
+ {
191
+ $ this ->getSelect ()->reset ();
192
+ parent ::_initSelect ();
193
+
194
+ $ this ->addWebsiteGroupDateFilter ($ websiteId , $ customerGroupId , $ now );
195
+ }
196
+
197
+ /**
198
+ * Return select object to determine all active rules not needing a coupon code.
199
+ *
200
+ * @return Select
201
+ */
202
+ private function getNoCouponCodeSelect ()
203
+ {
204
+ $ noCouponSelect = clone $ this ->getSelect ();
205
+
206
+ $ noCouponSelect ->where (
207
+ 'main_table.coupon_type = ? ' ,
208
+ Rule::COUPON_TYPE_NO_COUPON
209
+ );
210
+
211
+ $ noCouponSelect ->columns ([Coupon::KEY_CODE => new \Zend_Db_Expr ('NULL ' )]);
212
+
213
+ return $ noCouponSelect ;
214
+ }
215
+
216
+ /**
217
+ * Determine all active rules that are valid for the given coupon code.
218
+ *
219
+ * @param $couponCode
220
+ * @return Select
221
+ */
222
+ private function getCouponCodeSelect ($ couponCode )
223
+ {
224
+ $ couponSelect = clone $ this ->getSelect ();
225
+
226
+ $ this ->joinCouponTable ($ couponCode , $ couponSelect );
227
+
228
+ $ notExpired = $ this ->getConnection ()->quoteInto (
229
+ '(rule_coupons.expiration_date IS NULL OR rule_coupons.expiration_date >= ?) ' ,
230
+ $ this ->_date ->date ()->format ('Y-m-d ' )
231
+ );
232
+
233
+ $ isAutogeneratedCoupon =
234
+ $ this ->getConnection ()->quoteInto ('main_table.coupon_type = ? ' , Rule::COUPON_TYPE_AUTO )
235
+ . ' AND ' .
236
+ $ this ->getConnection ()->quoteInto ('rule_coupons.type = ? ' , CouponInterface::TYPE_GENERATED );
237
+
238
+ $ isValidSpecificCoupon =
239
+ $ this ->getConnection ()->quoteInto ('(main_table.coupon_type = ?) ' , Rule::COUPON_TYPE_SPECIFIC )
240
+ . ' AND ( ' .
241
+ '(main_table.use_auto_generation = 1 AND rule_coupons.type = 1) '
242
+ . ' OR ' .
243
+ '(main_table.use_auto_generation = 0 AND rule_coupons.type = 0) '
244
+ . ') ' ;
245
+
246
+ $ couponSelect ->where (
247
+ "$ notExpired AND ( $ isAutogeneratedCoupon OR $ isValidSpecificCoupon) " ,
248
+ null ,
249
+ Select::TYPE_CONDITION
250
+ );
251
+
252
+ return $ couponSelect ;
253
+ }
254
+
255
+ /**
256
+ * @param $couponCode
257
+ * @param Select $couponSelect
258
+ */
259
+ private function joinCouponTable ($ couponCode , Select $ couponSelect )
260
+ {
261
+ $ couponJoinCondition =
262
+ 'main_table.rule_id = rule_coupons.rule_id '
263
+ . ' AND ' .
264
+ $ this ->getConnection ()->quoteInto ('main_table.coupon_type <> ? ' , Rule::COUPON_TYPE_NO_COUPON )
265
+ . ' AND ' .
266
+ $ this ->getConnection ()->quoteInto ('rule_coupons.code = ? ' , $ couponCode );
267
+
268
+ $ couponSelect ->joinInner (
269
+ ['rule_coupons ' => $ this ->getTable ('salesrule_coupon ' )],
270
+ $ couponJoinCondition ,
271
+ [Coupon::KEY_CODE ]
272
+ );
273
+ }
274
+
230
275
/**
231
276
* Filter collection by website(s), customer group(s) and date.
232
277
* Filter collection to only active rules.
0 commit comments