| @@ -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 | } |