| @@ -1,8 +1,9 @@ | ||
| 1 | 1 | <?php |
| 2 | 2 | |
| 3 | 3 | namespace FluentCart\Api\Resource; |
| 4 | 4 | |
| 5 | +use FluentCart\Api\Meta; | |
| 5 | 6 | use FluentCart\App\Events\StockChanged; |
| 6 | 7 | use FluentCart\App\Helpers\Helper; |
| 7 | 8 | use FluentCart\App\Helpers\ProductAdminHelper; |
| 8 | 9 | use FluentCart\App\Models\ProductDetail; |
| @@ -141,8 +142,29 @@ | ||
| 141 | 142 | $detail->post_id, |
| 142 | 143 | $variationIds, |
| 143 | 144 | __("the product variation type was changed to 'Simple'", 'fluent-cart') |
| 144 | 145 | ); |
| 146 | + | |
| 147 | + // The surviving variant (variationIds[0]) is kept, but a Simple | |
| 148 | + // product has no editor control to view, replace, or clear its | |
| 149 | + // image (VariantTitleMedia only shows for simple_variations / | |
| 150 | + // advanced_variations). Leaving that image attached would let it | |
| 151 | + // keep rendering on the storefront gallery with no way for the | |
| 152 | + // merchant to find or remove it, so it is cleared with the same | |
| 153 | + // switch the admin UI now warns about. | |
| 154 | + // | |
| 155 | + // variation_ids is client-supplied, so confirm the surviving id | |
| 156 | + // actually belongs to this product before deleting its media — | |
| 157 | + // otherwise a caller could point it at an unrelated product's | |
| 158 | + // variation and wipe that variation's image instead. | |
| 159 | + $keptVariantBelongsToProduct = \FluentCart\App\Models\ProductVariation::query() | |
| 160 | + ->where('id', $variationIds[0]) | |
| 161 | + ->where('post_id', $detail->post_id) | |
| 162 | + ->exists(); | |
| 163 | + | |
| 164 | + if ($keptVariantBelongsToProduct) { | |
| 165 | + Meta::deleteVariationMedia($variationIds[0]); | |
| 166 | + } | |
| 145 | 167 | } |
| 146 | 168 | } |
| 147 | 169 | |
| 148 | 170 | // Switching INTO Advanced Variations (from Simple or Simple Variations) |
| @@ -175,11 +197,17 @@ | ||
| 175 | 197 | |
| 176 | 198 | $data['min_price'] = Arr::get($data, 'min_price') ?: ($detail->min_price ?? 0); |
| 177 | 199 | $data['max_price'] = Arr::get($data, 'max_price') ?: ($detail->max_price ?? 0); |
| 178 | 200 | |
| 179 | - // Handle Default Variation | |
| 180 | - if (empty(Arr::get($data, 'default_variation_id'))) { | |
| 181 | - $data['default_variation_id'] = NULL; | |
| 201 | + // Handle Default Variation. Only act when the caller actually supplied the | |
| 202 | + // key: a partial update that never mentions it must leave the stored value | |
| 203 | + // alone, while an explicitly empty value still clears it. | |
| 204 | + if (Arr::has($data, 'default_variation_id')) { | |
| 205 | + if (empty(Arr::get($data, 'default_variation_id'))) { | |
| 206 | + $data['default_variation_id'] = NULL; | |
| 207 | + } | |
| 208 | + } else { | |
| 209 | + unset($data['default_variation_id']); | |
| 182 | 210 | } |
| 183 | 211 | |
| 184 | 212 | // Handle other_info merge |
| 185 | 213 | if (Arr::has($data, 'other_info')) { |
| @@ -190,9 +218,12 @@ | ||
| 190 | 218 | $mergedOtherInfo = array_merge($existingOtherInfo, $newOtherInfo); |
| 191 | 219 | |
| 192 | 220 | // Handle subscription-specific logic |
| 193 | 221 | if (Arr::get($mergedOtherInfo, 'payment_type') == 'subscription' && Arr::get($mergedOtherInfo, 'manage_setup_fee') == 'yes') { |
| 194 | - $signupFee = Helper::toCent(floatval(Arr::get($mergedOtherInfo, 'signup_fee', 0))); | |
| 222 | + // Cents in, and $mergedOtherInfo may carry the already-cents stored | |
| 223 | + // value when the caller did not resend signup_fee — roundCent is | |
| 224 | + // idempotent, so neither case is rescaled. | |
| 225 | + $signupFee = Helper::roundCent(Arr::get($mergedOtherInfo, 'signup_fee', 0)); | |
| 195 | 226 | $mergedOtherInfo['signup_fee'] = $signupFee; |
| 196 | 227 | } |
| 197 | 228 | |
| 198 | 229 | $data['other_info'] = $mergedOtherInfo; |