@extends('layouts.app', ['title' => 'Expediente · '.$client->name]) @section('header_actions') Volver @endsection @section('content') @php $phoneClean = preg_replace('/\D+/', '', $client->phone ?? ''); $waPhone = strlen($phoneClean) === 10 ? '52'.$phoneClean : $phoneClean; $isAdmin = auth()->user()?->isAdmin(); @endphp {{-- ============ ENCABEZADO DEL EXPEDIENTE ============ --}}
{{ strtoupper(mb_substr($client->name, 0, 1)) }}

{{ $client->name }}

{{-- Estado del cliente (Negociación/Cerrado…) oculto a petición --}}
ID #{{ $client->id }} · {{ $client->rfc ?: 'sin RFC' }} @if($client->advisor) · {{ $client->advisor->name }}@endif
@if($client->phone) {{ $client->phone }}@endif {{-- Email de contacto oculto a peticion --}} @if($client->source) {{ $client->source }}@endif @if($client->interest) {{ $client->interest }}@endif
@if($client->phone)WhatsApp@endif {{-- Boton Email oculto a peticion --}}
{{-- ============ KPIs ============ --}}
${{ number_format($client->total_purchased, 0) }}
Total
${{ number_format($client->total_paid, 0) }}
Abonado
${{ number_format($client->total_balance, 0) }}
Saldo
{{-- ============ TABS ============ --}}
{{-- ===== TAB: VINCULACIÓN DE INMUEBLES ===== --}}
Terrenos vinculados
Terrenos vinculados al cliente con esquema de pago y expediente
@if($client->accounts->count())
@foreach($client->accounts as $account) @php $statusClass = $account->status === 'Liquidado' ? 'green' : ($account->status === 'Vencido' ? 'red' : 'amber'); $accountProjectLogo = $account->lote?->project?->logo_path ?? $account->lote?->manzana?->project?->logo_path ?? $account->property?->project?->logo_path ?? null; @endphp
{{ $account->status }} @if($isAdmin)
@endif
@if($accountProjectLogo && project_logo_url($accountProjectLogo)) Logo del proyecto @endif
{{ $account->unit_label }}
Cuenta #{{ $account->id }}{{ $account->lote?->superficie_m2 ? ' · '.number_format((float) $account->lote->superficie_m2, 0).' m²' : ($account->property?->location ? ' · '.$account->property->location : '') }}
@if($account->payment_type)
{{ $account->plan_label }} @if($account->payment_type === 'Plazo' && $account->monthly_payment) ${{ number_format((float) $account->monthly_payment, 2) }}/mes @endif
@endif
Total
${{ number_format((float) $account->total_price, 0) }}
Abonado
${{ number_format((float) $account->paid_amount, 0) }}
Saldo
${{ number_format($account->balance, 0) }}
Progreso de pago{{ $account->progress }}%
@php // Sólo abonos activos: los cancelados no cuentan como movimiento. $activePayments = $account->payments->filter(fn ($p) => ($p->status ?? '') !== 'Cancelado'); $lastActivePayment = $activePayments->sortByDesc('payment_date')->first(); @endphp @if($activePayments->count())
{{ $activePayments->count() }} abono(s) · último: {{ optional($lastActivePayment?->payment_date)->format('d/m/Y') ?? '—' }}
@endif
@if($account->payment_type === 'Plazo') @endif
@endforeach
@else
Aún no hay terrenos vinculados a este cliente.
Usa "Vincular Terreno" para asociar proyecto, manzana, lote y esquema de pago.
@endif
{{-- ===== TAB: APARTADOS ===== --}} {{-- Los apartados NO se muestran en el expediente: viven en el módulo Apartados hasta que se convierten en vinculación. Ya convertidos, aparecen en la pestaña Vinculación. --}} {{-- ===== TAB: MOVIMIENTOS ===== --}}
Historial de movimientos
Solo movimientos de lotes: vinculaciones y abonos separados por terreno.
@if($timeline->count()) @php $movGroups = []; foreach($timeline as $mItem){ $movGroups[$mItem['lote_key'] ?? 'general'] = $mItem['lote_label'] ?? 'General'; } @endphp
@foreach($movGroups as $mk => $mlabel) @endforeach
@foreach($timeline as $m) @php $movSearch = trim(implode(' ', array_filter([ $m['title'] ?? null, $m['detail'] ?? null, $m['reference'] ?? null, $m['lote_label'] ?? null, isset($m['amount']) ? number_format((float) $m['amount'], 2, '.', '') : null, ]))); $movDate = $m['date'] ? optional($m['date'])->format('Y-m-d') : ''; @endphp
{{ $m['title'] }} @if(! is_null($m['amount'])) ${{ number_format($m['amount'], 2) }} @endif
{{ $m['detail'] }}
@if(($m['kind'] ?? '') === 'abono' && ($m['payment_status'] ?? '') === 'Cancelado')
Abono cancelado{{ !empty($m['cancellation_reason']) ? ': '.$m['cancellation_reason'] : '' }}
@endif @if(!empty($m['receipt'])) @endif @if(($m['kind'] ?? '') === 'abono' && ($m['payment_status'] ?? '') === 'Aplicado' && !empty($m['payment_id']))
@csrf @method('PATCH')
@endif
{{ optional($m['date'])->format('d/m/Y H:i') ?? '—' }} · {{ optional($m['date'])->diffForHumans() }}
@endforeach
No hay movimientos con esos filtros.
@else
Sin movimientos registrados todavía.
@endif
{{-- ===== TAB: CONTRATOS ===== --}}
Contratos del cliente
Separados estrictamente por terreno/lote vinculado.
@php $unlinkedContracts = $client->contracts->filter(function ($contract) use ($client) { return ! $client->accounts->contains(function ($account) use ($contract) { $sameLote = $account->lote_id && $contract->lote_id && (int) $account->lote_id === (int) $contract->lote_id; $sameProperty = $account->property_id && $contract->property_id && (int) $account->property_id === (int) $contract->property_id; return $sameLote || $sameProperty; }); })->values(); $contractFolders = $client->accounts->map(function ($account) use ($client, $unlinkedContracts) { $linkedContracts = $client->contracts->filter(function ($contract) use ($account) { $sameLote = $account->lote_id && $contract->lote_id && (int) $account->lote_id === (int) $contract->lote_id; $sameProperty = $account->property_id && $contract->property_id && (int) $account->property_id === (int) $contract->property_id; return $sameLote || $sameProperty; })->values(); // Los contratos antiguos/generales sin lote_id/property_id se muestran en todas las vinculaciones. $contracts = $linkedContracts ->merge($unlinkedContracts) ->unique('id') ->values(); return [ 'account' => $account, 'name' => $account->unit_label, 'contracts' => $contracts, 'general_count' => $unlinkedContracts->count(), ]; }); $linkedContractTotal = $contractFolders->sum(fn ($folder) => $folder['contracts']->count()); @endphp @if($client->accounts->count())
Los contratos se generan automáticamente al vincular el terreno. Cada carpeta muestra los contratos propios de ese terreno/lote y también los contratos generales aplicables.
@foreach($contractFolders as $folder) @php $account = $folder['account']; $folderName = $folder['name']; $folderContracts = $folder['contracts']; @endphp
{{ $folderName }}
Cuenta #{{ $account->id }} · {{ $account->payment_type ?: 'Sin forma de pago' }} · {{ $folderContracts->count() }} contrato(s) de este terreno
{{ $folderContracts->count() }} documento(s)
@if($folderContracts->count())
@foreach($folderContracts as $contract) @endforeach
DocumentoDatosCreado
{{ $contract->title }}
{{ $contract->template?->name ?? 'Documento libre' }} @if(empty($contract->lote_id) && empty($contract->property_id)) · Contrato general @endif
@if($contract->data_refreshed_at) {{ $contract->data_refreshed_at->format('d/m/Y H:i') }} @else Generados al enrolar @endif {{ optional($contract->created_at)->format('d/m/Y') }}
Imprimir / PDF @if($contract->template && $contract->status !== 'Firmado' && ! $contract->signed_at)
@csrf
@endif @if($contract->body_before_refresh)
@csrf
@endif
@else
Este terreno aún no tiene contratos enrolados. Enrola contratos seleccionando esta vinculación.
@endif
@endforeach @else
Primero vincula un terreno/lote al cliente. Después podrás enrolar contratos por terreno.
@endif
{{-- ===== TAB: EXPEDIENTE / DOCUMENTOS ===== --}}
Documentos del cliente
Documentación personal del cliente en la nube (INE, comprobantes, contratos…)
@if($client->documents->count())
@endif
{{-- Subir documentos --}}
@csrf
{{-- Listado de documentos --}} @if($client->documents->count()) {{-- Form de borrado masivo: los checkboxes lo referencian con form="doc-bulk-form" --}}
@csrf @method('DELETE')
@foreach($client->documents as $doc) @php $ext = strtolower(pathinfo($doc->name, PATHINFO_EXTENSION)); $fi = str_contains($ext,'pdf') ? 'fa-file-pdf' : (str_contains($ext,'xls') ? 'fa-file-excel' : (str_contains($ext,'doc') ? 'fa-file-word' : (str_contains($ext,'zip') ? 'fa-file-zipper' : 'fa-file'))); $docViewUrl = route('clients.documents.show', $doc); $docPrintUrl = route('clients.documents.print', $doc); @endphp
{{-- Checkbox de selección (visible sólo en modo seleccionar) --}} {{-- Botón imprimir (siempre visible) --}}
@if($doc->type === 'image') @else @endif
{{ $doc->name }}
{{ $doc->category }} · {{ $doc->size_human }}
@endforeach
@else
Sin documentos. Sube la documentación personal del cliente.
@endif
{{-- ===== TAB: DATOS ===== --}}
Datos del cliente
Nombre
{{ $client->name }}
Teléfono / Celular
{{ $client->phone ?: '—' }}
Asesor
{{ $client->advisor?->name ?? 'Sin asignar' }}
RFC
{{ $client->rfc ?: '—' }}
CURP
{{ $client->curp ?: '—' }}
Estado civil
{{ $client->estado_civil ?: '—' }}
Ocupación
{{ $client->ocupacion ?: '—' }}
Domicilio
{{ $client->domicilio ?: '—' }}
@if($client->calle || $client->colonia || $client->municipio || $client->estado)
Calle y número
{{ trim(($client->calle ?: '').' '.($client->numero ? '#'.$client->numero : '')) ?: '—' }}
Colonia
{{ $client->colonia ?: '—' }}
Código Postal
{{ $client->codigo_postal ?: '—' }}
Municipio
{{ $client->municipio ?: '—' }}
Estado
{{ $client->estado ?: '—' }}
@endif
@endsection @push('styles') @endpush @section('modals') @if($isAdmin && $client->accounts->count()) @foreach($client->accounts as $account) @endforeach @endif {{-- Editar cliente --}} {{-- Vincular lote/inmueble (crear cuenta) --}} {{-- Registrar abono por cuenta --}} @foreach($client->accounts as $account) @endforeach @endsection @push('scripts') @endpush @push('scripts') @endpush