PluginProbe
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler / 1.6.5
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler v1.6.5
1.6.6 1.6.5 1.6.4 1.6.3 1.6.2 1.6.1 1.6.0 1.5.4 1.5.5 1.5.3 1.5.2 1.5.1 1.5.0 1.4.2 1.4.1 1.4.0 1.3.28 1.3.27 1.3.26 1.3.25 1.3.23 1.3.22 1.3.21 1.3.20 1.3.19 All 49 releases
← All changes | app/Http/Requests/ProductVariationRequest.php +111 -16 1.3.27 → 1.6.5 View file →
@@ -1,8 +1,10 @@
1 1 <?php
2 2
3 3 namespace FluentCart\App\Http\Requests;
4 4
5 +use FluentCart\App\Helpers\Helper;
6 +use FluentCart\App\Http\Rules\RequiredWhenRule;
5 7 use FluentCart\Framework\Foundation\RequestGuard;
6 8 use FluentCart\Framework\Support\Arr;
7 9
8 10 class ProductVariationRequest extends RequestGuard
@@ -73,8 +75,32 @@
73 75 * @return array
74 76 */
75 77 public function rules()
76 78 {
79 + // total_stock / available / committed / on_hold are `INT(11) NULL DEFAULT 0`
80 + // (ProductVariationMigrator), so an empty counter is a legitimate stored state for
81 + // a variation that never tracked stock. ProductEditModel::createOrUpdatePricing()
82 + // posts the variation back exactly as the drawer loaded it, NULLs included, so
83 + // demanding a number unconditionally rejected a plain price edit on such a row.
84 + //
85 + // This cannot be expressed as `nullable|numeric`: Validator::filterExcludeables()
86 + // drops EVERY rule for a falsy value the moment `nullable` is present, which
87 + // would disarm the conditional requirement too. It cannot sit beside a
88 + // `required_if` string either — filterRequiredIf() discarded this closure along
89 + // with every other rule whenever tracking was off, leaving the guard inert. The
90 + // requirement is a RequiredWhenRule closure below for that reason.
91 + $numericWhenProvided = function ($attribute, $value) {
92 + if ($value === null || $value === '') {
93 + return null;
94 + }
95 +
96 + if (!is_numeric($value)) {
97 + return esc_html__('Stock quantity must be a number.', 'fluent-cart');
98 + }
99 +
100 + return null;
101 + };
102 +
77 103 return [
78 104 'variants.variation_title' => 'required|sanitizeText|maxLength:200',
79 105 'variants.sku' => 'nullable|sanitizeText|maxLength:30|unique:fct_product_variations,sku' . ($this->get('variants.id') ? ',' . $this->get('variants.id') : ''),
80 106 'variants.item_price' => 'nullable|numeric|min:0',
@@ -92,9 +118,15 @@
92 118 return null;
93 119 },
94 120 ],
95 121 'variants.manage_cost' => 'nullable|sanitizeText|maxLength:10',
96 - 'variants.item_cost' => 'required_if:variants.manage_cost,true',
122 + 'variants.item_cost' => [
123 + RequiredWhenRule::make(
124 + 'variants.manage_cost',
125 + 'true',
126 + esc_html__('Item cost is required.', 'fluent-cart')
127 + ),
128 + ],
97 129 'variants.fulfillment_type' => 'required|sanitizeText|maxLength:100',
98 130 'variants.shipping_class' => function ($attr, $value) {
99 131 if ($value && !(\FluentCart\App\Models\ShippingClass::find(intval($value)))) {
100 132 return __('The selected shipping class does not exist.', 'fluent-cart');
@@ -102,11 +134,35 @@
102 134 return null;
103 135 },
104 136
105 137 'variants.manage_stock' => 'nullable|numeric',
106 - 'variants.stock_status' => 'required_if:variants.manage_stock,1|sanitizeText|maxLength:50',
107 - 'variants.total_stock' => 'required|numeric',
108 - 'variants.available' => 'required|numeric',
138 + 'variants.stock_status' => [
139 + RequiredWhenRule::make(
140 + 'variants.manage_stock',
141 + '1',
142 + esc_html__('Stock status is required.', 'fluent-cart')
143 + ),
144 + 'sanitizeText',
145 + 'maxLength:50',
146 + ],
147 + // Quantities are only demanded once tracking is actually on, mirroring
148 + // stock_status directly above.
149 + 'variants.total_stock' => [
150 + RequiredWhenRule::make(
151 + 'variants.manage_stock',
152 + '1',
153 + esc_html__('Stock quantity is required when inventory tracking is on.', 'fluent-cart')
154 + ),
155 + $numericWhenProvided,
156 + ],
157 + 'variants.available' => [
158 + RequiredWhenRule::make(
159 + 'variants.manage_stock',
160 + '1',
161 + esc_html__('Available quantity is required when inventory tracking is on.', 'fluent-cart')
162 + ),
163 + $numericWhenProvided,
164 + ],
109 165 // 'variants.available' => [
110 166 // 'required',
111 167 // 'numeric',
112 168 // function ($attribute, $value, $fail) {
@@ -115,10 +171,12 @@
115 171 // }
116 172 // return null;
117 173 // },
118 174 // ],
119 - 'variants.committed' => 'required|numeric',
120 - 'variants.on_hold' => 'required|numeric',
175 + // Ledger columns the merchant never edits — they are maintained by the stock
176 + // listeners, so they only have to be a number when the payload carries one.
177 + 'variants.committed' => [$numericWhenProvided],
178 + 'variants.on_hold' => [$numericWhenProvided],
121 179
122 180 'variants.serial_index' => 'nullable|numeric',
123 181
124 182 'variants.other_info' => 'required|array',
@@ -131,9 +189,9 @@
131 189 }
132 190 if (!empty($value) && !is_numeric($value)) {
133 191 return __('Times must be a number.', 'fluent-cart');
134 192 }
135 - return null;
193 + return Helper::installmentTimesError($this->get('variants.other_info'));
136 194 },
137 195 ],
138 196 'variants.other_info.trial_days' => [
139 197 function ($attribute, $value) {
@@ -148,13 +206,43 @@
148 206 }
149 207 return null;
150 208 },
151 209 ],
152 - 'variants.other_info.repeat_interval' => 'required_if:variants.other_info.payment_type,subscription|sanitizeText|maxLength:100',
210 + 'variants.other_info.repeat_interval' => [
211 + RequiredWhenRule::make(
212 + 'variants.other_info.payment_type',
213 + 'subscription',
214 + esc_html__('Interval is required.', 'fluent-cart')
215 + ),
216 + 'sanitizeText',
217 + 'maxLength:100',
218 + ],
153 219 'variants.other_info.billing_summary' => 'nullable|sanitizeTextArea|maxLength:255',
154 - 'variants.other_info.manage_setup_fee' => 'required_if:variants.other_info.payment_type,subscription|sanitizeText|maxLength:100',
155 - 'variants.other_info.signup_fee' => 'required_if:variants.other_info.manage_setup_fee,yes',
156 - 'variants.other_info.signup_fee_name' => 'required_if:variants.other_info.manage_setup_fee,yes|sanitizeText|maxLength:100',
220 + 'variants.other_info.manage_setup_fee' => [
221 + RequiredWhenRule::make(
222 + 'variants.other_info.payment_type',
223 + 'subscription',
224 + esc_html__('Setup Fee option is required.', 'fluent-cart')
225 + ),
226 + 'sanitizeText',
227 + 'maxLength:100',
228 + ],
229 + 'variants.other_info.signup_fee' => [
230 + RequiredWhenRule::make(
231 + 'variants.other_info.manage_setup_fee',
232 + 'yes',
233 + esc_html__('Setup Fee Amount is required.', 'fluent-cart')
234 + ),
235 + ],
236 + 'variants.other_info.signup_fee_name' => [
237 + RequiredWhenRule::make(
238 + 'variants.other_info.manage_setup_fee',
239 + 'yes',
240 + esc_html__('Setup Fee Name is required.', 'fluent-cart')
241 + ),
242 + 'sanitizeText',
243 + 'maxLength:100',
244 + ],
157 245 'variants.other_info.package_slug' => 'nullable|sanitizeText|maxLength:100',
158 246 'variants.other_info.weight' => 'nullable|numeric',
159 247 'variants.other_info.weight_unit' => 'nullable|sanitizeText|maxLength:10',
160 248 'variants.other_info.length' => 'nullable|numeric',
@@ -159,9 +247,19 @@
159 247 'variants.other_info.weight_unit' => 'nullable|sanitizeText|maxLength:10',
160 248 'variants.other_info.length' => 'nullable|numeric',
161 249 'variants.other_info.width' => 'nullable|numeric',
162 250 'variants.other_info.height' => 'nullable|numeric',
251 + 'variants.other_info.tax_class' => ['nullable', function ($attribute, $value) {
252 + if (empty($value)) {
253 + return null;
254 + }
163 255
256 + return empty(\FluentCart\App\Models\TaxClass::query()->where('slug', sanitize_text_field($value))->first())
257 + ? __('Invalid Tax Class.', 'fluent-cart')
258 + : null;
259 + }],
260 + 'variants.other_info.tax_exempt' => 'nullable|sanitizeText|in:yes,no',
261 +
164 262 'variants.downloadable' => 'nullable|sanitizeText|maxLength:10',
165 263 ];
166 264 }
167 265
@@ -193,18 +291,13 @@
193 291 'variants.sku.unique' => esc_html__('The SKU must be unique.', 'fluent-cart'),
194 292 'variants.item_price.required' => esc_html__('Price is required.', 'fluent-cart'),
195 293 'variants.item_price.numeric' => esc_html__('Price must be a number.', 'fluent-cart'),
196 294 'variants.item_price.min' => esc_html__('Price must be a positive number greater than 0.', 'fluent-cart'),
197 - 'variants.stock_status.required_if' => esc_html__('Stock status is required.', 'fluent-cart'),
198 - 'variants.item_cost.required_if' => esc_html__('Item cost is required.', 'fluent-cart'),
199 295 'variants.fulfillment_type.required' => esc_html__('Fulfilment Type is required.', 'fluent-cart'),
200 296
201 297 'variants.other_info.description.max' => esc_html__('Description may not be greater than 255 characters.', 'fluent-cart'),
202 298 'variants.other_info.payment_type.required' => esc_html__('Payment Type is required.', 'fluent-cart'),
203 299 'variants.other_info.times.required_if' => esc_html__('Times is required.', 'fluent-cart'),
204 - 'variants.other_info.repeat_interval.required_if' => esc_html__('Interval is required.', 'fluent-cart'),
205 - 'variants.other_info.signup_fee.required_if' => esc_html__('Setup Fee Amount is required.', 'fluent-cart'),
206 - 'variants.other_info.signup_fee_name.required_if' => esc_html__('Setup Fee Name is required.', 'fluent-cart'),
207 300 'variants.other_info.trial_days.numeric' => esc_html__('Trial days must be a number.', 'fluent-cart'),
208 301 'variants.other_info.trial_days.max' => esc_html__('Trial period cannot exceed 365 days.', 'fluent-cart'),
209 302 ];
210 303 }
@@ -266,8 +359,10 @@
266 359 'variants.other_info.weight_unit' => 'sanitize_text_field',
267 360 'variants.other_info.length' => 'floatval',
268 361 'variants.other_info.width' => 'floatval',
269 362 'variants.other_info.height' => 'floatval',
363 + 'variants.other_info.tax_class' => 'sanitize_text_field',
364 + 'variants.other_info.tax_exempt' => 'sanitize_text_field',
270 365 //'variants.other_info.purchasable' => 'sanitize_text_field',
271 366 ];
272 367
273 368 }