diff --git a/app/Http/Controllers/SalesController.php b/app/Http/Controllers/SalesController.php
index 87a30b6..0a0a4e8 100755
--- a/app/Http/Controllers/SalesController.php
+++ b/app/Http/Controllers/SalesController.php
@@ -194,225 +194,6 @@ class SalesController extends Controller
->make(true);
}
- /*public function users(){
-
- if(Request::get('reset') === 'filter'){
- return redirect(route('admin_sales_users'));
- }
- $data = [
-
- ];
- return view('admin.sales.users', $data);
- }
-
- public function usersDetail($id)
- {
- $ShoppingOrder = ShoppingOrder::find($id);
- if($ShoppingOrder->shipped === 0 && ! $ShoppingOrder->shipped_status_locked){
- $ShoppingOrder->shipped = 1;
- $ShoppingOrder->save();
- }
-
- $data = [
- 'shopping_order' => $ShoppingOrder,
- 'isAdmin' => true,
- 'isView' => 'sales_user',
- ];
- return view('admin.sales.user_detail', $data);
- }
-
- public function usersStore($id)
- {
- die("keine funktion");
- $data = [
- 'shopping_order' => ShoppingOrder::find($id),
- 'isAdmin' => true,
- ];
- return view('admin.sales.user_detail', $data);
- }
-
- public function usersDatatable(){
-
- $query = ShoppingOrder::with('shopping_user', 'shopping_payments')->select('shopping_orders.*')->where('shopping_orders.auth_user_id', '!=', NULL);
-
- return \DataTables::eloquent($query)
- ->addColumn('id', function (ShoppingOrder $ShoppingOrder) {
- return '';
- })
- ->addColumn('created_at', function (ShoppingOrder $ShoppingOrder) {
- return $ShoppingOrder->created_at->format("d.m.Y");
- })
- ->addColumn('txaction', function (ShoppingOrder $ShoppingOrder) {
- return Payment::getShoppingOrderBadge($ShoppingOrder);
- })
- ->addColumn('total_shipping', function (ShoppingOrder $ShoppingOrder) {
- return $ShoppingOrder->getFormattedTotalShipping()." €";
- })
- ->addColumn('payment', function (ShoppingOrder $ShoppingOrder) {
- return $ShoppingOrder->getLastShoppingPayment('getPaymentType');
- })
- ->addColumn('shipped', function (ShoppingOrder $ShoppingOrder) {
- return ''.$ShoppingOrder->getShippedType().'';
- })
- ->addColumn('payment_for', function (ShoppingOrder $ShoppingOrder) {
- return Payment::getPaymentForTypeBadge($ShoppingOrder);
- })
- ->addColumn('reference', function (ShoppingOrder $ShoppingOrder) {
- return $ShoppingOrder->getLastShoppingPayment('reference');
- })
- ->addColumn('orders', function (ShoppingOrder $ShoppingOrder) {
- return $ShoppingOrder->shopping_user ? $ShoppingOrder->shopping_user->orders : '';
- })
- ->orderColumn('id', 'id $1')
- ->orderColumn('txaction', 'txaction $1')
- ->orderColumn('payment_for', 'payment_for $1')
- ->orderColumn('shipped', 'shipped $1')
- ->orderColumn('total_shipping', 'total_shipping $1')
-
- ->rawColumns(['id', 'txaction', 'payment_for', 'shipped'])
- ->make(true);
- }
-
- public function customers()
- {
- if(Request::get('reset') === 'filter'){
- set_user_attr('filter_txaction', null);
- set_user_attr('filter_member_id', null);
- return redirect(route('admin_sales'));
- }
- $filter_members = ShoppingOrder::join('users', 'member_id', '=', 'users.id')->groupBy('member_id')->join('user_accounts', 'account_id', '=', 'user_accounts.id')->select('users.id', 'users.email', 'user_accounts.first_name', 'user_accounts.last_name')->get(); //->pluck('email', 'id')->unique()->toArray();
- $data = [
- 'filter_members' => $filter_members,
- ];
- return view('admin.sales.customers', $data);
- }
-
- public function customersDetail($id)
- {
- $ShoppingOrder = ShoppingOrder::find($id);
- if($ShoppingOrder->shipped === 0 && ! $ShoppingOrder->shipped_status_locked){
- $ShoppingOrder->shipped = 1;
- $ShoppingOrder->save();
- }
- $data = [
- 'shopping_order' => $ShoppingOrder,
- 'isAdmin' => true,
- 'isView' => 'sales_customer',
- ];
- return view('admin.sales.customer_detail', $data);
- }
-
- public function customersStore($id)
- {
- $data = Request::all();
- $change_member_error = false;
- if($data['action']==='shopping-order-change-member'){
- if(!isset($data['change_member_key']) || $data['change_member_key'] !== config('main.edit_data_pass')){
- $change_member_error = "Das Passwort ist falsch.";
- }else{
- //change
- $shopping_order = ShoppingOrder::findOrFail($data['id']);
- CustomerPriority::newMemberForOrder($shopping_order, $data['change_member_id'], $data['customer_set_member_for']);
- \Session()->flash('alert-save', true);
- return redirect(route('admin_sales_detail', [$shopping_order->id]));
- }
- }
- if($data['action']==='shopping-user-is-like-member'){
- if(!isset($data['change_member_key']) || $data['change_member_key'] !== config('main.edit_data_pass')){
- \Session()->flash('alert-error', 'Das Passwort ist falsch.');
- return redirect($data['back']);
- }else{
- if(!isset($data['is_like_shopping_user_id'])){
- \Session()->flash('alert-error', 'Keine Änderung ausgewählt');
- return redirect($data['back']);
- }
- $shopping_user = ShoppingUser::findOrFail($data['id']);
- $set_like_shopping_user = ShoppingUser::findOrFail($data['is_like_shopping_user_id']);
- $send_member_mail = isset($data['send_member_mail']) ? true : false;
- $change_shopping_user = isset($data['change_shopping_user']) ? true : false;
- //Mail send in setIsLike
- CustomerPriority::setIsLike($shopping_user, $set_like_shopping_user, $send_member_mail, $change_shopping_user);
- \Session()->flash('alert-save', true);
- return redirect($data['back']);
- }
- }
- $data = [
- 'change_member_error' => $change_member_error,
- 'shopping_order' => ShoppingOrder::find($id),
- 'isAdmin' => true,
- 'isView' => 'sales_customer',
- ];
- return view('admin.sales._detail', $data);
- }
-
- public function customersDatatable(){
-
- $query = ShoppingOrder::with('shopping_user')->select('shopping_orders.*')->where('shopping_orders.auth_user_id', NULL);
-
- set_user_attr('filter_txaction', Request::get('filter_txaction'));
- if(Request::get('filter_txaction') != ""){
- if(Request::get('filter_txaction') === 'NULL'){
- $query->where('txaction', '=', NULL);
-
- }else{
- $query->where('txaction', '=', Request::get('filter_txaction'));
- }
- }
- set_user_attr('filter_member_id', Request::get('filter_member_id'));
- if(Request::get('filter_member_id') != ""){
- $query->where('member_id', '=', Request::get('filter_member_id'));
- }
-
- return \DataTables::eloquent($query)
- ->addColumn('id', function (ShoppingOrder $ShoppingOrder) {
- return '';
- })
- ->addColumn('created_at', function (ShoppingOrder $ShoppingOrder) {
- return $ShoppingOrder->created_at->format("d.m.Y");
- })
- ->addColumn('txaction', function (ShoppingOrder $ShoppingOrder) {
- return Payment::getShoppingOrderBadge($ShoppingOrder);
- })
- ->addColumn('total_shipping', function (ShoppingOrder $ShoppingOrder) {
- return $ShoppingOrder->getFormattedTotalShipping()." €";
- })
- ->addColumn('payment', function (ShoppingOrder $ShoppingOrder) {
- return $ShoppingOrder->getLastShoppingPayment('getPaymentType');
- })
- ->addColumn('shipped', function (ShoppingOrder $ShoppingOrder) {
- return ''.$ShoppingOrder->getShippedType().'';
- })
- ->addColumn('payment_for', function (ShoppingOrder $ShoppingOrder) {
- return Payment::getPaymentForTypeBadge($ShoppingOrder);
- })
- ->addColumn('reference', function (ShoppingOrder $ShoppingOrder) {
- return $ShoppingOrder->getLastShoppingPayment('reference');
- })
- ->addColumn('member_id', function (ShoppingOrder $ShoppingOrder) {
- if($ShoppingOrder->member_id) {
- return $ShoppingOrder->member_id ? '' . $ShoppingOrder->member->getFullName() . '' : '';
- }
- if($ShoppingOrder->shopping_user && $ShoppingOrder->shopping_user->is_like){
- return '';
- }
- return '';
- })
-
- ->orderColumn('id', 'id $1')
- ->orderColumn('txaction', 'txaction $1')
- ->orderColumn('payment_for', 'payment_for $1')
- ->orderColumn('member_id', 'member_id $1')
- ->orderColumn('shipped', 'shipped $1')
- ->orderColumn('total_shipping', 'total_shipping $1')
- ->rawColumns(['id', 'member_id', 'txaction', 'user_shop_id', 'payment_for', 'shipped'])
- ->make(true);
- }*/
-
public function store()
{
$data = Request::all();
diff --git a/dev/product management /entwicklungsplan-aktualisiert-06-07-2026.md b/dev/product management /entwicklungsplan-aktualisiert-06-07-2026.md
index e1b8069..ebd88e3 100644
--- a/dev/product management /entwicklungsplan-aktualisiert-06-07-2026.md
+++ b/dev/product management /entwicklungsplan-aktualisiert-06-07-2026.md
@@ -282,6 +282,7 @@ Der Warenkorb kommt also **per Knopfdruck** (Fulfillment-Übergabe), nicht autom
| 20.07.2026 | **Review-Fix: Abwicklungs-Board (Rechte-Links, 500er-Cap, Kleinigkeiten)** | **(1) Copyreader-Links:** Bestellnummern in Abwicklung + API-Bestellungen verlinkten auf `admin_sales_detail` (admin ≥ 7) — für Versand-Mitarbeiter (copyreader, Kundenentscheid AP-33 Punkt 4) ein stiller Redirect nach `/home`; Link jetzt nur für Admins, sonst reiner Text. **(2) 500er-Cap:** Kacheln „Kommissionieren/Versandfertig" und deren Filter-Listen rechneten auf den neuesten 500 offenen Bestellungen — bei Rückstau wären ausgerechnet die **ältesten versandfertigen unsichtbar** gewesen, und der Hinweis-Banner verglich den Post-Filter-Count (erschien nie). Jetzt: Zähler + Filter über **alle** offenen Bestellungen, gekappt wird nur die Anzeige („neueste :n von :total" + „Alle anzeigen"). `whereDate()` → `whereBetween` (indexfähig, Vorbereitung Index-AP). **(3)** Packlisten-Toggle nutzt `data-order-id` statt Link-Text-Scraping; Gewicht über `formatNumber()`. | `OrderFulfillmentTest` +2 (copyreader ohne Link / admin mit Link; 18 grün) |
| 20.07.2026 | **Review-Fix: Rohstoffbestand — Badge vs. Rezeptur-Scope** | Der 17.07. gedrehte Default-Scope „recipe" blendet rezepturlose Rohstoffe aus, das Sidebar-Badge (`criticalIngredientCount`) zählt aber alle aktiven — ein kritischer Rohstoff außerhalb der Rezepturen ergab Badge „1", Seite „0 kritisch". Jetzt zeigt die recipe-Ansicht einen **Hinweis** („N kritische außerhalb der Herstellerrezepturen — Alle anzeigen"), sobald das Badge mehr zählt als die Liste; Empty-State-Text scope-abhängig korrigiert. Badge-Zählregel bewusst unverändert (global) — der Hinweis schließt die Lücke, ohne dass eine kritische Neuentwicklung ganz aus dem Badge fällt. | `RawMaterialStockTest` +1 (Hinweis im recipe-Scope, keiner im all-Scope; 20 grün) |
| 20.07.2026 | **Review-Fix: Performance — Indizes + schlanke API-Log-Seite** | **Indizes** (Migration `2026_07_20_100000`): `shopping_orders.shipped`/`shipped_at`/`created_at` (Board-Filter + Tages-Kacheln, vorher nur `txaction`/`shipping_option`) und `products.wp_number` (SKU-Auflösung der API-Bestellungen, vorher ohne Index). **API-Bestellungen-Seite:** lud für jede der bis zu 500 Zeilen das komplette JSON-`payload` und serialisierte es zusätzlich pretty-printed in Inline-`data`-Attribute (Mehr-MB-HTML). Jetzt: neue Spalte `positions_count` (Migration `2026_07_20_110000` mit Backfill), Liste selektiert **ohne payload**, der Inhalt lädt erst beim Öffnen des Modals über neuen `show`-Endpoint (`api-orders/{id}`); Cap-Hinweis „neueste :n von :total". | `ApiOrderLogTest` +2 (positions_count gesetzt, Inhalt on-demand; 11 grün) |
+| 20.07.2026 | **Review-Fix: toten Code entfernt (SalesController)** | Der Review fand die AP-33-`shipped_status_locked`-Guards in `usersDetail()`/`customersDetail()` wirkungslos, weil beide Methoden samt `users()`/`usersStore()`/`customersStore()` in einem großen `/*…*/`-Block (Zeilen 197-414) auskommentiert waren — die zugehörigen Routen ebenfalls (bereits auskommentiert in `web.php`). Der aktive Guard sitzt korrekt in `detail()`. Toten Methoden-Block und die auskommentierten Routen ersatzlos entfernt (−219 Zeilen); nichts Aktives referenzierte sie (Verlinkungen nur in `_bak`-Views). | Volle Suite unverändert **296 grün, 1107 Assertions** |
| _offen_ | AP-32 | Versand im System: DHL-Etiketten per API — **neu aufgenommen + Ansatz dokumentiert (§4, 15.07.2026)**; Klärungspunkte (DHL-Konto, Versandprodukte, Druck-Setup) für nächste Anforderungsrunde notiert. **Zwischenstand 17.07.2026: Brückenlösung aktiv** (Label aus dem Shop, s. Protokollzeile) — deckt den Versand-Flow ab, bis die DHL-Klärungen da sind | – |
| _offen_ | AP-14/15/16/17 | Audit-Trail, Blockrechte, 2FA, WaWi-Einstellungen | – |
diff --git a/routes/web.php b/routes/web.php
index bd21f61..9e0b822 100644
--- a/routes/web.php
+++ b/routes/web.php
@@ -430,17 +430,6 @@ Route::domain(config('app.domain'))->group(function () {
Route::get('/admin/sales/send_logistic_mail/{id}', [SalesController::class, 'sendLogisticMail'])->name('admin_sales_send_logistic_mail');
- /*
- Route::get('/admin/sales/users', [SalesController::class, 'users'])->name('admin_sales_users');
- Route::get('/admin/sales/users/detail/{id}', [SalesController::class, 'usersDetail'])->name('admin_sales_users_detail');
- Route::post('/admin/sales/users/detail/{id}', [SalesController::class, 'usersStore'])->name('admin_sales_users_detail');
- Route::get('/admin/sales/users/datatable', [SalesController::class, 'usersDatatable'])->name('admin_sales_users_datatable');
-
- Route::get('/admin/sales/customers', [SalesController::class, 'customers'])->name('admin_sales_customers');
- Route::get('/admin/sales/customers/detail/{id}', [SalesController::class, 'customersDetail'])->name('admin_sales_customers_detail');
- Route::post('/admin/sales/customers/detail/{id}', [SalesController::class, 'customersStore'])->name('admin_sales_customers_detail');
- Route::get('/admin/sales/customers/datatable', [SalesController::class, 'customersDatatable'])->name('admin_sales_customers_datatable');
- */
Route::post('/admin/sales/store', [SalesController::class, 'store'])->name('admin_sales_store');
Route::post('/admin/sales/invoice', [SalesController::class, 'invoice'])->name('admin_sales_invoice');
Route::post('/admin/sales/invoice/cancellation', [SalesController::class, 'invoiceCancellation'])->name('admin_sales_invoice_cancellation');