Skip to content

Commit 99683e5

Browse files
Merge pull request #4151 from woocommerce/dev/PCP-5961-validation-issue-context
Validation issue refactoring (5961)
2 parents d2a46ea + ddc16ed commit 99683e5

57 files changed

Lines changed: 3234 additions & 1626 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

modules/ppcp-store-sync/src/CartValidation/CouponValidator/CouponContextBuilder.php

Lines changed: 101 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
use WooCommerce\PayPalCommerce\StoreSync\Helper\CartHelper;
1616
use WooCommerce\PayPalCommerce\StoreSync\Helper\ProductManager;
1717
use WooCommerce\PayPalCommerce\StoreSync\Schema\PayPalCart;
18+
use WooCommerce\PayPalCommerce\StoreSync\Validation\Context\IssueContext;
19+
use WooCommerce\PayPalCommerce\StoreSync\Validation\Context\PricingErrorContext;
1820

1921
/**
2022
* Builds context data for coupon validation issues.
@@ -44,11 +46,11 @@ public function __construct( ProductManager $product_manager, DiscountCalculator
4446
* Builds context by calling declared context builders.
4547
*
4648
* @param string $issue_type The issue type.
47-
* @param string $code The coupon code.
48-
* @param PayPalCart $cart The cart context.
49-
* @param WC_Coupon|null $wc_coupon The WC coupon object.
50-
* @param array $builders Array of builder names to call.
51-
* @param array $extra Extra context data.
49+
* @param string $code The coupon code.
50+
* @param PayPalCart $cart The cart context.
51+
* @param WC_Coupon|null $wc_coupon The WC coupon object.
52+
* @param array $builders Array of builder names to call.
53+
* @param array $extra Extra context data.
5254
* @return array The built context.
5355
*/
5456
public function build_coupon_context(
@@ -75,11 +77,11 @@ public function build_coupon_context(
7577
/**
7678
* Dispatches to the appropriate builder method.
7779
*
78-
* @param string $builder The builder name.
79-
* @param string $code The coupon code.
80-
* @param PayPalCart $cart The cart context.
80+
* @param string $builder The builder name.
81+
* @param string $code The coupon code.
82+
* @param PayPalCart $cart The cart context.
8183
* @param WC_Coupon|null $wc_coupon The WC coupon object.
82-
* @param array $extra Extra context data.
84+
* @param array $extra Extra context data.
8385
* @return array The context data from the builder.
8486
*/
8587
private function call_builder( string $builder, string $code, PayPalCart $cart, ?WC_Coupon $wc_coupon, array $extra ): array {
@@ -108,10 +110,10 @@ private function call_builder( string $builder, string $code, PayPalCart $cart,
108110
/**
109111
* Builds alternative coupons context.
110112
*
111-
* @param string $code The coupon code.
112-
* @param PayPalCart $cart The cart context.
113+
* @param string $code The coupon code.
114+
* @param PayPalCart $cart The cart context.
113115
* @param WC_Coupon|null $wc_coupon The WC coupon object.
114-
* @param array $extra Extra context data.
116+
* @param array $extra Extra context data.
115117
* @return array The context data.
116118
*/
117119
private function build_alternatives( string $code, PayPalCart $cart, ?WC_Coupon $wc_coupon, array $extra ): array {
@@ -130,10 +132,10 @@ private function build_alternatives( string $code, PayPalCart $cart, ?WC_Coupon
130132
/**
131133
* Builds expiration context.
132134
*
133-
* @param string $code The coupon code.
134-
* @param PayPalCart $cart The cart context.
135+
* @param string $code The coupon code.
136+
* @param PayPalCart $cart The cart context.
135137
* @param WC_Coupon|null $wc_coupon The WC coupon object.
136-
* @param array $extra Extra context data.
138+
* @param array $extra Extra context data.
137139
* @return array The context data.
138140
*/
139141
private function build_expiration( string $code, PayPalCart $cart, ?WC_Coupon $wc_coupon, array $extra ): array {
@@ -147,17 +149,17 @@ private function build_expiration( string $code, PayPalCart $cart, ?WC_Coupon $w
147149
}
148150

149151
return array(
150-
'expiration_date' => $expiration_date->format( 'c' ),
152+
'expiration_date' => $expiration_date->getTimestamp(),
151153
);
152154
}
153155

154156
/**
155157
* Builds usage limits context.
156158
*
157-
* @param string $code The coupon code.
158-
* @param PayPalCart $cart The cart context.
159+
* @param string $code The coupon code.
160+
* @param PayPalCart $cart The cart context.
159161
* @param WC_Coupon|null $wc_coupon The WC coupon object.
160-
* @param array $extra Extra context data.
162+
* @param array $extra Extra context data.
161163
* @return array The context data.
162164
*/
163165
private function build_usage_limits( string $code, PayPalCart $cart, ?WC_Coupon $wc_coupon, array $extra ): array {
@@ -174,10 +176,10 @@ private function build_usage_limits( string $code, PayPalCart $cart, ?WC_Coupon
174176
/**
175177
* Builds minimum spend context.
176178
*
177-
* @param string $code The coupon code.
178-
* @param PayPalCart $cart The cart context.
179+
* @param string $code The coupon code.
180+
* @param PayPalCart $cart The cart context.
179181
* @param WC_Coupon|null $wc_coupon The WC coupon object.
180-
* @param array $extra Extra context data.
182+
* @param array $extra Extra context data.
181183
* @return array The context data.
182184
*/
183185
private function build_minimum_spend( string $code, PayPalCart $cart, ?WC_Coupon $wc_coupon, array $extra ): array {
@@ -201,10 +203,10 @@ private function build_minimum_spend( string $code, PayPalCart $cart, ?WC_Coupon
201203
/**
202204
* Builds maximum spend context.
203205
*
204-
* @param string $code The coupon code.
205-
* @param PayPalCart $cart The cart context.
206+
* @param string $code The coupon code.
207+
* @param PayPalCart $cart The cart context.
206208
* @param WC_Coupon|null $wc_coupon The WC coupon object.
207-
* @param array $extra Extra context data.
209+
* @param array $extra Extra context data.
208210
* @return array The context data.
209211
*/
210212
private function build_maximum_spend( string $code, PayPalCart $cart, ?WC_Coupon $wc_coupon, array $extra ): array {
@@ -226,10 +228,10 @@ private function build_maximum_spend( string $code, PayPalCart $cart, ?WC_Coupon
226228
/**
227229
* Builds eligible items context.
228230
*
229-
* @param string $code The coupon code.
230-
* @param PayPalCart $cart The cart context.
231+
* @param string $code The coupon code.
232+
* @param PayPalCart $cart The cart context.
231233
* @param WC_Coupon|null $wc_coupon The WC coupon object.
232-
* @param array $extra Extra context data.
234+
* @param array $extra Extra context data.
233235
* @return array The context data.
234236
*/
235237
private function build_eligible_items( string $code, PayPalCart $cart, ?WC_Coupon $wc_coupon, array $extra ): array {
@@ -249,10 +251,10 @@ private function build_eligible_items( string $code, PayPalCart $cart, ?WC_Coupo
249251
/**
250252
* Builds stacking conflict context.
251253
*
252-
* @param string $code The coupon code.
253-
* @param PayPalCart $cart The cart context.
254+
* @param string $code The coupon code.
255+
* @param PayPalCart $cart The cart context.
254256
* @param WC_Coupon|null $wc_coupon The WC coupon object.
255-
* @param array $extra Extra context data.
257+
* @param array $extra Extra context data.
256258
* @return array The context data.
257259
*/
258260
private function build_stacking( string $code, PayPalCart $cart, ?WC_Coupon $wc_coupon, array $extra ): array {
@@ -285,10 +287,10 @@ private function build_stacking( string $code, PayPalCart $cart, ?WC_Coupon $wc_
285287
/**
286288
* Builds email restriction context.
287289
*
288-
* @param string $code The coupon code.
289-
* @param PayPalCart $cart The cart context.
290+
* @param string $code The coupon code.
291+
* @param PayPalCart $cart The cart context.
290292
* @param WC_Coupon|null $wc_coupon The WC coupon object.
291-
* @param array $extra Extra context data.
293+
* @param array $extra Extra context data.
292294
* @return array The context data.
293295
*/
294296
private function build_email_restriction( string $code, PayPalCart $cart, ?WC_Coupon $wc_coupon, array $extra ): array {
@@ -309,8 +311,8 @@ private function build_email_restriction( string $code, PayPalCart $cart, ?WC_Co
309311
* Gets alternative coupon suggestions via filter.
310312
*
311313
* @param string $failed_code The coupon code that failed.
312-
* @param string $reason The failure reason.
313-
* @param PayPalCart $cart The cart context.
314+
* @param string $reason The failure reason.
315+
* @param PayPalCart $cart The cart context.
314316
* @return array Array of alternative coupon codes.
315317
*/
316318
private function get_alternative_coupons( string $failed_code, string $reason, PayPalCart $cart ): array {
@@ -323,6 +325,68 @@ private function get_alternative_coupons( string $failed_code, string $reason, P
323325
);
324326
}
325327

328+
/**
329+
* Builds a typed IssueContext instance from a resolved context array.
330+
*
331+
* Returns null for issue types that have no matching context class.
332+
*
333+
* @param string $issue_type The coupon issue type (e.g. 'COUPON_EXPIRED').
334+
* @param string $coupon_code The coupon code that triggered the issue.
335+
* @param array $args The flat context array built by build_coupon_context().
336+
* @return IssueContext|null
337+
*/
338+
public function build_coupon_issue_context( string $issue_type, string $coupon_code, array $args ): ?IssueContext {
339+
$context = null;
340+
switch ( $issue_type ) {
341+
case 'COUPON_EXPIRED':
342+
$context = PricingErrorContext::create_discount_expired();
343+
break;
344+
345+
case 'USAGE_LIMIT_EXCEEDED':
346+
$context = PricingErrorContext::create_discount_usage_limit_exceeded();
347+
break;
348+
349+
case 'MINIMUM_ORDER_NOT_MET':
350+
$context = PricingErrorContext::create_discount_minimum_not_met();
351+
break;
352+
353+
case 'COUPON_NOT_APPLICABLE':
354+
case 'COUPON_EMAIL_RESTRICTED':
355+
$context = PricingErrorContext::create_discount_customer_ineligible();
356+
break;
357+
358+
case 'COUPON_STACKING_NOT_ALLOWED':
359+
$context = PricingErrorContext::create_promotional_conflict();
360+
break;
361+
}
362+
363+
if ( ! $context ) {
364+
return null;
365+
}
366+
367+
$context->coupon_code( $coupon_code );
368+
if ( isset( $args['expiration_date'] ) ) {
369+
$context->expiration_date( (int) $args['expiration_date'] );
370+
}
371+
if ( isset( $args['usage_limit'] ) && is_numeric( $args['usage_limit'] ) ) {
372+
$context->usage_limit( (int) $args['usage_limit'] );
373+
}
374+
if ( isset( $args['minimum_required'] ) ) {
375+
$context->minimum_order_amount( (string) $args['minimum_required'] );
376+
}
377+
if ( isset( $args['current_usage'] ) ) {
378+
$context->current_usage( (int) $args['current_usage'] );
379+
}
380+
if ( isset( $args['currency_code'] ) ) {
381+
$context->currency_code( (string) $args['currency_code'] );
382+
}
383+
if ( isset( $args['current_discount'] ) ) {
384+
$context->discount_amount( (string) $args['current_discount'] );
385+
}
386+
387+
return $context;
388+
}
389+
326390
/**
327391
* Gets eligible items for a coupon via filter.
328392
*
@@ -331,7 +395,7 @@ private function get_alternative_coupons( string $failed_code, string $reason, P
331395
* excluded items, sale items, and third-party plugin logic.
332396
*
333397
* @param WC_Coupon $wc_coupon The WC coupon.
334-
* @param PayPalCart $cart The cart context.
398+
* @param PayPalCart $cart The cart context.
335399
* @return array Array of eligible variant IDs.
336400
*/
337401
private function get_eligible_items( WC_Coupon $wc_coupon, PayPalCart $cart ): array {

modules/ppcp-store-sync/src/CartValidation/CouponValidator/CouponResolutionBuilder.php

Lines changed: 40 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
use WooCommerce\PayPalCommerce\StoreSync\Enums\Priority;
1616
use WooCommerce\PayPalCommerce\StoreSync\Helper\CartHelper;
1717
use WooCommerce\PayPalCommerce\StoreSync\Schema\PayPalCart;
18-
use WooCommerce\PayPalCommerce\StoreSync\Schema\ResolutionOption;
18+
use WooCommerce\PayPalCommerce\StoreSync\Validation\Resolution\ResolutionOption;
1919

2020
/**
2121
* Builds resolution options for coupon validation issues.
@@ -26,11 +26,11 @@ class CouponResolutionBuilder {
2626
* Builds resolutions from config keys, with special handling for stacking.
2727
*
2828
* @param string $issue_type The issue type.
29-
* @param array $keys Resolution keys from config.
30-
* @param string $code The coupon code.
31-
* @param array $context The context data.
32-
* @param PayPalCart $cart The cart context.
33-
* @param WC_Coupon|null $wc_coupon The WC coupon object.
29+
* @param array $keys Resolution keys from config.
30+
* @param string $code The coupon code.
31+
* @param array $context The context data.
32+
* @param PayPalCart $cart The cart context.
33+
* @param WC_Coupon|null $wc_coupon The WC coupon object.
3434
* @return ResolutionOption[] The resolution options.
3535
*/
3636
public function build_resolution_options(
@@ -61,50 +61,52 @@ public function build_resolution_options(
6161
/**
6262
* Dispatches to the appropriate resolution factory method.
6363
*
64-
* @param string $key The resolution key.
64+
* @param string $key The resolution key.
6565
* @param array $context The context data.
66-
* @param PayPalCart $cart The cart context.
66+
* @param PayPalCart $cart The cart context.
6767
* @return ResolutionOption|null The resolution option or null if key not recognized.
6868
*/
6969
private function build_resolution_by_key( string $key, array $context, PayPalCart $cart ): ?ResolutionOption {
7070
switch ( $key ) {
7171
case 'try_different':
72-
return ResolutionOption::apply_different_coupon( 'Try a different coupon code', Priority::HIGH );
72+
return ResolutionOption::create_apply_different_coupon()
73+
->label( 'Try a different coupon code' )
74+
->priority( Priority::HIGH );
7375

7476
case 'remove':
75-
return ResolutionOption::remove_coupon( 'Continue without coupon', Priority::MEDIUM );
77+
return ResolutionOption::create_remove_coupon()
78+
->label( 'Continue without coupon' )
79+
->priority( Priority::MEDIUM );
7680

7781
case 'modify_cart':
78-
return ResolutionOption::modify_cart(
79-
'Add eligible items to use this coupon',
80-
array( 'priority' => Priority::HIGH )
81-
);
82+
return ResolutionOption::create_modify_cart()
83+
->label( 'Add eligible items to use this coupon' )
84+
->priority( Priority::HIGH );
8285

8386
case 'view_available':
84-
return ResolutionOption::redirect_to_merchant(
85-
'View available offers',
86-
'',
87-
array( 'priority' => Priority::LOW )
88-
);
87+
return ResolutionOption::create_redirect_to_merchant()
88+
->label( 'View available offers' )
89+
->priority( Priority::LOW );
8990

9091
case 'suggest_alternative':
91-
return ResolutionOption::apply_different_coupon( 'Try a different coupon', Priority::MEDIUM );
92+
return ResolutionOption::create_apply_different_coupon()
93+
->label( 'Try a different coupon' )
94+
->priority( Priority::MEDIUM );
9295

9396
case 'add_items_to_minimum':
9497
$formatted_amount = isset( $context['shortage_amount'] )
9598
? CartHelper::format_price( $context['shortage_amount'], $cart )
9699
: '';
97100

98-
return ResolutionOption::modify_cart(
99-
sprintf( 'Add %s more to qualify', $formatted_amount ),
100-
array(
101-
'priority' => Priority::HIGH,
102-
'amount_needed' => $formatted_amount,
103-
)
104-
);
101+
return ResolutionOption::create_modify_cart()
102+
->label( sprintf( 'Add %s more to qualify', $formatted_amount ) )
103+
->priority( Priority::HIGH )
104+
->set_meta( 'amount_needed', $formatted_amount );
105105

106106
case 'continue_without':
107-
return ResolutionOption::remove_coupon( 'Continue without coupon', Priority::LOW );
107+
return ResolutionOption::create_remove_coupon()
108+
->label( 'Continue without coupon' )
109+
->priority( Priority::LOW );
108110

109111
default:
110112
return null;
@@ -114,9 +116,9 @@ private function build_resolution_by_key( string $key, array $context, PayPalCar
114116
/**
115117
* Builds stacking-specific resolutions with savings comparison.
116118
*
117-
* @param string $code The coupon code.
119+
* @param string $code The coupon code.
118120
* @param array $context The context data.
119-
* @param PayPalCart $cart The cart context.
121+
* @param PayPalCart $cart The cart context.
120122
* @return ResolutionOption[] The resolution options.
121123
*/
122124
private function build_stacking_resolutions( string $code, array $context, PayPalCart $cart ): array {
@@ -128,16 +130,14 @@ private function build_stacking_resolutions( string $code, array $context, PayPa
128130
$formatted_attempted = CartHelper::format_price( $attempted_discount, $cart );
129131

130132
return array(
131-
ResolutionOption::keep_current_coupon(
132-
sprintf( 'Keep %s (saves %s)', $code, $formatted_current ),
133-
Priority::HIGH,
134-
array( 'savings' => $formatted_current )
135-
),
136-
ResolutionOption::apply_different_coupon(
137-
sprintf( 'Switch to %s (saves %s)', $attempted_coupon, $formatted_attempted ),
138-
Priority::LOW,
139-
array( 'savings' => $formatted_attempted )
140-
),
133+
ResolutionOption::create_keep_current_coupon()
134+
->label( sprintf( 'Keep %s (saves %s)', $code, $formatted_current ) )
135+
->priority( Priority::HIGH )
136+
->set_meta( 'savings', $formatted_current ),
137+
ResolutionOption::create_apply_different_coupon()
138+
->label( sprintf( 'Switch to %s (saves %s)', $attempted_coupon, $formatted_attempted ) )
139+
->priority( Priority::LOW )
140+
->set_meta( 'savings', $formatted_attempted ),
141141
);
142142
}
143143
}

0 commit comments

Comments
 (0)