@extends('layouts.enterprise') @section('title', 'Emails') @section('content') @php $isAccountant = request()->routeIs('accountant.*'); $indexRoute = $isAccountant ? route('accountant.incomes.emails.index') : route('admin.incomes.emails.index'); $detailRouteName = $isAccountant ? 'accountant.incomes.emails.show' : 'admin.incomes.emails.show'; $destroyRouteName = $isAccountant ? 'accountant.incomes.emails.destroy' : 'admin.incomes.emails.destroy'; $suggestionRouteName = $isAccountant ? 'accountant.incomes.emails.items.suggestion' : 'admin.incomes.emails.items.suggestion'; $generateInvoiceRoute = $isAccountant ? route('accountant.incomes.generate_invoice') : route('admin.incomes.generate_invoice'); $hasActiveFilters = (($filters['q'] ?? '') !== '') || (($filters['status'] ?? 'all') !== 'all') || (($filters['groupable'] ?? 'all') !== 'all') || (($filters['client_id'] ?? null) !== null); $groupableGroups = $emailBuckets['groupableGroups'] ?? collect(); $nonGroupableEmails = $emailBuckets['nonGroupableEmails'] ?? collect(); $bucketCounts = $emailBuckets['counts'] ?? [ 'all' => $emails->total(), 'groupable_emails' => 0, 'groupable_groups' => 0, 'non_groupable' => 0, ]; @endphp

Emails inbound

Aquí revisamos los correos transaccionales que llegan por webhook, sus líneas del recibo y el dato que puede ayudarte a decidir si una solicitud es agrupable por cliente + RC.

@if(session('ok'))
{{ session('ok') }}
@endif @if($errors->any())
@endif

Webhook inbound

Esta es la URL que debe recibir el POST del proveedor de correo inbound.

Header: {{ $webhookHeaderName }}
{{ $webhookUrl }}
Body esperado: `eventType = EMAIL_INBOUND` Se guarda body + campos parseados + líneas La agrupación visible toma el motor configurado en el cliente
Filtros @if($hasActiveFilters) Activos @endif
Limpiar Resultados: {{ $emails->total() }}
Disponibles {{ $bucketCounts['all'] }} Agrupables {{ $bucketCounts['groupable_emails'] }} No agrupables {{ $bucketCounts['non_groupable'] }}
Grupos activos: {{ $bucketCounts['groupable_groups'] }} No agrupables: {{ $bucketCounts['non_groupable'] }}
@forelse($emails as $email) @php $usageSummary = (array) ($email->usage_summary ?? []); $usageStatus = (string) ($usageSummary['status'] ?? 'available'); $isActionable = in_array($usageStatus, ['available', 'partial'], true); $usageFolios = collect($usageSummary['folios'] ?? [])->filter()->values(); $usageJns = collect($usageSummary['jns'] ?? [])->filter()->values(); $jnPreview = (array) ($email->jn_preview ?? []); $previewJns = collect($jnPreview['jns'] ?? [])->filter()->values(); $groupingPreview = (array) ($email->grouping_preview ?? []); $operationalGroupCount = (int) ($email->operational_group_count ?? 0); $emailItems = $email->items ?? collect(); $singleEmailItem = $emailItems->count() === 1 ? $emailItems->first() : null; $singleItemCandidates = $singleEmailItem ? collect($email->candidate_options[(int) $singleEmailItem->id] ?? [])->values() : collect(); $firstInboundItem = $emailItems->first(); $inboundItemSummary = $firstInboundItem?->description ? \Illuminate\Support\Str::limit(preg_replace('/\s+/', ' ', (string) $firstInboundItem->description) ?: (string) $firstInboundItem->description, 90) : 'Sin item detectado'; $inboundItemAmount = $emailItems->sum(fn ($item) => (float) ($item->total ?? 0)); $resolvedInboundAmount = $inboundItemAmount > 0 ? $inboundItemAmount : (float) ($email->total_amount ?? 0); @endphp @empty @endforelse
Recibido Cliente OC RC cliente Tipo RC Entrega Item inbound Monto inbound Líneas Total Grupo JN Uso Acciones
{{ optional($email->received_at)->format('Y-m-d H:i') ?: '-' }}
{{ $email->sender_email ?: '-' }}
{{ $email->client?->name ?: ($email->client_name ?: '-') }}
{{ $email->client_rfc ?: '-' }}
{{ $email->purchase_order_number ?: '-' }}
{{ $email->purchase_order_date_raw ?: '-' }}
{{ $email->purchase_receipt_number ?: '-' }}
{{ $email->purchase_receipt_date_raw ?: '-' }}
{{ $groupingPreview['label'] ?? 'Sin tipo RC detectado' }}
{{ $groupingPreview['engine_label'] ?? 'Sin agrupación' }}
{{ $email->delivery_office_code ?: '-' }}
{{ $email->delivery_office_name ?: ($email->delivery_address ?: '-') }}
{{ $inboundItemSummary }}
@if($emailItems->count() > 1)
+{{ $emailItems->count() - 1 }} línea(s) más
@endif
{{ $resolvedInboundAmount > 0 ? '$'.number_format($resolvedInboundAmount, 2) : '-' }} {{ $email->items_count ?: $email->line_items_count }} {{ $email->total_amount !== null ? '$'.number_format((float) $email->total_amount, 2) : '-' }} @if($operationalGroupCount > 1 && $isActionable) Grupo de {{ $operationalGroupCount }}
{{ $email->operational_group_label ?: 'Listo para agrupar' }}
@elseif(!$isActionable) Usado
Fuera de sugerencias activas
@else Solo
Sin grupo operativo
@endif
{{ $jnPreview['label'] ?? 'Sin JN detectado' }} @if($previewJns->isNotEmpty())
{{ $previewJns->take(3)->implode(', ') }}
@if($previewJns->count() > 3)
+{{ $previewJns->count() - 3 }} más
@endif @else
Sin solicitud sugerida todavía
@endif @if($isActionable && $singleEmailItem)
@csrf @method('PATCH') @if($singleItemCandidates->isEmpty())
Abre el detalle para revisar opciones manuales.
@endif
@elseif($isActionable)
Edita por línea en el detalle del email.
@endif
{{ $usageSummary['label'] ?? 'Disponible' }} @if($usageFolios->isNotEmpty())
Vista previa: {{ $usageFolios->implode(', ') }}
@endif @if($usageJns->isNotEmpty())
JN: {{ $usageJns->implode(', ') }}
@endif @if(($usageSummary['count_available'] ?? 0) > 0)
Líneas disponibles: {{ (int) ($usageSummary['count_available'] ?? 0) }}
@endif
Ver detalle
@csrf @method('DELETE') Eliminar
Aún no hay correos inbound registrados.
{{ $emails->onEachSide(1)->links('vendor.pagination.enterprise') }}
Correos agrupables: {{ $bucketCounts['groupable_emails'] }} Grupos activos: {{ $bucketCounts['groupable_groups'] }}
@if($groupableGroups->isEmpty())

Todavía no hay grupos listos

Aquí iremos mostrando los correos que sí caen en el mismo cliente + tipo de RC + moneda según el motor de agrupación del cliente.

@else @endif
Disponibles pero sin grupo activo: {{ $bucketCounts['non_groupable'] }}
@forelse($nonGroupableEmails as $email) @php $jnPreview = (array) ($email->jn_preview ?? []); $previewJns = collect($jnPreview['jns'] ?? [])->filter()->values(); $groupingPreview = (array) ($email->grouping_preview ?? []); $emailItems = $email->items ?? collect(); $firstInboundItem = $emailItems->first(); $inboundItemSummary = $firstInboundItem?->description ? \Illuminate\Support\Str::limit(preg_replace('/\s+/', ' ', (string) $firstInboundItem->description) ?: (string) $firstInboundItem->description, 90) : 'Sin item detectado'; $resolvedInboundAmount = (float) ($emailItems->sum(fn ($item) => (float) ($item->total ?? 0)) ?: ($email->total_amount ?? 0)); @endphp @empty @endforelse
Recibido Cliente RC cliente Tipo RC Item inbound Monto inbound Entrega JN sugerido Acciones
{{ optional($email->received_at)->format('Y-m-d H:i') ?: '-' }} {{ $email->client?->name ?: ($email->client_name ?: '-') }} {{ $email->purchase_receipt_number ?: '-' }} {{ $groupingPreview['label'] ?? 'Sin tipo RC detectado' }}
{{ $inboundItemSummary }}
@if($emailItems->count() > 1)
+{{ $emailItems->count() - 1 }} línea(s) más
@endif
{{ $resolvedInboundAmount > 0 ? '$'.number_format($resolvedInboundAmount, 2) : '-' }} {{ trim(collect([$email->delivery_office_code, $email->delivery_office_name ?: $email->delivery_address])->filter()->implode(' | ')) ?: '-' }} {{ $previewJns->isNotEmpty() ? $previewJns->implode(', ') : 'Sin solicitud sugerida todavía' }} Ver detalle
No hay correos disponibles fuera de grupo.
@endsection @push('styles') @endpush