authorize('viewAny', Customer::class); $perPage = $request->has('limit') ? $request->limit : 10; $filters = $request->all(); $customers = Customer::query() ->with('creator') ->whereCompany() ->applyFilters($filters) ->paginateData($perPage); $this->withAccountSummaries($customers); $companyTotal = Customer::whereCompany()->count(); return CustomerResource::collection($customers)->additional([ 'meta' => ['customer_total_count' => $companyTotal], ]); } /** * File a new contact. * * Address blocks and custom-field values ride along inside the service's * transaction; the request object decides whether either was supplied. */ public function store(CustomerRequest $request) { $this->authorize('create', Customer::class); $created = $this->customerService->create( $request->customerAttributes(), $request->shippingAddress(), $request->billingAddress(), $request->customFields(), ); $this->withAccountSummaries([$created]); return new CustomerResource($created); } public function show(Customer $customer) { $this->authorize('view', $customer); $this->withAccountSummaries([$customer]); return new CustomerResource($customer); } /** * Overwrite a contact. * * Two rules live in the service rather than the request: a currency change * is refused once any document exists, and the address rows are replaced * wholesale — so an update carrying no address block at all leaves the * contact with none. */ public function update(CustomerRequest $request, Customer $customer) { $this->authorize('update', $customer); $saved = $this->customerService->update( $customer, $request->customerAttributes(), $request->shippingAddress(), $request->billingAddress(), $request->customFields(), ); $this->withAccountSummaries([$saved]); return new CustomerResource($saved); } /** * Erase a batch of contacts together with everything filed against them — * estimates, invoices, payments, expenses, recurring invoices, addresses — * in a single transaction. * * The submitted ids were checked against the customers table globally, but * are narrowed to the active company here, so an id belonging to somebody * else clears validation and is then quietly dropped from the batch. */ public function delete(DeleteCustomersRequest $request) { $this->authorize('delete multiple customers'); $targets = Customer::whereCompany()->whereIn('id', $request->ids)->pluck('id'); $this->customerService->delete($targets); return response()->json(['success' => true]); } /** * Hang the account summaries on the models that are about to be rendered. * * A paginator cannot be handed over as-is: cast to an array it yields the * envelope, not the rows. Everything else — a collection, or a one-element * array wrapping a single model — goes straight through. */ private function withAccountSummaries(mixed $customers): void { if ($customers instanceof LengthAwarePaginator) { $customers = $customers->getCollection(); } $this->customerStatementQuery->hydrateAccountSummaries($customers); } }