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/Helpers/AdminOrderProcessor.php +72 -2 1.4.2 → 1.6.5 View file →
@@ -106,8 +106,21 @@
106 106 if (!$fulfillmentType) {
107 107 $fulfillmentType = 'digital';
108 108 }
109 109
110 + // Snapshot the variant's attribute set so admin-created orders carry
111 + // the same pa_* attribute map as storefront orders.
112 + if (!isset($args['item_attributes'])) {
113 + $args['item_attributes'] = AttributeHelper::getProductItemAttributes(
114 + Arr::get($checkoutItem, 'object_id', 0),
115 + Arr::get($checkoutItem, 'post_id', 0)
116 + );
117 + }
118 +
119 + if (!isset($args['variation_type'])) {
120 + $args['variation_type'] = ($variation && $variation->product_detail) ? $variation->product_detail->variation_type : '';
121 + }
122 +
110 123 $item = [
111 124 'payment_type' => $paymentType,
112 125 'post_id' => Arr::get($checkoutItem, 'post_id'),
113 126 'object_id' => Arr::get($checkoutItem, 'object_id'),
@@ -262,13 +275,35 @@
262 275 'note' => Arr::get($this->args, 'note', ''),
263 276 'ip_address' => Arr::get($this->args, 'ip_address', ''),
264 277 'config' => [
265 278 'user_tz' => Arr::get($this->args, 'user_tz', ''),
279 + 'source' => 'admin',
266 280 ],
267 281 ];
268 282
269 283 $totalAmount = $orderData['subtotal'] - $orderData['coupon_discount_total'] - $orderData['manual_discount_total'] + $orderData['shipping_total'] + $orderData['tax_total'];
270 284 $orderData['total_amount'] = $totalAmount > 0 ? $totalAmount : 0;
285 +
286 + /**
287 + * Filter the prepared order data before it is used for order creation.
288 + *
289 + * This mirrors the frontend checkout hook so integrations can adjust
290 + * order payload fields consistently for manual admin orders too.
291 + *
292 + * @param array $orderData Prepared order data array.
293 + * @param array $context {
294 + * Additional context for the filter.
295 + *
296 + * @type array $items Formatted order items with prices and quantities.
297 + * @type array $args Admin order arguments: customer data, payment
298 + * method, shipping, tax, notes, and IP data.
299 + * }
300 + */
301 + $orderData = apply_filters('fluent_cart/checkout/order_data', $orderData, [
302 + 'items' => $this->formattedIOrderItems,
303 + 'args' => $this->args,
304 + ]);
305 +
271 306 $this->orderData = $orderData;
272 307 }
273 308
274 309
@@ -373,8 +408,37 @@
373 408 $subscriptionData = $this->subscriptionData;
374 409 $subscriptionData['customer_id'] = $customerId;
375 410 $subscriptionData['parent_order_id'] = $this->orderModel->id;
376 411 $this->subscriptionModel = Subscription::query()->create($subscriptionData);
412 +
413 + // bill_count counting can't tell "billed cycle" from "something else
414 + // charged alongside it." Must be decided at creation: payment-method switching
415 + // also sets is_trial_days_simulated, so the flag alone can't be trusted at runtime.
416 + $isSimulated = Arr::get($subscriptionData, 'config.is_trial_days_simulated', 'no') === 'yes';
417 + $trialDays = (int)Arr::get($subscriptionData, 'trial_days', 0);
418 + $billTimes = (int)$this->subscriptionModel->bill_times;
419 + $orderTotal = (int)$this->orderModel->total_amount;
420 +
421 + // Simulated trial, $0 first cycle: consumes a cycle but produces no
422 + // total > 0 transaction — add billed_cycles_offset so it still counts.
423 + if ($isSimulated
424 + && $billTimes > 0
425 + && (int)$this->subscriptionModel->signup_fee <= 0
426 + && !$orderTotal
427 + ) {
428 + $this->subscriptionModel->updateMeta('billed_cycles_offset', 1);
429 + }
430 +
431 + // Real trial with a signup fee: the initial charge is the signup fee only,
432 + // but it IS a total > 0 transaction linked to the subscription — mark
433 + // billed_cycles_deduction so it does NOT count as a cycle.
434 + if (!$isSimulated
435 + && $trialDays > 0
436 + && $billTimes > 0
437 + && $orderTotal > 0
438 + ) {
439 + $this->subscriptionModel->updateMeta('billed_cycles_deduction', 1);
440 + }
377 441 }
378 442
379 443 // Let's create the transaction
380 444 $transactionData = [
@@ -492,9 +556,9 @@
492 556 if ($firstPrice < $recurringPrice) {
493 557 Arr::set($item, 'other_info.trial_days', PaymentHelper::getIntervalDays(Arr::get($item, 'other_info.repeat_interval')));
494 558 Arr::set($item, 'other_info.signup_fee', $firstPrice);
495 559 Arr::set($item, 'other_info.manage_setup_fee', 'yes');
496 - Arr::set($item, 'other_info.times', Arr::get($item, 'other_info.times', 0) > 1 ? Arr::get($item, 'other_info.times', 0) - 1 : 0);
560 + // times stays the full installment count — the simulated trial cycle is installment #1
497 561 } else if ($firstPrice > $recurringPrice) {
498 562 Arr::set($item, 'other_info.signup_fee', $firstPrice - $recurringPrice);
499 563 Arr::set($item, 'other_info.manage_setup_fee', 'yes');
500 564 Arr::set($item, 'other_info.trial_days', 0);
@@ -520,11 +584,16 @@
520 584 'bill_count' => 0,
521 585 'quantity' => 1,
522 586 'variation_id' => Arr::get($item, 'object_id', 0),
523 587 'status' => Status::SUBSCRIPTION_PENDING,
588 + 'collection_method' => Status::SUBSCRIPTION_METHOD_MANUAL,
524 589 'config' => [
525 590 'currency' => $this->orderData['currency'],
526 591 'is_trial_days_simulated' => Arr::get($subscriptionPricing, 'is_trial_days_simulated', 'no'),
592 + // Snapshot the variant attribute map + variation type from the order
593 + // item so the subscription carries the same pa_* set behind its item_name.
594 + 'item_attributes' => Arr::get($item, 'other_info.item_attributes', []),
595 + 'variation_type' => Arr::get($item, 'other_info.variation_type', ''),
527 596 ]
528 597 ];
529 598
530 599 $this->subscriptionData = wp_parse_args($subscriptionPricing, $subscriptionItem);
@@ -656,9 +725,10 @@
656 725 $result['trial_days'] = $adjustedTrialDays;
657 726 $result['is_trial_days_simulated'] = 'yes';
658 727 $result['signup_fee'] = $firstCycleCost;
659 728 $result['manage_setup_fee'] = 'yes';
660 - $result['times'] = $times > 0 ? $times - 1 : 0;
729 + // bill_times stays the full installment count. The simulated trial cycle IS the
730 + // first installment; gateways derive remaining remote cycles from is_trial_days_simulated.
661 731 } else if ($firstCycleCost > $recurringAmount) {
662 732 $result['trial_days'] = 0;
663 733 $result['signup_fee'] = $firstCycleCost - $recurringAmount;
664 734 $result['manage_setup_fee'] = 'yes';