with('person') ->when($request->input('search'), function ($que, $search) { $que->whereHas('person', function ($q) use ($search) { $q->where('full_name', 'ilike', '%'.$search.'%'); }); }) ->where('active', 1) ->addSelect([ // Number of client cases for this client that have at least one active contract 'cases_with_active_contracts_count' => DB::query() ->from('client_cases') ->join('contracts', 'contracts.client_case_id', '=', 'client_cases.id') ->selectRaw('COUNT(DISTINCT client_cases.id)') ->whereColumn('client_cases.client_id', 'clients.id') ->whereNull('contracts.deleted_at') ->whereExists(function ($q) { $q->from('contract_segment') ->whereColumn('contract_segment.contract_id', 'contracts.id') ->where('contract_segment.active', true); }), // Sum of account balances for active contracts that belong to this client's cases 'active_contracts_balance_sum' => DB::query() ->from('contracts') ->join('accounts', 'accounts.contract_id', '=', 'contracts.id') ->selectRaw('COALESCE(SUM(accounts.balance_amount), 0)') ->whereExists(function ($q) { $q->from('client_cases') ->whereColumn('client_cases.id', 'contracts.client_case_id') ->whereColumn('client_cases.client_id', 'clients.id'); }) ->whereNull('contracts.deleted_at') ->whereExists(function ($q) { $q->from('contract_segment') ->whereColumn('contract_segment.contract_id', 'contracts.id') ->where('contract_segment.active', true); }), ]) ->orderByDesc('created_at'); return Inertia::render('Client/Index', [ 'clients' => $query ->paginate(15) ->withQueryString(), 'filters' => $request->only(['search']), ]); } public function show(Client $client, Request $request) { $data = $client::query() ->with(['person' => fn ($que) => $que->with(['addresses', 'phones', 'bankAccounts', 'emails'])]) ->findOrFail($client->id); $types = [ 'address_types' => \App\Models\Person\AddressType::all(), 'phone_types' => \App\Models\Person\PhoneType::all(), ]; return Inertia::render('Client/Show', [ 'client' => $data, 'client_cases' => $data->clientCases() ->with(['person', 'client.person']) ->when($request->input('search'), fn ($que, $search) => $que->whereHas( 'person', fn ($q) => $q->where('full_name', 'ilike', '%'.$search.'%') )) ->addSelect([ 'active_contracts_count' => \DB::query() ->from('contracts') ->selectRaw('COUNT(*)') ->whereColumn('contracts.client_case_id', 'client_cases.id') ->whereNull('contracts.deleted_at') ->whereExists(function ($q) { $q->from('contract_segment') ->whereColumn('contract_segment.contract_id', 'contracts.id') ->where('contract_segment.active', true); }), 'active_contracts_balance_sum' => \DB::query() ->from('contracts') ->join('accounts', 'accounts.contract_id', '=', 'contracts.id') ->selectRaw('COALESCE(SUM(accounts.balance_amount), 0)') ->whereColumn('contracts.client_case_id', 'client_cases.id') ->whereNull('contracts.deleted_at') ->whereExists(function ($q) { $q->from('contract_segment') ->whereColumn('contract_segment.contract_id', 'contracts.id') ->where('contract_segment.active', true); }), ]) ->where('active', 1) ->orderByDesc('created_at') ->paginate(15) ->withQueryString(), 'types' => $types, 'filters' => $request->only(['search']), ]); } public function contracts(Client $client, Request $request) { $data = $client->load(['person' => fn ($q) => $q->with(['addresses', 'phones', 'bankAccounts', 'emails'])]); $from = $request->input('from'); $to = $request->input('to'); $search = $request->input('search'); $contractsQuery = \App\Models\Contract::query() ->whereHas('clientCase', function ($q) use ($client) { $q->where('client_id', $client->id); }) ->with([ 'clientCase:id,uuid,person_id', 'clientCase.person:id,full_name', 'segments' => function ($q) { $q->wherePivot('active', true)->select('segments.id', 'segments.name'); }, 'account:id,accounts.contract_id,balance_amount', ]) ->select(['id', 'uuid', 'reference', 'start_date', 'client_case_id']) ->whereNull('deleted_at') ->when($from || $to, function ($q) use ($from, $to) { if (! empty($from)) { $q->whereDate('start_date', '>=', $from); } if (! empty($to)) { $q->whereDate('start_date', '<=', $to); } }) ->when($search, function ($q) use ($search) { $q->where(function ($inner) use ($search) { $inner->where('reference', 'ilike', '%'.$search.'%') ->orWhereHas('clientCase.person', function ($p) use ($search) { $p->where('full_name', 'ilike', '%'.$search.'%'); }); }); }) ->orderByDesc('start_date'); $types = [ 'address_types' => \App\Models\Person\AddressType::all(), 'phone_types' => \App\Models\Person\PhoneType::all(), ]; return Inertia::render('Client/Contracts', [ 'client' => $data, 'contracts' => $contractsQuery->paginate(20)->withQueryString(), 'filters' => $request->only(['from', 'to', 'search']), 'types' => $types, ]); } public function store(Request $request) { DB::transaction(function () use ($request) { $address = $request->input('address'); $phone = $request->input('phone'); $person = \App\Models\Person\Person::create([ 'nu' => rand(100000, 200000), 'first_name' => $request->input('first_name'), 'last_name' => $request->input('last_name'), 'full_name' => $request->input('full_name'), 'gender' => null, 'birthday' => null, 'tax_number' => $request->input('tax_number'), 'social_security_number' => $request->input('social_security_number'), 'description' => $request->input('description'), 'group_id' => 1, 'type_id' => 2, ]); $person->addresses()->create([ 'address' => $address['address'], 'country' => $address['country'], 'type_id' => $address['type_id'], ]); $person->phones()->create([ 'nu' => $phone['nu'], 'country_code' => $phone['country_code'], 'type_id' => $phone['type_id'], ]); $person->client()->create(); }); // \App\Models\Person\PersonAddress::create($address); return to_route('client'); } public function update(Client $client, Request $request) { return to_route('client.show', $client); } /** * Emergency endpoint: if the linked person record is missing (hard deleted) or soft deleted, * create a new minimal Person and re-point all related child records (emails, phones, addresses, bank accounts, * client cases) from the old person_id to the new one, then update the client itself. */ public function emergencyCreatePerson(Client $client, Request $request) { $oldPersonId = $client->person_id; // If person exists and is not trashed, abort – nothing to do /** @var \App\Models\Person\Person|null $existing */ $existing = \App\Models\Person\Person::withTrashed()->find($oldPersonId); if ($existing && ! $existing->trashed()) { return redirect()->back()->with('flash', [ 'type' => 'info', 'message' => 'Person already exists – emergency creation not needed.', ]); } $data = $request->validate([ 'full_name' => ['nullable', 'string', 'max:255'], 'first_name' => ['nullable', 'string', 'max:255'], 'last_name' => ['nullable', 'string', 'max:255'], 'tax_number' => ['nullable', 'string', 'max:99'], 'social_security_number' => ['nullable', 'string', 'max:99'], 'description' => ['nullable', 'string', 'max:500'], ]); // Provide sensible fallbacks. $fullName = $data['full_name'] ?? trim(($data['first_name'] ?? '').' '.($data['last_name'] ?? '')); if ($fullName === '') { $fullName = 'Unknown Person'; } $newPerson = null; \DB::transaction(function () use ($oldPersonId, $client, $fullName, $data, &$newPerson) { $newPerson = \App\Models\Person\Person::create([ 'nu' => null, // boot event will generate 'first_name' => $data['first_name'] ?? null, 'last_name' => $data['last_name'] ?? null, 'full_name' => $fullName, 'gender' => null, 'birthday' => null, 'tax_number' => $data['tax_number'] ?? null, 'social_security_number' => $data['social_security_number'] ?? null, 'description' => $data['description'] ?? 'Emergency recreated person', 'group_id' => 1, 'type_id' => 2, ]); // Re-point related records referencing the old (missing) person id $tables = [ 'emails', 'person_phones', 'person_addresses', 'bank_accounts', 'client_cases', ]; foreach ($tables as $table) { \DB::table($table)->where('person_id', $oldPersonId)->update(['person_id' => $newPerson->id]); } // Finally update the client itself (only this one; avoid touching other potential clients) $client->person_id = $newPerson->id; $client->save(); }); return redirect()->back()->with('flash', [ 'type' => 'success', 'message' => 'New person created and related records re-linked.', 'person_uuid' => $newPerson?->uuid, ]); } }