Commit Graph

22 Commits

Author SHA1 Message Date
Efrain (Helgen)
5dd33f2af1 Requisiciones: correcciones de la prueba en PRUEBAS
- .oculto con !important: las clases con display propio (.acciones, .tiles) definidas después
  le ganaban y seguían visibles los botones Devolver/Rechazar (y podía pasar con la tarjeta de
  margen de Cobranza). Prueba en interfaz.test.js.
- Pago a proveedor en otra moneda: el aviso dice "La orden es en MXN…" (antes "El proyecto…").
  Prueba en requisiciones.test.js.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 12:19:02 -06:00
Efrain (Helgen)
fdb273793f Compras: requisiciones de materiales y pago a proveedores
- Requisiciones (ROL_REQUISICION): el trabajador pide materiales sin precios para un proyecto
  activo con contrato firmado, adjunta la cotización del proveedor (PDF/JPG/PNG/HEIC, 10 MB) y
  solo ve las suyas (el servidor filtra por el correo del token), sin montos, cliente, OC ni pagos.
- SOLO_REQUISICION: cuentas que solo ven la pestaña Requisiciones; doPost les niega lo demás.
- Estados: Solicitada → Autorizada (genera la OC) → Pagada → Cerrada; Rechazada, Devuelta
  (corregir y reenviar con el mismo folio) y Cancelada. Historial visible para el solicitante.
- Toda OC sale de una requisición autorizada: se quitan la emisión directa y el botón "Recibida".
  La OC guarda req_folio y su PDF lleva "Requisición: REQ-…". Cancelar la OC regresa la
  requisición a Solicitada.
- Pago a proveedor (PGP-AAAA-NNNN, hoja PagosProveedores en el libro de Compras): comprobante
  obligatorio, monto = total de la OC ±1.00, tipo de cambio si es otra moneda. OC y requisición
  a Pagada; cancelar el pago las regresa (no si ya está cerrada). Una OC pagada no se cancela.
- Cierre: "Recibí el material" (solicitante) o compras con motivo; la OC queda Recibida.
- Pruebas: tests/requisiciones.test.js nueva; compras, interfaz, cobranza y sesión actualizadas.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 11:56:20 -06:00
Efrain (Helgen)
06ef8249c3 Cobranza: buscador para abrir cualquier proyecto desde el resumen (también sin cotización aceptada)
Sin él, un proyecto nuevo no se podía abrir para marcar su primera
cotización aceptada (el resumen solo lista proyectos con contrato).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 11:36:29 -06:00
Efrain (Helgen)
b99630ba00 Resumen de cobranza: ordenar por moneda antes que por saldo; prueba en PRUEBAS completa (CLAUDE.md)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 11:33:31 -06:00
Efrain (Helgen)
c99e89d91f Cobranza: resumen de todos los proyectos (cobrado, por cobrar, margen) y CSV
- resumenCobranzaGeneral() (solo ROL_COBRANZA; solo lee): una lectura de
  Contratos, Esquemas, Pagos, Proyectos y órdenes de compra. Usa
  resumenCobranza por proyecto: mismas cifras que la vista de cada proyecto.
- Totales por moneda (nunca MXN + USD), solo de contratos firmados. Los
  "Por firmar" se muestran sin sumar; los proyectos cancelados no aparecen.
- Por proyecto: contratado, cobrado, saldo, avance, siguiente etapa, último
  pago, costo de órdenes y margen (costosDeOrdenes, compartida con la vista).
- Interfaz: el resumen es la pantalla inicial de Cobranza; filtros, clic
  para abrir el proyecto, "← Resumen" y Descargar CSV (UTF-8 con BOM, texto
  con = + - @ neutralizado).
- Pruebas: cobranza, compras e interfaz ampliadas; las ocho pasan.

Sin publicar (CLAUDE.md §9).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 11:12:24 -06:00
Efrain (Helgen)
1a02e2df3f Compras: órdenes de compra por proyecto, proveedores y margen (fase 3)
- Rol nuevo ROL_COMPRAS (Script Properties): solo ese rol crea, ve, recibe y
  cancela órdenes; el servidor lo revisa en cada llamada y los demás reciben
  codigo PERMISO. getDatosIniciales agrega permisos.compras.
- Órdenes OC-AAAA-NNNN con el mismo candado e idempotencia, solo en proyectos
  Activos con contrato firmado. Estados Emitida / Recibida / Cancelada (con
  motivo; nunca se borran). La línea de negocio sale del contrato.
- Libro y carpeta "Cotizador Helgen — Compras" aparte y restringidos con
  privado_(): ni en la Base de datos ni en la carpeta del proyecto ni en
  cobranza. Pestañas OrdenesCompra, Proveedores (se llena sola) y Bitacora.
- PDF de la orden siempre con la razón social de Helgen y los términos
  TERMINOS_OC (se agregan solos a Config). Sin tipo de cambio ni línea.
- Costo siempre capturado, nunca el precio de venta. Orden en otra moneda que
  el proyecto: tipo de cambio obligatorio y costo convertido.
- Margen (contratado sin IVA − órdenes vigentes sin IVA) solo en la vista de
  cobranza; preparado para sumar nómina y horas. Con órdenes vigentes la
  aceptada no puede cambiar de moneda.
- Cobranza y compras comparten la mecánica de área privada (carpetaArea_,
  libroArea_, enRol_). Los proyectos nuevos ya no crean "Órdenes de compra".
- Interfaz: pestaña Compras y tarjeta de margen en Cobranza.
- Pruebas: tests/compras.test.js nuevo; interfaz, cobranza, sesión, proyectos
  y cotización actualizadas (oc.html de ejemplo). Las ocho pasan.

Sin publicar: falta respaldo, prueba en PRUEBAS, clasp y wrangler (CLAUDE.md §9).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 18:49:07 -06:00
Efrain (Helgen)
09e9b7acfb Cobranza: ocultar "Marcar aceptada" y "Contrato firmado" en proyectos cancelados
vistaCobranza_ manda puedeAceptar y puedeFirmar (falsos si el proyecto está
Cancelado o, en el caso de aceptar, si ya está firmado); la interfaz oculta esos
botones y muestra un aviso con cómo reactivarlo. El servidor ya rechazaba ambas
acciones. Pruebas de cobranza e interfaz ampliadas. Publicar la API antes que la
interfaz.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 18:26:03 -06:00
Efrain (Helgen)
c75e754137 Cobranza: prepararCobranza() restringe primero la carpeta y luego el libro
El libro hereda de la carpeta el acceso del dominio y Drive no deja quitar un permiso
heredado en el hijo (403 cannotDeletePermission). El simulacro de la API de Drive
ahora reproduce la herencia; la raíz de Mi unidad simulada ya no está compartida.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 17:57:22 -06:00
Efrain (Helgen)
bfe4661968 Cobranza: restringir acceso con la API de Drive (DriveApp.setSharing da Access denied)
En helgen.tech DriveApp.setSharing falla con "Access denied: DriveApp." aunque el
script es dueño, con Permission.NONE y con VIEW. privado_() ahora lista los permisos
con la API REST de Drive y borra los de tipo domain/anyone, con el token del script
(sin permisos nuevos). Si no puede, lanza error. Simulacro de la API en las pruebas.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 17:55:17 -06:00
Efrain (Helgen)
7e3256415a Cobranza: privado_() usa Permission.VIEW (NONE no está soportado y da Access denied)
prepararCobranza() falló en la API real con "Access denied: DriveApp." en setSharing.
Con Access.PRIVATE el permiso se ignora. El simulacro ahora rechaza NONE como Google.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 17:53:17 -06:00
Efrain (Helgen)
768554557f Cobranza: carpeta, libro, subcarpetas y comprobantes en acceso restringido
Workspace de helgen.tech comparte como lector con todo el dominio lo que se crea:
la carpeta y el libro de cobranza nacieron visibles para cualquier vendedor.
privado_() los deja en Restringido (conserva a tania@ y leo@) y prepararCobranza()
repara los que ya existen. Prueba: el simulacro de Drive comparte lo nuevo con el dominio.
CLAUDE.md: IDs de la carpeta y el libro de cobranza reales.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 17:47:45 -06:00
Efrain (Helgen)
b1b3bb94d0 Cobranza: fechas de pago y firma legibles (Sheets las convertía en fecha)
En la prueba de punta a punta la fecha del pago se mostraba como
"Tue Oct 20 2026 00:00:00 GMT-0600…": appendRow convierte "AAAA-MM-DD" en fecha
aunque la columna tenga formato de texto. Ahora fecha_pago y fecha_firma se
escriben con apóstrofo (texto, sin depender de la zona horaria de la hoja) y al
leer se normalizan las filas ya guardadas como fecha. Firmar y desbloquear ya no
reescriben aceptada_por / fecha_aceptacion. La prueba simula ese comportamiento
de Sheets.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 17:32:31 -06:00
Efrain (Helgen)
decddbb973 Cotizaciones: columna V con el tipo de cambio usado en cotizaciones USD
La interfaz manda el tipo de cambio con que convirtió los precios del catálogo;
el servidor lo valida (1–1000, 4 decimales) y lo guarda solo en cotizaciones en
USD. En MXN, en filas viejas o con precios capturados sin tipo de cambio queda
vacía. El encabezado se agrega solo (patrón de ENCABEZADOS_COTIZACIONES).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 17:11:56 -06:00
Efrain (Helgen)
6fb43f84a1 Catálogo: precio en USD automático con el tipo de cambio del día
- Pestaña "Tipo de cambio" en el catálogo: Google Finance (USD→MXN), un valor
  manual opcional que manda, y el tipo de cambio en uso (rango TIPO_CAMBIO).
  Se crea sola y se recalcula cada hora.
- Columna I "Precio USD (sin IVA, automático)": fórmula por renglón (no
  ARRAYFORMULA, para que appendRow no escriba al final de la hoja); los
  renglones sin fórmula se reparan solos; protegida con advertencia.
- En cotizaciones USD, al elegir un producto se pone su precio en dólares; al
  cambiar de moneda se convierten los renglones del catálogo no modificados y
  se avisa de los capturados a mano. La pestaña Catálogo muestra el precio USD
  y el tipo de cambio en uso.
- Pruebas de catálogo e interfaz ampliadas; CLAUDE.md actualizado.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 17:01:57 -06:00
Efrain (Helgen)
d08f4025f5 Cobranza: esquema de Energy editable solo por el administrador; columnas USD en el PDF
- Esquema de Helgen Energy: solo ADMIN_DESBLOQUEO lo edita (antes de firmar),
  con motivo obligatorio que queda en la Bitacora junto con el esquema anterior.
  Al cambiar entre cotizaciones de Energy se conserva el esquema; al pasar de
  Helgen a Energy se pone el 60/30/10 (con confirmación si reemplaza otro).
- PDF en USD: columnas "P. Unitario (USD)" e "Importe (USD)"; en MXN sin cambios.
- Prueba de que la cotización USD imprime el mismo bloque de términos que MXN.
- Pruebas de cobranza e interfaz ampliadas; CLAUDE.md actualizado.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 16:49:18 -06:00
Efrain (Helgen)
745c9e3a66 Fase 2: cobranza por proyecto y cotizaciones en USD
- Cobranza en un libro y una carpeta aparte ("Cotizador Helgen — Cobranza"),
  fuera de la carpeta maestra, porque la Base de datos la lee todo el dominio.
- Cotización aceptada (una por proyecto; línea y moneda salen de ella),
  esquema de pagos (Energy fijo 60/30/10; Helgen editable, suma 100%),
  contrato firmado que bloquea todo, desbloqueo solo del administrador con
  motivo, pagos PAG-AAAA-NNNN con comprobante obligatorio (PDF/imagen,
  revisado por contenido), excedente a la siguiente etapa, tolerancia de 1.00,
  tipo de cambio cuando la moneda del pago es otra, cancelación con motivo y
  bitácora.
- Permisos en el servidor: ROL_COBRANZA y ADMIN_DESBLOQUEO en Script
  Properties; sin rol, codigo PERMISO y ningún dato de cobranza.
- Un proyecto con contrato firmado no admite cotizaciones nuevas y sale del
  buscador de cotizaciones.
- Cotizaciones en MXN o USD (columna U, PDF e importe con letra en dólares);
  en USD el catálogo no pone el precio.
- Proyectos nuevos sin subcarpeta "Pagos".
- Pruebas: nueva cobranza.test.js; interfaz, cotización, sesión y proyectos
  ampliadas. CLAUDE.md actualizado (§4.5.2, §5, §6.10, §6.11, regla 12, §9).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 14:54:23 -06:00
Efrain (Helgen)
18e305a36f Revert "Historial: nombre del vendedor desde el directorio de Google de helgen.tech"
Se decidió no publicar la búsqueda en el directorio (servicio People): el nombre de un vendedor viejo
(p. ej. leo@helgen.tech) se corrige solo cuando guarde una cotización con sesión. appsscript.json vuelve a
quedar sin servicios avanzados y Code.js igual a lo publicado en @5. Nunca se hizo clasp push de c6c4b93.

This reverts commit c6c4b93.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 13:45:30 -06:00
Efrain (Helgen)
c6c4b93da3 Historial: nombre del vendedor desde el directorio de Google de helgen.tech
Las cotizaciones de vendedores que nunca guardaron con sesión (p. ej. leo@helgen.tech) seguían mostrando el
correo. Ahora, para correos @helgen.tech sin nombre en la hoja, listarCotizaciones busca el nombre y apellido
en el directorio de la organización (servicio avanzado People, searchDirectoryPeople con perfiles del
dominio), solo acepta la persona con el correo exacto y guarda el resultado 6 h en caché. Si el directorio
falla o no hay permiso, se muestra el correo como antes. No escribe en la hoja.

probarDirectorio() autoriza el permiso desde el editor. Pruebas en sesion.test.js. CLAUDE.md §3, §4.4,
§4.6 y §6.8 (orden: push, autorizar, deploy).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 13:34:27 -06:00
Efrain (Helgen)
3c410b46ad Historial: nombre del vendedor también en cotizaciones viejas
Las filas anteriores a la sesión con Google no tienen vendedor_nombre/apellido y mostraban el correo.
listarCotizaciones ahora arma un mapa correo → "Nombre Apellido" con las filas que sí lo tienen
(nombresDeVendedores, gana la más reciente) y lo usa para las viejas (vendedorDeFila); si el correo no
se conoce, sigue mostrando el correo. Solo cambia la respuesta: la hoja no se modifica.

Pruebas en sesion.test.js. CLAUDE.md: §4.4, §4.6 y registros de prueba en la hoja real
(HLG-2026-0005, HLG-2026-0006, PRY-2026-0001) para excluirlos de reportes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 12:58:24 -06:00
Efrain (Helgen)
c04f878168 Proyecto rechazado al guardar: la interfaz limpia el campo y refresca la lista
Si el servidor rechaza la cotización porque el proyecto se canceló, se cerró o no existe, la etiqueta seguía
diciendo "Proyecto activo". Ahora el error lleva codigo PROYECTO (errorProyecto_ en Code.js, doPost lo
pasa a la respuesta, la capa api-web lo pone en err.codigo) y errorAlGuardar limpia el campo Proyecto,
refresca la lista de proyectos y muestra el aviso. Otros errores no tocan el campo.

Pruebas: tests/interfaz.test.js (nueva: DOM y fetch simulados), codigo PROYECTO en proyectos.test.js y
sesion.test.js. CLAUDE.md §8 y §10.2.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 12:29:56 -06:00
Efrain (Helgen)
aae01d6918 Proyectos: folio PRY-AAAA-NNNN, carpeta en Drive y cotizaciones ligadas por proyecto_folio
- Pestaña "Proyectos" (se crea sola) con cliente_id (RFC o SIN-RFC:nombre|empresa), estado con lista
  desplegable y vendedor del token. Folio con contador propio CONSECUTIVO_PROY_AAAA bajo el mismo candado.
- Carpeta Cotizador Helgen/Proyectos/AAAA/<folio> — <cliente> — <nombre>/ con Cotizaciones,
  Órdenes de compra y Pagos; las subcarpetas borradas o renombradas se vuelven a crear.
- Cotizaciones: columna T proyecto_folio (las viejas quedan vacías = "Sin proyecto"); con proyecto, el PDF
  va a su carpeta Cotizaciones. Proyecto inexistente o no Activo se rechaza sin gastar folio.
- Interfaz: campo Proyecto opcional con buscador y "Nuevo proyecto" en el módulo 1; columna en Historial.
- Pruebas: tests/proyectos.test.js (Drive simulado con árbol de carpetas) y sesion.test.js ampliada.
- CLAUDE.md: §6.9 Proyectos y §6.10 modelo futuro (OC, Pagos, Gastos, cotización "Aceptada").

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 10:59:37 -06:00
Efrain (Helgen)
bbb34768b4 Respaldo inicial del Cotizador Helgen (19/10/2026)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 16:14:08 -06:00