mirror of
https://github.com/efrainsosa27/cotizador-helgen.git
synced 2026-10-24 05:16:48 +00:00
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>
This commit is contained in:
parent
11708f2f1f
commit
768554557f
@ -33,7 +33,7 @@ autorrelleno, y claves SAT internas por producto para contabilidad (con sugerenc
|
|||||||
| Catálogo "Catálogo Helgen" (`CATALOGO_ID`) | `11Rr0G7ZHE8PqXWsw9OpPPfGLQx8Yz7g8h1wkjJY2ahQ` |
|
| Catálogo "Catálogo Helgen" (`CATALOGO_ID`) | `11Rr0G7ZHE8PqXWsw9OpPPfGLQx8Yz7g8h1wkjJY2ahQ` |
|
||||||
| Carpeta PDF "Cotizaciones Helgen (PDF)" (`FOLDER_ID`) | `1CDN1LyOlA1L6z5dvBp49tlqu0fDoxdX8` (subcarpeta `2026` = `1X6m0nexpc3TzJd9XWBmGOAeZuiJE3Pig`) |
|
| Carpeta PDF "Cotizaciones Helgen (PDF)" (`FOLDER_ID`) | `1CDN1LyOlA1L6z5dvBp49tlqu0fDoxdX8` (subcarpeta `2026` = `1X6m0nexpc3TzJd9XWBmGOAeZuiJE3Pig`) |
|
||||||
| "Catálogos SAT (CFDI 4.0)" (`SAT_ID`) | `1B2t9tXuEj1i9JO9ybqsuH4PN1K47CqMZgoEBzd-Dmc8` (subcarpeta "Catálogos SAT" `15OYcUp41onVJoW7--b3gnLZY8ymvYbUZ`) |
|
| "Catálogos SAT (CFDI 4.0)" (`SAT_ID`) | `1B2t9tXuEj1i9JO9ybqsuH4PN1K47CqMZgoEBzd-Dmc8` (subcarpeta "Catálogos SAT" `15OYcUp41onVJoW7--b3gnLZY8ymvYbUZ`) |
|
||||||
| Carpeta "Cotizador Helgen — Cobranza" (`COBRANZA_FOLDER_ID`) | Fuera de la carpeta maestra, en Mi unidad. Se crea con `prepararCobranza()` (ID pendiente: anotarlo aquí al crearla en la API real). Lectores: tania@ y leo@ (§6.11) |
|
| Carpeta "Cotizador Helgen — Cobranza" (`COBRANZA_FOLDER_ID`) | `1A9uoRdhFdWOBacWCxx4Ktw1d9iQH7JRc` (creada el 20/10/2026 con `prepararCobranza()`; fuera de la carpeta maestra, en Mi unidad). Libro "Cotizador Helgen — Cobranza (datos)" (`COBRANZA_DB_ID`) `1uk9FNzTkQPjvWJw2R5wb8HXZl9c8ldYvyB64c8yjHL0`. Lectores: tania@ y leo@ (§6.11) |
|
||||||
| Respaldos de la migración | Carpeta "Respaldos 2026-10-19" `1hxx0tgh4cLZMqoyf-eT8l14UAde7Y-L1` (copias de base de datos y catálogo antes del cambio) |
|
| Respaldos de la migración | Carpeta "Respaldos 2026-10-19" `1hxx0tgh4cLZMqoyf-eT8l14UAde7Y-L1` (copias de base de datos y catálogo antes del cambio) |
|
||||||
| Respaldos antes de Proyectos | Carpeta "Respaldos 2026-10-20" `1rFGY_k_YRRo243p3xL2RpibQ9jkEo634`: Base de datos `1PBQvmwI412IMyJb74qBWN5ZcyhSuxK3xGU9cV5KPxTE`, Catálogo `1GxBpAvQxVNJyFJSLnaY9umA5L2qf4dqX36S4uehHUNc` (antes de la pestaña Proyectos y la columna T) |
|
| Respaldos antes de Proyectos | Carpeta "Respaldos 2026-10-20" `1rFGY_k_YRRo243p3xL2RpibQ9jkEo634`: Base de datos `1PBQvmwI412IMyJb74qBWN5ZcyhSuxK3xGU9cV5KPxTE`, Catálogo `1GxBpAvQxVNJyFJSLnaY9umA5L2qf4dqX36S4uehHUNc` (antes de la pestaña Proyectos y la columna T) |
|
||||||
| Respaldos antes de Cobranza | Carpeta "Respaldos 2026-10-20 (antes de Cobranza)" `1C_yGbX0Gk_cZBwegdswPFvuzCVDdyTLF` (en la carpeta maestra): Base de datos `14C3d7w2ouaygtCGi0BP1QocGiqG4gf5Umh9O4KHgdow`, Catálogo `140_1sfwXF9u_aL1egDPYSbsE0rMwELGPZ5rkGVdzQnc` (antes de las columnas U:V, la columna I del catálogo y la pestaña "Tipo de cambio"). Verificadas contra los originales |
|
| Respaldos antes de Cobranza | Carpeta "Respaldos 2026-10-20 (antes de Cobranza)" `1C_yGbX0Gk_cZBwegdswPFvuzCVDdyTLF` (en la carpeta maestra): Base de datos `14C3d7w2ouaygtCGi0BP1QocGiqG4gf5Umh9O4KHgdow`, Catálogo `140_1sfwXF9u_aL1egDPYSbsE0rMwELGPZ5rkGVdzQnc` (antes de las columnas U:V, la columna I del catálogo y la pestaña "Tipo de cambio"). Verificadas contra los originales |
|
||||||
@ -381,6 +381,11 @@ Cobranza: `ROL_COBRANZA` (correos separados por coma: efrain@, tania@, leo@), `A
|
|||||||
- **Datos en un libro aparte** dentro de la carpeta "Cotizador Helgen — Cobranza", fuera de la carpeta maestra: la Base de
|
- **Datos en un libro aparte** dentro de la carpeta "Cotizador Helgen — Cobranza", fuera de la carpeta maestra: la Base de
|
||||||
datos y la carpeta maestra están compartidas como **lector con todo el dominio helgen.tech** (revisado el 20/10/2026), así
|
datos y la carpeta maestra están compartidas como **lector con todo el dominio helgen.tech** (revisado el 20/10/2026), así
|
||||||
que una pestaña Pagos ahí la vería cualquier vendedor. **Nunca poner datos de cobranza en la Base de datos.**
|
que una pestaña Pagos ahí la vería cualquier vendedor. **Nunca poner datos de cobranza en la Base de datos.**
|
||||||
|
- **Workspace comparte con todo helgen.tech lo que se crea** (pasó el 20/10/2026: la carpeta y el libro de cobranza nacieron
|
||||||
|
con "helgen.tech: lector" aunque el código no compartía nada). Por eso todo lo de cobranza (carpeta, libro, subcarpetas de
|
||||||
|
año y proyecto, comprobantes) pasa por `privado_()`, que lo deja en "Restringido" y conserva a las personas agregadas una por
|
||||||
|
una. `prepararCobranza()` repara carpeta y libro si alguien los vuelve a compartir. Si se crean archivos en otra área privada,
|
||||||
|
hacer lo mismo.
|
||||||
- **Permisos en el servidor:** `ROL_COBRANZA` (Script Properties, no en el código) para ver y cambiar todo lo de cobranza;
|
- **Permisos en el servidor:** `ROL_COBRANZA` (Script Properties, no en el código) para ver y cambiar todo lo de cobranza;
|
||||||
`ADMIN_DESBLOQUEO` solo para desbloquear. Los demás reciben `codigo: "PERMISO"` y `getDatosIniciales` no les manda nada de
|
`ADMIN_DESBLOQUEO` solo para desbloquear. Los demás reciben `codigo: "PERMISO"` y `getDatosIniciales` no les manda nada de
|
||||||
cobranza (solo `permisos` en falso para ocultar la pestaña).
|
cobranza (solo `permisos` en falso para ocultar la pestaña).
|
||||||
|
|||||||
33
Code.js
33
Code.js
@ -1049,12 +1049,21 @@ function exigirCobranza_() {
|
|||||||
if (!esCobranza_()) throw errorPermiso_('No tienes permiso de cobranza.');
|
if (!esCobranza_()) throw errorPermiso_('No tienes permiso de cobranza.');
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Carpeta de cobranza. Se crea sola en Mi unidad (sin compartir). Si se borró, error: no se crea otra a escondidas. */
|
/**
|
||||||
|
* Deja un archivo o carpeta de cobranza en "Restringido": quita el acceso por enlace o de todo el dominio y conserva a
|
||||||
|
* las personas agregadas una por una (tania@, leo@). Workspace comparte con todo helgen.tech lo que se crea (§6.11).
|
||||||
|
*/
|
||||||
|
function privado_(item) {
|
||||||
|
if (item.getSharingAccess() !== DriveApp.Access.PRIVATE) item.setSharing(DriveApp.Access.PRIVATE, DriveApp.Permission.NONE);
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Carpeta de cobranza. Se crea sola en Mi unidad (restringida). Si se borró, error: no se crea otra a escondidas. */
|
||||||
function carpetaCobranza_() {
|
function carpetaCobranza_() {
|
||||||
const props = PropertiesService.getScriptProperties();
|
const props = PropertiesService.getScriptProperties();
|
||||||
const id = props.getProperty('COBRANZA_FOLDER_ID');
|
const id = props.getProperty('COBRANZA_FOLDER_ID');
|
||||||
if (!id) {
|
if (!id) {
|
||||||
const carpeta = DriveApp.createFolder(NOMBRE_CARPETA_COBRANZA);
|
const carpeta = privado_(DriveApp.createFolder(NOMBRE_CARPETA_COBRANZA));
|
||||||
props.setProperty('COBRANZA_FOLDER_ID', carpeta.getId());
|
props.setProperty('COBRANZA_FOLDER_ID', carpeta.getId());
|
||||||
return carpeta;
|
return carpeta;
|
||||||
}
|
}
|
||||||
@ -1063,7 +1072,7 @@ function carpetaCobranza_() {
|
|||||||
if (!carpeta || carpeta.isTrashed()) {
|
if (!carpeta || carpeta.isTrashed()) {
|
||||||
throw new Error('No se encontró la carpeta de cobranza (COBRANZA_FOLDER_ID). Revisa la Papelera de Drive.');
|
throw new Error('No se encontró la carpeta de cobranza (COBRANZA_FOLDER_ID). Revisa la Papelera de Drive.');
|
||||||
}
|
}
|
||||||
return carpeta;
|
return privado_(carpeta);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Libro de cobranza; la primera vez lo crea dentro de la carpeta de cobranza (bajo candado). */
|
/** Libro de cobranza; la primera vez lo crea dentro de la carpeta de cobranza (bajo candado). */
|
||||||
@ -1098,7 +1107,7 @@ function abrirLibroCobranza_(id) {
|
|||||||
function crearLibroCobranza_() {
|
function crearLibroCobranza_() {
|
||||||
const carpeta = carpetaCobranza_();
|
const carpeta = carpetaCobranza_();
|
||||||
const ss = SpreadsheetApp.create(NOMBRE_LIBRO_COBRANZA);
|
const ss = SpreadsheetApp.create(NOMBRE_LIBRO_COBRANZA);
|
||||||
DriveApp.getFileById(ss.getId()).moveTo(carpeta);
|
privado_(DriveApp.getFileById(ss.getId())).moveTo(carpeta);
|
||||||
Object.keys(HOJAS_COBRANZA).forEach(nombre => hojaCobranza_(ss, nombre));
|
Object.keys(HOJAS_COBRANZA).forEach(nombre => hojaCobranza_(ss, nombre));
|
||||||
const sobrante = ss.getSheetByName('Hoja 1') || ss.getSheetByName('Sheet1');
|
const sobrante = ss.getSheetByName('Hoja 1') || ss.getSheetByName('Sheet1');
|
||||||
if (sobrante && ss.getSheets().length > 1) ss.deleteSheet(sobrante);
|
if (sobrante && ss.getSheets().length > 1) ss.deleteSheet(sobrante);
|
||||||
@ -1466,9 +1475,9 @@ function registrarPago(p) {
|
|||||||
const hojaPagos = d.hojas.pagos;
|
const hojaPagos = d.hojas.pagos;
|
||||||
const folio = siguienteFolio_(PREFIJO_PAGO, hojaPagos, 'CONSECUTIVO_PAG_');
|
const folio = siguienteFolio_(PREFIJO_PAGO, hojaPagos, 'CONSECUTIVO_PAG_');
|
||||||
const anio = String(proyecto.folio).split('-')[1] || hoyIso_().slice(0, 4);
|
const anio = String(proyecto.folio).split('-')[1] || hoyIso_().slice(0, 4);
|
||||||
const carpeta = subcarpeta_(subcarpeta_(carpetaCobranza_(), anio), proyecto.folio);
|
const carpeta = privado_(subcarpeta_(privado_(subcarpeta_(carpetaCobranza_(), anio)), proyecto.folio));
|
||||||
const archivo = carpeta.createFile(Utilities.newBlob(bytes, tipo.mime,
|
const archivo = privado_(carpeta.createFile(Utilities.newBlob(bytes, tipo.mime,
|
||||||
nombreComprobante(folio, pago.metodo, pago.referencia, tipo.ext)));
|
nombreComprobante(folio, pago.metodo, pago.referencia, tipo.ext))));
|
||||||
|
|
||||||
const t = textoCelda;
|
const t = textoCelda;
|
||||||
hojaPagos.appendRow([
|
hojaPagos.appendRow([
|
||||||
@ -1511,13 +1520,15 @@ function cancelarPago(folioPago, motivo) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Correr UNA vez desde el editor: crea la carpeta "Cotizador Helgen — Cobranza" (en Mi unidad, sin compartir) y su
|
* Correr desde el editor: crea la carpeta "Cotizador Helgen — Cobranza" (en Mi unidad) y su libro, y deja los dos en
|
||||||
* libro. También sirve para autorizar permisos antes de publicar (§6.8). No comparte nada.
|
* "Restringido" (también si ya existían). Sirve para autorizar permisos antes de publicar (§6.8). No comparte nada.
|
||||||
*/
|
*/
|
||||||
function prepararCobranza() {
|
function prepararCobranza() {
|
||||||
const libro = libroCobranza_();
|
const libro = libroCobranza_();
|
||||||
Logger.log('Carpeta de cobranza: ' + carpetaCobranza_().getUrl());
|
const archivoLibro = privado_(DriveApp.getFileById(libro.getId()));
|
||||||
Logger.log('Libro de cobranza: ' + libro.getUrl());
|
const carpeta = carpetaCobranza_();
|
||||||
|
Logger.log('Carpeta de cobranza: ' + carpeta.getUrl() + ' (acceso: ' + carpeta.getSharingAccess() + ')');
|
||||||
|
Logger.log('Libro de cobranza: ' + libro.getUrl() + ' (acceso: ' + archivoLibro.getSharingAccess() + ')');
|
||||||
Logger.log('ROL_COBRANZA: ' + (PropertiesService.getScriptProperties().getProperty('ROL_COBRANZA') || '(vacío: nadie tiene acceso)'));
|
Logger.log('ROL_COBRANZA: ' + (PropertiesService.getScriptProperties().getProperty('ROL_COBRANZA') || '(vacío: nadie tiene acceso)'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -34,11 +34,13 @@ function libro(id,nombre,pestanas){const L={id,nombre,pest:pestanas||{},getId:()
|
|||||||
|
|
||||||
// ---- Drive simulado ----
|
// ---- Drive simulado ----
|
||||||
let nId=0; const carpetas={}, archivos={};
|
let nId=0; const carpetas={}, archivos={};
|
||||||
function carpeta(nombre,padre){const f={id:'F'+(++nId),nombre,padre,hijos:[],archivos:[],trashed:false,
|
// Como Workspace de helgen.tech: todo lo nuevo nace compartido con el dominio (acceso 'DOMAIN')
|
||||||
|
const compartible=o=>Object.assign(o,{acceso:'DOMAIN',getSharingAccess:()=>o.acceso,setSharing:(a,p)=>{o.acceso=a;o.permiso=p}});
|
||||||
|
function carpeta(nombre,padre){const f=compartible({id:'F'+(++nId),nombre,padre,hijos:[],archivos:[],trashed:false}); Object.assign(f,{
|
||||||
getId:()=>f.id,getName:()=>f.nombre,getUrl:()=>'https://drive/'+f.id,isTrashed:()=>f.trashed,
|
getId:()=>f.id,getName:()=>f.nombre,getUrl:()=>'https://drive/'+f.id,isTrashed:()=>f.trashed,
|
||||||
getFoldersByName:n=>{const l=f.hijos.filter(h=>h.nombre===n);let i=0;return{hasNext:()=>i<l.length,next:()=>l[i++]}},
|
getFoldersByName:n=>{const l=f.hijos.filter(h=>h.nombre===n);let i=0;return{hasNext:()=>i<l.length,next:()=>l[i++]}},
|
||||||
createFolder:n=>{const h=carpeta(n,f);f.hijos.push(h);return h},
|
createFolder:n=>{const h=carpeta(n,f);f.hijos.push(h);return h},
|
||||||
createFile:b=>{const id='A'+(++nId);const a={id,blob:b,padre:f};archivos[id]=a;f.archivos.push(a);return{getId:()=>id,getUrl:()=>'https://drive/file/'+id}}};
|
createFile:b=>{const id='A'+(++nId);const a=compartible({id,blob:b,padre:f});archivos[id]=a;f.archivos.push(a);return Object.assign(a,{getId:()=>id,getUrl:()=>'https://drive/file/'+id})}});
|
||||||
carpetas[f.id]=f;return f}
|
carpetas[f.id]=f;return f}
|
||||||
const miUnidad=carpeta('Mi unidad',null), maestra=miUnidad.createFolder('Cotizador Helgen'), pdfRaiz=maestra.createFolder('Cotizaciones Helgen (PDF)');
|
const miUnidad=carpeta('Mi unidad',null), maestra=miUnidad.createFolder('Cotizador Helgen'), pdfRaiz=maestra.createFolder('Cotizaciones Helgen (PDF)');
|
||||||
const ruta=f=>f.padre?ruta(f.padre)+'/'+f.nombre:f.nombre;
|
const ruta=f=>f.padre?ruta(f.padre)+'/'+f.nombre:f.nombre;
|
||||||
@ -71,12 +73,14 @@ const ctx={console:{...console,warn(){}},Date,JSON,
|
|||||||
return{getResponseCode:()=>c?200:400,getContentText:()=>JSON.stringify(c||{error:'invalid_token'})}}},
|
return{getResponseCode:()=>c?200:400,getContentText:()=>JSON.stringify(c||{error:'invalid_token'})}}},
|
||||||
ContentService:{MimeType:{JSON:'json'},createTextOutput:t=>({t,setMimeType(){return this}})},
|
ContentService:{MimeType:{JSON:'json'},createTextOutput:t=>({t,setMimeType(){return this}})},
|
||||||
SpreadsheetApp:{flush(){},openById:id=>{if(!libros[id])throw new Error('No encontrado');return libros[id]},
|
SpreadsheetApp:{flush(){},openById:id=>{if(!libros[id])throw new Error('No encontrado');return libros[id]},
|
||||||
create:n=>{const L=libro('S'+(++nId),n,{'Hoja 1':hoja([])});archivos[L.id]={id:L.id,padre:miUnidad};return L},
|
create:n=>{const L=libro('S'+(++nId),n,{'Hoja 1':hoja([])});archivos[L.id]=compartible({id:L.id,padre:miUnidad});return L},
|
||||||
newDataValidation:()=>{const b={requireValueInList:()=>b,setAllowInvalid:()=>b,setHelpText:()=>b,build:()=>({})};return b}},
|
newDataValidation:()=>{const b={requireValueInList:()=>b,setAllowInvalid:()=>b,setHelpText:()=>b,build:()=>({})};return b}},
|
||||||
DriveApp:{getFolderById:id=>{if(!carpetas[id])throw new Error('No se encontró');return carpetas[id]},
|
DriveApp:{getFolderById:id=>{if(!carpetas[id])throw new Error('No se encontró');return carpetas[id]},
|
||||||
createFolder:n=>miUnidad.createFolder(n),getRootFolder:()=>miUnidad,
|
createFolder:n=>miUnidad.createFolder(n),getRootFolder:()=>miUnidad,
|
||||||
getFileById:id=>({getParents:()=>{let d=false;return{hasNext:()=>!d,next:()=>{d=true;return archivos[id].padre}}},
|
getFileById:id=>({getParents:()=>{let d=false;return{hasNext:()=>!d,next:()=>{d=true;return archivos[id].padre}}},
|
||||||
moveTo:f=>{archivos[id].padre=f}})},
|
moveTo:f=>{archivos[id].padre=f},getSharingAccess:()=>archivos[id].getSharingAccess(),
|
||||||
|
setSharing:(a,p)=>archivos[id].setSharing(a,p),getId:()=>id}),
|
||||||
|
Access:{PRIVATE:'PRIVATE',DOMAIN:'DOMAIN'},Permission:{NONE:'NONE',VIEW:'VIEW'}},
|
||||||
};
|
};
|
||||||
vm.createContext(ctx); vm.runInContext(src,ctx);
|
vm.createContext(ctx); vm.runInContext(src,ctx);
|
||||||
const post=(quien,funcion,...args)=>JSON.parse(ctx.doPost({postData:{contents:JSON.stringify({funcion,args,token:quien})}}).t);
|
const post=(quien,funcion,...args)=>JSON.parse(ctx.doPost({postData:{contents:JSON.stringify({funcion,args,token:quien})}}).t);
|
||||||
@ -169,6 +173,8 @@ assert.equal(ruta(carpCob),'Mi unidad/Cotizador Helgen — Cobranza');
|
|||||||
const LC=libros[props.COBRANZA_DB_ID];
|
const LC=libros[props.COBRANZA_DB_ID];
|
||||||
assert.equal(LC.nombre,'Cotizador Helgen — Cobranza (datos)'); assert.equal(archivos[LC.id].padre,carpCob);
|
assert.equal(LC.nombre,'Cotizador Helgen — Cobranza (datos)'); assert.equal(archivos[LC.id].padre,carpCob);
|
||||||
assert.deepEqual(Object.keys(LC.pest).sort(),['Bitacora','Contratos','Esquemas','Pagos']);
|
assert.deepEqual(Object.keys(LC.pest).sort(),['Bitacora','Contratos','Esquemas','Pagos']);
|
||||||
|
// Restringidos aunque Workspace comparta lo nuevo con todo el dominio
|
||||||
|
assert.equal(carpCob.acceso,'PRIVATE','carpeta de cobranza restringida'); assert.equal(archivos[LC.id].acceso,'PRIVATE','libro restringido');
|
||||||
const H=n=>LC.pest[n].m, bit=()=>H('Bitacora').slice(1);
|
const H=n=>LC.pest[n].m, bit=()=>H('Bitacora').slice(1);
|
||||||
|
|
||||||
let v=ok('tania','getCobranzaProyecto',p1.toLowerCase());
|
let v=ok('tania','getCobranzaProyecto',p1.toLowerCase());
|
||||||
@ -264,6 +270,7 @@ const comp=archivos[fp[14]];
|
|||||||
assert.equal(ruta(comp.padre),'Mi unidad/Cotizador Helgen — Cobranza/'+ANIO+'/'+p1);
|
assert.equal(ruta(comp.padre),'Mi unidad/Cotizador Helgen — Cobranza/'+ANIO+'/'+p1);
|
||||||
assert.equal(comp.blob.name,r.folio+' — Transferencia SPEI 123.pdf'); assert.equal(comp.blob.mime,'application/pdf');
|
assert.equal(comp.blob.name,r.folio+' — Transferencia SPEI 123.pdf'); assert.equal(comp.blob.mime,'application/pdf');
|
||||||
assert.deepEqual(Buffer.from(comp.blob.bytes),PDF); assert.equal(fp[15],r.url);
|
assert.deepEqual(Buffer.from(comp.blob.bytes),PDF); assert.equal(fp[15],r.url);
|
||||||
|
assert.deepEqual([comp.acceso,comp.padre.acceso,comp.padre.padre.acceso],['PRIVATE','PRIVATE','PRIVATE'],'comprobante y subcarpetas restringidos');
|
||||||
assert(!Object.values(carpetas).some(f=>f.nombre==='Pagos')&&vivos(maestra).every(f=>!/Cobranza/.test(f.nombre)),'nada de cobranza en la carpeta maestra');
|
assert(!Object.values(carpetas).some(f=>f.nombre==='Pagos')&&vivos(maestra).every(f=>!/Cobranza/.test(f.nombre)),'nada de cobranza en la carpeta maestra');
|
||||||
function vivos(f){return f.hijos.filter(h=>!h.trashed)}
|
function vivos(f){return f.hijos.filter(h=>!h.trashed)}
|
||||||
// Idempotencia: el mismo envío devuelve el mismo folio sin duplicar
|
// Idempotencia: el mismo envío devuelve el mismo folio sin duplicar
|
||||||
@ -316,6 +323,9 @@ assert.deepEqual([fu[4],fu[5],fu[6],fu[7],fu[8]],[18500,'MXN',18.5,1000,'USD']);
|
|||||||
assert.deepEqual([r.vista.resumen.moneda,r.vista.resumen.pagado,r.vista.resumen.saldo],['USD',1000,0]);
|
assert.deepEqual([r.vista.resumen.moneda,r.vista.resumen.pagado,r.vista.resumen.saldo],['USD',1000,0]);
|
||||||
falla('efrain','registrarPago',[pago({proyectoFolio:p2,monto:1.01,monedaPago:'USD'})],/excede el saldo/);
|
falla('efrain','registrarPago',[pago({proyectoFolio:p2,monto:1.01,monedaPago:'USD'})],/excede el saldo/);
|
||||||
assert.equal(ruta(archivos[fu[14]].padre),'Mi unidad/Cotizador Helgen — Cobranza/'+ANIO+'/'+p2);
|
assert.equal(ruta(archivos[fu[14]].padre),'Mi unidad/Cotizador Helgen — Cobranza/'+ANIO+'/'+p2);
|
||||||
|
// Si alguien vuelve a compartir carpeta o libro con el dominio, prepararCobranza() (y el siguiente pago) los restringe
|
||||||
|
carpCob.acceso='DOMAIN'; archivos[LC.id].acceso='DOMAIN'; ctx.prepararCobranza();
|
||||||
|
assert.deepEqual([carpCob.acceso,archivos[LC.id].acceso],['PRIVATE','PRIVATE'],'prepararCobranza repara el acceso');
|
||||||
// Los datos de cobranza nunca van en la Base de datos (todo el dominio la puede leer)
|
// Los datos de cobranza nunca van en la Base de datos (todo el dominio la puede leer)
|
||||||
assert.deepEqual(Object.keys(db.pest).sort(),['Config','Cotizaciones','Proyectos']);
|
assert.deepEqual(Object.keys(db.pest).sort(),['Config','Cotizaciones','Proyectos']);
|
||||||
assert(!JSON.stringify(db.pest).includes('PAG-'));
|
assert(!JSON.stringify(db.pest).includes('PAG-'));
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user