


















(function () {
  var CR = window.CR, R = window.React, h = R.createElement;
  var useState = R.useState, useEffect = R.useEffect, useRef = R.useRef;
  var MESES = ['enero', 'febrero', 'marzo', 'abril', 'mayo', 'junio', 'julio', 'agosto', 'septiembre', 'octubre', 'noviembre', 'diciembre'];
  var ESTADOS = [['Pagable', 'Pagables'], ['PNU', 'PNU'], ['No pagable', 'No pagables']];
  function today() { return new Intl.DateTimeFormat('en-CA', { timeZone: 'America/Argentina/Buenos_Aires', year: 'numeric', month: '2-digit', day: '2-digit' }).format(new Date()); }
  /* La quincena se pide con UN día de ella (?fecha=): el 1 para la primera y el 16 para la segunda. El servidor
     devuelve desde y hasta (la segunda de octubre termina el 31; la de febrero, el 28 o el 29). */
  function quincenaDeHoy() { var t = today(); return { mes: t.slice(0, 7), quincena: Number(t.slice(8, 10)) <= 15 ? 1 : 2 }; }
  function fechaDe(periodo) { return periodo.mes + (periodo.quincena === 2 ? '-16' : '-01'); }
  function money(n) {
    if (typeof n !== 'number' || !isFinite(n)) return '—';
    return (n < 0 ? '−$' : '$') + Math.abs(n).toLocaleString('es-AR', { minimumFractionDigits: 2, maximumFractionDigits: 2 });
  }
  function diaMes(f) { var p = String(f || '').split('-'); return p.length === 3 ? p[2] + '/' + p[1] : (f || '—'); }
  function etiquetaPeriodo(p) {
    return (p.quincena === 1 ? '1ª' : '2ª') + ' quincena de ' + MESES[p.mes - 1] + ' ' + p.anio + ' (' + diaMes(p.desde) + ' al ' + diaMes(p.hasta) + ')';
  }
  // '101_150' -> '101 a 150 km'; 'MAS_150' -> 'más de 150 km'. Es un formato, no una lista de rangos.
  function rango(clave) {
    var m = /^(\d+)_(\d+)$/.exec(clave || ''), mas = /^MAS_(\d+)$/.exec(clave || '');
    return m ? m[1] + ' a ' + m[2] + ' km' : mas ? 'más de ' + mas[1] + ' km' : (clave || '—');
  }
  function kmConComa(n) { return String(n).replace('.', ','); }
  function nombreRed(datos, clave) { var r = ((datos && datos.redes) || []).filter(function (x) { return x.clave === clave; })[0]; return r ? r.nombre : (clave || 'Sin red'); }












  var CABECERA_PROVEEDOR = ['Fecha', 'Vehículo', 'Costo', 'Adicional', 'Descuentos/Paquetes faltantes', 'Total Costo'];
  function cero() { return { costo: 0, adicional: 0, descuento: 0, total: 0 }; }
  function acumular(acc, x) {
    acc.costo += Number(x.costo || 0); acc.adicional += Number(x.adicional || 0);
    acc.descuento += Number(x.descuento || 0); acc.total += Number(x.total || 0);
    return acc;
  }
  function csvProveedor(proveedor, servicios, redes) {
    var fila = window.CR.csv.filaCsv, fechaAr = window.CR.liquidacion.fechaAr, importeAr = window.CR.liquidacion.importeAr;
    var celdas = function (a) { return [importeAr(a.costo), importeAr(a.adicional), importeAr(a.descuento > 0 ? -a.descuento : 0), importeAr(a.total)]; };
    var nombre = proveedor || 'Sin proveedor';
    var lineas = [fila(['PROVEEDOR', nombre])];
    var orden = (redes || []).map(function (r) { return r.clave; });
    var porRed = {};
    (servicios || []).forEach(function (s) {
      if (!s.liquida || (s.proveedor || 'Sin proveedor') !== nombre) return;
      if (orden.indexOf(s.red) === -1) orden.push(s.red);
      (porRed[s.red] = porRed[s.red] || []).push(s);
    });
    var lista = orden.filter(function (r) { return porRed[r] && porRed[r].length; });
    var totalProveedor = cero();
    lista.forEach(function (red, i) {
      if (i > 0) lineas.push('');
      var rotulo = ((redes || []).filter(function (x) { return x.clave === red; })[0] || {}).nombre || red || 'Sin red';
      lineas.push(fila(['RED DE SERVICIO: ' + rotulo]));
      lineas.push(fila(CABECERA_PROVEEDOR));
      // fecha × patente: dos servicios de la misma unidad el mismo día (AM y PM) son UNA fila, sumada
      var dias = {}, fechas = [];
      porRed[red].forEach(function (s) {
        var f = String(s.fecha || '');
        if (!dias[f]) { dias[f] = { patentes: {}, orden: [] }; fechas.push(f); }
        var pat = String(s.patente || '').trim() || 'Sin patente';
        if (!dias[f].patentes[pat]) { dias[f].patentes[pat] = cero(); dias[f].orden.push(pat); }
        acumular(dias[f].patentes[pat], { costo: s.pagoProveedor && s.pagoProveedor.monto, adicional: s.adicionales, descuento: s.descuentos, total: s.totalProveedor });
      });
      var totalRed = cero();
      fechas.sort().forEach(function (f) {
        var dia = dias[f], totalDia = cero();
        dia.orden.slice().sort(function (a, b) { return a.localeCompare(b, 'es'); }).forEach(function (pat) {
          lineas.push(fila([fechaAr(f), pat].concat(celdas(dia.patentes[pat]))));
          acumular(totalDia, dia.patentes[pat]);
        });
        lineas.push(fila(['Total ' + fechaAr(f), ''].concat(celdas(totalDia))));
        acumular(totalRed, totalDia);
      });
      if (lista.length > 1) lineas.push(fila(['Total ' + rotulo, ''].concat(celdas(totalRed))));
      acumular(totalProveedor, totalRed);
    });
    lineas.push(fila(['TOTAL ' + nombre, ''].concat(celdas(totalProveedor))));
    return lineas.join('\n');
  }
  function nombreCsvProveedor(proveedor, periodo) {
    return 'craneo-liquidacion-ultima-milla-' + String(proveedor || 'sin-proveedor').replace(/[^A-Za-z0-9-]+/g, '_')
      + '-' + periodo.anio + '-' + periodo.mes + '-Q' + periodo.quincena + '.csv';
  }
  window.CR.liquidacionLastMile = { csvProveedor: csvProveedor, nombreCsvProveedor: nombreCsvProveedor };

  /* Cómo se calculó cada lado de un servicio, con los datos que manda el servidor (tarifa entera, factor, rango,
     km, vigencia y motivo). Un importe que falta lleva SU motivo, nunca un $0 mudo. */
  function comoMeli(s, datos) {
    var m = s.cobroMeli || {};
    if (s.estado === 'No pagable') return 'Cumplimiento ' + s.cumplimiento + ' %: de 0 a 89 % no se cobra (sin proporcional).';
    if (m.motivo) return ((datos.motivos && datos.motivos.meli) || {})[m.motivo] || m.motivo;
    var base = nombreRed(datos, s.red) + ' · ' + s.tipoUnidad + ' · ' + rango(m.rangoKm) + ' · ' + s.cicloTarifa + ': ' + money(m.tarifa)
      + (m.vigenteDesde ? ' (desde ' + diaMes(m.vigenteDesde) + ')' : '');
    if (s.estado === 'PNU') return 'PNU: 50 % de ' + base + '.';
    return base + (s.km != null ? ' · ' + kmConComa(s.km) + ' km' + (m.kmRedondeado !== s.km ? ' → ' + m.kmRedondeado + ' km' : '') : '') + '.';
  }
  function comoProveedor(s, datos) {
    var p = s.pagoProveedor || {};
    if (s.estado === 'No pagable') return 'Cumplimiento ' + s.cumplimiento + ' %: no se paga.';
    if (p.motivo) return ((datos.motivos && datos.motivos.proveedor) || {})[p.motivo] || p.motivo;
    var base = 'Tarifa ' + s.patente + ' ' + s.cicloTarifa + ': ' + money(p.tarifa) + (p.vigenteDesde ? ' (desde ' + diaMes(p.vigenteDesde) + ')' : '');
    return s.estado === 'PNU' ? 'PNU: 50 % de la ' + base.charAt(0).toLowerCase() + base.slice(1) + '.' : base + '.';
  }
  function claseEstado(estado) { return estado === 'Pagable' ? 'is-ok' : estado === 'No pagable' ? 'is-no' : estado === 'PNU' ? 'is-pnu' : ''; }

  function incidenciasCierre(datos, irAOperacion) {
    var cierre = datos.cierre;
    if (!cierre || cierre.existe || !cierre.pendientes) return null;
    var ids = new Set(cierre.cuposVisibles || []);
    var filas = datos.servicios.concat(datos.pendientes).filter(function (s) { return ids.has(s.id); });
    return h('section', { className: 'alerta alerta--media', 'aria-label': 'Incidencias del cierre' },
      h('h3', null, 'Servicios que impiden cerrar la quincena (' + cierre.pendientes + ')'),
      filas.length ? h('div', { className: 'lm-table-wrap' }, h('table', { className: 'lm-table' },
        h('thead', null, h('tr', null, ['Fecha', 'Centro / ciclo / cupo', 'Patente', 'ID de ruta', 'Qué corregir'].map(function (t) { return h('th', { key: t }, t); }))),
        h('tbody', null, filas.map(function (s) {
          var faltaOperacion = !s.liquida || !s.patente;
          var faltaProveedor = s.liquida && (!s.pagoProveedor || s.pagoProveedor.motivo);
          var faltaMeli = datos.permisos.verMeli && s.liquida && (!s.cobroMeli || s.cobroMeli.motivo);
          var revisarGrilla = faltaMeli && s.cobroMeli && ['sin_km','km_fuera_de_grilla','fuera_de_grilla'].includes(s.cobroMeli.motivo);
          return h('tr', { key: s.id }, h('td', null, s.fecha), h('td', null, s.svc + ' / ' + s.ciclo + ' / ' + s.cupo),
            h('td', null, s.patente || 'Sin asignar'), h('td', null, s.idRuta || 'Sin ID'),
            h('td', null, faltaOperacion ? h('p', null, datos.motivos.pendiente[s.motivoPendiente] || 'Revisá la asignación en Operación.') : null,
              faltaProveedor ? h('p', null, 'Revisá la tarifa de proveedor de ' + s.patente + ' para el ciclo ' + s.cicloTarifa + ', vigente el ' + s.fecha + '.') : null,
              faltaMeli ? h('p', null, (s.cobroMeli && datos.motivos.meli[s.cobroMeli.motivo]) || 'Revisá la tarifa de Mercado Libre.') : null,
              revisarGrilla && s.cobroMeli.motivo !== 'sin_km' ? h('p', null, 'Verificá los datos del servicio en Operación. Si son correctos, Administración debe revisar la cobertura de la grilla.') : null,
              faltaOperacion || revisarGrilla ? h('a', { className: 'btn btn--sm', href: '#/last-mile', onClick: function () { irAOperacion(s); } }, 'Ver operación de ese día') : null,
              faltaProveedor || (faltaMeli && !revisarGrilla) ? h('a', { className: 'btn btn--sm', href: '#/last-mile/tarifas' }, 'Ver tarifas') : null));
        })))) : null,
      cierre.pendientes > filas.length ? h('p', null, 'Administración puede revisar las demás incidencias de esta quincena.') : null);
  }
  function LastMileLiquidacion(props) {
    var habilitado = !!props.permissions;
    var ca = useState(null), cierreAccion = ca[0], setCierreAccion = ca[1];
    var p = useState(quincenaDeHoy), periodo = p[0], setPeriodo = p[1];
    var d = useState(null), datos = d[0], setDatos = d[1];
    var l = useState(true), loading = l[0], setLoading = l[1];
    var e = useState(''), error = e[0], setError = e[1];
    var ms = useState(''), mensaje = ms[0], setMensaje = ms[1];
    var rv = useState(0), revision = rv[0], setRevision = rv[1];
    var b = useState(false), busy = b[0], setBusy = b[1];
    var sel = useState(''), seleccion = sel[0], setSeleccion = sel[1];
    var pc = useState(''), provCsv = pc[0], setProvCsv = pc[1];
    var aj = useState(null), ajuste = aj[0], setAjuste = aj[1];
    var an = useState(null), anulando = an[0], setAnulando = an[1];
    /* Cada respuesta sólo puede afectar al contexto que la originó. El GET y los POST llevan
       generaciones separadas: revocar acceso, cambiar período o desmontar invalida ambas sin
       cancelar una operación que el servidor ya pudo confirmar. */
    var gs = useRef(0), getSeq = gs;
    var ps = useRef(0), postSeq = ps;
    var cs = useRef(0), contextoSeq = cs;
    var mr = useRef(true), montado = mr;
    var er = useRef(''), errorRevocacion = er;
    useEffect(function () {
      montado.current = true;
      return function () { montado.current = false; contextoSeq.current++; getSeq.current++; postSeq.current++; };
    }, []);
    useEffect(function () {
      var contexto = ++contextoSeq.current;
      // El estado ocupado pertenece al contexto anterior, no a la nueva lectura.
      // Sus respuestas tardías siguen invalidadas por postSeq/contextoSeq.
      setBusy(false); setMensaje(''); setCierreAccion(null);
      return function () {
        if (contextoSeq.current === contexto) contextoSeq.current++;
        postSeq.current++;
      };
    }, [habilitado, props.permissions]);
    useEffect(function () {
      if (!habilitado) {
        getSeq.current++; setDatos(null); setAjuste(null); setAnulando(null); setSeleccion(''); setProvCsv('');
        setMensaje(''); setError(''); setBusy(false); setLoading(false); return;
      }
      var pedido = ++getSeq.current, contexto = contextoSeq.current;
      function vigente() { return montado.current && getSeq.current === pedido && contextoSeq.current === contexto; }
      setLoading(true); setDatos(null); setAjuste(null); setAnulando(null); setCierreAccion(null); setError(errorRevocacion.current || '');
      CR.backend.lastMileGet('liquidacion', fechaDe(periodo)).then(function (j) {
        if (!vigente()) return;
        if (!j || !j.permisos || typeof j.permisos.verMeli !== 'boolean' || typeof j.permisos.ajustar !== 'boolean') {
          setDatos(null); setError('No pudimos confirmar tus permisos para esta liquidación. Actualizá para volver a intentar.'); return;
        }
        errorRevocacion.current = ''; setError(''); setDatos(j);
      })
        .catch(function (err) { if (vigente()) { setDatos(null); setError(err.message); } })
        .finally(function () { if (vigente()) setLoading(false); });
      return function () { if (getSeq.current === pedido) getSeq.current++; };
    }, [periodo.mes, periodo.quincena, revision, habilitado, props.permissions]);
    if (!habilitado) return h('section', { className: 'card lm-section' }, h('h2', null, 'Liquidación'),
      h('p', null, 'Tu cuenta todavía no está habilitada para Última Milla.'));
    var verMeli = !!(datos && datos.permisos && datos.permisos.verMeli);
    var puedeAjustar = !loading && !!(datos && datos.permisos && datos.permisos.ajustar);
    var servicios = (datos && datos.servicios) || [], pendientes = (datos && datos.pendientes) || [];
    // Sin totales (una respuesta que no es la del contrato) no se inventan: los importes salen «—» y no hay avisos.
    var t = (datos && datos.totales) || { sinFacturarMeli: { servicios: 0, porMotivo: {} } };
    var proveedores = (datos && datos.porProveedor) || [];
    var provCsvValido = proveedores.some(function (x) { return x.proveedor === provCsv; }) ? provCsv : '';
    var filasPatente = !seleccion ? [] : servicios.concat(pendientes).filter(function (s) { return (s.proveedor + '|' + s.patente) === seleccion; });
    function cambiarPeriodo(cambio) {
      contextoSeq.current++; getSeq.current++; postSeq.current++;
      setBusy(false);
      setSeleccion(''); setProvCsv(''); setAjuste(null); setAnulando(null); setCierreAccion(null); setMensaje(''); setPeriodo(Object.assign({}, periodo, cambio));
    }
    function descargarCsv() {
      if (!provCsvValido || !datos) return;
      window.CR.csv.descargarTexto(nombreCsvProveedor(provCsvValido, datos.periodo), csvProveedor(provCsvValido, servicios, datos.redes));
    }
    function abrirAjuste(s) {
      if (!puedeAjustar) return;
      setMensaje(''); setError(''); setAnulando(null);
      setAjuste({ servicio: s, id: window.crypto.randomUUID(), tipo: 'adicional', monto: '', paquetes: '', motivo: '' });
    }
    function postVigente(pedido, contexto) {
      return montado.current && postSeq.current === pedido && contextoSeq.current === contexto;
    }
    function revocacionPost(err) {
      errorRevocacion.current = err.message;
      getSeq.current++; postSeq.current++;
      setDatos(null); setAjuste(null); setAnulando(null); setSeleccion(''); setProvCsv('');
      setMensaje(''); setError(err.message); setBusy(false); setLoading(true);
      // Relee permisos y datos actuales. No vuelve a ejecutar el POST que recibió 403.
      setRevision(function (v) { return v + 1; });
    }
    function errorPost(err, pedido, contexto) {
      if (!postVigente(pedido, contexto)) return;
      if (err.status === 403) { revocacionPost(err); return; }
      setError(err.message);
    }
    function guardarAjuste(ev) {
      ev.preventDefault();
      if (busy || !ajuste || !puedeAjustar) return;
      if (!String(ajuste.monto).trim() || !String(ajuste.motivo).trim()) { setError('Completá el monto y el motivo del ajuste.'); return; }
      var pedido = ++postSeq.current, contexto = contextoSeq.current;
      setBusy(true); setError('');
      CR.backend.lastMilePost('liquidacion/ajuste', { id: ajuste.id, servicioId: ajuste.servicio.id, tipo: ajuste.tipo, monto: String(ajuste.monto).trim(),
        paquetes: ajuste.tipo === 'descuento_paquetes' && String(ajuste.paquetes).trim() ? Number(ajuste.paquetes) : null, motivo: ajuste.motivo })
        .then(function () {
          if (!postVigente(pedido, contexto)) return;
          postSeq.current++; setBusy(false); setAjuste(null); setMensaje('Ajuste guardado. La liquidación se actualizó.'); setRevision(function (v) { return v + 1; });
        })
        .catch(function (err) { errorPost(err, pedido, contexto); })
        .finally(function () { if (postVigente(pedido, contexto)) setBusy(false); });
    }
    function anular(ev) {
      ev.preventDefault();
      if (busy || !anulando || !puedeAjustar) return;
      if (!String(anulando.motivo).trim()) { setError('Escribí por qué se anula el ajuste.'); return; }
      var pedido = ++postSeq.current, contexto = contextoSeq.current;
      setBusy(true); setError('');
      CR.backend.lastMilePost('liquidacion/ajuste/anular', { id: anulando.id, motivo: anulando.motivo })
        .then(function () {
          if (!postVigente(pedido, contexto)) return;
          postSeq.current++; setBusy(false); setAnulando(null); setMensaje('Ajuste anulado: deja de sumar y queda a la vista, tachado.'); setRevision(function (v) { return v + 1; });
        })
        .catch(function (err) { errorPost(err, pedido, contexto); })
        .finally(function () { if (postVigente(pedido, contexto)) setBusy(false); });
    }
    function campo(label, props, opciones) {
      return h('label', { className: 'lm-field' }, h('span', null, label), opciones
        ? h('select', Object.assign({ className: 'input' }, props), opciones.map(function (o) { return h('option', { key: o[0], value: o[0] }, o[1]); }))
        : h('input', Object.assign({ className: 'input' }, props)));
    }
    function abrirCierre(accion) {
      if (busy || loading || !datos || !datos.cierre || datos.permisos[accion] !== true) return;
      setAjuste(null); setAnulando(null); setError('');
      setCierreAccion({accion:accion,operacionId:window.crypto.randomUUID(),fecha:datos.periodo.desde,
        revision:datos.cierre.revision,huella:datos.cierre.huella,motivo:''});
    }
    function verFoto(revisionFoto) {
      if(busy || loading || !datos) return;
      var pedido=++postSeq.current,contexto=contextoSeq.current;
      setBusy(true);setError('');setCierreAccion(null);setAjuste(null);setAnulando(null);
      CR.backend.lastMilePost('liquidacion/foto',{fecha:datos.periodo.desde,revision:revisionFoto}).then(function(j){
        if(!postVigente(pedido,contexto)) return;
        if(!j || !j.cierre || !j.cierre.historica || !j.permisos || typeof j.permisos.verMeli !== 'boolean') throw new Error('No pudimos verificar esa foto.');
        setDatos(j);setSeleccion('');setProvCsv('');setMensaje('');
      }).catch(function(err){errorPost(err,pedido,contexto);}).finally(function(){if(postVigente(pedido,contexto))setBusy(false);});
    }
    function confirmarCierre(ev) {
      ev.preventDefault();
      if (busy || loading || !cierreAccion || !datos || datos.permisos[cierreAccion.accion] !== true) return;
      if (cierreAccion.accion === 'reabrir' && !cierreAccion.motivo.trim()) { setError('Escribí el motivo de reapertura.'); return; }
      var pedido = ++postSeq.current, contexto = contextoSeq.current, accion = cierreAccion.accion;
      setBusy(true); setError('');
      CR.backend.lastMilePost('liquidacion/' + accion, cierreAccion).then(function () {
        if (!postVigente(pedido, contexto)) return;
        postSeq.current++; setBusy(false); setCierreAccion(null);
        setMensaje(accion === 'cerrar' ? 'Quincena cerrada. La foto quedó guardada en el historial.' : 'Quincena reabierta. Podés corregirla y volver a cerrar; la foto anterior se conserva.');
        setRevision(function (v) { return v + 1; });
      }).catch(function (err) {
        errorPost(err,pedido,contexto);
        if (postVigente(pedido,contexto) && err.status === 409) { setCierreAccion(null); setMensaje('No se aplicó el cambio: '+err.message); setRevision(function(v){return v+1;}); }
      }).finally(function(){if(postVigente(pedido,contexto)) setBusy(false);});
    }
    function tarjeta(titulo, valor, detalle, clase) {
      return h('div', { className: 'card' + (clase ? ' ' + clase : ''), key: titulo }, h('span', { className: 'muted' }, titulo), h('strong', null, loading ? '—' : valor),
        detalle ? h('small', null, detalle) : null);
    }
    var tipos = (datos && datos.tiposAjuste) || { adicional: 'Adicional', descuento_paquetes: 'Descuento por paquetes faltantes' };
    var ref = datos && datos.referenciaAdicionales;
    var motivosMeli = (datos && datos.motivos && datos.motivos.meli) || {};
    var motivosPend = (datos && datos.motivos && datos.motivos.pendiente) || {};
    /* Ocho columnas y no doce: los estados van debajo de la cantidad de servicios. Con una columna por estado la
       tabla no entraba en 1.280 px y lo que quedaba afuera era justo la plata (total y margen). */
    var cabeceraPatentes = ['Proveedor / patente', 'Servicios'].concat(verMeli ? ['Facturación MELI'] : [],
      ['Costo proveedor', 'Ajustes', 'Total proveedor'], verMeli ? ['Margen'] : [], ['']);
    function celdasResumen(x) {
      return [h(R.Fragment, null, h('span', { 'data-cantidad': '' }, x.servicios),
        // Espacios duros entre el número y su rótulo: al partirse en dos renglones, «0» no queda lejos de «pend.».
        h('small', null, x.pagables + '\u00a0pag. · ' + x.pnu + '\u00a0PNU · ' + x.noPagables + '\u00a0no\u00a0pag. · ' + x.pendientes + '\u00a0pend.')),
      ].concat(verMeli ? [money(x.facturacionMeli)] : [], [money(x.costoProveedor), money(x.ajustesNeto), money(x.totalProveedor)], verMeli ? [money(x.margen)] : []);
    }
    return h('div', { className: 'lm-liquidacion' },
      h('div', { className: 'lm-toolbar card' },
        campo('Mes', { type: 'month', value: periodo.mes, required: true, disabled: busy, 'aria-label': 'Mes de la liquidación',
          onChange: function (ev) { if (ev.target.value) cambiarPeriodo({ mes: ev.target.value }); } }),
        campo('Quincena', { value: String(periodo.quincena), disabled: busy, 'aria-label': 'Quincena de la liquidación',
          onChange: function (ev) { cambiarPeriodo({ quincena: Number(ev.target.value) }); } }, [['1', '1ª quincena (1 al 15)'], ['2', '2ª quincena (16 a fin de mes)']]),
        h('button', { type: 'button', className: 'btn', disabled: busy || loading, onClick: function () { setMensaje(''); setRevision(function (v) { return v + 1; }); } }, 'Actualizar')),
      // Con el ajuste abierto, el error se muestra adentro del diálogo (es el que lo causó), no detrás.
      error && !ajuste ? h('p', { className: 'alerta alerta--alta', role: 'alert' }, error) : null,
      mensaje ? h('p', { className: 'lm-success', role: 'status' }, mensaje) : null,
      loading && !datos ? h('p', { role: 'status' }, 'Cargando liquidación…') : null,
      datos ? h(R.Fragment, null,
        h('section', {className:'card lm-section lm-cierre-panel', 'data-novedad': 'lm-cierre-quincena'},
          h('div',{className:'lm-actions'},h('h2',null,datos.cierre && datos.cierre.historica ? 'Foto histórica · revisión '+datos.cierre.revision : datos.cierre && datos.cierre.existe ? 'Quincena cerrada' : 'Quincena abierta'),
            datos.cierre && datos.cierre.historica ? h('button',{type:'button',className:'btn',disabled:busy,onClick:function(){setRevision(function(v){return v+1;});}},'Volver a la quincena actual') : null,
            datos.cierre && datos.cierre.existe && datos.permisos.reabrir === true ? h('button',{type:'button',className:'btn',disabled:busy || loading,onClick:function(){abrirCierre('reabrir');}},'Reabrir con motivo') : null,
            datos.cierre && !datos.cierre.existe && datos.permisos.cerrar === true ? h('button',{type:'button',className:'btn btn--primary',disabled:busy || loading || datos.cierre.pendientes > 0,onClick:function(){abrirCierre('cerrar');}},'Cerrar quincena') : null),
          h('p',{className:'lm-flota-note lm-liquidacion-provisoria',role:'note','data-liquidacion-provisoria': datos.cierre && datos.cierre.existe ? undefined : ''},
            h('b',null,etiquetaPeriodo(datos.periodo)+'. '),(datos.cierre && datos.cierre.nota) || ''),
          incidenciasCierre(datos, props.irAOperacion),
          cierreAccion ? h('form',{onSubmit:confirmarCierre,'aria-label':'Confirmar '+cierreAccion.accion+' quincena'},
            h('h3',null,cierreAccion.accion === 'cerrar' ? 'Confirmar el cierre' : 'Reabrir para corregir'),
            h('p',null,cierreAccion.accion === 'cerrar' ? 'Se conservarán los servicios, las tarifas aplicadas y los ajustes que revisaste. Para cambiar esta quincena habrá que reabrirla con motivo.' : 'Los importes volverán a ser provisorios y usarán las tarifas vigentes para sus fechas. La foto anterior seguirá en el historial. Revisá los cambios antes de cerrar nuevamente.'),
            cierreAccion.accion === 'reabrir' ? campo('Motivo de reapertura',{value:cierreAccion.motivo,required:true,maxLength:300,disabled:busy,onChange:function(ev){setCierreAccion(Object.assign({},cierreAccion,{motivo:ev.target.value}));}}) : null,
            h('div',{className:'lm-actions'},h('button',{type:'submit',className:'btn btn--primary',disabled:busy},busy ? 'Guardando…' : cierreAccion.accion === 'cerrar' ? 'Confirmar cierre' : 'Confirmar reapertura'),h('button',{type:'button',className:'btn',disabled:busy,onClick:function(){setCierreAccion(null);}},'Cancelar'))) : null,
          datos.cierre && datos.cierre.historial && datos.cierre.historial.length ? h('details',{'data-novedad':'lm-cierre-historial'},h('summary',null,'Historial de cierres y reaperturas'),
            h('ol',null,datos.cierre.historial.map(function(x){return h('li',{key:x.revision},h('b',null,(x.accion==='cerrar'?'Cierre':'Reapertura')+' · revisión '+x.revision),h('span',null,' — '+x.fecha+' · '+x.por),x.motivo ? h('p',null,x.motivo) : null,
              x.accion==='cerrar' ? h('button',{type:'button',className:'btn',disabled:busy || loading,onClick:function(){verFoto(x.revision);}},'Ver foto '+x.revision) : null);}))) : null),
        h('div', { className: 'lm-summary lm-liquidacion-resumen' },
          verMeli ? tarjeta('Facturación Mercado Libre', money(t.facturacionMeli), t.sinFacturarMeli && t.sinFacturarMeli.servicios ? t.sinFacturarMeli.servicios + ' servicio(s) sin facturar' : null) : null,
          tarjeta('Costo proveedores', money(t.totalProveedor), money(t.costoProveedor) + ' de tarifa · ajustes ' + money(t.ajustesNeto)),
          verMeli ? tarjeta('Margen', money(t.margen), t.margenSobreFacturacion == null ? 'sin facturación' : (Math.round(t.margenSobreFacturacion * 1000) / 10).toLocaleString('es-AR') + ' % de la facturación') : null,
          tarjeta('Servicios', t.servicios, t.pagables + ' pagables · ' + t.pnu + ' PNU · ' + t.noPagables + ' no pagables · ' + t.pendientes + ' pendientes')),
        verMeli && t.sinFacturarMeli && t.sinFacturarMeli.servicios ? h('p', { className: 'alerta alerta--media', role: 'status', 'data-sin-facturar': '' },
          h('b', null, t.sinFacturarMeli.servicios + ' servicio(s) no se pueden facturar a Mercado Libre. '),
          Object.keys(t.sinFacturarMeli.porMotivo || {}).map(function (k) { return t.sinFacturarMeli.porMotivo[k] + ' — ' + (motivosMeli[k] || k); }).join(' · ')) : null,
        t.sinTarifaProveedor ? h('p', { className: 'alerta alerta--media', role: 'status', 'data-sin-tarifa-proveedor': '' },
          h('b', null, t.sinTarifaProveedor + ' servicio(s) sin tarifa de proveedor. '), 'No suman costo hasta que se cargue la tarifa de su patente y ciclo en Tarifas.') : null,
        t.negativos ? h('p', { className: 'alerta alerta--media', role: 'status', 'data-negativos': '' },
          h('b', null, t.negativos + ' servicio(s) quedan en negativo. '), 'Un descuento es mayor que lo que se le paga al proveedor por ese servicio: revisalo.') : null,

        h('section', { className: 'card lm-section', 'data-novedad': 'lm-liquidacion' },
          h('h2', null, 'Por proveedor y patente'),
          h('p', { className: 'muted' }, 'El proveedor es la agencia de la asignación de cada servicio. Elegí una patente para ver cómo se calculó cada servicio.' + (puedeAjustar ? ' Podés cargar y anular ajustes sobre el pago al proveedor.' : ' Tu acceso a la liquidación es de consulta.')),
          h('div', { className: 'lm-actions lm-liquidacion-csv' },
            campo('CSV del proveedor', { value: provCsvValido, disabled: busy || !proveedores.length, 'aria-label': 'Proveedor del CSV',
              onChange: function (ev) { setProvCsv(ev.target.value); } }, [['', 'Elegí el proveedor']].concat(proveedores.map(function (x) { return [x.proveedor, x.proveedor]; }))),
            h('button', { type: 'button', className: 'btn btn--primary', disabled: !provCsvValido, onClick: descargarCsv,
              title: provCsvValido ? 'Baja el CSV de ' + provCsvValido + ' con el formato de Primera Milla: fecha, vehículo, costo, adicional, descuentos y total, un bloque por red' : 'Elegí el proveedor' },
              'Descargar CSV del proveedor')),
          !proveedores.length ? h('p', { className: 'lm-empty' }, 'No hay servicios asignados en esta quincena.') :
            h('div', { className: 'lm-table-wrap' }, h('table', { className: 'lm-table lm-liquidacion-tabla' },
              h('thead', null, h('tr', null, cabeceraPatentes.map(function (x) { return h('th', { key: x }, x); }))),
              h('tbody', null, proveedores.map(function (pv) {
                return [h('tr', { key: 'p-' + pv.proveedor, className: 'lm-liquidacion-proveedor' },
                  [h('th', { key: 'n', scope: 'rowgroup' }, pv.proveedor)].concat(celdasResumen(pv).map(function (v, i) { return h('td', { key: i }, h('b', null, v)); })).concat([h('td', { key: 'x' })]))]
                  .concat(pv.patentes.map(function (pt) {
                    var clave = pv.proveedor + '|' + pt.patente, abierta = seleccion === clave;
                    return h('tr', { key: clave, className: abierta ? 'is-abierta' : '' },
                      [h('td', { key: 'n' }, pt.patente, pt.sinTarifaProveedor ? h('small', null, 'sin tarifa de proveedor') : null)]
                        .concat(celdasResumen(pt).map(function (v, i) { return h('td', { key: i }, v); }))
                        .concat([h('td', { key: 'x' }, h('button', { type: 'button', className: 'btn btn--sm', 'aria-expanded': abierta,
                          'aria-label': (abierta ? 'Ocultar' : 'Ver') + ' detalle de ' + pt.patente, onClick: function () { setAjuste(null); setAnulando(null); setSeleccion(abierta ? '' : clave); } },
                          abierta ? 'Ocultar' : 'Ver detalle'))]));
                  }));
              })),
              h('tfoot', null, h('tr', null, [h('th', { key: 'n', scope: 'row' }, 'Total de la quincena')].concat(celdasResumen(t).map(function (v, i) { return h('td', { key: i }, h('b', null, v)); })).concat([h('td', { key: 'x' })])))))),
        seleccion ? h('section', { className: 'card lm-section', 'aria-labelledby': 'lm-liquidacion-detalle-titulo', 'data-liquidacion-detalle': seleccion },
          h('h2', { id: 'lm-liquidacion-detalle-titulo' }, 'Detalle de ' + seleccion.split('|')[1] + ' · ' + seleccion.split('|')[0]),
          h('div', { className: 'lm-table-wrap' }, h('table', { className: 'lm-table' },
            h('thead', null, h('tr', null, ['Fecha', 'SVC / ciclo', 'Unidad', 'ID Ruta / km / cumpl.', 'Estado'].concat(verMeli ? ['Mercado Libre'] : [], ['Proveedor', 'Ajustes', 'Total proveedor', '']).map(function (x) { return h('th', { key: x }, x); }))),
            h('tbody', null, filasPatente.map(function (s) {
              return h('tr', { key: s.id, 'data-servicio': s.id },
                h('td', null, diaMes(s.fecha)),
                h('td', null, s.svc + ' / ' + s.ciclo, h('small', null, nombreRed(datos, s.red))),
                h('td', null, s.tipoUnidad),
                h('td', null, s.idRuta || '—', h('small', null, (s.km != null ? kmConComa(s.km) + ' km' : 'sin km') + ' · ' + (s.cumplimiento != null ? s.cumplimiento + ' %' : '—'))),
                h('td', null, h('span', { className: 'lm-status ' + claseEstado(s.estado) }, s.estado), s.motivoPendiente ? h('small', null, motivosPend[s.motivoPendiente] || s.motivoPendiente) : null),
                verMeli ? h('td', null, s.liquida ? money(s.cobroMeli && s.cobroMeli.monto) : '—', s.liquida ? h('small', { 'data-como': 'meli' }, comoMeli(s, datos)) : null) : null,
                h('td', null, s.liquida ? money(s.pagoProveedor.monto) : '—', s.liquida ? h('small', { 'data-como': 'proveedor' }, comoProveedor(s, datos)) : null),
                h('td', null, !(s.ajustes || []).length ? '—' : h('ul', { className: 'lm-ajustes' }, s.ajustes.map(function (a) {
                  return h('li', { key: a.id, className: a.anulado ? 'is-anulado' : '' },
                    h('span', null, (a.tipo === 'adicional' ? '+ ' : '− ') + money(a.monto) + ' · ' + (tipos[a.tipo] || a.tipo) + (a.paquetes ? ' (' + a.paquetes + ' paq.)' : '')),
                    h('small', null, a.motivo + (a.cargadoPor ? ' · ' + a.cargadoPor : '') + (a.anulado ? ' · anulado: ' + (a.motivoAnulacion || '') : '')),
                    puedeAjustar && !a.anulado ? h('button', { type: 'button', className: 'btn btn--ghost btn--sm', disabled: busy, 'aria-label': 'Anular ajuste ' + money(a.monto),
                      onClick: function () { setAjuste(null); setError(''); setAnulando({ id: a.id, monto: a.monto, motivo: '' }); } }, 'Anular') : null);
                }))),
                h('td', null, s.liquida ? h('b', { className: s.negativo ? 'lm-negativo' : '' }, money(s.totalProveedor)) : '—'),
                h('td', null, puedeAjustar && s.liquida ? h('button', { type: 'button', className: 'btn btn--sm', disabled: busy, onClick: function () { abrirAjuste(s); } }, 'Ajustar') : null));
            })))),
          puedeAjustar && anulando ? h('form', { className: 'lm-form lm-anular', onSubmit: anular },
            campo('Motivo de la anulación de ' + money(anulando.monto), { required: true, maxLength: 300, value: anulando.motivo, disabled: busy, 'aria-label': 'Motivo de la anulación',
              onChange: function (ev) { setAnulando(Object.assign({}, anulando, { motivo: ev.target.value })); } }),
            h('div', { className: 'lm-actions' },
              h('button', { type: 'button', className: 'btn', disabled: busy, onClick: function () { setAnulando(null); } }, 'Cancelar'),
              h('button', { className: 'btn btn--primary', disabled: busy }, busy ? 'Anulando…' : 'Confirmar anulación'))) : null) : null,
        pendientes.length ? h('section', { className: 'card lm-section', 'aria-labelledby': 'lm-liquidacion-pendientes-titulo' },
          h('h2', { id: 'lm-liquidacion-pendientes-titulo' }, 'Pendientes (' + pendientes.length + ')'),
          h('p', { className: 'muted' }, 'No se liquidan hasta completar lo que les falta en Operación: no suman ni se cobran.'),
          h('div', { className: 'lm-table-wrap' }, h('table', { className: 'lm-table lm-liquidacion-pendientes' },
            h('thead', null, h('tr', null, ['Fecha', 'SVC / ciclo', 'Unidad', 'Patente', 'Falta'].map(function (x) { return h('th', { key: x }, x); }))),
            h('tbody', null, pendientes.map(function (s) {
              return h('tr', { key: s.id }, h('td', null, diaMes(s.fecha)), h('td', null, s.svc + ' / ' + s.ciclo), h('td', null, s.tipoUnidad),
                h('td', null, s.patente || 'Sin asignar'), h('td', null, motivosPend[s.motivoPendiente] || s.motivoPendiente));
            }))))) : null) : null,
      puedeAjustar && ajuste ? h('div', { className: 'lm-modal' }, h('section', { className: 'card lm-editor', role: 'dialog', 'aria-modal': 'true', 'aria-labelledby': 'lm-ajuste-titulo' },
        h('h2', { id: 'lm-ajuste-titulo' }, 'Ajuste · ' + ajuste.servicio.patente + ' · ' + diaMes(ajuste.servicio.fecha) + ' · ' + ajuste.servicio.svc + ' / ' + ajuste.servicio.ciclo),
        h('p', { className: 'muted' }, 'Sobre lo que se le paga al proveedor: ' + money(ajuste.servicio.pagoProveedor && ajuste.servicio.pagoProveedor.monto) + '. Queda en la liquidación y en Auditoría; se puede anular.'),
        error ? h('p', { role: 'alert', className: 'alerta alerta--alta' }, error) : null,
        h('form', { className: 'lm-form', onSubmit: guardarAjuste },
          campo('Tipo de ajuste', { value: ajuste.tipo, disabled: busy, onChange: function (ev) { setAjuste(Object.assign({}, ajuste, { tipo: ev.target.value })); } },
            Object.keys(tipos).map(function (k) { return [k, tipos[k]]; })),
          campo('Monto ($)', { value: ajuste.monto, inputMode: 'decimal', placeholder: 'ej. 14500 o 2300,50', maxLength: 14, required: true, disabled: busy, autoComplete: 'off',
            onChange: function (ev) { setAjuste(Object.assign({}, ajuste, { monto: ev.target.value })); } }),
          ajuste.tipo === 'descuento_paquetes' ? campo('Paquetes faltantes (opcional)', { type: 'number', min: 1, max: 10000, step: 1, value: ajuste.paquetes, disabled: busy,
            onChange: function (ev) { setAjuste(Object.assign({}, ajuste, { paquetes: ev.target.value })); } }) : null,
          campo('Motivo', { value: ajuste.motivo, maxLength: 300, required: true, disabled: busy, onChange: function (ev) { setAjuste(Object.assign({}, ajuste, { motivo: ev.target.value })); } }),
          // La tabla de adicionales por km del 29/09 es REFERENCIA: no se aplica sola (falta que BRAINS la confirme).
          ajuste.tipo === 'adicional' && ref ? h('div', { className: 'lm-referencia', role: 'note', 'data-referencia-adicionales': '' },
            h('p', null, h('b', null, 'Referencia: adicionales por km extra. '), ref.nota),
            h('table', { className: 'lm-table' }, h('thead', null, h('tr', null, ['Unidad', 'Km', 'Adicional'].map(function (x) { return h('th', { key: x }, x); }))),
              h('tbody', null, (ref.filas || []).map(function (f) { return h('tr', { key: f.unidad + f.rango }, h('td', null, f.unidad), h('td', null, f.rango), h('td', null, money(f.monto))); })))) : null,
          h('div', { className: 'lm-actions' },
            h('button', { type: 'button', className: 'btn', disabled: busy, onClick: function () { setAjuste(null); setError(''); } }, 'Cancelar'),
            h('button', { className: 'btn btn--primary', disabled: busy }, busy ? 'Guardando…' : 'Guardar ajuste'))))) : null);
  }
  CR.screens.LastMileLiquidacion = LastMileLiquidacion;
})();
