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 +142 -17 1.3.19 → 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
@@ -58,8 +60,15 @@
58 60 ];
59 61 }
60 62 $data['variants']['other_info'] = $variantOtherInfo;
61 63
64 + if (Arr::get($variantOtherInfo, 'payment_type') === 'onetime') {
65 + $subscriptionFields = ['trial_days', 'times', 'repeat_interval', 'billing_summary', 'manage_setup_fee', 'signup_fee', 'signup_fee_name', 'setup_fee_per_item'];
66 + foreach ($subscriptionFields as $field) {
67 + unset($data['variants']['other_info'][$field]);
68 + }
69 + }
70 +
62 71 return $data;
63 72 }
64 73
65 74 /**
@@ -66,8 +75,32 @@
66 75 * @return array
67 76 */
68 77 public function rules()
69 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 +
70 103 return [
71 104 'variants.variation_title' => 'required|sanitizeText|maxLength:200',
72 105 'variants.sku' => 'nullable|sanitizeText|maxLength:30|unique:fct_product_variations,sku' . ($this->get('variants.id') ? ',' . $this->get('variants.id') : ''),
73 106 'variants.item_price' => 'nullable|numeric|min:0',
@@ -85,9 +118,15 @@
85 118 return null;
86 119 },
87 120 ],
88 121 'variants.manage_cost' => 'nullable|sanitizeText|maxLength:10',
89 - '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 + ],
90 129 'variants.fulfillment_type' => 'required|sanitizeText|maxLength:100',
91 130 'variants.shipping_class' => function ($attr, $value) {
92 131 if ($value && !(\FluentCart\App\Models\ShippingClass::find(intval($value)))) {
93 132 return __('The selected shipping class does not exist.', 'fluent-cart');
@@ -95,11 +134,35 @@
95 134 return null;
96 135 },
97 136
98 137 'variants.manage_stock' => 'nullable|numeric',
99 - 'variants.stock_status' => 'required_if:variants.manage_stock,1|sanitizeText|maxLength:50',
100 - 'variants.total_stock' => 'required|numeric',
101 - '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 + ],
102 165 // 'variants.available' => [
103 166 // 'required',
104 167 // 'numeric',
105 168 // function ($attribute, $value, $fail) {
@@ -108,10 +171,12 @@
108 171 // }
109 172 // return null;
110 173 // },
111 174 // ],
112 - 'variants.committed' => 'required|numeric',
113 - '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],
114 179
115 180 'variants.serial_index' => 'nullable|numeric',
116 181
117 182 'variants.other_info' => 'required|array',
@@ -116,15 +181,68 @@
116 181
117 182 'variants.other_info' => 'required|array',
118 183 'variants.other_info.description' => 'nullable|sanitizeTextArea|maxLength:255',
119 184 'variants.other_info.payment_type' => 'required|sanitizeText|in:onetime,subscription',
120 - 'variants.other_info.times' => 'nullable|numeric',
121 - 'variants.other_info.trial_days' => 'nullable|numeric|max:365',
122 - 'variants.other_info.repeat_interval' => 'required_if:variants.other_info.payment_type,subscription|sanitizeText|maxLength:100',
185 + 'variants.other_info.times' => [
186 + function ($attribute, $value) {
187 + if ($this->get('variants.other_info.payment_type') !== 'subscription') {
188 + return null;
189 + }
190 + if (!empty($value) && !is_numeric($value)) {
191 + return __('Times must be a number.', 'fluent-cart');
192 + }
193 + return Helper::installmentTimesError($this->get('variants.other_info'));
194 + },
195 + ],
196 + 'variants.other_info.trial_days' => [
197 + function ($attribute, $value) {
198 + if ($this->get('variants.other_info.payment_type') !== 'subscription') {
199 + return null;
200 + }
201 + if (!empty($value) && !is_numeric($value)) {
202 + return __('Trial days must be a number.', 'fluent-cart');
203 + }
204 + if (!empty($value) && $value > 365) {
205 + return __('Trial period cannot exceed 365 days.', 'fluent-cart');
206 + }
207 + return null;
208 + },
209 + ],
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 + ],
123 219 'variants.other_info.billing_summary' => 'nullable|sanitizeTextArea|maxLength:255',
124 - 'variants.other_info.manage_setup_fee' => 'required_if:variants.other_info.payment_type,subscription|sanitizeText|maxLength:100',
125 - 'variants.other_info.signup_fee' => 'required_if:variants.other_info.manage_setup_fee,yes',
126 - '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 + ],
127 245 'variants.other_info.package_slug' => 'nullable|sanitizeText|maxLength:100',
128 246 'variants.other_info.weight' => 'nullable|numeric',
129 247 'variants.other_info.weight_unit' => 'nullable|sanitizeText|maxLength:10',
130 248 'variants.other_info.length' => 'nullable|numeric',
@@ -129,9 +247,19 @@
129 247 'variants.other_info.weight_unit' => 'nullable|sanitizeText|maxLength:10',
130 248 'variants.other_info.length' => 'nullable|numeric',
131 249 'variants.other_info.width' => 'nullable|numeric',
132 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 + }
133 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 +
134 262 'variants.downloadable' => 'nullable|sanitizeText|maxLength:10',
135 263 ];
136 264 }
137 265
@@ -163,18 +291,13 @@
163 291 'variants.sku.unique' => esc_html__('The SKU must be unique.', 'fluent-cart'),
164 292 'variants.item_price.required' => esc_html__('Price is required.', 'fluent-cart'),
165 293 'variants.item_price.numeric' => esc_html__('Price must be a number.', 'fluent-cart'),
166 294 'variants.item_price.min' => esc_html__('Price must be a positive number greater than 0.', 'fluent-cart'),
167 - 'variants.stock_status.required_if' => esc_html__('Stock status is required.', 'fluent-cart'),
168 - 'variants.item_cost.required_if' => esc_html__('Item cost is required.', 'fluent-cart'),
169 295 'variants.fulfillment_type.required' => esc_html__('Fulfilment Type is required.', 'fluent-cart'),
170 296
171 297 'variants.other_info.description.max' => esc_html__('Description may not be greater than 255 characters.', 'fluent-cart'),
172 298 'variants.other_info.payment_type.required' => esc_html__('Payment Type is required.', 'fluent-cart'),
173 299 'variants.other_info.times.required_if' => esc_html__('Times is required.', 'fluent-cart'),
174 - 'variants.other_info.repeat_interval.required_if' => esc_html__('Interval is required.', 'fluent-cart'),
175 - 'variants.other_info.signup_fee.required_if' => esc_html__('Setup Fee Amount is required.', 'fluent-cart'),
176 - 'variants.other_info.signup_fee_name.required_if' => esc_html__('Setup Fee Name is required.', 'fluent-cart'),
177 300 'variants.other_info.trial_days.numeric' => esc_html__('Trial days must be a number.', 'fluent-cart'),
178 301 'variants.other_info.trial_days.max' => esc_html__('Trial period cannot exceed 365 days.', 'fluent-cart'),
179 302 ];
180 303 }
@@ -236,8 +359,10 @@
236 359 'variants.other_info.weight_unit' => 'sanitize_text_field',
237 360 'variants.other_info.length' => 'floatval',
238 361 'variants.other_info.width' => 'floatval',
239 362 'variants.other_info.height' => 'floatval',
363 + 'variants.other_info.tax_class' => 'sanitize_text_field',
364 + 'variants.other_info.tax_exempt' => 'sanitize_text_field',
240 365 //'variants.other_info.purchasable' => 'sanitize_text_field',
241 366 ];
242 367
243 368 }