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.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 trunk All 48 releases
← All changes | api/Resource/ProductResource.php +39 -13 1.5.3 → 1.6.5 View file →
@@ -129,12 +129,12 @@
129 129 * 'variants' => (array) Required. Variants of the product.
130 130 * 'id' => (int) Required. The variant ID.
131 131 * 'post_id' => (int) Required. The product ID.
132 132 * 'variant_title' => (string) Required. The variant title.
133 - * 'item_price' => (float) Required. The item price.
134 - * 'compare_price' => (float) Required. The compare price.
133 + * 'item_price' => (int) Required. The item price in CENTS (129900 = $1,299.00).
134 + * 'compare_price' => (int) Required. The compare price, in cents.
135 135 * 'manage_cost' => (string) Optional. Whether to manage costs.
136 - * 'item_cost' => (float) Required if manage cost is yes. The item cost.
136 + * 'item_cost' => (int) Required if manage cost is yes. The item cost, in cents.
137 137 * 'manage_stock' => (string) Required. Whether to manage stock.
138 138 * 'stock_status' => (string) Required. The stock status.
139 139 * 'stock' => (int) Required. The stock quantity.
140 140 * 'media' => (array) Optional. Info of media files for each variant.
@@ -187,11 +187,12 @@
187 187 'compare_price',
188 188 'item_cost',
189 189 ];
190 190
191 + // Amounts arrive in CENTS; normalize float artifacts without scaling.
191 192 foreach ($priceColumns as $column) {
192 193 if (Arr::has($variant, $column)) {
193 - $variant[$column] = Arr::get($variant, $column) * 100;
194 + $variant[$column] = Helper::roundCent(Arr::get($variant, $column));
194 195 }
195 196 }
196 197
197 198 unset($variant['rowId']);
@@ -209,12 +210,15 @@
209 210 }
210 211
211 212 $variantData = $variant;
212 213
213 - // Remove empty sku and shipping_class to prevent unique constraint violation
214 - if (array_key_exists('sku', $variantData) && empty($variantData['sku'])) {
215 - unset($variantData['sku']);
214 + // An explicitly cleared sku ('' or null) must persist as NULL so the
215 + // stored value is actually cleared, while still avoiding the sku_unique
216 + // constraint (MySQL treats multiple NULLs as distinct, unlike '').
217 + if (array_key_exists('sku', $variantData) && ($variantData['sku'] === '' || $variantData['sku'] === null)) {
218 + $variantData['sku'] = null;
216 219 }
220 + // Remove empty shipping_class to prevent unique constraint violation
217 221 if (array_key_exists('shipping_class', $variantData) && empty($variantData['shipping_class'])) {
218 222 unset($variantData['shipping_class']);
219 223 }
220 224
@@ -221,9 +225,9 @@
221 225 // Handle other_info
222 226 if (!empty($otherInfo)) {
223 227 if (Arr::get($otherInfo, 'payment_type') == 'subscription') {
224 228 if (Arr::get($otherInfo, 'manage_setup_fee') == 'yes') {
225 - $signupFee = Helper::toCent(floatval(Arr::get($otherInfo, 'signup_fee', 0)));
229 + $signupFee = Helper::roundCent(Arr::get($otherInfo, 'signup_fee', 0));
226 230 Arr::set($otherInfo, 'signup_fee', $signupFee);
227 231 }
228 232 $variantData['payment_type'] = 'subscription';
229 233 } else {
@@ -249,16 +253,35 @@
249 253 'compare_price',
250 254 'item_cost',
251 255 ];
252 256
257 + // Amounts arrive in CENTS; normalize without scaling.
253 258 foreach ($priceColumns as $column) {
254 259 if (Arr::has($variant, $column)) {
255 - $variant[$column] = Arr::get($variant, $column) * 100;
260 + $variant[$column] = Helper::roundCent(Arr::get($variant, $column));
256 261 }
257 262 }
258 263 unset($variant['rowId']);
259 - $variant['serial_index'] = $index + 1;
260 264
265 + // serial_index is display ordering the caller owns, not a column derived
266 + // from this loop. An advanced-variation save carries only the rows the
267 + // merchant actually touched, so deriving it from the payload index
268 + // renumbered an edited row to the front and left two variations sharing a
269 + // position. The reorder path sends an explicit serial_index for every row
270 + // and bulk edit round-trips the stored one, so an absent value means
271 + // "unchanged": drop the column and let batchUpdate's `ELSE serial_index`
272 + // keep what is stored.
273 + if (!isset($variant['serial_index']) || $variant['serial_index'] === '') {
274 + unset($variant['serial_index']);
275 + }
276 +
277 + // An explicitly cleared sku ('' or null) must persist as NULL so the
278 + // stored value is actually cleared, while still avoiding the sku_unique
279 + // constraint (MySQL treats multiple NULLs as distinct, unlike '').
280 + if (array_key_exists('sku', $variant) && ($variant['sku'] === '' || $variant['sku'] === null)) {
281 + $variant['sku'] = null;
282 + }
283 +
261 284 // Recalculate stock_status from available and manage_stock
262 285 if (isset($variant['manage_stock'])) {
263 286 if ($variant['manage_stock']) {
264 287 $avail = intval(Arr::get($variant, 'available', 0));
@@ -271,9 +294,9 @@
271 294
272 295 if (!empty($otherInfo)) {
273 296 if (Arr::get($otherInfo, 'payment_type') == 'subscription') {
274 297 if (Arr::get($otherInfo, 'manage_setup_fee') == 'yes') {
275 - $signupFee = Helper::toCent(floatval(Arr::get($otherInfo, 'signup_fee', 0)));
298 + $signupFee = Helper::roundCent(Arr::get($otherInfo, 'signup_fee', 0));
276 299 Arr::set($otherInfo, 'signup_fee', $signupFee);
277 300 }
278 301 }
279 302 $variant['other_info'] = $otherInfo;
@@ -305,10 +328,13 @@
305 328
306 329
307 330 }
308 331
309 - $defaultVariationId = Arr::get($detail, 'default_variation_id');
310 - $detail['default_variation_id'] = $defaultVariationId;
332 + // Deliberately NOT defaulted here. $detail is a partial row — the editor
333 + // stages only what the merchant touched — so materialising this key as null
334 + // told ProductDetailResource::update() to clear the stored Default Variant
335 + // on every unrelated save (an inline price edit was enough). Absent now
336 + // means "unchanged"; an explicit empty value still clears it.
311 337
312 338 // Recalculate min_price / max_price from current variant prices
313 339 $variantPriceRange = ProductVariation::query()
314 340 ->where('post_id', $postId)