Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
| Total | |
14.55% |
8 / 55 |
|
0.00% |
0 / 7 |
CRAP | |
0.00% |
0 / 1 |
| CustomerController | |
14.55% |
8 / 55 |
|
0.00% |
0 / 7 |
86.51 | |
0.00% |
0 / 1 |
| index | |
57.14% |
8 / 14 |
|
0.00% |
0 / 1 |
1.08 | |||
| create | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
| store | |
0.00% |
0 / 26 |
|
0.00% |
0 / 1 |
20 | |||
| show | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
| edit | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
| update | |
0.00% |
0 / 8 |
|
0.00% |
0 / 1 |
2 | |||
| destroy | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
6 | |||
| 1 | <?php |
| 2 | |
| 3 | namespace App\Http\Controllers\admin; |
| 4 | |
| 5 | use App\Http\Controllers\Controller; |
| 6 | use App\Models\admin\Customer; |
| 7 | use App\Service\PosService; |
| 8 | use App\Traits\PosTrait; |
| 9 | use Illuminate\Http\Request; |
| 10 | use Illuminate\Http\Response; |
| 11 | use Illuminate\Support\Facades\Auth; |
| 12 | use Illuminate\Support\Facades\DB; |
| 13 | use Illuminate\Support\Facades\Log; |
| 14 | |
| 15 | class CustomerController extends Controller |
| 16 | { |
| 17 | use PosTrait; |
| 18 | |
| 19 | /** |
| 20 | * Display a listing of the resource. |
| 21 | * |
| 22 | * @return Response |
| 23 | */ |
| 24 | public function index(Request $request) |
| 25 | { |
| 26 | $search = trim((string) $request->input('search')); |
| 27 | |
| 28 | $customers = Customer::query() |
| 29 | ->when($search !== '', function ($q) use ($search) { |
| 30 | $q->where(function ($q) use ($search) { |
| 31 | $q->where('name', 'like', "%{$search}%") |
| 32 | ->orWhere('phone', 'like', "%{$search}%") |
| 33 | ->orWhere('email', 'like', "%{$search}%") |
| 34 | ->orWhere('customer_code', 'like', "%{$search}%"); |
| 35 | }); |
| 36 | }) |
| 37 | ->orderByDesc('id') |
| 38 | ->paginate(20) |
| 39 | ->withQueryString(); |
| 40 | |
| 41 | return view('admin.people.customer-list', compact('customers')); |
| 42 | } |
| 43 | |
| 44 | /** |
| 45 | * Show the form for creating a new resource. |
| 46 | * |
| 47 | * @return Response |
| 48 | */ |
| 49 | public function create() |
| 50 | { |
| 51 | return view('admin.people.add-customer'); |
| 52 | } |
| 53 | |
| 54 | /** |
| 55 | * Store a newly created resource in storage. |
| 56 | * |
| 57 | * @return Response |
| 58 | */ |
| 59 | public function store(Request $request) |
| 60 | { |
| 61 | // BUG FIX: the "Add Customer" modal now only collects Name/Phone/Address |
| 62 | // (see admin.modal.customer-create) — 'email' => 'required' would have |
| 63 | // rejected every submission from that form since it no longer has an |
| 64 | // email field at all. `phone` matches the real DB constraint (NOT NULL |
| 65 | // on `customers.phone`), the rest stay nullable/back-compat for any |
| 66 | // other caller of this same route (e.g. admin/people/add-customer.blade.php). |
| 67 | $data['request'] = $request->validate([ |
| 68 | 'name' => 'required', |
| 69 | 'phone' => 'required', |
| 70 | 'email' => 'nullable|email', |
| 71 | 'country_id' => 'nullable', |
| 72 | 'city_id' => 'nullable', |
| 73 | 'address' => 'nullable', |
| 74 | 'description' => 'nullable', |
| 75 | 'picture' => 'nullable', |
| 76 | // Wholesale/customer-group pricing (Phase 7) — only ever posted |
| 77 | // when the feature is on (see admin.modal.customer-create). |
| 78 | 'customer_group_id' => 'nullable|exists:customer_groups,id', |
| 79 | ]); |
| 80 | try { |
| 81 | DB::beginTransaction(); |
| 82 | $data['value'] = ['customer_code' => 'c-' . rand(0, 9999), 'created_by' => Auth::user()->id]; |
| 83 | $data = array_merge($data['value'], $data['request']); |
| 84 | $customer = Customer::query()->create($data); |
| 85 | if ($request->hasFile('picture')) { |
| 86 | $data['picture'] = $this->FileProcessing($request->file('picture'), PosService::CUSTOMER_IMAGE, 429, 500); |
| 87 | $customer->update(['pictures' => $data['picture']]); |
| 88 | } |
| 89 | DB::commit(); |
| 90 | if ($request->prev == 'admin.sale.create') { |
| 91 | // BUG FIX: this used to redirect to `admin.sale.back` |
| 92 | // (SaleController::saleBackPage) — a dead leftover from the old |
| 93 | // per-customer SaleCard cart system. It renders this exact same |
| 94 | // `admin.sales.create` view but never passes a `stocks` variable, |
| 95 | // which the redesigned POS page's product grid requires — so |
| 96 | // adding a customer from the POS page's "+" button crashed the |
| 97 | // page instead of just returning you to the POS with the new |
| 98 | // customer selected. Go back to the real POS route instead, with |
| 99 | // the new customer preselected via SaleController::create()'s |
| 100 | // `?customer=` support. |
| 101 | return redirect()->route('admin.sale.create', ['customer' => $customer->id]); |
| 102 | } |
| 103 | |
| 104 | return redirect()->back()->with('success', 'Category Successfully Inserted'); |
| 105 | } catch (\Throwable $e) { |
| 106 | DB::rollBack(); |
| 107 | |
| 108 | // BUG FIX: was `dd(...)` — crashed with a raw debug dump instead of |
| 109 | // a usable error, same pattern already fixed elsewhere in this app. |
| 110 | Log::error('Customer store failed: ' . $e->getMessage(), ['exception' => $e]); |
| 111 | |
| 112 | return redirect()->back()->withInput()->with('error', 'Something went wrong while saving the customer. Please try again.'); |
| 113 | } |
| 114 | } |
| 115 | |
| 116 | /** |
| 117 | * Display the specified resource. |
| 118 | * |
| 119 | * @return Response |
| 120 | */ |
| 121 | public function show(Customer $customer) |
| 122 | { |
| 123 | // |
| 124 | } |
| 125 | |
| 126 | /** |
| 127 | * Show the form for editing the specified resource. |
| 128 | * |
| 129 | * @return Response |
| 130 | */ |
| 131 | public function edit(Customer $customer) |
| 132 | { |
| 133 | // |
| 134 | } |
| 135 | |
| 136 | /** |
| 137 | * Update the specified resource in storage. |
| 138 | * |
| 139 | * FIX: this used to be an empty stub — the "Edit" button on the |
| 140 | * customer list rendered but silently did nothing, since the form it |
| 141 | * points to (admin.modal.customer-create, in edit mode) submitted to |
| 142 | * this route and got a blank 200 response back. Implemented for real, |
| 143 | * matching the same validation as store(). |
| 144 | * |
| 145 | * @return Response |
| 146 | */ |
| 147 | public function update(Request $request, Customer $customer) |
| 148 | { |
| 149 | $data = $request->validate([ |
| 150 | 'name' => 'required|string|max:255', |
| 151 | 'phone' => 'required|string|max:30', |
| 152 | 'address' => 'nullable|string|max:500', |
| 153 | 'customer_group_id' => 'nullable|exists:customer_groups,id', |
| 154 | ]); |
| 155 | |
| 156 | $customer->update($data); |
| 157 | |
| 158 | return redirect()->back()->with('success', 'Customer updated successfully.'); |
| 159 | } |
| 160 | |
| 161 | /** |
| 162 | * Remove the specified resource from storage. |
| 163 | * |
| 164 | * FIX: also an empty stub before — the "Delete" button did nothing. |
| 165 | * A customer with existing sales/orders is blocked from deletion |
| 166 | * (would otherwise orphan those records / break the FK) and gets an |
| 167 | * honest error instead of a silent no-op. |
| 168 | * |
| 169 | * @return Response |
| 170 | */ |
| 171 | public function destroy(Customer $customer) |
| 172 | { |
| 173 | if ($customer->sales()->exists()) { |
| 174 | return redirect()->back()->with('error', 'This customer has existing orders and cannot be deleted.'); |
| 175 | } |
| 176 | |
| 177 | $customer->delete(); |
| 178 | |
| 179 | return redirect()->back()->with('success', 'Customer deleted successfully.'); |
| 180 | } |
| 181 | } |