.
This commit is contained in:
@@ -0,0 +1,111 @@
|
||||
// packages/api/v1/repositories/comandasRepo.mjs
|
||||
|
||||
import { withTenantClient } from './db.mjs';
|
||||
import { loadColumns, loadPrimaryKey } from '../routes/utils/schemaInspector.mjs';
|
||||
|
||||
const TABLE = 'comandas';
|
||||
const VALID_IDENT = /^[a-z_][a-z0-9_]*$/i;
|
||||
|
||||
export async function listComandas({ schema, abierta, limit }) {
|
||||
return withTenantClient(schema, async (db) => {
|
||||
const max = Math.min(parseInt(limit || 200, 10), 1000);
|
||||
const { rows } = await db.query(
|
||||
`SELECT * FROM public.f_comandas_resumen($1, $2)`,
|
||||
[abierta, max]
|
||||
);
|
||||
return rows;
|
||||
});
|
||||
}
|
||||
|
||||
export async function getDetalleItems({ schema, id }) {
|
||||
return withTenantClient(schema, async (db) => {
|
||||
const { rows } = await db.query(
|
||||
`SELECT id_det_comanda, id_producto, producto_nombre,
|
||||
cantidad, pre_unitario, subtotal, observaciones
|
||||
FROM public.v_comandas_detalle_items
|
||||
WHERE id_comanda = $1::int
|
||||
ORDER BY id_det_comanda`,
|
||||
[id]
|
||||
);
|
||||
return rows;
|
||||
});
|
||||
}
|
||||
|
||||
export async function abrirComanda({ schema, id }) {
|
||||
return withTenantClient(schema, async (db) => {
|
||||
const st = await db.query(`SELECT eliminada FROM public.${q(TABLE)} WHERE id_comanda = $1`, [id]);
|
||||
if (!st.rowCount) return null;
|
||||
if (st.rows[0].eliminada === true) {
|
||||
const err = new Error('Comanda eliminada. Debe restaurarse antes de abrir.');
|
||||
err.http = { status: 409 };
|
||||
throw err;
|
||||
}
|
||||
const { rows } = await db.query(`SELECT public.f_abrir_comanda($1) AS data`, [id]);
|
||||
return rows[0]?.data || null;
|
||||
});
|
||||
}
|
||||
|
||||
export async function cerrarComanda({ schema, id }) {
|
||||
return withTenantClient(schema, async (db) => {
|
||||
const { rows } = await db.query(`SELECT public.f_cerrar_comanda($1) AS data`, [id]);
|
||||
return rows[0]?.data || null;
|
||||
});
|
||||
}
|
||||
|
||||
export async function restaurarComanda({ schema, id }) {
|
||||
return withTenantClient(schema, async (db) => {
|
||||
const { rows } = await db.query(`SELECT public.f_restaurar_comanda($1) AS data`, [id]);
|
||||
return rows[0]?.data || null;
|
||||
});
|
||||
}
|
||||
|
||||
export async function eliminarComanda({ schema, id }) {
|
||||
return withTenantClient(schema, async (db) => {
|
||||
const { rows } = await db.query(`SELECT public.f_eliminar_comanda($1) AS data`, [id]);
|
||||
return rows[0]?.data || null;
|
||||
});
|
||||
}
|
||||
|
||||
export async function patchComanda({ schema, id, payload }) {
|
||||
return withTenantClient(schema, async (db) => {
|
||||
const columns = await loadColumns(db, TABLE);
|
||||
const updatable = new Set(
|
||||
columns
|
||||
.filter(c =>
|
||||
!c.is_primary &&
|
||||
!c.is_identity &&
|
||||
!(String(c.column_default || '').startsWith('nextval('))
|
||||
)
|
||||
.map(c => c.column_name)
|
||||
);
|
||||
|
||||
const sets = [];
|
||||
const params = [];
|
||||
let idx = 1;
|
||||
for (const [k, v] of Object.entries(payload || {})) {
|
||||
if (!VALID_IDENT.test(k)) continue;
|
||||
if (!updatable.has(k)) continue;
|
||||
sets.push(`${q(k)} = $${idx++}`);
|
||||
params.push(v);
|
||||
}
|
||||
if (!sets.length) return { error: 'Nada para actualizar' };
|
||||
|
||||
const pks = await loadPrimaryKey(db, TABLE);
|
||||
if (pks.length !== 1) {
|
||||
const err = new Error('PK compuesta no soportada');
|
||||
err.http = { status: 400 };
|
||||
throw err;
|
||||
}
|
||||
params.push(id);
|
||||
|
||||
const { rows } = await db.query(
|
||||
`UPDATE ${q(TABLE)} SET ${sets.join(', ')} WHERE ${q(pks[0])} = $${idx} RETURNING *`,
|
||||
params
|
||||
);
|
||||
return rows[0] || null;
|
||||
});
|
||||
}
|
||||
|
||||
function q(ident) {
|
||||
return `"${String(ident).replace(/"/g, '""')}"`;
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
// packages/api/v1/repositories/db.mjs
|
||||
|
||||
import { poolTenants } from '@suitecoffee/db';
|
||||
|
||||
const VALID_IDENT = /^[a-zA-Z_][a-zA-Z0-9_]*$/;
|
||||
|
||||
export async function withTenantClient(req, fn, { trx = false } = {}) {
|
||||
const schema = req?.tenant?.schema;
|
||||
if (!schema || !VALID_IDENT.test(schema)) {
|
||||
throw new Error('Schema de tenant no resuelto/ inválido');
|
||||
}
|
||||
const client = await poolTenants.connect();
|
||||
try {
|
||||
if (trx) await client.query('BEGIN');
|
||||
await client.query(`SET LOCAL search_path = "${schema}", public`);
|
||||
const result = await fn(client);
|
||||
if (trx) await client.query('COMMIT');
|
||||
return result;
|
||||
} catch (e) {
|
||||
if (trx) await client.query('ROLLBACK');
|
||||
throw e;
|
||||
} finally {
|
||||
client.release();
|
||||
}
|
||||
}
|
||||
|
||||
export async function tquery(req, sql, params = [], opts = {}) {
|
||||
return withTenantClient(req, (c) => c.query(sql, params), opts);
|
||||
}
|
||||
Reference in New Issue
Block a user