PluginProbe
Packeta / 2.1
Packeta v2.1
2.3.2 2.3.1 trunk 1.2.2 1.2.3 1.2.4 1.2.5 1.2.6 1.3.0 1.3.1 1.3.2 1.4 1.4.1 1.4.2 1.4.3 1.5.0 1.5.1 1.5.2 1.5.3 1.5.4 1.6.0 1.6.1 1.6.2 1.6.3 1.6.4 All 56 releases
← All changes | src/Packetery/Module/Carrier/OptionsPage.php +504 -167 1.6.12.1 View file →
@@ -8,18 +8,23 @@
8 8 declare( strict_types=1 );
9 9
10 10 namespace Packetery\Module\Carrier;
11 11
12 +use Packetery\Core\CoreHelper;
12 13 use Packetery\Core\Entity\Carrier;
13 -use Packetery\Core\Helper;
14 14 use Packetery\Core\Rounder;
15 +use Packetery\Latte\Engine;
16 +use Packetery\Module\Dashboard\DashboardPage;
15 17 use Packetery\Module\FormFactory;
16 18 use Packetery\Module\FormValidators;
19 +use Packetery\Module\Framework\WpAdapter;
17 20 use Packetery\Module\MessageManager;
18 -use Packetery\Module\Options\FeatureFlagManager;
19 -use Packetery\Latte\Engine;
21 +use Packetery\Module\ModuleHelper;
22 +use Packetery\Module\Options\FlagManager\FeatureFlagProvider;
20 23 use Packetery\Module\PaymentGatewayHelper;
24 +use Packetery\Module\Views\UrlBuilder;
21 25 use Packetery\Nette\Forms\Container;
26 +use Packetery\Nette\Forms\Control;
22 27 use Packetery\Nette\Forms\Form;
23 28 use Packetery\Nette\Http\Request;
24 29
25 30 /**
@@ -28,80 +33,84 @@
28 33 * @package Packetery\Options
29 34 */
30 35 class OptionsPage {
31 36
32 - public const FORM_FIELD_NAME = 'name';
37 + public const FORM_FIELD_NAME = 'name';
38 + public const FORM_FIELD_ACTIVE = 'active';
39 + public const FORM_FIELD_WEIGHT_LIMITS = 'weight_limits';
40 + public const FORM_FIELD_PRODUCT_VALUE_LIMITS = 'product_value_limits';
41 + public const FORM_FIELD_PRICING_TYPE = 'pricing_type';
42 +
33 43 public const SLUG = 'packeta-country';
44 + public const PARAMETER_COUNTRY_CODE = 'country_code';
45 + public const PARAMETER_CARRIER_ID = 'carrier_id';
34 46 public const MINIMUM_CHECKED_VENDORS = 2;
35 47
36 48 /**
37 - * PacketeryLatte_engine.
38 - *
39 - * @var Engine PacketeryLatte engine.
49 + * @var Engine
40 50 */
41 51 private $latteEngine;
42 52
43 53 /**
44 - * Carrier repository.
45 - *
46 - * @var EntityRepository Carrier repository.
54 + * @var EntityRepository
47 55 */
48 56 private $carrierRepository;
49 57
50 58 /**
51 - * Form factory.
52 - *
53 - * @var FormFactory Form factory.
59 + * @var FormFactory
54 60 */
55 61 private $formFactory;
56 62
57 63 /**
58 - * Packetery\Nette Request.
59 - *
60 - * @var Request Packetery\Nette Request.
64 + * @var Request
61 65 */
62 66 private $httpRequest;
63 67
64 68 /**
65 - * CountryListingPage.
66 - *
67 - * @var CountryListingPage CountryListingPage.
69 + * @var CountryListingPage
68 70 */
69 71 private $countryListingPage;
70 72
71 73 /**
72 - * Message manager.
73 - *
74 74 * @var MessageManager
75 75 */
76 76 private $messageManager;
77 77
78 78 /**
79 - * Internal pickup points config.
80 - *
81 79 * @var PacketaPickupPointsConfig
82 80 */
83 81 private $pickupPointsConfig;
84 82
85 83 /**
86 - * Feature flag.
87 - *
88 - * @var FeatureFlagManager
84 + * @var FeatureFlagProvider
89 85 */
90 - private $featureFlag;
86 + private $featureFlagProvider;
91 87
92 88 /**
93 - * Plugin constructor.
94 - *
95 - * @param Engine $latteEngine PacketeryLatte_engine.
96 - * @param EntityRepository $carrierRepository Carrier repository.
97 - * @param FormFactory $formFactory Form factory.
98 - * @param Request $httpRequest Packetery\Nette Request.
99 - * @param CountryListingPage $countryListingPage CountryListingPage.
100 - * @param MessageManager $messageManager Message manager.
101 - * @param PacketaPickupPointsConfig $pickupPointsConfig Internal pickup points config.
102 - * @param FeatureFlagManager $featureFlag Feature flag.
89 + * @var CarDeliveryConfig
103 90 */
91 + private $carDeliveryConfig;
92 +
93 + /**
94 + * @var CarrierOptionsFactory
95 + */
96 + private $carrierOptionsFactory;
97 +
98 + /**
99 + * @var ModuleHelper
100 + */
101 + private $moduleHelper;
102 +
103 + /**
104 + * @var UrlBuilder
105 + */
106 + private $urlBuilder;
107 +
108 + /**
109 + * @var WpAdapter
110 + */
111 + private $wpAdapter;
112 +
104 113 public function __construct(
105 114 Engine $latteEngine,
106 115 EntityRepository $carrierRepository,
107 116 FormFactory $formFactory,
@@ -108,18 +117,28 @@
108 117 Request $httpRequest,
109 118 CountryListingPage $countryListingPage,
110 119 MessageManager $messageManager,
111 120 PacketaPickupPointsConfig $pickupPointsConfig,
112 - FeatureFlagManager $featureFlag
121 + FeatureFlagProvider $featureFlagProvider,
122 + CarDeliveryConfig $carDeliveryConfig,
123 + CarrierOptionsFactory $carrierOptionsFactory,
124 + ModuleHelper $moduleHelper,
125 + UrlBuilder $urlBuilder,
126 + WpAdapter $wpAdapter
113 127 ) {
114 - $this->latteEngine = $latteEngine;
115 - $this->carrierRepository = $carrierRepository;
116 - $this->formFactory = $formFactory;
117 - $this->httpRequest = $httpRequest;
118 - $this->countryListingPage = $countryListingPage;
119 - $this->messageManager = $messageManager;
120 - $this->pickupPointsConfig = $pickupPointsConfig;
121 - $this->featureFlag = $featureFlag;
128 + $this->latteEngine = $latteEngine;
129 + $this->carrierRepository = $carrierRepository;
130 + $this->formFactory = $formFactory;
131 + $this->httpRequest = $httpRequest;
132 + $this->countryListingPage = $countryListingPage;
133 + $this->messageManager = $messageManager;
134 + $this->pickupPointsConfig = $pickupPointsConfig;
135 + $this->featureFlagProvider = $featureFlagProvider;
136 + $this->carDeliveryConfig = $carDeliveryConfig;
137 + $this->carrierOptionsFactory = $carrierOptionsFactory;
138 + $this->moduleHelper = $moduleHelper;
139 + $this->urlBuilder = $urlBuilder;
140 + $this->wpAdapter = $wpAdapter;
122 141 }
123 142
124 143 /**
125 144 * Registers WP callbacks.
@@ -125,9 +144,9 @@
125 144 * Registers WP callbacks.
126 145 */
127 146 public function register(): void {
128 147 add_submenu_page(
129 - \Packetery\Module\Options\Page::SLUG,
148 + DashboardPage::SLUG,
130 149 __( 'Carrier settings', 'packeta' ),
131 150 __( 'Carrier settings', 'packeta' ),
132 151 'manage_options',
133 152 self::SLUG,
@@ -151,9 +170,9 @@
151 170
152 171 $form = $this->formFactory->create( $optionId );
153 172
154 173 $form->addCheckbox(
155 - 'active',
174 + self::FORM_FIELD_ACTIVE,
156 175 __( 'Active carrier', 'packeta' ) . ':'
157 176 );
158 177
159 178 $form->addText( self::FORM_FIELD_NAME, __( 'Display name', 'packeta' ) . ':' )
@@ -159,18 +178,18 @@
159 178 $form->addText( self::FORM_FIELD_NAME, __( 'Display name', 'packeta' ) . ':' )
160 179 ->setRequired();
161 180
162 181 $carrierOptions = get_option( $optionId );
163 - if ( $this->featureFlag->isSplitActive() ) {
164 - $vendorCheckboxes = $this->getVendorCheckboxesConfig( $carrierData['id'], ( $carrierOptions ? $carrierOptions : null ) );
165 - if ( $vendorCheckboxes ) {
182 + if ( $this->featureFlagProvider->isSplitActive() ) {
183 + $vendorCheckboxes = $this->getVendorCheckboxesConfig( $carrierData['id'], ( $carrierOptions !== false ? $carrierOptions : null ) );
184 + if ( count( $vendorCheckboxes ) > 0 ) {
166 185 $vendorsContainer = $form->addContainer( 'vendor_groups' );
167 186 foreach ( $vendorCheckboxes as $checkboxConfig ) {
168 187 $checkboxControl = $vendorsContainer->addCheckbox( $checkboxConfig['group'], $checkboxConfig['name'] );
169 - if ( true === $checkboxConfig['disabled'] ) {
188 + if ( $checkboxConfig['disabled'] === true ) {
170 189 $checkboxControl->setDisabled()->setOmitted( false );
171 190 }
172 - if ( true === $checkboxConfig['default'] ) {
191 + if ( $checkboxConfig['default'] === true ) {
173 192 $checkboxControl->setDefaultValue( true );
174 193 }
175 194 }
176 195 }
@@ -175,21 +194,45 @@
175 194 }
176 195 }
177 196 }
178 197
179 - $weightLimits = $form->addContainer( 'weight_limits' );
180 - if ( empty( $carrierData['weight_limits'] ) ) {
198 + $form->addSelect(
199 + self::FORM_FIELD_PRICING_TYPE,
200 + __( 'Pricing type', 'packeta' ),
201 + [
202 + Options::PRICING_TYPE_BY_WEIGHT => __( 'By weight', 'packeta' ),
203 + Options::PRICING_TYPE_BY_PRODUCT_VALUE => __( 'By product value', 'packeta' ),
204 + ]
205 + )
206 + ->setDefaultValue( Options::PRICING_TYPE_BY_WEIGHT )
207 + ->addCondition( Form::EQUAL, Options::PRICING_TYPE_BY_WEIGHT )
208 + ->toggle( $this->createFieldContainerId( $form, self::FORM_FIELD_WEIGHT_LIMITS ) )
209 + ->endCondition()
210 + ->addCondition( Form::EQUAL, Options::PRICING_TYPE_BY_PRODUCT_VALUE )
211 + ->toggle( $this->createFieldContainerId( $form, self::FORM_FIELD_PRODUCT_VALUE_LIMITS ) );
212 +
213 + $weightLimits = $form->addContainer( self::FORM_FIELD_WEIGHT_LIMITS );
214 + if ( ! isset( $carrierData[ self::FORM_FIELD_WEIGHT_LIMITS ] ) || count( $carrierData[ self::FORM_FIELD_WEIGHT_LIMITS ] ) === 0 ) {
181 215 $this->addWeightLimit( $weightLimits, 0 );
182 216 } else {
183 - foreach ( $carrierData['weight_limits'] as $index => $limit ) {
217 + foreach ( $carrierData[ self::FORM_FIELD_WEIGHT_LIMITS ] as $index => $limit ) {
184 218 $this->addWeightLimit( $weightLimits, $index );
185 219 }
186 220 }
187 221
222 + $productValueLimits = $form->addContainer( self::FORM_FIELD_PRODUCT_VALUE_LIMITS );
223 + if ( ! isset( $carrierData[ self::FORM_FIELD_PRODUCT_VALUE_LIMITS ] ) || count( $carrierData[ self::FORM_FIELD_PRODUCT_VALUE_LIMITS ] ) === 0 ) {
224 + $this->addProductValueLimit( $productValueLimits, 0 );
225 + } else {
226 + foreach ( $carrierData[ self::FORM_FIELD_PRODUCT_VALUE_LIMITS ] as $index => $limit ) {
227 + $this->addProductValueLimit( $productValueLimits, $index );
228 + }
229 + }
230 +
188 231 // We don't expect id to be empty in this situation. This would indicate a data save error.
189 232 $carrier = $this->carrierRepository->getAnyById( (string) $carrierData['id'] );
190 233
191 - if ( null !== $carrier && $carrier->supportsCod() ) {
234 + if ( $carrier !== null && $carrier->supportsCod() ) {
192 235 $form->addText( 'default_COD_surcharge', __( 'Default COD surcharge', 'packeta' ) . ':' )
193 236 ->setRequired( false )
194 237 ->addRule( Form::FLOAT )
195 238 ->addRule( Form::MIN, null, 0 );
@@ -194,9 +237,9 @@
194 237 ->addRule( Form::FLOAT )
195 238 ->addRule( Form::MIN, null, 0 );
196 239
197 240 $surchargeLimits = $form->addContainer( 'surcharge_limits' );
198 - if ( ! empty( $carrierData['surcharge_limits'] ) ) {
241 + if ( isset( $carrierData['surcharge_limits'] ) && count( $carrierData['surcharge_limits'] ) > 0 ) {
199 242 foreach ( $carrierData['surcharge_limits'] as $index => $limit ) {
200 243 $this->addSurchargeLimit( $surchargeLimits, $index );
201 244 }
202 245 }
@@ -209,10 +252,27 @@
209 252 ->setDefaultValue( Rounder::DONT_ROUND );
210 253 }
211 254
212 255 $item = $form->addText( 'free_shipping_limit', __( 'Free shipping limit', 'packeta' ) . ':' );
213 - $item->addRule( $form::FLOAT, __( 'Please enter a valid decimal number.', 'packeta' ) );
256 + $item->setRequired( false )
257 + ->addRule( Form::FLOAT )
258 + ->addRule( Form::MIN, null, 0 );
214 259
260 + if ( $carrier !== null && $carrier->isCarDelivery() ) {
261 + $daysUntilShipping = $form->addText( 'days_until_shipping', __( 'Number of days until shipping', 'packeta' ) . ':' );
262 + $daysUntilShipping->setRequired()
263 + ->addRule( $form::INTEGER, __( 'Please, enter a full number.', 'packeta' ) )
264 + ->addRule( $form::MIN, null, 0 );
265 +
266 + $shippingTimeCutOff = $form->addText( 'shipping_time_cut_off', __( 'Shipping time cut off', 'packeta' ) . ':' );
267 + $shippingTimeCutOff->setHtmlAttribute( 'class', 'date-picker' )
268 + ->setHtmlType( 'time' )
269 + ->setRequired( false )
270 + ->setNullable()
271 + // translators: %s: Represents the time we stop taking more orders for next shipment.
272 + ->addRule( [ FormValidators::class, 'hasClockTimeFormat' ], __( 'Time must be between %1$s and %2$s.', 'packeta' ), [ '00:00', '23:59' ] );
273 + }
274 +
215 275 $couponFreeShipping = $form->addContainer( 'coupon_free_shipping' );
216 276 $couponFreeShipping->addCheckbox( 'active', __( 'Apply free shipping coupon', 'packeta' ) );
217 277 $couponFreeShipping->addCheckbox( 'allow_for_fees', __( 'Apply free shipping coupon for fees', 'packeta' ) )
218 278 ->addConditionOn( $form['coupon_free_shipping']['active'], Form::FILLED )
@@ -217,12 +277,69 @@
217 277 $couponFreeShipping->addCheckbox( 'allow_for_fees', __( 'Apply free shipping coupon for fees', 'packeta' ) )
218 278 ->addConditionOn( $form['coupon_free_shipping']['active'], Form::FILLED )
219 279 ->toggle( $this->createCouponFreeShippingForFeesContainerId( $form ) );
220 280
281 + $dimensionsRestrictions = $form->addContainer( 'dimensions_restrictions' );
282 + $dimensionsRestrictions->addCheckbox( 'active', $this->wpAdapter->__( 'Maximum package size', 'packeta' ) );
283 + if (
284 + strpos( $carrier->getId(), Carrier::VENDOR_GROUP_ZBOX ) !== false ||
285 + strpos( $carrier->getId(), Carrier::VENDOR_GROUP_ZPOINT ) === 0 ||
286 + ( $carrier->hasPickupPoints() && is_numeric( $carrier->getId() ) )
287 + ) {
288 + $dimensionsRestrictions->addText( 'length', $this->wpAdapter->__( 'Length (cm)', 'packeta' ) )
289 + ->setNullable()
290 + ->addConditionOn( $form['dimensions_restrictions']['active'], Form::FILLED )
291 + ->toggle( $this->createDimensionRestrictionContainerId( $form ) )
292 + ->setRequired()
293 + ->addRule( Form::INTEGER, $this->wpAdapter->__( 'Provide a full number!', 'packeta' ) )
294 + ->addRule( Form::MIN, 'Value must be greater than 0', 1 );
295 + $dimensionsRestrictions->addText( 'width', $this->wpAdapter->__( 'Width (cm)', 'packeta' ) )
296 + ->setNullable()
297 + ->addConditionOn( $form['dimensions_restrictions']['active'], Form::FILLED )
298 + ->toggle( $this->createDimensionRestrictionContainerId( $form ) )
299 + ->setRequired()
300 + ->addRule( Form::INTEGER, $this->wpAdapter->__( 'Provide a full number!', 'packeta' ) )
301 + ->addRule( Form::MIN, 'Value must be greater than 0', 1 );
302 + $dimensionsRestrictions->addText( 'height', $this->wpAdapter->__( 'Height (cm)', 'packeta' ) )
303 + ->setNullable()
304 + ->addConditionOn( $form['dimensions_restrictions']['active'], Form::FILLED )
305 + ->toggle( $this->createDimensionRestrictionContainerId( $form ) )
306 + ->setRequired()
307 + ->addRule( Form::INTEGER, $this->wpAdapter->__( 'Provide a full number!', 'packeta' ) )
308 + ->addRule( Form::MIN, 'Value must be greater than 0', 1 );
309 + } else {
310 +
311 + $maximumLength = $dimensionsRestrictions->addText( 'maximum_length', $this->wpAdapter->__( 'Maximum length (cm)', 'packeta' ) );
312 + $maximumLength->setNullable();
313 + $maximumLength->addConditionOn( $form['dimensions_restrictions']['active'], Form::FILLED )
314 + ->toggle( $this->createDimensionRestrictionContainerId( $form ) )
315 + ->addRule( Form::INTEGER, $this->wpAdapter->__( 'Provide a full number!', 'packeta' ) )
316 + ->addRule( Form::MIN, $this->wpAdapter->__( 'Value must be greater than 0', 'packeta' ), 1 );
317 +
318 + $dimensionsSum = $dimensionsRestrictions->addText( 'dimensions_sum', $this->wpAdapter->__( 'Sum of dimensions (cm)', 'packeta' ) );
319 + $dimensionsSum->setNullable();
320 + $dimensionsSum->addConditionOn( $form['dimensions_restrictions']['active'], Form::FILLED )
321 + ->toggle( $this->createDimensionRestrictionContainerId( $form ) )
322 + ->addRule( Form::INTEGER, $this->wpAdapter->__( 'Provide a full number!', 'packeta' ) )
323 + ->addRule( Form::MIN, $this->wpAdapter->__( 'Value must be greater than 0', 'packeta' ), 1 );
324 +
325 + $maximumLength->addConditionOn( $form['dimensions_restrictions']['active'], Form::FILLED )
326 + ->addConditionOn( $dimensionsSum, Form::BLANK )
327 + ->addRule( Form::FILLED, $this->wpAdapter->__( 'You have to fill in Maximum length or Sum of dimensions', 'packeta' ) );
328 +
329 + $dimensionsSum->addConditionOn( $form['dimensions_restrictions']['active'], Form::FILLED )
330 + ->addConditionOn( $maximumLength, Form::BLANK )
331 + ->addRule( Form::FILLED, $this->wpAdapter->__( 'You have to fill in Maximum length or Sum of dimensions', 'packeta' ) );
332 + }
333 +
221 334 $form->addHidden( 'id' )->setRequired();
222 335 $form->addSubmit( 'save' );
223 336
224 - if ( false === $carrier->hasPickupPoints() && in_array( $carrier->getCountry(), Carrier::ADDRESS_VALIDATION_COUNTRIES, true ) ) {
337 + if (
338 + $carrier->isCarDelivery() === false &&
339 + $carrier->hasPickupPoints() === false &&
340 + in_array( $carrier->getCountry(), Carrier::ADDRESS_VALIDATION_COUNTRIES, true )
341 + ) {
225 342 $addressValidationOptions = [
226 343 'none' => __( 'No address validation', 'packeta' ),
227 344 'optional' => __( 'Optional address validation', 'packeta' ),
228 345 'required' => __( 'Required address validation', 'packeta' ),
@@ -246,9 +363,9 @@
246 363
247 364 $form->onValidate[] = [ $this, 'validateOptions' ];
248 365 $form->onSuccess[] = [ $this, 'updateOptions' ];
249 366
250 - if ( false === $carrierOptions ) {
367 + if ( $carrierOptions === false ) {
251 368 $carrierOptions = [
252 369 'id' => $carrierData['id'],
253 370 self::FORM_FIELD_NAME => $carrierData['name'],
254 371 ];
@@ -268,9 +385,25 @@
268 385 private function createCouponFreeShippingForFeesContainerId( Form $form ): string {
269 386 return sprintf( '%s_apply_free_shipping_coupon_allow_for_fees', $form->getName() );
270 387 }
271 388
389 + private function createDimensionRestrictionContainerId( Form $form ): string {
390 + return sprintf( '%s_dimension_restrictions', $form->getName() );
391 + }
392 +
272 393 /**
394 + * Creates container id for given field.
395 + *
396 + * @param Form $form Form.
397 + * @param string $field Field name.
398 + *
399 + * @return string
400 + */
401 + private function createFieldContainerId( Form $form, string $field ): string {
402 + return sprintf( '%s_%s_containerId', $form->getName(), $field );
403 + }
404 +
405 + /**
273 406 * Creates settings form.
274 407 *
275 408 * @param array $carrierData Carrier data.
276 409 *
@@ -280,11 +413,16 @@
280 413 $optionId = OptionPrefixer::getOptionId( $carrierData['id'] );
281 414
282 415 $form = $this->formFactory->create( $optionId . '_template' );
283 416
284 - $weightLimitsTemplate = $form->addContainer( 'weight_limits' );
417 + $form->addSelect( self::FORM_FIELD_PRICING_TYPE );
418 +
419 + $weightLimitsTemplate = $form->addContainer( self::FORM_FIELD_WEIGHT_LIMITS );
285 420 $this->addWeightLimit( $weightLimitsTemplate, 0 );
286 421
422 + $valueLimitsTemplate = $form->addContainer( self::FORM_FIELD_PRODUCT_VALUE_LIMITS );
423 + $this->addProductValueLimit( $valueLimitsTemplate, 0 );
424 +
287 425 $surchargeLimitsTemplate = $form->addContainer( 'surcharge_limits' );
288 426 $this->addSurchargeLimit( $surchargeLimitsTemplate, 0 );
289 427
290 428 return $form;
@@ -297,14 +435,15 @@
297 435 */
298 436 public function validateOptions( Form $form ): void {
299 437 if ( $form->hasErrors() ) {
300 438 add_settings_error( '', '', esc_attr( __( 'Some carrier data is invalid', 'packeta' ) ) );
439 +
301 440 return;
302 441 }
303 442
304 443 $options = $form->getValues( 'array' );
305 444
306 - if ( $this->featureFlag->isSplitActive() ) {
445 + if ( $this->featureFlagProvider->isSplitActive() ) {
307 446 $checkedVendors = $this->getCheckedVendors( $options );
308 447 if (
309 448 isset( $options['vendor_groups'] ) &&
310 449 count( $options['vendor_groups'] ) >= self::MINIMUM_CHECKED_VENDORS &&
@@ -315,15 +454,28 @@
315 454 $form->addError( $vendorMessage );
316 455 }
317 456 }
318 457
319 - $this->checkOverlapping(
320 - $form,
321 - $options,
322 - 'weight_limits',
323 - 'weight',
324 - __( 'Weight rules are overlapping, please fix them.', 'packeta' )
325 - );
458 + if ( $options[ self::FORM_FIELD_PRICING_TYPE ] === Options::PRICING_TYPE_BY_WEIGHT ) {
459 + $this->checkOverlapping(
460 + $form,
461 + $options,
462 + self::FORM_FIELD_WEIGHT_LIMITS,
463 + 'weight',
464 + __( 'Weight rules are overlapping, please fix them.', 'packeta' )
465 + );
466 + }
467 +
468 + if ( $options[ self::FORM_FIELD_PRICING_TYPE ] === Options::PRICING_TYPE_BY_PRODUCT_VALUE ) {
469 + $this->checkOverlapping(
470 + $form,
471 + $options,
472 + self::FORM_FIELD_PRODUCT_VALUE_LIMITS,
473 + 'price',
474 + __( 'Product price rules are overlapping, please fix them.', 'packeta' )
475 + );
476 + }
477 +
326 478 if ( isset( $options['surcharge_limits'] ) ) {
327 479 $this->checkOverlapping(
328 480 $form,
329 481 $options,
@@ -343,14 +495,29 @@
343 495 */
344 496 public function updateOptions( Form $form ): void {
345 497 $options = $form->getValues( 'array' );
346 498 $newVendors = $this->getCheckedVendors( $options );
347 - if ( $newVendors ) {
499 + if ( count( $newVendors ) > 0 ) {
348 500 $options['vendor_groups'] = $newVendors;
349 501 }
350 502
351 - $options = $this->mergeNewLimits( $options, 'weight_limits' );
352 - $options = $this->sortLimits( $options, 'weight_limits', 'weight' );
503 + $persistedOptions = $this->carrierOptionsFactory->createByCarrierId( $options['id'] );
504 + $persistedOptionsArray = $persistedOptions->toArray();
505 +
506 + if ( $options[ self::FORM_FIELD_PRICING_TYPE ] === Options::PRICING_TYPE_BY_WEIGHT ) {
507 + $options = $this->mergeNewLimits( $options, self::FORM_FIELD_WEIGHT_LIMITS );
508 + $options = $this->sortLimits( $options, self::FORM_FIELD_WEIGHT_LIMITS, 'weight' );
509 +
510 + $options[ self::FORM_FIELD_PRODUCT_VALUE_LIMITS ] = $persistedOptionsArray[ self::FORM_FIELD_PRODUCT_VALUE_LIMITS ] ?? [];
511 + }
512 +
513 + if ( $options[ self::FORM_FIELD_PRICING_TYPE ] === Options::PRICING_TYPE_BY_PRODUCT_VALUE ) {
514 + $options = $this->mergeNewLimits( $options, self::FORM_FIELD_PRODUCT_VALUE_LIMITS );
515 + $options = $this->sortLimits( $options, self::FORM_FIELD_PRODUCT_VALUE_LIMITS, 'value' );
516 +
517 + $options[ self::FORM_FIELD_WEIGHT_LIMITS ] = $persistedOptionsArray[ self::FORM_FIELD_WEIGHT_LIMITS ] ?? [];
518 + }
519 +
353 520 if ( isset( $options['surcharge_limits'] ) ) {
354 521 $options = $this->mergeNewLimits( $options, 'surcharge_limits' );
355 522 $options = $this->sortLimits( $options, 'surcharge_limits', 'order_price' );
356 523 }
@@ -358,14 +525,11 @@
358 525 update_option( OptionPrefixer::getOptionId( $options['id'] ), $options );
359 526 $this->messageManager->flash_message( __( 'Settings saved', 'packeta' ), MessageManager::TYPE_SUCCESS, MessageManager::RENDERER_PACKETERY, 'carrier-country' );
360 527
361 528 if ( wp_safe_redirect(
362 - add_query_arg(
363 - [
364 - 'page' => self::SLUG,
365 - 'code' => $this->httpRequest->getQuery( 'code' ),
366 - ],
367 - get_admin_url( null, 'admin.php' )
529 + $this->createUrl(
530 + $this->httpRequest->getQuery( self::PARAMETER_COUNTRY_CODE ),
531 + $this->httpRequest->getQuery( self::PARAMETER_CARRIER_ID )
368 532 ),
369 533 303
370 534 ) ) {
371 535 exit;
@@ -372,77 +536,163 @@
372 536 }
373 537 }
374 538
375 539 /**
376 - * Renders page.
540 + * Gets carrier template params.
541 + *
542 + * @param Carrier|null $carrier Carrier.
543 + *
544 + * @return array|null
377 545 */
546 + private function getCarrierTemplateData( ?Carrier $carrier ): ?array {
547 + if ( $carrier === null ) {
548 + return null;
549 + }
550 +
551 + if ( $carrier->isCarDelivery() && $this->carDeliveryConfig->isDisabled() ) {
552 + return null;
553 + }
554 +
555 + $post = $this->httpRequest->getPost();
556 + if ( isset( $post['id'] ) && $post['id'] === $carrier->getId() ) {
557 + $formTemplate = $this->createFormTemplate( $post );
558 + $form = $this->createForm( $post );
559 + if ( $form->isSubmitted() ) {
560 + $form->fireEvents();
561 + }
562 + } else {
563 + $carrierData = $carrier->__toArray();
564 + $options = get_option( OptionPrefixer::getOptionId( $carrier->getId() ) );
565 + if ( $options !== false ) {
566 + $carrierData += $options;
567 + }
568 + $formTemplate = $this->createFormTemplate( $carrierData );
569 + $form = $this->createForm( $carrierData );
570 + }
571 +
572 + return [
573 + 'form' => $form,
574 + 'formTemplate' => $formTemplate,
575 + 'carrier' => $carrier,
576 + 'couponFreeShippingForFeesContainerId' => $this->createCouponFreeShippingForFeesContainerId( $form ),
577 + 'dimensionRestrictionContainerId' => $this->createDimensionRestrictionContainerId( $form ),
578 + 'weightLimitsContainerId' => $this->createFieldContainerId( $form, self::FORM_FIELD_WEIGHT_LIMITS ),
579 + 'productValueLimitsContainerId' => $this->createFieldContainerId( $form, self::FORM_FIELD_PRODUCT_VALUE_LIMITS ),
580 + 'isAvailableVendorsCountLow' => $this->isAvailableVendorsCountLowByCarrierId( $carrier->getId() ),
581 + ];
582 + }
583 +
378 584 public function render(): void {
379 - $countryIso = $this->httpRequest->getQuery( 'code' );
380 - if ( $countryIso ) {
381 - $countryCarriers = $this->carrierRepository->getByCountryIncludingNonFeed( $countryIso );
382 - $carriersData = [];
383 - $post = $this->httpRequest->getPost();
384 - foreach ( $countryCarriers as $carrier ) {
385 - if ( ! empty( $post ) && $post['id'] === $carrier->getId() ) {
386 - $formTemplate = $this->createFormTemplate( $post );
387 - $form = $this->createForm( $post );
388 - if ( $form->isSubmitted() ) {
389 - $form->fireEvents();
390 - }
391 - } else {
392 - $carrierData = $carrier->__toArray();
393 - $options = get_option( OptionPrefixer::getOptionId( $carrier->getId() ) );
394 - if ( false !== $options ) {
395 - $carrierData += $options;
396 - }
397 - $formTemplate = $this->createFormTemplate( $carrierData );
398 - $form = $this->createForm( $carrierData );
399 - }
585 + $countryIso = $this->httpRequest->getQuery( self::PARAMETER_COUNTRY_CODE );
586 + $carrierId = $this->httpRequest->getQuery( self::PARAMETER_CARRIER_ID );
400 587
401 - $carriersData[] = [
402 - 'form' => $form,
403 - 'formTemplate' => $formTemplate,
404 - 'carrier' => $carrier,
405 - 'couponFreeShippingForFeesContainerId' => $this->createCouponFreeShippingForFeesContainerId( $form ),
406 - ];
588 + if ( $carrierId !== null ) {
589 + $this->renderCarrierDetail( $carrierId );
590 + } elseif ( $countryIso !== null ) {
591 + $this->renderCountryCarriers( $countryIso );
592 + } else {
593 + $this->countryListingPage->render();
594 + }
595 + }
596 +
597 + private function renderCarrierDetail( string $carrierId ): void {
598 + $carrier = $this->carrierRepository->getAnyById( $carrierId );
599 + $carrierTemplateData = $this->getCarrierTemplateData( $carrier );
600 +
601 + if ( $carrier === null || $carrierTemplateData === null ) {
602 + $this->countryListingPage->render();
603 +
604 + return;
605 + }
606 +
607 + $this->latteEngine->render(
608 + PACKETERY_PLUGIN_DIR . '/template/carrier/detail.latte',
609 + array_merge_recursive(
610 + $this->getCommonTemplateParams(),
611 + [
612 + 'carrierTemplateData' => $carrierTemplateData,
613 + 'translations' => [
614 + 'title' => $carrier->getName(),
615 + ],
616 + ]
617 + )
618 + );
619 + }
620 +
621 + private function renderCountryCarriers( string $countryIso ): void {
622 + $countryCarriers = $this->carrierRepository->getByCountryIncludingNonFeed( $countryIso, true );
623 + $carriersData = [];
624 + foreach ( $countryCarriers as $carrier ) {
625 + $carrierTemplateData = $this->getCarrierTemplateData( $carrier );
626 + if ( $carrierTemplateData !== null ) {
627 + $carriersData[] = $carrierTemplateData;
407 628 }
629 + }
408 630
409 - $this->latteEngine->render(
410 - PACKETERY_PLUGIN_DIR . '/template/carrier/country.latte',
631 + $this->latteEngine->render(
632 + PACKETERY_PLUGIN_DIR . '/template/carrier/country.latte',
633 + array_merge_recursive(
634 + $this->getCommonTemplateParams(),
411 635 [
412 - 'forms' => $carriersData,
413 - 'country_iso' => $countryIso,
414 - 'globalCurrency' => get_woocommerce_currency_symbol(),
415 - 'flashMessages' => $this->messageManager->renderToString( MessageManager::RENDERER_PACKETERY, 'carrier-country' ),
416 - 'translations' => [
417 - 'cannotUseThisCarrierBecauseRequiresCustomsDeclaration' => __( 'This carrier cannot be used, because it requires a customs declaration.', 'packeta' ),
418 - 'delete' => __( 'Delete', 'packeta' ),
419 - 'weightRules' => __( 'Weight rules', 'packeta' ),
420 - 'addWeightRule' => __( 'Add weight rule', 'packeta' ),
421 - 'codSurchargeRules' => __( 'COD surcharge rules', 'packeta' ),
422 - 'addCodSurchargeRule' => __( 'Add COD surcharge rule', 'packeta' ),
423 - 'afterExceedingThisAmountShippingIsFree' => __( 'After exceeding this amount, shipping is free.', 'packeta' ),
424 - 'addressValidationDescription' => __( 'Customer address validation.', 'packeta' ),
425 - 'roundingDescription' => __( 'COD rounding for submitting data to Packeta', 'packeta' ),
426 - 'saveChanges' => __( 'Save changes', 'packeta' ),
427 - 'packeta' => __( 'Packeta', 'packeta' ),
428 - // translators: %s is country code.
429 - 'title' => sprintf( __( 'Country options: %s', 'packeta' ), strtoupper( $countryIso ) ),
430 -
431 - 'noKnownCarrierForThisCountry' => __( 'No carriers available for this country.', 'packeta' ),
432 - 'ageVerificationSupportedNotification' => __( 'When shipping via this carrier, you can order the Age Verification service. The service will get ordered automatically if there is at least 1 product in the order with the age verification setting.', 'packeta' ),
433 - 'carrierDoesNotSupportCod' => __( 'This carrier does not support COD payment.', 'packeta' ),
434 - 'allowedPickupPointTypes' => __( 'Pickup point types.', 'packeta' ),
435 - 'checkAtLeastTwo' => __( 'Check at least two types of pickup points or use a carrier which delivers to the desired pickup point type.', 'packeta' ),
636 + 'forms' => $carriersData,
637 + 'country_iso' => $countryIso,
638 + 'translations' => [
639 + 'title' => sprintf(
640 + // translators: %s is country code.
641 + $this->wpAdapter->__( 'Country options: %s', 'packeta' ),
642 + strtoupper( $countryIso )
643 + ),
436 644 ],
437 645 ]
438 - );
439 - } else {
440 - $this->countryListingPage->render();
441 - }
646 + )
647 + );
442 648 }
443 649
444 650 /**
651 + * @return array<string, null|string|bool|array<string, string>>
652 + */
653 + private function getCommonTemplateParams(): array {
654 + return [
655 + 'globalCurrency' => get_woocommerce_currency_symbol(),
656 + 'flashMessages' => $this->messageManager->renderToString( MessageManager::RENDERER_PACKETERY, 'carrier-country' ),
657 + 'isCzechLocale' => $this->moduleHelper->isCzechLocale(),
658 + 'logoZasilkovna' => $this->urlBuilder->buildAssetUrl( 'public/images/logo-zasilkovna.svg' ),
659 + 'logoPacketa' => $this->urlBuilder->buildAssetUrl( 'public/images/logo-packeta.svg' ),
660 + 'translations' => $this->getTranslations(),
661 + ];
662 + }
663 +
664 + /**
665 + * @return array<string, string>
666 + */
667 + private function getTranslations(): array {
668 + return [
669 + 'cannotUseThisCarrierBecauseRequiresCustomsDeclaration' => $this->wpAdapter->__( 'This carrier cannot be used, because it requires a customs declaration.', 'packeta' ),
670 + 'delete' => $this->wpAdapter->__( 'Delete', 'packeta' ),
671 + 'weightRules' => $this->wpAdapter->__( 'Weight rules', 'packeta' ),
672 + 'productValueRules' => $this->wpAdapter->__( 'Product value rules', 'packeta' ),
673 + 'addWeightRule' => $this->wpAdapter->__( 'Add weight rule', 'packeta' ),
674 + 'addProductValueRule' => $this->wpAdapter->__( 'Add product value rule', 'packeta' ),
675 + 'codSurchargeRules' => $this->wpAdapter->__( 'COD surcharge rules', 'packeta' ),
676 + 'addCodSurchargeRule' => $this->wpAdapter->__( 'Add COD surcharge rule', 'packeta' ),
677 + 'afterExceedingThisAmountShippingIsFree' => $this->wpAdapter->__( 'After exceeding this amount, shipping is free.', 'packeta' ),
678 + 'daysUntilShipping' => $this->wpAdapter->__( 'Number of business days it might take to process an order before shipping out a package.', 'packeta' ),
679 + 'shippingTimeCutOff' => $this->wpAdapter->__( 'A time of a day you stop taking in more orders for the next round of shipping.', 'packeta' ),
680 + 'addressValidationDescription' => $this->wpAdapter->__( 'Customer address validation.', 'packeta' ),
681 + 'roundingDescription' => $this->wpAdapter->__( 'COD rounding for submitting data to Packeta', 'packeta' ),
682 + 'saveChanges' => $this->wpAdapter->__( 'Save changes', 'packeta' ),
683 + 'packeta' => $this->wpAdapter->__( 'Packeta', 'packeta' ),
684 + 'noKnownCarrierForThisCountry' => $this->wpAdapter->__( 'No carriers available for this country.', 'packeta' ),
685 + 'ageVerificationSupportedNotification' => $this->wpAdapter->__( 'When shipping via this carrier, you can order the Age Verification service. The service will get ordered automatically if there is at least 1 product in the order with the age verification setting.', 'packeta' ),
686 + 'carrierDoesNotSupportCod' => $this->wpAdapter->__( 'This carrier does not support COD payment.', 'packeta' ),
687 + 'allowedPickupPointTypes' => $this->wpAdapter->__( 'Pickup point types', 'packeta' ),
688 + 'checkAtLeastTwo' => $this->wpAdapter->__( 'Check at least two types of pickup points or use a carrier which delivers to the desired pickup point type.', 'packeta' ),
689 + 'lowAvailableVendorsCount' => $this->wpAdapter->__( 'This carrier displays all types of pickup points at the same time in the checkout (retail store pickup points, Z-boxes).', 'packeta' ),
690 + 'carrierUnavailable' => $this->wpAdapter->__( 'This carrier is unavailable.', 'packeta' ),
691 + ];
692 + }
693 +
694 + /**
445 695 * Transforms new_ keys to common numeric.
446 696 *
447 697 * @param array $options Options to merge.
448 698 * @param string $limitsContainer Container id.
@@ -455,9 +705,9 @@
455 705 foreach ( $options[ $limitsContainer ] as $key => $option ) {
456 706 if ( is_int( $key ) ) {
457 707 $newOptions[ $key ] = $option;
458 708 }
459 - if ( 0 === strpos( (string) $key, 'new_' ) ) {
709 + if ( strpos( (string) $key, 'new_' ) === 0 ) {
460 710 $newOptions[] = $option;
461 711 }
462 712 }
463 713 $options[ $limitsContainer ] = $newOptions;
@@ -501,35 +751,98 @@
501 751 return $options;
502 752 }
503 753
504 754 /**
505 - * Adds limit fields to form.
755 + * @param Container $weightLimits
756 + * @param int|string $index
506 757 *
507 - * @param Container $weightLimits Container.
508 - * @param int|string $index Index.
509 - *
510 - * @return void
758 + * @throws \RuntimeException
511 759 */
512 760 private function addWeightLimit( Container $weightLimits, $index ): void {
513 - $limit = $weightLimits->addContainer( (string) $index );
514 - $item = $limit->addText( 'weight', __( 'Weight up to', 'packeta' ) . ':' );
515 - $item->setRequired();
516 - $item->addRule( Form::FLOAT, __( 'Please enter a valid decimal number.', 'packeta' ) );
761 + $form = $weightLimits->getForm();
762 + if ( $form === null ) {
763 + throw new \RuntimeException( 'Form is not attached to the container.' );
764 + }
765 +
766 + $pricingTypeComponent = $form->getComponent( self::FORM_FIELD_PRICING_TYPE, false );
767 +
768 + if ( ! $pricingTypeComponent instanceof Control ) {
769 + throw new \RuntimeException(
770 + sprintf(
771 + 'Component "%s" must be an instance of %s, %s given.',
772 + self::FORM_FIELD_PRICING_TYPE,
773 + Control::class,
774 + is_object( $pricingTypeComponent ) ? get_class( $pricingTypeComponent ) : gettype( $pricingTypeComponent )
775 + )
776 + );
777 + }
778 +
779 + $limit = $weightLimits->addContainer( (string) $index );
780 + $weightField = $limit->addText( 'weight', __( 'Weight up to', 'packeta' ) . ':' );
781 + $weightRules = $weightField->addConditionOn( $pricingTypeComponent, Form::EQUAL, Options::PRICING_TYPE_BY_WEIGHT );
782 + $weightRules->setRequired();
783 + $weightRules->addRule( Form::FLOAT, __( 'Please enter a valid decimal number.', 'packeta' ) );
784 + $weightRules->addRule( Form::MIN, null, 0 );
517 785 // translators: %d is numeric threshold.
518 - $item->addRule( [ FormValidators::class, 'greaterThan' ], __( 'Enter number greater than %d', 'packeta' ), 0.0 );
786 + $weightRules->addRule( [ FormValidators::class, 'greaterThan' ], __( 'Enter number greater than %d', 'packeta' ), 0.0 );
519 787
520 - $item->addFilter(
788 + $weightRules->addFilter(
521 789 function ( float $value ) {
522 - return Helper::simplifyWeight( $value );
790 + return CoreHelper::simplifyWeight( $value );
523 791 }
524 792 );
793 +
794 + $priceField = $limit->addText( 'price', __( 'Price', 'packeta' ) . ':' );
795 + $priceRules = $priceField->addConditionOn( $pricingTypeComponent, Form::EQUAL, Options::PRICING_TYPE_BY_WEIGHT );
796 + $priceRules->setRequired();
797 + $priceRules->addRule( Form::FLOAT, __( 'Please enter a valid decimal number.', 'packeta' ) );
798 + $priceRules->addRule( Form::MIN, null, 0 );
525 799 // translators: %d is numeric threshold.
526 - $item->addRule( [ FormValidators::class, 'greaterThan' ], __( 'Enter number greater than %d', 'packeta' ), 0.0 );
800 + $weightRules->addRule( [ FormValidators::class, 'greaterThan' ], __( 'Enter number greater than %d', 'packeta' ), 0.0 );
801 + }
527 802
528 - $item = $limit->addText( 'price', __( 'Price', 'packeta' ) . ':' );
529 - $item->setRequired();
530 - $item->addRule( Form::FLOAT, __( 'Please enter a valid decimal number.', 'packeta' ) );
531 - $item->addRule( Form::MIN, null, 0 );
803 + /**
804 + * @param Container $productValueLimits
805 + * @param int|string $index
806 + *
807 + * @throws \RuntimeException
808 + */
809 + private function addProductValueLimit( Container $productValueLimits, $index ): void {
810 + $form = $productValueLimits->getForm();
811 + if ( $form === null ) {
812 + throw new \RuntimeException( 'Form is not attached to the container.' );
813 + }
814 +
815 + $pricingTypeComponent = $form->getComponent( self::FORM_FIELD_PRICING_TYPE, false );
816 +
817 + if ( ! $pricingTypeComponent instanceof Control ) {
818 + throw new \RuntimeException(
819 + sprintf(
820 + 'Component "%s" must be an instance of %s, %s given.',
821 + self::FORM_FIELD_PRICING_TYPE,
822 + Control::class,
823 + is_object( $pricingTypeComponent ) ? get_class( $pricingTypeComponent ) : gettype( $pricingTypeComponent )
824 + )
825 + );
826 + }
827 +
828 + $limit = $productValueLimits->addContainer( $index );
829 +
830 + $valueField = $limit->addText( 'value', __( 'Product value up to', 'packeta' ) . ':' );
831 + $valueRules = $valueField->addConditionOn( $pricingTypeComponent, Form::EQUAL, Options::PRICING_TYPE_BY_PRODUCT_VALUE );
832 + $valueRules->setRequired();
833 + $valueRules->addRule( Form::FLOAT, __( 'Please enter a valid decimal number.', 'packeta' ) );
834 + // translators: %d is numeric threshold.
835 + $valueRules->addRule( Form::MIN, null, 0.0 );
836 + // translators: %d is numeric threshold.
837 + $valueRules->addRule( [ FormValidators::class, 'greaterThan' ], __( 'Enter number greater than %d', 'packeta' ), 0.0 );
838 +
839 + $priceField = $limit->addText( 'price', __( 'Price', 'packeta' ) . ':' );
840 + $priceRules = $priceField->addConditionOn( $pricingTypeComponent, Form::EQUAL, Options::PRICING_TYPE_BY_PRODUCT_VALUE );
841 + $priceRules->setRequired();
842 + $priceRules->addRule( Form::FLOAT, __( 'Please enter a valid decimal number.', 'packeta' ) );
843 + // translators: %d is numeric threshold.
844 + $priceRules->addRule( Form::MIN, null, 0.0 );
532 845 }
533 846
534 847 /**
535 848 * Adds limit fields to form.
@@ -559,20 +872,25 @@
559 872 /**
560 873 * Creates link to page.
561 874 *
562 875 * @param string|null $countryCode Country code.
876 + * @param string|null $carrierId Carrier ID.
563 877 *
564 878 * @return string
565 879 */
566 - public function createUrl( ?string $countryCode = null ): string {
880 + public function createUrl( ?string $countryCode = null, ?string $carrierId = null ): string {
567 881 $params = [
568 882 'page' => self::SLUG,
569 883 ];
570 884
571 - if ( null !== $countryCode ) {
572 - $params['code'] = $countryCode;
885 + if ( $countryCode !== null ) {
886 + $params[ self::PARAMETER_COUNTRY_CODE ] = $countryCode;
573 887 }
574 888
889 + if ( $carrierId !== null ) {
890 + $params[ self::PARAMETER_CARRIER_ID ] = $carrierId;
891 + }
892 +
575 893 return add_query_arg(
576 894 $params,
577 895 admin_url( 'admin.php' )
578 896 );
@@ -586,9 +904,9 @@
586 904 * @return array
587 905 */
588 906 private function getCheckedVendors( array $options ): array {
589 907 $vendorCodes = [];
590 - if ( ! empty( $options['vendor_groups'] ) ) {
908 + if ( isset( $options['vendor_groups'] ) ) {
591 909 $vendorCodes = $options['vendor_groups'];
592 910 }
593 911
594 912 $newVendors = [];
@@ -633,9 +951,9 @@
633 951 * @return array
634 952 */
635 953 private function getVendorCheckboxesConfig( string $carrierId, ?array $carrierOptions ): array {
636 954 $availableVendors = $this->getAvailableVendors( $carrierId );
637 - if ( null === $availableVendors ) {
955 + if ( $availableVendors === null || $this->isAvailableVendorsCountLowerThanRequiredMinimum( $availableVendors ) ) {
638 956 return [];
639 957 }
640 958
641 959 $vendorCheckboxes = [];
@@ -640,25 +958,21 @@
640 958
641 959 $vendorCheckboxes = [];
642 960 $vendorCarriers = $this->pickupPointsConfig->getVendorCarriers();
643 961 foreach ( $availableVendors as $vendorId ) {
644 - $vendorProvider = $vendorCarriers[ $vendorId ];
645 - $checkbox = [
962 + $vendorProvider = $vendorCarriers[ $vendorId ];
963 + $checkbox = [
646 964 'group' => $vendorProvider->getGroup(),
647 965 'name' => $vendorProvider->getName(),
648 966 'disabled' => null,
649 967 'default' => null,
650 968 ];
651 - $hasLowCountAvailable = count( $availableVendors ) <= self::MINIMUM_CHECKED_VENDORS;
652 - if ( $hasLowCountAvailable ) {
653 - $checkbox['disabled'] = true;
654 - }
655 969 $hasGroupSettingsSaved = isset( $carrierOptions['vendor_groups'] );
656 970 $hasTheGroupAllowed = (
657 971 $hasGroupSettingsSaved &&
658 972 in_array( $vendorProvider->getGroup(), $carrierOptions['vendor_groups'], true )
659 973 );
660 - if ( ! $hasGroupSettingsSaved || $hasLowCountAvailable || $hasTheGroupAllowed ) {
974 + if ( ! $hasGroupSettingsSaved || $hasTheGroupAllowed ) {
661 975 $checkbox['default'] = true;
662 976 }
663 977 $vendorCheckboxes[] = $checkbox;
664 978 }
@@ -665,5 +979,28 @@
665 979
666 980 return $vendorCheckboxes;
667 981 }
668 982
983 + /**
984 + * Check if the number of vendors is lower than the required minimum
985 + *
986 + * @param array $availableVendors Available vendors.
987 + *
988 + * @return bool
989 + */
990 + public function isAvailableVendorsCountLowerThanRequiredMinimum( array $availableVendors ): bool {
991 + return count( $availableVendors ) <= self::MINIMUM_CHECKED_VENDORS;
992 + }
993 +
994 + /**
995 + * Checks if the number of vendors is lower than the minimum required by the carrier id
996 + *
997 + * @param string $carrierId Carrier id.
998 + *
999 + * @return bool
1000 + */
1001 + public function isAvailableVendorsCountLowByCarrierId( string $carrierId ): bool {
1002 + $availableVendors = $this->getAvailableVendors( $carrierId );
1003 +
1004 + return is_array( $availableVendors ) && $this->isAvailableVendorsCountLowerThanRequiredMinimum( $availableVendors );
1005 + }
669 1006 }