Abo Einmalprodukte und Bestätigung abschließen

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Kevin 2026-06-05 15:28:08 +00:00
parent 2bdc9ada3c
commit 2269ce031f
57 changed files with 3647 additions and 371 deletions

View file

@ -3,14 +3,17 @@
namespace App\Http\Controllers\Portal;
use App\Http\Controllers\Controller;
use App\Http\Requests\Abo\AboOneTimeItemRequest;
use App\Models\Product;
use App\Models\ShoppingInstance;
use App\Models\ShoppingUser;
use App\Models\UserAbo;
use App\Models\UserAboItem;
use App\Models\UserAboOneTimeItem;
use App\Repositories\AboRepository;
use App\Services\AboHelper;
use App\Services\AboItemHistoryService;
use App\Services\AboOneTimeService;
use App\Services\AboOrderCart;
use App\Services\Shop;
use App\Services\UserService;
@ -68,6 +71,14 @@ class AboController extends Controller
AboOrderCart::initYard($user_abo);
$customer_detail = AboOrderCart::getCustomerDetail();
AboOrderCart::makeOrderYard($user_abo);
$baseCompCount = Yard::instance(AboOrderCart::INSTANCE)->getNumComp();
$oneTimeWindowOpen = AboHelper::isOneTimeWindowOpen($user_abo);
if ($oneTimeWindowOpen) {
AboOrderCart::addOneTimeItemsToYard($user_abo);
AboOrderCart::checkNumOfCompProducts($user_abo);
}
$summary = AboOrderCart::getSplitSummary();
return view('portal.abo.my_abo', [
'user_abo' => $user_abo,
@ -75,6 +86,9 @@ class AboController extends Controller
'view' => $view,
'comp_products' => [],
'isAdmin' => false,
'one_time_window_open' => $oneTimeWindowOpen,
'summary' => $summary,
'base_comp_count' => $baseCompCount,
]);
}
@ -98,11 +112,15 @@ class AboController extends Controller
if (Request::ajax()) {
$message = false;
// Yard für Gewichts-/Versandprüfung initialisieren (Versandland setzen)
AboOrderCart::initYard($user_abo);
// addProduct
if ($data['action'] === 'addProduct') {
if ($product = Product::find($data['product_id'])) {
if ($UserAboItem = UserAboItem::where('user_abo_id', $user_abo->id)->where('product_id', $product->id)->where('comp', 0)->first()) {
if (AboOrderCart::exceedsMaxWeight($user_abo, (int) $product->weight)) {
$message = __('msg.cart_max_weight_reached');
} elseif ($UserAboItem = UserAboItem::where('user_abo_id', $user_abo->id)->where('product_id', $product->id)->where('comp', 0)->first()) {
$qtyBefore = $UserAboItem->qty;
$UserAboItem->qty = $UserAboItem->qty + 1;
$UserAboItem->save();
@ -132,9 +150,14 @@ class AboController extends Controller
if ($isAddOnlyMode && $qty < $UserAboItem->qty) {
$qty = $UserAboItem->qty;
}
$UserAboItem->qty = $qty;
$UserAboItem->save();
AboItemHistoryService::logQtyChanged($user_abo, $UserAboItem, $qtyBefore, $qty, $view);
$additionalWeight = (int) $product->weight * ($qty - $qtyBefore);
if ($additionalWeight > 0 && AboOrderCart::exceedsMaxWeight($user_abo, $additionalWeight)) {
$message = __('msg.cart_max_weight_reached');
} else {
$UserAboItem->qty = $qty;
$UserAboItem->save();
AboItemHistoryService::logQtyChanged($user_abo, $UserAboItem, $qtyBefore, $qty, $view);
}
}
}
}
@ -187,15 +210,32 @@ class AboController extends Controller
AboOrderCart::initYard($user_abo);
AboOrderCart::makeOrderYard($user_abo);
$baseCompCount = Yard::instance(AboOrderCart::INSTANCE)->getNumComp();
if (AboHelper::isOneTimeWindowOpen($user_abo)) {
AboOrderCart::addOneTimeItemsToYard($user_abo->fresh());
}
AboOrderCart::checkNumOfCompProducts($user_abo);
$summary = AboOrderCart::getSplitSummary();
$error_message = $message ? $message : false;
$html_cart = view('admin.abo._order_abo_show', ['user_abo' => $user_abo, 'error_message' => $error_message, 'add_only_mode' => $isAddOnlyMode])->render();
$html_comp = view('user.order.comp_product', $data)->render();
$html_cart = view('admin.abo._order_abo_show', [
'user_abo' => $user_abo,
'error_message' => $error_message,
'split_mode' => AboHelper::isOneTimeWindowOpen($user_abo),
'summary' => $summary,
'add_only_mode' => $isAddOnlyMode,
])->render();
$html_comp = view('user.order.comp_product', array_merge($data, [
'cart_instance' => AboOrderCart::INSTANCE,
'base_comp_count' => $baseCompCount,
]))->render();
$html_summary = view('admin.abo._order_combined_summary', [
'summary' => $summary,
])->render();
$amount = $user_abo->getFormattedAmount();
return response()->json(['response' => true, 'data' => $data, 'html_cart' => $html_cart, 'html_comp' => $html_comp, 'amount' => $amount]);
return response()->json(['response' => true, 'data' => $data, 'html_cart' => $html_cart, 'html_comp' => $html_comp, 'html_summary' => $html_summary, 'amount' => $amount]);
}
}
}
@ -223,8 +263,8 @@ class AboController extends Controller
return \DataTables::eloquent($query)
->addColumn('add_card', function (Product $product) {
$tax_free = Yard::instance('shopping')->getUserTaxFree();
$price = $product->getFormattedPriceWith($tax_free, false, Yard::instance('shopping')->getUserCountry());
$tax_free = Yard::instance(AboOrderCart::INSTANCE)->getUserTaxFree();
$price = $product->getFormattedPriceWith($tax_free, false, Yard::instance(AboOrderCart::INSTANCE)->getUserCountry());
return '<button type="button" class="btn btn-sm btn-md-extra btn-secondary add-product-basket" data-product-id="'.$product->id.'" data-product-name="'.e($product->getLang('name')).'" data-product-price="'.$price.' &euro;">
<strong>&euro; '.$price.'</strong>&nbsp; +<span class="ion ion-md-cart"></span>
@ -244,10 +284,10 @@ class AboController extends Controller
return '<span class="no-line-break">'.$product->getFormattedPoints().'</span>';
})
->addColumn('price_net', function (Product $product) {
return '<span class="no-line-break">'.$product->getFormattedPriceWith(true, false, Yard::instance('shopping')->getUserCountry()).' €</span>'.'<span class="no-line-break">'.$product->getFormattedPriceCurrencyWith(true, true, Yard::instance('shopping')->getUserCountry()).'</span>';
return '<span class="no-line-break">'.$product->getFormattedPriceWith(true, false, Yard::instance(AboOrderCart::INSTANCE)->getUserCountry()).' €</span>'.'<span class="no-line-break">'.$product->getFormattedPriceCurrencyWith(true, true, Yard::instance(AboOrderCart::INSTANCE)->getUserCountry()).'</span>';
})
->addColumn('price_gross', function (Product $product) {
return '<span class="no-line-break">'.$product->getFormattedPriceWith(false, false, Yard::instance('shopping')->getUserCountry()).' €</span>'.'<span class="no-line-break">'.$product->getFormattedPriceCurrencyWith(true, true, Yard::instance('shopping')->getUserCountry()).'</span>';
return '<span class="no-line-break">'.$product->getFormattedPriceWith(false, false, Yard::instance(AboOrderCart::INSTANCE)->getUserCountry()).' €</span>'.'<span class="no-line-break">'.$product->getFormattedPriceCurrencyWith(true, true, Yard::instance(AboOrderCart::INSTANCE)->getUserCountry()).'</span>';
})
->addColumn('action', function (Product $product) {
return '<button class="btn btn-default btn-sm icon-btn md-btn-flat product-tooltip" title="details" data-modal="modal-lg"
@ -282,6 +322,11 @@ class AboController extends Controller
$this->checkPortalPermission($user_abo);
$ret = view('user.abo.modal_abo_show_products', compact('data', 'user_abo'))->render();
}
if ($data['action'] === 'abo-add-onetime') {
$user_abo = UserAbo::find($data['id']);
$this->checkPortalPermission($user_abo);
$ret = view('user.abo.modal_abo_onetime_products', compact('data', 'user_abo'))->render();
}
if ($data['action'] === 'abo_update_settings') {
$user_abo = UserAbo::find($data['id']);
$this->checkPortalPermission($user_abo);
@ -301,6 +346,127 @@ class AboController extends Controller
abort(404);
}
public function oneTime(AboOneTimeItemRequest $request, $view, $id)
{
$user_abo = UserAbo::findOrFail($id);
$this->checkPortalPermission($user_abo);
$isAddOnlyMode = AboHelper::isAddOnlyMode($user_abo, $view);
if (! AboHelper::isOneTimeWindowOpen($user_abo)) {
return response()->json([
'response' => false,
'message' => __('abo.onetime_window_closed'),
], 403);
}
AboOrderCart::initYard($user_abo);
$message = (new AboOneTimeService)->handleAction($user_abo, $request->validated());
AboOrderCart::initYard($user_abo);
AboOrderCart::makeOrderYard($user_abo);
$baseCompCount = Yard::instance(AboOrderCart::INSTANCE)->getNumComp();
AboOrderCart::addOneTimeItemsToYard($user_abo->fresh());
AboOrderCart::checkNumOfCompProducts($user_abo);
$user_abo = $user_abo->fresh();
$summary = AboOrderCart::getSplitSummary();
return response()->json([
'response' => ! $message,
'message' => $message ?: null,
'summary' => $summary,
'one_time_items' => $this->oneTimeItemsPayload($user_abo),
'html_onetime' => view('admin.abo._order_onetime_show', [
'user_abo' => $user_abo,
'summary' => $summary,
'error_message' => $message ?: false,
])->render(),
'html_abo' => view('admin.abo._order_abo_show', [
'user_abo' => $user_abo,
'split_mode' => true,
'summary' => $summary,
'only_show_products' => false,
'add_only_mode' => $isAddOnlyMode,
])->render(),
'html_summary' => view('admin.abo._order_combined_summary', [
'summary' => $summary,
])->render(),
'html_comp' => view('user.order.comp_product', [
'comp_products' => [],
'cart_instance' => AboOrderCart::INSTANCE,
'base_comp_count' => $baseCompCount,
])->render(),
]);
}
public function oneTimeDatatable($user_abo_id)
{
$user_abo = UserAbo::findOrFail($user_abo_id);
$this->checkPortalPermission($user_abo);
AboOrderCart::initYard($user_abo);
$query = Product::select('products.*')
->where('active', true)
->whereJsonContains('show_on', '3')
->orderBy('name', 'asc');
return \DataTables::eloquent($query)
->addColumn('add_card', function (Product $product) {
$tax_free = Yard::instance(AboOrderCart::INSTANCE)->getUserTaxFree();
$price = $product->getFormattedPriceWith($tax_free, false, Yard::instance(AboOrderCart::INSTANCE)->getUserCountry());
return '<button type="button" class="btn btn-sm btn-md-extra btn-warning add-onetime-product-basket" data-product-id="'.$product->id.'" data-product-name="'.e($product->getLang('name')).'" data-product-price="'.$price.' &euro;">
<strong>&euro; '.$price.'</strong>&nbsp; +<span class="ion ion-md-cart"></span>
</button>';
})
->addColumn('picture', function (Product $product) {
if (count($product->images)) {
return '<img class="img-fluid img-extra" alt="" src="'.route('product_image', [$product->images->first()->slug]).'">';
}
return '';
})
->addColumn('name', function (Product $product) {
return '<strong>'.$product->getLang('name').'</strong>';
})
->addColumn('points', function (Product $product) {
return '<span class="no-line-break">'.$product->getFormattedPoints().'</span>';
})
->addColumn('price_net', function (Product $product) {
return '<span class="no-line-break">'.$product->getFormattedPriceWith(true, false, Yard::instance(AboOrderCart::INSTANCE)->getUserCountry()).' €</span>';
})
->addColumn('price_gross', function (Product $product) {
return '<span class="no-line-break">'.$product->getFormattedPriceWith(false, false, Yard::instance(AboOrderCart::INSTANCE)->getUserCountry()).' €</span>';
})
->filterColumn('product', function ($query, $keyword) {
if ($keyword != '') {
$query->where('name', 'LIKE', '%'.$keyword.'%');
}
})
->orderColumn('name', 'name $1')
->orderColumn('number', 'number $1')
->rawColumns(['add_card', 'points', 'name', 'picture', 'price_net', 'price_gross'])
->make(true);
}
/**
* @return array<int, array<string, mixed>>
*/
private function oneTimeItemsPayload(UserAbo $user_abo): array
{
return $user_abo->one_time_items()->with('product')->get()->map(function (UserAboOneTimeItem $item) {
return [
'id' => $item->id,
'product_id' => $item->product_id,
'name' => $item->product?->getLang('name'),
'qty' => $item->qty,
'price' => $item->price,
'total' => round($item->price * $item->qty, 2),
];
})->all();
}
public function checkNeedBasisProduct($user_abo, $product, $order_item_id)
{
if (AboHelper::getAboShowOn($product) !== 'base') {