295 lines
12 KiB
PHP
295 lines
12 KiB
PHP
<?php
|
||
|
||
namespace App\Http\Controllers;
|
||
|
||
use App\Models\Client;
|
||
use DB;
|
||
use Illuminate\Http\Request;
|
||
use Inertia\Inertia;
|
||
|
||
class ClientController extends Controller
|
||
{
|
||
public function index(Client $client, Request $request)
|
||
{
|
||
$query = $client::query()
|
||
->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($request->integer('perPage', 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', 'client'])])
|
||
->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($request->integer('perPage', 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');
|
||
$segmentId = $request->input('segment');
|
||
|
||
$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.'%');
|
||
});
|
||
});
|
||
})
|
||
->when($segmentId, function ($q) use ($segmentId) {
|
||
$q->whereHas('segments', function ($s) use ($segmentId) {
|
||
$s->where('segments.id', $segmentId)
|
||
->where('contract_segment.active', true);
|
||
});
|
||
})
|
||
->orderByDesc('start_date');
|
||
|
||
$segments = \App\Models\Segment::orderBy('name')->get(['id', 'name']);
|
||
|
||
$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($request->integer('perPage', 20))->withQueryString(),
|
||
'filters' => $request->only(['from', 'to', 'search', 'segment']),
|
||
'segments' => $segments,
|
||
'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,
|
||
]);
|
||
}
|
||
}
|