This commit is contained in:
seb
2026-09-27 22:01:56 +02:00
parent 0cafa11a32
commit 87da854de4
12 changed files with 686 additions and 0 deletions

View File

@@ -0,0 +1,121 @@
import {
CREATE as MSSQL_CREATE,
} from '../../providers/mssql/queries/labeltemplate.js';
import {
CREATE as SQLITE_CREATE,
} from '../../providers/sqlite3/queries/labeltemplate.js';
import resolveSelectedCompanyAccess from '../companyuser/resolveSelectedCompanyAccess.js';
import makeRunner from '../runner.js';
const VALID_TYPES = new Set(['zpl']);
const VALID_DPIS = new Set([203, 300]);
function shape(row) {
return {
id: row.id,
company_id: row.company_id ?? row.companyId,
name: row.name,
width: row.width,
height: row.height,
dpi: row.dpi,
type: row.type,
content: typeof row.content === 'string' ? JSON.parse(row.content) : row.content,
thumb: row.thumb ?? null,
};
}
export default function create(provider) {
const access = resolveSelectedCompanyAccess(provider);
const isSqlite = provider?.dialect === 'sqlite3';
const createSql = isSqlite ? SQLITE_CREATE : MSSQL_CREATE;
/**
* Creates a new label template for the user's selected company.
*
* @param {number} googleAuthId - The id of the Google auth user.
* @param {object} input - Label template input.
* @param {string} input.name - Template name.
* @param {number} input.width - Canvas width in pixels.
* @param {number} input.height - Canvas height in pixels.
* @param {number} input.dpi - Printer DPI (203 or 300).
* @param {string} input.type - Template type ("zpl").
* @param {object} input.content - Template content (JSON).
* @param {Buffer|null} [input.thumb] - 50×50 1-bit PNG thumbnail buffer.
* @param {object} [tx] - Optional transaction to run within.
* @returns {Promise<{id: number, company_id: number, name: string, width: number, height: number, dpi: number, type: string, content: object, thumb: Buffer|null}>}
* @throws {Error} If required fields are missing or invalid.
*
* @example
* const template = await store.labeltemplate.create(googleAuthId, {
* name: 'Product Label',
* width: 448,
* height: 200,
* dpi: 203,
* type: 'zpl',
* content: { objects: [] },
* });
*/
return async (googleAuthId, input = {}, tx) => {
const name = typeof input.name === 'string' ? input.name.trim() : '';
if (!name) {
throw new Error('Name required');
}
const width = Number(input.width);
if (!Number.isInteger(width) || width <= 0) {
throw new Error('Invalid width');
}
const height = Number(input.height);
if (!Number.isInteger(height) || height <= 0) {
throw new Error('Invalid height');
}
const dpi = Number(input.dpi);
if (!VALID_DPIS.has(dpi)) {
throw new Error('Invalid dpi: must be 203 or 300');
}
const type = typeof input.type === 'string' ? input.type.trim() : '';
if (!type || !VALID_TYPES.has(type)) {
throw new Error('Invalid type: must be "zpl"');
}
if (input.content == null) {
throw new Error('Content required');
}
const content = typeof input.content === 'string'
? input.content
: JSON.stringify(input.content);
const thumb = input.thumb instanceof Buffer ? input.thumb : null;
const runner = makeRunner(provider, tx);
const { companyId } = await access(googleAuthId, runner);
const result = await runner.query(createSql, {
companyId,
name,
width,
height,
dpi,
type,
content,
thumb,
});
const row = result.rows[0] ?? {
id: result.insertId,
company_id: companyId,
name,
width,
height,
dpi,
type,
content,
thumb,
};
return shape(row);
};
}

View File

@@ -0,0 +1,35 @@
import { DELETE_BY_COMPANY_AND_ID as MSSQL_DELETE } from '../../providers/mssql/queries/labeltemplate.js';
import { DELETE_BY_COMPANY_AND_ID as SQLITE_DELETE } from '../../providers/sqlite3/queries/labeltemplate.js';
import resolveSelectedCompanyAccess from '../companyuser/resolveSelectedCompanyAccess.js';
import makeRunner from '../runner.js';
export default function del(provider) {
const access = resolveSelectedCompanyAccess(provider);
const sql =
provider?.dialect === 'sqlite3' ? SQLITE_DELETE : MSSQL_DELETE;
/**
* Deletes a label template for the user's selected company.
*
* @param {number} googleAuthId - The id of the Google auth user.
* @param {number} id - The label template ID to delete.
* @param {object} [tx] - Optional transaction to run within.
* @returns {Promise<void>}
* @throws {Error} If template is not found.
*/
return async (googleAuthId, id, tx) => {
const numericId = Number(id);
if (!Number.isInteger(numericId) || numericId <= 0) {
throw new Error('Label template not found');
}
const runner = makeRunner(provider, tx);
const { companyId } = await access(googleAuthId, runner);
const result = await runner.query(sql, { id: numericId, companyId });
if (!result.rowsAffected) {
throw new Error('Label template not found');
}
};
}

View File

@@ -0,0 +1,49 @@
import { GET_BY_COMPANY_AND_ID as MSSQL_GET_BY_ID } from '../../providers/mssql/queries/labeltemplate.js';
import { GET_BY_COMPANY_AND_ID as SQLITE_GET_BY_ID } from '../../providers/sqlite3/queries/labeltemplate.js';
import resolveSelectedCompanyAccess from '../companyuser/resolveSelectedCompanyAccess.js';
import makeRunner from '../runner.js';
function shape(row) {
return {
id: row.id,
company_id: row.company_id ?? row.companyId,
name: row.name,
width: row.width,
height: row.height,
dpi: row.dpi,
type: row.type,
content: typeof row.content === 'string' ? JSON.parse(row.content) : row.content,
thumb: row.thumb ?? null,
};
}
export default function getById(provider) {
const access = resolveSelectedCompanyAccess(provider);
const sql =
provider?.dialect === 'sqlite3' ? SQLITE_GET_BY_ID : MSSQL_GET_BY_ID;
/**
* Retrieves a label template by its ID for the user's selected company.
*
* @param {number} googleAuthId - The id of the Google auth user.
* @param {number} id - The label template ID.
* @param {object} [tx] - Optional transaction to run within.
* @returns {Promise<{id: number, company_id: number, name: string, width: number, height: number, dpi: number, type: string, content: object, thumb: Buffer|null}|undefined>}
*
* @example
* const template = await store.labeltemplate.getById(googleAuthId, 1);
*/
return async (googleAuthId, id, tx) => {
const numericId = Number(id);
if (!Number.isInteger(numericId) || numericId <= 0) {
return undefined;
}
const runner = makeRunner(provider, tx);
const { companyId } = await access(googleAuthId, runner);
const result = await runner.query(sql, { id: numericId, companyId });
const row = result.rows[0];
return row ? shape(row) : undefined;
};
}

View File

@@ -0,0 +1,44 @@
import { LIST_BY_COMPANY as MSSQL_LIST_BY_COMPANY } from '../../providers/mssql/queries/labeltemplate.js';
import { LIST_BY_COMPANY as SQLITE_LIST_BY_COMPANY } from '../../providers/sqlite3/queries/labeltemplate.js';
import resolveSelectedCompanyAccess from '../companyuser/resolveSelectedCompanyAccess.js';
import makeRunner from '../runner.js';
function shape(row) {
return {
id: row.id,
company_id: row.company_id ?? row.companyId,
name: row.name,
width: row.width,
height: row.height,
dpi: row.dpi,
type: row.type,
content: typeof row.content === 'string' ? JSON.parse(row.content) : row.content,
thumb: row.thumb ?? null,
};
}
export default function list(provider) {
const access = resolveSelectedCompanyAccess(provider);
const sql =
provider?.dialect === 'sqlite3'
? SQLITE_LIST_BY_COMPANY
: MSSQL_LIST_BY_COMPANY;
/**
* Lists all label templates for the user's selected company.
*
* @param {number} googleAuthId - The id of the Google auth user.
* @param {object} [tx] - Optional transaction to run within.
* @returns {Promise<Array<{id: number, company_id: number, name: string, width: number, height: number, dpi: number, type: string, content: object, thumb: Buffer|null}>>}
*
* @example
* const templates = await store.labeltemplate.list(42);
*/
return async (googleAuthId, tx) => {
const runner = makeRunner(provider, tx);
const { companyId } = await access(googleAuthId, runner);
const result = await runner.query(sql, { companyId });
return result.rows.map(shape);
};
}

View File

@@ -0,0 +1,141 @@
import {
GET_BY_COMPANY_AND_ID as MSSQL_GET_BY_ID,
UPDATE as MSSQL_UPDATE,
} from '../../providers/mssql/queries/labeltemplate.js';
import {
GET_BY_COMPANY_AND_ID as SQLITE_GET_BY_ID,
UPDATE as SQLITE_UPDATE,
} from '../../providers/sqlite3/queries/labeltemplate.js';
import resolveSelectedCompanyAccess from '../companyuser/resolveSelectedCompanyAccess.js';
import makeRunner from '../runner.js';
const VALID_TYPES = new Set(['zpl']);
const VALID_DPIS = new Set([203, 300]);
function shape(row) {
return {
id: row.id,
company_id: row.company_id ?? row.companyId,
name: row.name,
width: row.width,
height: row.height,
dpi: row.dpi,
type: row.type,
content: typeof row.content === 'string' ? JSON.parse(row.content) : row.content,
thumb: row.thumb ?? null,
};
}
export default function update(provider) {
const access = resolveSelectedCompanyAccess(provider);
const isSqlite = provider?.dialect === 'sqlite3';
const getSql = isSqlite ? SQLITE_GET_BY_ID : MSSQL_GET_BY_ID;
const updateSql = isSqlite ? SQLITE_UPDATE : MSSQL_UPDATE;
/**
* Updates a label template for the user's selected company.
*
* @param {number} googleAuthId - The id of the Google auth user.
* @param {number} id - The label template ID to update.
* @param {object} input - Update payload.
* @param {string} [input.name] - Template name.
* @param {number} [input.width] - Canvas width in pixels.
* @param {number} [input.height] - Canvas height in pixels.
* @param {number} [input.dpi] - Printer DPI (203 or 300).
* @param {string} [input.type] - Template type ("zpl").
* @param {object} [input.content] - Template content (JSON).
* @param {Buffer|null} [input.thumb] - 50×50 1-bit PNG thumbnail buffer.
* @param {object} [tx] - Optional transaction to run within.
* @returns {Promise<{id: number, company_id: number, name: string, width: number, height: number, dpi: number, type: string, content: object, thumb: Buffer|null}>}
* @throws {Error} If template not found or input invalid.
*/
return async (googleAuthId, id, input = {}, tx) => {
const numericId = Number(id);
if (!Number.isInteger(numericId) || numericId <= 0) {
throw new Error('Label template not found');
}
const runner = makeRunner(provider, tx);
const { companyId } = await access(googleAuthId, runner);
const existing = await runner.query(getSql, { id: numericId, companyId });
const current = existing.rows[0];
if (!current) {
throw new Error('Label template not found');
}
let name = current.name;
if (input.name !== undefined) {
const cleaned = typeof input.name === 'string' ? input.name.trim() : '';
if (!cleaned) throw new Error('Name required');
name = cleaned;
}
let width = current.width;
if (input.width !== undefined) {
const w = Number(input.width);
if (!Number.isInteger(w) || w <= 0) throw new Error('Invalid width');
width = w;
}
let height = current.height;
if (input.height !== undefined) {
const h = Number(input.height);
if (!Number.isInteger(h) || h <= 0) throw new Error('Invalid height');
height = h;
}
let dpi = current.dpi;
if (input.dpi !== undefined) {
const d = Number(input.dpi);
if (!VALID_DPIS.has(d)) throw new Error('Invalid dpi: must be 203 or 300');
dpi = d;
}
let type = current.type;
if (input.type !== undefined) {
const cleaned = typeof input.type === 'string' ? input.type.trim() : '';
if (!cleaned || !VALID_TYPES.has(cleaned)) throw new Error('Invalid type: must be "zpl"');
type = cleaned;
}
let content = current.content;
if (input.content !== undefined) {
if (input.content == null) throw new Error('Content required');
content = typeof input.content === 'string'
? input.content
: JSON.stringify(input.content);
}
let thumb = current.thumb ?? null;
if (input.thumb !== undefined) {
thumb = input.thumb instanceof Buffer ? input.thumb : null;
}
const result = await runner.query(updateSql, {
id: numericId,
companyId,
name,
width,
height,
dpi,
type,
content,
thumb,
});
const row = result.rows[0] ?? {
id: numericId,
company_id: companyId,
name,
width,
height,
dpi,
type,
content,
thumb,
};
return shape(row);
};
}