@php $serviceStatusOptions = ['Pendiente', 'Confirmada', 'Realizada', 'Cancelada']; $normalizeServiceStatus = function ($status) { return match ((string) $status) { 'Confirmado', 'Confirmada' => 'Confirmada', 'Completada', 'Realizada' => 'Realizada', 'Cancelado', 'Cancelada' => 'Cancelada', 'Cedida', 'Pendiente' => 'Pendiente', default => 'Pendiente', }; }; $serviceStatusLabel = $normalizeServiceStatus($serenata->service_status); $paymentBadge = match ((string) $serenata->payment_status) { 'Pagado' => 'bg-label-success', 'Abono parcial' => 'bg-label-info', 'Cancelada sin cobro' => 'bg-label-danger', default => 'bg-label-warning', }; $serviceBadge = match($serviceStatusLabel) { 'Realizada' => 'bg-label-success', 'Confirmada' => 'bg-label-success', 'Pendiente' => 'bg-label-warning', 'Cancelada' => 'bg-label-danger', default => 'bg-label-info', }; $isEmbedded = $embedded ?? false; $effectiveCommission = (float) $serenata->commission_amount; if ($effectiveCommission <= 0) { $effectiveCommission = max(0, (float) $serenata->profit_amount); } $effectiveProfit = (float) $serenata->profit_amount; $formatCop = fn ($value) => '$' . number_format((float) $value, 0, ',', '.'); $plannedPrefix = '[PLAN]'; $actualCustomerCharge = $serenata->actual_customer_charge !== null ? (float) $serenata->actual_customer_charge : null; $effectiveCustomerCharge = $actualCustomerCharge ?? (float) $serenata->total_charged; $paymentRows = collect($payments ?? [])->map(function ($payment) use ($plannedPrefix) { $note = trim((string) ($payment->note ?? '')); $isRefund = str_contains($note, '[REFUND]'); $isPlannedPayment = !$isRefund && str_starts_with($note, $plannedPrefix); return [ 'model' => $payment, 'is_refund' => $isRefund, 'is_planned' => $isPlannedPayment, 'is_applied' => !$isRefund && !$isPlannedPayment, ]; })->values(); $totalPaid = (float) collect($payments ?? [])->filter(function ($payment) use ($plannedPrefix) { $note = trim((string) ($payment->note ?? '')); $isRefund = str_contains($note, '[REFUND]'); return $note === '' || !str_starts_with($note, $plannedPrefix) || $isRefund; })->sum('amount'); $appliedPaymentsCount = $paymentRows->where('is_applied', true)->count(); $refundPaymentsCount = $paymentRows->where('is_refund', true)->count(); $plannedPaymentsCount = $paymentRows->where('is_planned', true)->count(); $pendingAmount = max(0, $effectiveCustomerCharge - $totalPaid); $paymentDiff = round($totalPaid - $effectiveCustomerCharge, 2); $hasPaymentMismatch = abs($paymentDiff) >= 1; $cededOverview = $cededOverview ?? [ 'status' => 'equilibrado', 'label' => 'Sin excedente', 'amount' => 0, 'signed_amount' => 0, ]; $externalPartnerMovements = collect($externalPartnerMovements ?? []); $externalMovementCount = $externalPartnerMovements->count(); $externalPartnerBalanceSummary = $externalPartnerBalanceSummary ?? []; $externalPartnerMovementTypeOptions = $externalPartnerMovementTypeOptions ?? []; $externalPartnerMovementDirectionOptions = $externalPartnerMovementDirectionOptions ?? []; $externalPartnerMovementSettlementOptions = $externalPartnerMovementSettlementOptions ?? []; $externalPartnerNameLookup = collect($externalPartnerOptions ?? []) ->mapWithKeys(fn ($item) => [(int) ($item['id'] ?? 0) => (string) ($item['name'] ?? '')]) ->merge( $externalPartnerMovements->mapWithKeys(function ($movement) { $partnerId = (int) ($movement->external_partner_id ?? 0); if ($partnerId <= 0) { return []; } return [ $partnerId => (string) ($movement->externalPartner->name ?? ('Externo #' . $partnerId)), ]; }) ) ->all(); $savedCededBreakdown = collect(old('ceded_cost_breakdown') ? (json_decode((string) old('ceded_cost_breakdown'), true) ?: []) : ($serenata->ceded_cost_breakdown ?? [])); $savedCededBreakdownLookup = $savedCededBreakdown ->filter(fn ($item) => is_array($item)) ->mapWithKeys(function ($item) { $source = (string) ($item['source'] ?? ''); $index = array_key_exists('index', $item) ? (string) $item['index'] : 'x'; if ($source === '') { return []; } return [$source . ':' . $index => $item]; }); $giftItems = collect($serenata->gift_items ?? [])->filter(function ($item) { return !(bool) data_get($item, 'is_deleted', false); }); $giftIncluded = $giftItems->filter(function ($item) { $type = strtolower((string) data_get($item, 'type', 'included')); return $type !== 'additional'; })->values(); $giftAdditional = $giftItems->filter(function ($item) { $type = strtolower((string) data_get($item, 'type', 'included')); return $type === 'additional'; })->values(); $formatGiftLine = function ($item): string { $name = (string) data_get($item, 'name', 'Obsequio'); $qty = max(1, (int) data_get($item, 'quantity', 1)); $unit = (float) data_get($item, 'unit_price', 0); return $name . " x{$qty} ($" . number_format($unit, 0, ',', '.') . ')'; }; $activeLineItems = collect($lineItems ?? [])->filter(fn ($item) => empty($item['is_deleted']))->values(); $deletedLineItemsCount = collect($lineItems ?? [])->filter(fn ($item) => !empty($item['is_deleted']))->count(); $costCategorySummary = collect([ [ 'key' => 'artist', 'label' => 'Artistas', 'icon' => 'tabler-music', 'tone' => 'primary', 'items' => $activeLineItems->where('source', 'artist'), ], [ 'key' => 'gift', 'label' => 'Obsequios', 'icon' => 'tabler-gift', 'tone' => 'warning', 'items' => $activeLineItems->where('source', 'gift'), ], [ 'key' => 'other_cost', 'label' => 'Otros costos', 'icon' => 'tabler-tool', 'tone' => 'info', 'items' => $activeLineItems->where('source', 'other_cost'), ], ])->map(function ($group) { $items = collect($group['items'] ?? []); return [ 'key' => $group['key'], 'label' => $group['label'], 'icon' => $group['icon'], 'tone' => $group['tone'], 'count' => $items->count(), 'total' => (float) $items->sum(fn ($item) => (float) ($item['total'] ?? 0)), ]; })->values(); $includedText = $giftIncluded->isNotEmpty() ? $giftIncluded->map($formatGiftLine)->implode(', ') : 'Ninguno'; $additionalText = $giftAdditional->isNotEmpty() ? $giftAdditional->map($formatGiftLine)->implode(', ') : 'Ninguno'; $shareLines = [ "SERVICIO {$serenata->code}", 'Grupo: ' . ($serenata->mariachiGroup?->name ?: 'Sin grupo'), "Cliente: {$serenata->payer_name}", 'Correo: ' . ($serenata->payer_email ?: '-'), "Teléfono 1: {$serenata->formatted_contact_phone_1}", 'Teléfono 2: ' . ($serenata->formatted_contact_phone_2 ?: '-'), "Dirección: {$serenata->service_address}", 'País: ' . ($serenata->service_country ?: 'Colombia'), 'Departamento: ' . ($serenata->service_department ?: '-'), "Ciudad/Municipio: {$serenata->locality}", 'Localidad: ' . ($serenata->service_locality ?: '-'), 'Sector/UPZ: ' . ($serenata->service_sector ?: '-'), "Barrio: {$serenata->neighborhood}", 'Fecha: ' . optional($serenata->event_date)->format('d/m/Y'), 'Horario estimado: ' . $serenata->eventWindowLabel(), "De parte de: {$serenata->from_name}", "Para quién: {$serenata->to_name}", 'Motivo: ' . ($serenata->reason ?: '-'), "Obsequios incluidos: {$includedText}", "Obsequios adicionales: {$additionalText}", 'Precio total: $' . number_format((float) ($serenata->total_charged ?? 0), 0, ',', '.'), ]; if (!empty($serenata->address_additional)) { $shareLines[] = "Dirección adicional: {$serenata->address_additional}"; } if (!empty($serenata->service_private_note)) { $shareLines[] = "Nota para el servicio: {$serenata->service_private_note}"; } $serenataShareText = implode("\n", $shareLines); $phoneDigits = $serenata->contact_phone_1_whatsapp_digits; $confirmMessage = implode("\n", [ "Hola {$serenata->payer_name}, te habla Camila Serenatas.", 'Queremos reconfirmar tu servicio:', 'Fecha: ' . optional($serenata->event_date)->format('d/m/Y'), 'Horario estimado: ' . $serenata->eventWindowLabel(), "Dirección: {$serenata->service_address}", 'Localidad: ' . ($serenata->service_locality ?: '-'), 'Sector: ' . ($serenata->service_sector ?: '-'), "Barrio: {$serenata->neighborhood}", "Ciudad/Municipio: {$serenata->locality}", '', '¿Todo sigue en pie? Muchas gracias.' ]); $waConfirmUrl = $phoneDigits !== '' ? 'https://wa.me/' . $phoneDigits . '?text=' . rawurlencode($confirmMessage) : null; $googleMapsQuery = implode(', ', array_filter([ trim((string) ($serenata->service_address ?? '')), trim((string) ($serenata->address_additional ?? '')), trim((string) ($serenata->service_sector ?? '')), trim((string) ($serenata->service_locality ?? '')), trim((string) ($serenata->neighborhood ?? '')), trim((string) ($serenata->service_department ?? '')), trim((string) ($serenata->locality ?? '')), trim((string) ($serenata->service_country ?? 'Colombia')), ])); $googleMapsFallbackUrl = $googleMapsQuery !== '' ? 'https://www.google.com/maps/search/?api=1&query=' . rawurlencode($googleMapsQuery) : null; $googleMapsAddressUrl = !empty($googleMapsAddressUrl ?? '') ? $googleMapsAddressUrl : $googleMapsFallbackUrl; $addressShareText = $googleMapsAddressUrl ?: trim((string) ($serenata->service_address ?? '')); $currentExternalPartnerName = $serenata->externalPartner?->name ?: 'Sin externo asignado'; $cededOverviewStatus = (string) ($cededOverview['status'] ?? 'equilibrado'); $cededOverviewBadge = match ($cededOverviewStatus) { 'por_cobrar' => 'bg-label-success', 'yo_debo' => 'bg-label-warning', default => 'bg-label-secondary', }; $eventDateHeadline = '-'; if ($serenata->event_date) { $eventDateValue = $serenata->event_date->copy(); $eventDateLabel = mb_strtoupper($eventDateValue->locale('es')->translatedFormat('d F'), 'UTF-8'); $eventTimeLabel = $serenata->eventWindowLabel(); if ((int) $eventDateValue->year !== (int) now()->year) { $eventDateLabel .= ' ' . $eventDateValue->format('Y'); } $eventDateHeadline = trim($eventDateLabel . ($eventTimeLabel !== '' ? (((int) $eventDateValue->year !== (int) now()->year ? ' | ' : ' ') . $eventTimeLabel) : '')); } $cededBreakdownItems = collect($lineItems ?? []) ->filter(fn ($item) => empty($item['is_deleted'])) ->values() ->map(function ($item) use ($savedCededBreakdownLookup) { $lookupKey = (string) ($item['source'] ?? '') . ':' . (string) ($item['index'] ?? '0'); $saved = $savedCededBreakdownLookup->get($lookupKey, []); $quantity = max(1, (int) ($saved['quantity'] ?? $item['quantity'] ?? 1)); $unitPrice = (float) ($saved['unit_price'] ?? $item['unit_price'] ?? 0); $included = array_key_exists('included', $saved) ? (bool) $saved['included'] : true; return [ 'source' => (string) ($item['source'] ?? ''), 'index' => (int) ($item['index'] ?? 0), 'name' => (string) ($saved['name'] ?? $item['name'] ?? 'Ítem'), 'category' => (string) ($item['category'] ?? ''), 'quantity' => $quantity, 'unit_price' => $unitPrice, 'included' => $included, 'total' => $included ? ($quantity * $unitPrice) : 0, ]; }) ->values(); $savedTransportItem = $savedCededBreakdown->first(fn ($item) => is_array($item) && (($item['source'] ?? '') === 'transport')); $cededTransportInitial = (float) old('ceded_transport_amount', $savedTransportItem['unit_price'] ?? $serenata->ceded_transport_amount); $cededBreakdownJson = $cededBreakdownItems ->push([ 'source' => 'transport', 'index' => 0, 'name' => 'Transporte manual', 'category' => 'Transporte', 'quantity' => 1, 'unit_price' => $cededTransportInitial, 'included' => $cededTransportInitial > 0, 'total' => $cededTransportInitial > 0 ? $cededTransportInitial : 0, ]) ->values() ->toJson(JSON_UNESCAPED_UNICODE); @endphp
Detalle de serenata

Serenata {{ $serenata->code }}

@if($serenata->mariachiGroup) {{ $serenata->mariachiGroup->name }} @endif
{{ $serenata->payment_status }} {{ $serviceStatusLabel }} @if($serenata->is_ceded) Cedida @if(($cededOverview['status'] ?? 'equilibrado') !== 'equilibrado') {{ $cededOverview['label'] }}: ${{ number_format((float) ($cededOverview['amount'] ?? 0), 0, ',', '.') }} @endif @endif
Evento {{ $eventDateHeadline }}
Creado {{ $serenata->created_at?->format('d/m/Y H:i') }}
@if($serviceStatusLabel === 'Cancelada') @endif
@if($waConfirmUrl) Reconfirmar @else @endif @unless($isEmbedded) Volver @endunless
Estado del servicio

Actualiza aquí el seguimiento operativo de la serenata.

@csrf @method('PATCH')
@if (session('status'))
{{ session('status') }}
@endif @if ($errors->any())
@endif @if($hasPaymentMismatch)
Advertencia: registrado ${{ number_format($totalPaid, 0, ',', '.') }} frente a un cobro real esperado de ${{ number_format($effectiveCustomerCharge, 0, ',', '.') }}. @if($paymentDiff > 0) Excedente: ${{ number_format($paymentDiff, 0, ',', '.') }} @else Pendiente: ${{ number_format(abs($paymentDiff), 0, ',', '.') }} @endif
@endif
Precio pactado {{ $formatCop($serenata->total_charged) }} Valor cotizado al cliente
Cobro real {{ $formatCop($effectiveCustomerCharge) }} Base actual del ingreso
Me costó {{ $formatCop($serenata->base_cost) }} Costo interno del servicio
Me quedó {{ $effectiveProfit < 0 ? '- ' : '' }}{{ $formatCop(abs($effectiveProfit)) }} {{ $effectiveProfit < 0 ? 'El servicio quedó por debajo del costo' : 'Margen actual de la serenata' }}
Información de la serenata
Fecha y hora {{ $eventDateHeadline }}
Estado {{ $serviceStatusLabel }}
Grupo {{ $serenata->mariachiGroup?->name ?: 'Sin grupo' }}
De parte de {{ $serenata->from_name }}
Para quién {{ $serenata->to_name }}
Motivo {{ $serenata->reason ?: '-' }}
Cliente
{{ strtoupper(mb_substr($serenata->payer_name, 0, 1)) }}
{{ $serenata->payer_name }}
Código {{ $serenata->code }} Servicio para {{ $serenata->to_name }}
@if(!empty($serenata->customer_id)) Ver ficha @endif @if(!empty($serenata->formatted_contact_phone_1)) {{ $serenata->formatted_contact_phone_1 }} @endif
Contacto principal {{ $serenata->formatted_contact_phone_1 }} Principal
Contacto alterno {{ $serenata->formatted_contact_phone_2 ?: '-' }} Respaldo
Correo {{ $serenata->payer_email ?: '-' }} Seguimiento
@csrf @method('PATCH')
Dirección
Ubicación principal @if($googleMapsAddressUrl) {{ $serenata->service_address }} @else {{ $serenata->service_address }} @endif
Barrio {{ $serenata->neighborhood }} {{ $serenata->locality }}
País {{ $serenata->service_country ?: 'Colombia' }}
Departamento {{ $serenata->service_department ?: '-' }}
Ciudad/Municipio {{ $serenata->locality }}
@if(!empty($serenata->service_locality))
Localidad {{ $serenata->service_locality }}
@endif @if(!empty($serenata->service_sector))
Sector / UPZ {{ $serenata->service_sector }}
@endif
Barrio {{ $serenata->neighborhood }}
@if(!empty($serenata->address_additional))
Adicional {{ $serenata->address_additional }}
@endif
Nota Interna
@csrf @method('PATCH')
Detalle de costos
@foreach($costCategorySummary as $group)
{{ $group['label'] }} {{ $formatCop($group['total']) }} {{ $group['count'] }} {{ \Illuminate\Support\Str::plural('ítem', $group['count']) }}
@endforeach
Total activo {{ $formatCop($serenata->base_cost) }} {{ $activeLineItems->count() }} {{ \Illuminate\Support\Str::plural('ítem', $activeLineItems->count()) }}
@if($deletedLineItemsCount > 0)
{{ $deletedLineItemsCount }} {{ \Illuminate\Support\Str::plural('ítem eliminado', $deletedLineItemsCount) }} aún visible en la tabla para trazabilidad.
@endif
@forelse($lineItems as $item) @empty @endforelse
Categoría Ítem Precio Cant. Total Acciones
{{ $item['category'] }} @if(!empty($item['is_deleted'])) {{ $item['name'] }} Eliminado @if(!empty($item['delete_reason']))
Motivo: {{ $item['delete_reason'] }}
@endif @else {{ $item['name'] }} @endif
${{ number_format((float) $item['unit_price'], 0, ',', '.') }} {{ $item['quantity'] }} ${{ number_format((float) $item['total'], 0, ',', '.') }} @if(empty($item['is_deleted']))
@csrf @method('PATCH')
Confirma eliminación del ítem.
@else
@csrf @method('PATCH')
@endif
Sin ítems seleccionados en esta serenata.

Obsequios que pueden ir incluidos en el servicio.

@csrf
@foreach(($giftOptions ?? []) as $gift) @if(($gift['type'] ?? 'included') === 'included')
{{ $gift['name'] }}
${{ number_format((float) ($gift['price'] ?? 0), 0, ',', '.') }}
@endif @endforeach

Obsequios que se suman como adicional al servicio.

@csrf
@foreach(($giftOptions ?? []) as $gift) @if(($gift['type'] ?? 'included') !== 'included')
{{ $gift['name'] }}
${{ number_format((float) ($gift['price'] ?? 0), 0, ',', '.') }}
@endif @endforeach

Selecciona artistas para esta serenata y ajusta precio cuando aplique.

@csrf
@foreach(($artistOptions ?? []) as $artist)
{{ $artist['name'] }}
${{ number_format((float) ($artist['price'] ?? 0), 0, ',', '.') }}
@endforeach

Agrega otros costos del servicio.

@csrf
@foreach(($otherCostOptions ?? []) as $cost)
{{ $cost['name'] }}
${{ number_format((float) ($cost['price'] ?? 0), 0, ',', '.') }}
@endforeach
Precio base {{ $formatCop($serenata->base_price) }}
Costo del servicio {{ $formatCop($serenata->base_cost) }}
Descuento @if((float) $serenata->discount_amount > 0) - {{ $formatCop($serenata->discount_amount) }} @else $0 @endif
Me quedó {{ $effectiveProfit < 0 ? '- ' : '' }}{{ $formatCop(abs($effectiveProfit)) }}
Pactado final {{ $formatCop($serenata->total_charged) }}
@if((float) ($serenata->commission_rate ?? 0) > 0)
Comisión {{ $formatCop($effectiveCommission) }}
@endif
@if((float) $serenata->discount_amount > 0)
Nombre Valor
Descuento aplicado - ${{ number_format((float) $serenata->discount_amount, 0, ',', '.') }}
@endif
Historial del servicio
Estado actual {{ $serviceStatusLabel }}
Pago {{ $serenata->payment_status }}
@if(!empty($serenata->cancellation_reason))
Motivo de cancelación {{ $serenata->cancellation_reason }}
@endif
@forelse(($serviceStatusHistories ?? collect()) as $history) @empty @endforelse
Cambio Usuario Fecha
{{ $history->to_service_status ? $normalizeServiceStatus($history->to_service_status) : '-' }} {{ $history->changedByUser->name ?? 'Sistema' }} {{ $history->created_at?->format('d/m/Y H:i:s') }}
{{ $serviceStatusLabel }} Sistema {{ $serenata->created_at?->format('d/m/Y H:i:s') }}
Pago ({{ $paymentRows->count() }})
Estado de pago
{{ $serenata->payment_status }} {{ $appliedPaymentsCount }} aplicados
Cobro real esperado {{ $formatCop($effectiveCustomerCharge) }} Base actual del servicio
Cobrado neto {{ $formatCop($totalPaid) }} {{ $refundPaymentsCount }} reembolsos registrados
Pendiente {{ $formatCop($pendingAmount) }} {{ $plannedPaymentsCount }} movimientos planeados
Cobro real al cliente

Úsalo solo si el cliente terminó pagando distinto a lo pactado.

@csrf @method('PATCH')
Si lo dejas vacío, usamos el precio pactado.
@if($serenata->actual_customer_charge_recorded_at) Última actualización: {{ $serenata->actual_customer_charge_recorded_at?->format('d/m/Y H:i') }} @else Sin ajustes manuales aún. @endif
Registrar movimiento

Añade aquí un pago real que sí entró o quedó registrado.

@csrf
Historial de movimientos

Resumen de pagos aplicados, pendientes planeados y reembolsos.

@forelse($paymentRows as $paymentRow) @php $payment = $paymentRow['model']; $isRefund = (bool) $paymentRow['is_refund']; $isPlannedPayment = (bool) $paymentRow['is_planned']; @endphp @empty @endforelse
Fecha Método Estado Referencia Importe Registrado por
{{ $payment->paid_at?->format('d/m/Y H:i') }} {{ $payment->payment_method_name }} @if($isRefund) Reembolso @elseif($isPlannedPayment) Pendiente @else Aplicado @endif {{ $payment->transaction_reference ?: '-' }} {{ $formatCop($payment->amount) }} {{ $payment->registeredBy->name ?? 'Sistema' }}
Aún no hay pagos registrados.
@csrf @method('PATCH')
Actividad y logs
  • Serenata creada ({{ $serenata->code }})
    {{ $serenata->created_at?->format('d/m/Y H:i') }}

    Registro inicial en el sistema.

  • Evento programado
    {{ optional($serenata->event_date)->format('d/m/Y') }} {{ $serenata->eventWindowLabel() }}

    Motivo: {{ $serenata->reason }}

  • Estado actual: {{ $serviceStatusLabel }}
    {{ $serenata->updated_at?->format('d/m/Y H:i') }}
    @if(!empty($serenata->cancellation_reason))

    Motivo cancelación: {{ $serenata->cancellation_reason }}

    @elseif($serenata->is_ceded)

    {{ $cededOverview['label'] ?? 'Sin excedente' }}: ${{ number_format((float) ($cededOverview['amount'] ?? 0), 0, ',', '.') }}

    @endif
@forelse($statusHistories->take(50) as $history) @empty @endforelse
Movimiento Usuario Fecha
{{ $history->note ?: 'Cambio de estado' }} {{ $history->changedByUser->name ?? 'Sistema' }} {{ $history->created_at?->format('d/m/Y H:i:s') }}
Sin movimientos registrados.
Cesión operativa
Estado
{{ $serenata->is_ceded ? 'Cedida activa' : 'No cedida' }} @if($serenata->is_ceded) {{ $cededOverview['label'] ?? 'Sin excedente' }} @endif
Externo actual {{ $currentExternalPartnerName }} Responsable operativo
Tarifa externa {{ $formatCop($serenata->ceded_total_cost) }} Valor acordado actual
Ganancia / ajuste {{ $formatCop((float) ($cededOverview['amount'] ?? 0)) }} Base: {{ $formatCop($effectiveCustomerCharge) }}
@csrf @method('PATCH')
is_ceded))>

Activa este flujo cuando otra agrupación realiza el servicio y necesitas controlar tarifa, ajuste y conciliación.

Mismos costos de la serenata, con ajustes rápidos

Tomamos artistas, obsequios y otros costos del servicio actual. Solo cambias lo que Patricia te cobre distinto.

Artistas
$0
Obsequios
$0
Otros costos
$0
Transporte manual
$0
@php $artistBreakdownItems = $cededBreakdownItems->where('source', 'artist')->values(); $giftBreakdownItems = $cededBreakdownItems->where('source', 'gift')->values(); $otherBreakdownItems = $cededBreakdownItems->where('source', 'other_cost')->values(); @endphp

Ajusta solo los músicos cuyo valor cambie con el externo.

@forelse($artistBreakdownItems as $item)
Total: ${{ number_format((float) $item['total'], 0, ',', '.') }}
@empty
No hay artistas activos en esta serenata.
@endforelse

Si el externo mantiene los mismos obsequios, no cambias nada. Si algo sube o se quita, lo ajustas aquí.

@forelse($giftBreakdownItems as $item)
Total: ${{ number_format((float) $item['total'], 0, ',', '.') }}
@empty
No hay obsequios activos en esta serenata.
@endforelse

Aquí entran costos extras del servicio que el externo te cobre también.

@forelse($otherBreakdownItems as $item)
Total: ${{ number_format((float) $item['total'], 0, ',', '.') }}
@empty
No hay otros costos activos en esta serenata.
@endforelse

Si el externo te cobra transporte aparte y no está en los ítems del servicio, lo registras aquí.

Ejemplo: si todo sigue igual pero Patricia te subió $10.000 de transporte, lo pones aquí.
Si la dejas en 0, usamos la suma automática de los ítems de arriba.
ceded_settled_at)))>
@if(!empty($serenata->ceded_settled_at)) Última conciliación: {{ $serenata->ceded_settled_at?->format('d/m/Y H:i') }} @endif
Movimientos y saldos con externos

Aquí registras cesiones, cambios pendientes, compensaciones con serenata, ajustes en efectivo y penalidades.

@if(!empty($externalPartnerBalanceSummary))
@foreach($externalPartnerBalanceSummary as $partnerId => $summary) @php $balanceStatus = (string) ($summary['status'] ?? 'equilibrado'); $balanceBadge = match ($balanceStatus) { 'a_favor' => 'bg-label-success', 'en_contra' => 'bg-label-danger', default => 'bg-label-secondary', }; @endphp
{{ $externalPartnerNameLookup[(int) $partnerId] ?? ('Externo #' . $partnerId) }}
Saldo global con este externo
{{ $summary['label'] ?? 'Saldo' }}
${{ number_format((float) ($summary['amount'] ?? 0), 0, ',', '.') }}
@endforeach
@endif
@csrf
Registrar movimiento con externo

Controla aquí deudas, compensaciones y ajustes de una forma separada a la cesión principal.

Selecciona el externo y el sistema te lo mostrará en lenguaje simple.
Historial de movimientos

{{ $externalMovementCount }} registros vinculados a esta serenata.

@forelse($externalPartnerMovements as $movement) @empty @endforelse
Fecha Externo Movimiento Dirección Valor Compensación Promesa Referencia Nota
{{ $movement->created_at?->format('d/m/Y H:i') }} {{ $movement->externalPartner->name ?? 'Sin externo' }} {{ $externalPartnerMovementTypeOptions[$movement->movement_type] ?? ucfirst(str_replace('_', ' ', (string) $movement->movement_type)) }} {{ $externalPartnerMovementDirectionOptions[$movement->direction] ?? $movement->direction }} ${{ number_format((float) $movement->amount, 0, ',', '.') }} {{ $externalPartnerMovementSettlementOptions[$movement->settlement_kind] ?? ucfirst((string) $movement->settlement_kind) }} {{ $movement->due_date?->format('d/m/Y') ?: '-' }} {{ $movement->counterpart_reference ?: '-' }} {{ $movement->notes ?: '-' }}
Todavía no hay movimientos registrados para esta serenata.