is_abo) { return ['success' => false, 'message' => __('abo.extern_done_only_abo')]; } $userAboOrder = UserAboOrder::where('shopping_order_id', $shoppingOrder->id)->first(); $userAbo = $userAboOrder?->user_abo; if (! $userAboOrder || ! $userAbo) { return ['success' => false, 'message' => __('abo.extern_done_no_order')]; } if ((int) $userAboOrder->status === self::STATUS_EXTERN_DONE) { return ['success' => false, 'message' => __('abo.extern_done_already')]; } if ($userAboOrder->paid || $shoppingOrder->paid) { return ['success' => false, 'message' => __('abo.extern_done_already_paid')]; } try { DB::transaction(function () use ($shoppingOrder, $userAboOrder, $userAbo): void { $this->creditPoints($shoppingOrder); $userAboOrder->update([ 'status' => self::STATUS_EXTERN_DONE, 'paid' => false, ]); // Lieferung bestaetigen, Bestellung bleibt aber unbezahlt. $shoppingOrder->shipped = 2; $shoppingOrder->save(); // Blockade aufheben: Abo zurueck in den regulaeren Zyklus. $userAbo->update([ 'status' => self::STATUS_ABO_OKAY, 'active' => true, 'last_date' => now(), 'next_date' => AboHelper::setNextDate(now(), $userAbo->abo_interval), ]); }); // Wie bei einer erfolgreichen Ausfuehrung: Incentive tracken und // Einmal-Artikel entfernen. Beide Aufrufe sind idempotent. IncentiveTracker::trackAboActivated($shoppingOrder); AboOneTimeService::purgeAfterExecution($userAbo); \Log::channel('abo_order')->info('AboExternalCompletionService: Bestellung extern erledigt', [ 'abo_id' => $userAbo->id, 'order_id' => $shoppingOrder->id, ]); } catch (\Throwable $e) { \Log::channel('abo_order')->error('AboExternalCompletionService: Fehler beim externen Erledigen', [ 'order_id' => $shoppingOrder->id, 'error' => $e->getMessage(), ]); return ['success' => false, 'message' => __('abo.extern_done_error', ['error' => $e->getMessage()])]; } return ['success' => true, 'message' => __('abo.extern_done_success')]; } /** * Erledigt die juengste angehaltene Bestellung eines Abos (Einstieg ueber die Abo-Detailseite). * * @return array{success: bool, message: string} */ public function completeForAbo(UserAbo $userAbo): array { $shoppingOrder = $this->resolveHeldOrder($userAbo); if (! $shoppingOrder) { return ['success' => false, 'message' => __('abo.extern_done_no_order')]; } return $this->complete($shoppingOrder); } private function resolveHeldOrder(UserAbo $userAbo): ?ShoppingOrder { return $userAbo->user_abo_orders() ->where('paid', false) ->where('status', self::STATUS_ABO_HOLD) ->orderByDesc('id') ->first() ?->shopping_order; } private function creditPoints(ShoppingOrder $shoppingOrder): void { $alreadyCredited = UserSalesVolume::where('shopping_order_id', $shoppingOrder->id)->exists(); if ($alreadyCredited) { return; } SalesPointsVolume::User($shoppingOrder); } }