From 76ea6642f4e29f8e12d5ac0740efb46362f7e62d Mon Sep 17 00:00:00 2001 From: seb Date: Sat, 31 Oct 2026 21:02:50 +0100 Subject: [PATCH] u --- API.md | 6 + API.printerjob.md | 100 +++++++++++++++++ src/index.js | 13 +++ src/providers/mssql/downgrade/5_to_4.js | 13 +++ src/providers/mssql/purgeSql.js | 1 + src/providers/mssql/queries/printerjob.js | 115 ++++++++++++++++++++ src/providers/mssql/schema.js | 19 ++++ src/providers/mssql/upgrade/4_to_5.js | 15 +++ src/providers/sqlite3/downgrade/5_to_4.js | 13 +++ src/providers/sqlite3/queries/printerjob.js | 18 +++ src/providers/sqlite3/schema.js | 18 +++ src/providers/sqlite3/upgrade/4_to_5.js | 15 +++ src/store/printerjob/_shared.js | 14 +++ src/store/printerjob/create.js | 71 ++++++++++++ src/store/printerjob/delete.js | 46 ++++++++ src/store/printerjob/getById.js | 49 +++++++++ src/store/printerjob/list.js | 74 +++++++++++++ src/store/printerjob/updateState.js | 66 +++++++++++ 18 files changed, 666 insertions(+) create mode 100644 API.printerjob.md create mode 100644 src/providers/mssql/downgrade/5_to_4.js create mode 100644 src/providers/mssql/queries/printerjob.js create mode 100644 src/providers/mssql/upgrade/4_to_5.js create mode 100644 src/providers/sqlite3/downgrade/5_to_4.js create mode 100644 src/providers/sqlite3/queries/printerjob.js create mode 100644 src/providers/sqlite3/upgrade/4_to_5.js create mode 100644 src/store/printerjob/_shared.js create mode 100644 src/store/printerjob/create.js create mode 100644 src/store/printerjob/delete.js create mode 100644 src/store/printerjob/getById.js create mode 100644 src/store/printerjob/list.js create mode 100644 src/store/printerjob/updateState.js diff --git a/API.md b/API.md index 56b66f6..bfa637b 100644 --- a/API.md +++ b/API.md @@ -421,6 +421,12 @@ The store is exposed as a `LibDashStore` instance (see `src/index.js`). Each met - `touchLastseen(id: number, agentversion?: string|null, tx?: object): Promise<{id: number}>` - `update(googleAuthId: number, input: {id: number, name?: string, sort?: number}, tx?: object): Promise<{id: number, companyId: number, name: string, token: string|null, pairingpin: string|null, pairedAt: string|null, clientserial: string|null, clientmodel: string|null, clientstate: string|null, agentversion: string|null, lastseenAt: string|null, updatedAt: string, sort: number}>` - `updateState(id: number, clientstate: string|null, tx?: object): Promise<{id: number, companyId: number, name: string, token: string|null, pairingpin: string|null, pairedAt: string|null, clientserial: string|null, clientmodel: string|null, clientstate: string|null, agentversion: string|null, lastseenAt: string|null, updatedAt: string, sort: number}>` +- [printerjob](API.printerjob.md) + - `create(googleAuthId: number, input: {agentprinterId: number, data: string, type?: string, state?: string}, tx?: object): Promise` + - `delete(googleAuthId: number, id: number, tx?: object): Promise` + - `getById(auth: number|{agentId: number}, id: number, tx?: object): Promise` + - `list(auth: number|{agentId: number}, filters?: {agentprinterId?: number, state?: string}, tx?: object): Promise>` + - `updateState(auth: number|{agentId: number}, id: number, state: string, tx?: object): Promise` - [retoure](API.retoure.md) - `create(googleAuthId: number, body: {number?: string, date: string, entityId?: number, externalNumber?: string, lines?: Array}, tx?: object): Promise` - `createShipment(googleAuthId: number, retoureId: number, input: object, tx?: object): Promise` diff --git a/API.printerjob.md b/API.printerjob.md new file mode 100644 index 0000000..af3c05c --- /dev/null +++ b/API.printerjob.md @@ -0,0 +1,100 @@ +# printerjob + +This document is auto-generated from the JSDoc comments in `src/store` by running `npm run api-doc`. + +## `printerjob.create` + +*Source: `src/store/printerjob/create.js`* + +Creates a new printer job for an agent printer belonging to the user's selected company. + +**Parameters** + +| Name | Type | Description | +| --- | --- | --- | +| `googleAuthId` | `number` | The id of the Google auth user. | +| `input` | `object` | | +| `input.agentprinterId` | `number` | Target agent printer ID. | +| `input.data` | `string` | Job payload (ZPL code, etc.). | +| `[input.type]` | `string` | Job type ("zpl", "pdf", etc., defaults to agentprinter type or "zpl"). | +| `[input.state]` | `string` | Initial state (defaults to "pending"). | +| `[tx]` | `object` | Optional transaction to run within. | + +**Returns** + +`Promise` — Created printer job with printeragentId attached. + +## `printerjob.delete` + +*Source: `src/store/printerjob/delete.js`* + +Deletes a printer job belonging to the user's selected company. + +**Parameters** + +| Name | Type | Description | +| --- | --- | --- | +| `googleAuthId` | `number` | The id of the Google auth user. | +| `id` | `number` | Printer job ID. | +| `[tx]` | `object` | Optional transaction. | + +**Returns** + +`Promise` — The deleted job's snapshot. + +## `printerjob.getById` + +*Source: `src/store/printerjob/getById.js`* + +Retrieves a printer job by ID. If caller is an agent ({ agentId: number }), checks that the job belongs to that printer agent. If caller is a user (googleAuthId: number), checks that the job belongs to the selected company. + +**Parameters** + +| Name | Type | Description | +| --- | --- | --- | +| `auth` | `number|{agentId: number}` | User's googleAuthId or { agentId }. | +| `id` | `number` | Printer job ID. | +| `[tx]` | `object` | Optional transaction. | + +**Returns** + +`Promise` + +## `printerjob.list` + +*Source: `src/store/printerjob/list.js`* + +Lists printer jobs. If caller is an agent ({ agentId: number }), lists jobs for that agent. If caller is a user (googleAuthId: number), lists jobs for the selected company. + +**Parameters** + +| Name | Type | Description | +| --- | --- | --- | +| `auth` | `number|{agentId: number}` | User's googleAuthId or { agentId }. | +| `[filters]` | `object` | | +| `[filters.agentprinterId]` | `number` | Filter by agentprinterId (company callers only). | +| `[filters.state]` | `string` | Filter by job state (e.g. "pending"). | +| `[tx]` | `object` | Optional transaction. | + +**Returns** + +`Promise>` + +## `printerjob.updateState` + +*Source: `src/store/printerjob/updateState.js`* + +Updates a printer job's state ("processing", "completed", "cancelled", etc.). + +**Parameters** + +| Name | Type | Description | +| --- | --- | --- | +| `auth` | `number|{agentId: number}` | User's googleAuthId or { agentId }. | +| `id` | `number` | Printer job ID. | +| `state` | `string` | New state. | +| `[tx]` | `object` | Optional transaction. | + +**Returns** + +`Promise` — Updated printer job. diff --git a/src/index.js b/src/index.js index af56fc8..d976bd6 100644 --- a/src/index.js +++ b/src/index.js @@ -215,6 +215,11 @@ import agentprinterFindOrCreate from './store/agentprinter/findOrCreate.js'; import agentprinterUpdate from './store/agentprinter/update.js'; import agentprinterOrder from './store/agentprinter/order.js'; import agentprinterDelete from './store/agentprinter/delete.js'; +import printerjobCreate from './store/printerjob/create.js'; +import printerjobGetById from './store/printerjob/getById.js'; +import printerjobList from './store/printerjob/list.js'; +import printerjobUpdateState from './store/printerjob/updateState.js'; +import printerjobDelete from './store/printerjob/delete.js'; import modelconversationCreate from './store/modelconversation/create.js'; import modelconversationGet from './store/modelconversation/get.js'; import modelconversationList from './store/modelconversation/list.js'; @@ -673,6 +678,14 @@ export default class LibDashStore { delete: agentprinterDelete(provider), }; + this.printerjob = { + create: printerjobCreate(provider), + getById: printerjobGetById(provider), + list: printerjobList(provider), + updateState: printerjobUpdateState(provider), + delete: printerjobDelete(provider), + }; + this.modelconversation = { create: modelconversationCreate(provider), get: modelconversationGet(provider), diff --git a/src/providers/mssql/downgrade/5_to_4.js b/src/providers/mssql/downgrade/5_to_4.js new file mode 100644 index 0000000..21c0cb6 --- /dev/null +++ b/src/providers/mssql/downgrade/5_to_4.js @@ -0,0 +1,13 @@ +/** + * Schema 5 → 4 (MSSQL): + * - Drops printerjob table + * @param {{ query: (sql: string, params?: object) => Promise, assertVersion: (v: number) => Promise, setVersion: (v: number) => Promise }} ctx + */ +export default async function downgrade({ query, assertVersion, setVersion }) { + await assertVersion(5); + + await query(`DROP TABLE IF EXISTS printerjob;`); + + await setVersion(4); +} + diff --git a/src/providers/mssql/purgeSql.js b/src/providers/mssql/purgeSql.js index 8095100..03f5569 100644 --- a/src/providers/mssql/purgeSql.js +++ b/src/providers/mssql/purgeSql.js @@ -28,6 +28,7 @@ export const TABLES = [ 'posterminalobituary', 'posterminalcategory', 'posterminal', + 'printerjob', 'agentprinter', 'printeragent', 'printer', diff --git a/src/providers/mssql/queries/printerjob.js b/src/providers/mssql/queries/printerjob.js new file mode 100644 index 0000000..e4c70f3 --- /dev/null +++ b/src/providers/mssql/queries/printerjob.js @@ -0,0 +1,115 @@ +export const PRINTERJOB_COLUMNS = ` + pj.id, + pj.agentprinter_id AS agentprinterId, + pj.type, + pj.state, + pj.data, + pj.created_at AS createdAt, + pj.completed_at AS completedAt +`; + +export const CREATE = ` +INSERT INTO printerjob (agentprinter_id, type, state, data, created_at) +OUTPUT + INSERTED.id, + INSERTED.agentprinter_id AS agentprinterId, + INSERTED.type, + INSERTED.state, + INSERTED.data, + INSERTED.created_at AS createdAt, + INSERTED.completed_at AS completedAt +VALUES (@agentprinterId, @type, @state, @data, SYSUTCDATETIME()) +`; + +export const GET_BY_ID_FOR_COMPANY = ` +SELECT +${PRINTERJOB_COLUMNS}, + ap.printeragent_id AS printeragentId +FROM printerjob pj +JOIN agentprinter ap ON ap.id = pj.agentprinter_id +JOIN printeragent pa ON pa.id = ap.printeragent_id +WHERE pj.id = @id AND pa.company_id = @companyId +`; + +export const GET_BY_ID_FOR_AGENT = ` +SELECT +${PRINTERJOB_COLUMNS}, + ap.printeragent_id AS printeragentId +FROM printerjob pj +JOIN agentprinter ap ON ap.id = pj.agentprinter_id +WHERE pj.id = @id AND ap.printeragent_id = @agentId +`; + +export const LIST_BY_COMPANY = ` +SELECT +${PRINTERJOB_COLUMNS}, + ap.printeragent_id AS printeragentId +FROM printerjob pj +JOIN agentprinter ap ON ap.id = pj.agentprinter_id +JOIN printeragent pa ON pa.id = ap.printeragent_id +WHERE pa.company_id = @companyId +ORDER BY pj.id DESC +`; + +export const LIST_BY_COMPANY_AND_AGENTPRINTER = ` +SELECT +${PRINTERJOB_COLUMNS}, + ap.printeragent_id AS printeragentId +FROM printerjob pj +JOIN agentprinter ap ON ap.id = pj.agentprinter_id +JOIN printeragent pa ON pa.id = ap.printeragent_id +WHERE pa.company_id = @companyId AND pj.agentprinter_id = @agentprinterId +ORDER BY pj.id DESC +`; + +export const LIST_BY_AGENT = ` +SELECT +${PRINTERJOB_COLUMNS}, + ap.printeragent_id AS printeragentId +FROM printerjob pj +JOIN agentprinter ap ON ap.id = pj.agentprinter_id +WHERE ap.printeragent_id = @agentId +ORDER BY pj.id ASC +`; + +export const LIST_BY_AGENT_AND_STATE = ` +SELECT +${PRINTERJOB_COLUMNS}, + ap.printeragent_id AS printeragentId +FROM printerjob pj +JOIN agentprinter ap ON ap.id = pj.agentprinter_id +WHERE ap.printeragent_id = @agentId AND pj.state = @state +ORDER BY pj.id ASC +`; + +export const UPDATE_STATE_FOR_AGENT = ` +UPDATE printerjob +SET + state = @state, + completed_at = CASE WHEN @state IN ('completed', 'cancelled') THEN SYSUTCDATETIME() ELSE completed_at END +WHERE id = @id AND agentprinter_id IN ( + SELECT ap.id FROM agentprinter ap WHERE ap.printeragent_id = @agentId +) +`; + +export const UPDATE_STATE_FOR_COMPANY = ` +UPDATE printerjob +SET + state = @state, + completed_at = CASE WHEN @state IN ('completed', 'cancelled') THEN SYSUTCDATETIME() ELSE completed_at END +WHERE id = @id AND agentprinter_id IN ( + SELECT ap.id FROM agentprinter ap + JOIN printeragent pa ON pa.id = ap.printeragent_id + WHERE pa.company_id = @companyId +) +`; + +export const DELETE_BY_COMPANY_AND_ID = ` +DELETE FROM printerjob +WHERE id = @id AND agentprinter_id IN ( + SELECT ap.id FROM agentprinter ap + JOIN printeragent pa ON pa.id = ap.printeragent_id + WHERE pa.company_id = @companyId +) +`; + diff --git a/src/providers/mssql/schema.js b/src/providers/mssql/schema.js index e97b740..4e6323a 100644 --- a/src/providers/mssql/schema.js +++ b/src/providers/mssql/schema.js @@ -1050,6 +1050,24 @@ BEGIN END `; +export const INIT_PRINTERJOB = ` +IF NOT EXISTS (SELECT * FROM sys.tables WHERE name = 'printerjob') +BEGIN + CREATE TABLE printerjob ( + id INT IDENTITY(1,1) PRIMARY KEY, + agentprinter_id INT NOT NULL, + type NVARCHAR(50) NOT NULL, + state NVARCHAR(50) NOT NULL CONSTRAINT DF_printerjob_state DEFAULT 'pending', + data NVARCHAR(MAX) NOT NULL, + created_at DATETIME2 NOT NULL CONSTRAINT DF_printerjob_created_at DEFAULT SYSUTCDATETIME(), + completed_at DATETIME2 NULL, + CONSTRAINT FK_printerjob_agentprinter FOREIGN KEY (agentprinter_id) REFERENCES agentprinter(id) ON DELETE CASCADE + ); + CREATE INDEX IX_printerjob_agentprinter_id ON printerjob(agentprinter_id); + CREATE INDEX IX_printerjob_state ON printerjob(state); +END +`; + export const INIT_LABELTEMPLATE = ` IF NOT EXISTS (SELECT * FROM sys.tables WHERE name = 'labeltemplate') BEGIN @@ -1707,6 +1725,7 @@ export const SCHEMA_SCRIPTS = [ INIT_PRINTER, INIT_PRINTERAGENT, INIT_AGENTPRINTER, + INIT_PRINTERJOB, INIT_LABELTEMPLATE, INIT_FILE, INIT_MANIFESTATTACHMENT, diff --git a/src/providers/mssql/upgrade/4_to_5.js b/src/providers/mssql/upgrade/4_to_5.js new file mode 100644 index 0000000..d1dee9b --- /dev/null +++ b/src/providers/mssql/upgrade/4_to_5.js @@ -0,0 +1,15 @@ +import { INIT_PRINTERJOB } from '../schema.js'; + +/** + * Schema 4 → 5 (MSSQL): + * - Creates printerjob table + * @param {{ query: (sql: string, params?: object) => Promise, assertVersion: (v: number) => Promise, setVersion: (v: number) => Promise }} ctx + */ +export default async function upgrade({ query, assertVersion, setVersion }) { + await assertVersion(4); + + await query(INIT_PRINTERJOB); + + await setVersion(5); +} + diff --git a/src/providers/sqlite3/downgrade/5_to_4.js b/src/providers/sqlite3/downgrade/5_to_4.js new file mode 100644 index 0000000..8a706b3 --- /dev/null +++ b/src/providers/sqlite3/downgrade/5_to_4.js @@ -0,0 +1,13 @@ +/** + * Schema 5 → 4 (SQLite): + * - Drops printerjob table + * @param {{ exec: (sql: string) => void, assertVersion: (v: number) => Promise, setVersion: (v: number) => Promise }} ctx + */ +export default async function downgrade({ exec, assertVersion, setVersion }) { + await assertVersion(5); + + exec(`DROP TABLE IF EXISTS printerjob;`); + + await setVersion(4); +} + diff --git a/src/providers/sqlite3/queries/printerjob.js b/src/providers/sqlite3/queries/printerjob.js new file mode 100644 index 0000000..ce04dc6 --- /dev/null +++ b/src/providers/sqlite3/queries/printerjob.js @@ -0,0 +1,18 @@ +import * as source from '../../mssql/queries/printerjob.js'; +import { translateQueries } from './translate.js'; + +const queries = translateQueries(source, 'printerjob'); +export const { + PRINTERJOB_COLUMNS, + CREATE, + GET_BY_ID_FOR_COMPANY, + GET_BY_ID_FOR_AGENT, + LIST_BY_COMPANY, + LIST_BY_COMPANY_AND_AGENTPRINTER, + LIST_BY_AGENT, + LIST_BY_AGENT_AND_STATE, + UPDATE_STATE_FOR_AGENT, + UPDATE_STATE_FOR_COMPANY, + DELETE_BY_COMPANY_AND_ID, +} = queries; + diff --git a/src/providers/sqlite3/schema.js b/src/providers/sqlite3/schema.js index b4ba58f..acb10a3 100644 --- a/src/providers/sqlite3/schema.js +++ b/src/providers/sqlite3/schema.js @@ -728,6 +728,23 @@ CREATE INDEX IF NOT EXISTS IX_agentprinter_printeragent_id ON agentprinter(printeragent_id); `; +export const INIT_PRINTERJOB = ` +CREATE TABLE IF NOT EXISTS printerjob ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + agentprinter_id INTEGER NOT NULL, + type TEXT NOT NULL, + state TEXT NOT NULL DEFAULT 'pending', + data TEXT NOT NULL, + created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')), + completed_at TEXT, + FOREIGN KEY (agentprinter_id) REFERENCES agentprinter(id) ON DELETE CASCADE +); +CREATE INDEX IF NOT EXISTS IX_printerjob_agentprinter_id + ON printerjob(agentprinter_id); +CREATE INDEX IF NOT EXISTS IX_printerjob_state + ON printerjob(state); +`; + export const INIT_LABELTEMPLATE = ` CREATE TABLE IF NOT EXISTS labeltemplate ( id INTEGER PRIMARY KEY AUTOINCREMENT, @@ -1263,6 +1280,7 @@ export const SCHEMA_SCRIPTS = [ INIT_PRINTER, INIT_PRINTERAGENT, INIT_AGENTPRINTER, + INIT_PRINTERJOB, INIT_LABELTEMPLATE, INIT_FILE, INIT_MANIFESTATTACHMENT, diff --git a/src/providers/sqlite3/upgrade/4_to_5.js b/src/providers/sqlite3/upgrade/4_to_5.js new file mode 100644 index 0000000..4efbe10 --- /dev/null +++ b/src/providers/sqlite3/upgrade/4_to_5.js @@ -0,0 +1,15 @@ +import { INIT_PRINTERJOB } from '../schema.js'; + +/** + * Schema 4 → 5 (SQLite): + * - Creates printerjob table + * @param {{ exec: (sql: string) => void, assertVersion: (v: number) => Promise, setVersion: (v: number) => Promise }} ctx + */ +export default async function upgrade({ exec, assertVersion, setVersion }) { + await assertVersion(4); + + exec(INIT_PRINTERJOB); + + await setVersion(5); +} + diff --git a/src/store/printerjob/_shared.js b/src/store/printerjob/_shared.js new file mode 100644 index 0000000..2b417d1 --- /dev/null +++ b/src/store/printerjob/_shared.js @@ -0,0 +1,14 @@ +export function shape(row) { + if (!row) return null; + return { + id: row.id, + agentprinterId: row.agentprinterId, + printeragentId: row.printeragentId != null ? Number(row.printeragentId) : null, + type: row.type, + state: row.state, + data: row.data, + createdAt: row.createdAt instanceof Date ? row.createdAt.toISOString() : (row.createdAt ?? null), + completedAt: row.completedAt instanceof Date ? row.completedAt.toISOString() : (row.completedAt ?? null), + }; +} + diff --git a/src/store/printerjob/create.js b/src/store/printerjob/create.js new file mode 100644 index 0000000..b7eb20c --- /dev/null +++ b/src/store/printerjob/create.js @@ -0,0 +1,71 @@ +import { CREATE as MSSQL_CREATE } from '../../providers/mssql/queries/printerjob.js'; +import { CREATE as SQLITE_CREATE } from '../../providers/sqlite3/queries/printerjob.js'; +import resolveSelectedCompanyAccess from '../companyuser/resolveSelectedCompanyAccess.js'; +import makeRunner from '../runner.js'; +import { shape } from './_shared.js'; + +const VERIFY_AGENTPRINTER = ` +SELECT ap.id, ap.printeragent_id AS printeragentId, ap.type, ap.enabled +FROM agentprinter ap +JOIN printeragent pa ON pa.id = ap.printeragent_id +WHERE ap.id = @agentprinterId AND pa.company_id = @companyId +`; + +export default function create(provider) { + const access = resolveSelectedCompanyAccess(provider); + const isSqlite = provider?.dialect === 'sqlite3'; + const createSql = isSqlite ? SQLITE_CREATE : MSSQL_CREATE; + + /** + * Creates a new printer job for an agent printer belonging to the user's selected company. + * + * @param {number} googleAuthId - The id of the Google auth user. + * @param {object} input + * @param {number} input.agentprinterId - Target agent printer ID. + * @param {string} input.data - Job payload (ZPL code, etc.). + * @param {string} [input.type] - Job type ("zpl", "pdf", etc., defaults to agentprinter type or "zpl"). + * @param {string} [input.state] - Initial state (defaults to "pending"). + * @param {object} [tx] - Optional transaction to run within. + * @returns {Promise} Created printer job with printeragentId attached. + */ + return async (googleAuthId, input = {}, tx) => { + const runner = makeRunner(provider, tx); + const { companyId } = await access(googleAuthId, runner); + + const apId = Number(input.agentprinterId ?? input.agentprinter_id); + if (!Number.isInteger(apId) || apId <= 0) { + throw new Error('agentprinterId is required'); + } + + const data = input.data != null ? String(input.data) : ''; + if (!data.trim()) { + throw new Error('data is required'); + } + + const verified = await runner.query(VERIFY_AGENTPRINTER, { agentprinterId: apId, companyId }); + if (verified.rows.length === 0) { + throw new Error('Agentprinter not found'); + } + + const apRow = verified.rows[0]; + const printeragentId = Number(apRow.printeragentId); + const type = typeof input.type === 'string' && input.type.trim() + ? input.type.trim().toLowerCase() + : (apRow.type ? String(apRow.type).toLowerCase() : 'zpl'); + const state = typeof input.state === 'string' && input.state.trim() + ? input.state.trim().toLowerCase() + : 'pending'; + + const result = await runner.query(createSql, { + agentprinterId: apId, + type, + state, + data, + }); + + const job = shape(result.rows[0]); + job.printeragentId = printeragentId; + return job; + }; +} + diff --git a/src/store/printerjob/delete.js b/src/store/printerjob/delete.js new file mode 100644 index 0000000..c97eaff --- /dev/null +++ b/src/store/printerjob/delete.js @@ -0,0 +1,46 @@ +import { + DELETE_BY_COMPANY_AND_ID as MSSQL_DELETE, + GET_BY_ID_FOR_COMPANY as MSSQL_GET, +} from '../../providers/mssql/queries/printerjob.js'; +import { + DELETE_BY_COMPANY_AND_ID as SQLITE_DELETE, + GET_BY_ID_FOR_COMPANY as SQLITE_GET, +} from '../../providers/sqlite3/queries/printerjob.js'; +import resolveSelectedCompanyAccess from '../companyuser/resolveSelectedCompanyAccess.js'; +import makeRunner from '../runner.js'; +import { shape } from './_shared.js'; + +export default function deleteJob(provider) { + const access = resolveSelectedCompanyAccess(provider); + const isSqlite = provider?.dialect === 'sqlite3'; + const deleteSql = isSqlite ? SQLITE_DELETE : MSSQL_DELETE; + const getSql = isSqlite ? SQLITE_GET : MSSQL_GET; + + /** + * Deletes a printer job belonging to the user's selected company. + * + * @param {number} googleAuthId - The id of the Google auth user. + * @param {number} id - Printer job ID. + * @param {object} [tx] - Optional transaction. + * @returns {Promise} The deleted job's snapshot. + */ + return async (googleAuthId, id, tx) => { + const jId = Number(id); + if (!Number.isInteger(jId) || jId <= 0) { + throw new Error('Printerjob not found'); + } + + const runner = makeRunner(provider, tx); + const { companyId } = await access(googleAuthId, runner); + + const existing = await runner.query(getSql, { id: jId, companyId }); + if (existing.rows.length === 0) { + throw new Error('Printerjob not found'); + } + + const job = shape(existing.rows[0]); + await runner.query(deleteSql, { id: jId, companyId }); + return job; + }; +} + diff --git a/src/store/printerjob/getById.js b/src/store/printerjob/getById.js new file mode 100644 index 0000000..a0c29cb --- /dev/null +++ b/src/store/printerjob/getById.js @@ -0,0 +1,49 @@ +import { + GET_BY_ID_FOR_COMPANY as MSSQL_GET_BY_ID_FOR_COMPANY, + GET_BY_ID_FOR_AGENT as MSSQL_GET_BY_ID_FOR_AGENT, +} from '../../providers/mssql/queries/printerjob.js'; +import { + GET_BY_ID_FOR_COMPANY as SQLITE_GET_BY_ID_FOR_COMPANY, + GET_BY_ID_FOR_AGENT as SQLITE_GET_BY_ID_FOR_AGENT, +} from '../../providers/sqlite3/queries/printerjob.js'; +import resolveSelectedCompanyAccess from '../companyuser/resolveSelectedCompanyAccess.js'; +import makeRunner from '../runner.js'; +import { shape } from './_shared.js'; + +export default function getById(provider) { + const access = resolveSelectedCompanyAccess(provider); + const isSqlite = provider?.dialect === 'sqlite3'; + const getForCompanySql = isSqlite ? SQLITE_GET_BY_ID_FOR_COMPANY : MSSQL_GET_BY_ID_FOR_COMPANY; + const getForAgentSql = isSqlite ? SQLITE_GET_BY_ID_FOR_AGENT : MSSQL_GET_BY_ID_FOR_AGENT; + + /** + * Retrieves a printer job by ID. + * If caller is an agent ({ agentId: number }), checks that the job belongs to that printer agent. + * If caller is a user (googleAuthId: number), checks that the job belongs to the selected company. + * + * @param {number|{agentId: number}} auth - User's googleAuthId or { agentId }. + * @param {number} id - Printer job ID. + * @param {object} [tx] - Optional transaction. + * @returns {Promise} + */ + return async (auth, id, tx) => { + const jId = Number(id); + if (!Number.isInteger(jId) || jId <= 0) { + return null; + } + + const runner = makeRunner(provider, tx); + + if (typeof auth === 'object' && auth !== null && auth.agentId != null) { + const aId = Number(auth.agentId); + if (!Number.isInteger(aId) || aId <= 0) return null; + const result = await runner.query(getForAgentSql, { id: jId, agentId: aId }); + return result.rows.length > 0 ? shape(result.rows[0]) : null; + } + + const { companyId } = await access(auth, runner); + const result = await runner.query(getForCompanySql, { id: jId, companyId }); + return result.rows.length > 0 ? shape(result.rows[0]) : null; + }; +} + diff --git a/src/store/printerjob/list.js b/src/store/printerjob/list.js new file mode 100644 index 0000000..c7cefec --- /dev/null +++ b/src/store/printerjob/list.js @@ -0,0 +1,74 @@ +import { + LIST_BY_COMPANY as MSSQL_LIST_BY_COMPANY, + LIST_BY_COMPANY_AND_AGENTPRINTER as MSSQL_LIST_BY_COMPANY_AND_AGENTPRINTER, + LIST_BY_AGENT as MSSQL_LIST_BY_AGENT, + LIST_BY_AGENT_AND_STATE as MSSQL_LIST_BY_AGENT_AND_STATE, +} from '../../providers/mssql/queries/printerjob.js'; +import { + LIST_BY_COMPANY as SQLITE_LIST_BY_COMPANY, + LIST_BY_COMPANY_AND_AGENTPRINTER as SQLITE_LIST_BY_COMPANY_AND_AGENTPRINTER, + LIST_BY_AGENT as SQLITE_LIST_BY_AGENT, + LIST_BY_AGENT_AND_STATE as SQLITE_LIST_BY_AGENT_AND_STATE, +} from '../../providers/sqlite3/queries/printerjob.js'; +import resolveSelectedCompanyAccess from '../companyuser/resolveSelectedCompanyAccess.js'; +import makeRunner from '../runner.js'; +import { shape } from './_shared.js'; + +export default function list(provider) { + const access = resolveSelectedCompanyAccess(provider); + const isSqlite = provider?.dialect === 'sqlite3'; + const listCompanySql = isSqlite ? SQLITE_LIST_BY_COMPANY : MSSQL_LIST_BY_COMPANY; + const listCompanyApSql = isSqlite ? SQLITE_LIST_BY_COMPANY_AND_AGENTPRINTER : MSSQL_LIST_BY_COMPANY_AND_AGENTPRINTER; + const listAgentSql = isSqlite ? SQLITE_LIST_BY_AGENT : MSSQL_LIST_BY_AGENT; + const listAgentStateSql = isSqlite ? SQLITE_LIST_BY_AGENT_AND_STATE : MSSQL_LIST_BY_AGENT_AND_STATE; + + /** + * Lists printer jobs. + * If caller is an agent ({ agentId: number }), lists jobs for that agent. + * If caller is a user (googleAuthId: number), lists jobs for the selected company. + * + * @param {number|{agentId: number}} auth - User's googleAuthId or { agentId }. + * @param {object} [filters] + * @param {number} [filters.agentprinterId] - Filter by agentprinterId (company callers only). + * @param {string} [filters.state] - Filter by job state (e.g. "pending"). + * @param {object} [tx] - Optional transaction. + * @returns {Promise>} + */ + return async (auth, filters = {}, tx) => { + const runner = makeRunner(provider, tx); + + if (typeof auth === 'object' && auth !== null && auth.agentId != null) { + const aId = Number(auth.agentId); + if (!Number.isInteger(aId) || aId <= 0) return []; + const state = typeof filters.state === 'string' && filters.state.trim() + ? filters.state.trim().toLowerCase() + : null; + + let result; + if (state) { + result = await runner.query(listAgentStateSql, { agentId: aId, state }); + } else { + result = await runner.query(listAgentSql, { agentId: aId }); + } + return result.rows.map(shape); + } + + const { companyId } = await access(auth, runner); + const apId = filters.agentprinterId != null ? Number(filters.agentprinterId) : null; + + let result; + if (apId && Number.isInteger(apId) && apId > 0) { + result = await runner.query(listCompanyApSql, { companyId, agentprinterId: apId }); + } else { + result = await runner.query(listCompanySql, { companyId }); + } + + let rows = result.rows.map(shape); + if (filters.state) { + const targetState = String(filters.state).trim().toLowerCase(); + rows = rows.filter((r) => r.state === targetState); + } + return rows; + }; +} + diff --git a/src/store/printerjob/updateState.js b/src/store/printerjob/updateState.js new file mode 100644 index 0000000..c9ebf55 --- /dev/null +++ b/src/store/printerjob/updateState.js @@ -0,0 +1,66 @@ +import { + UPDATE_STATE_FOR_COMPANY as MSSQL_UPDATE_COMPANY, + UPDATE_STATE_FOR_AGENT as MSSQL_UPDATE_AGENT, + GET_BY_ID_FOR_COMPANY as MSSQL_GET_COMPANY, + GET_BY_ID_FOR_AGENT as MSSQL_GET_AGENT, +} from '../../providers/mssql/queries/printerjob.js'; +import { + UPDATE_STATE_FOR_COMPANY as SQLITE_UPDATE_COMPANY, + UPDATE_STATE_FOR_AGENT as SQLITE_UPDATE_AGENT, + GET_BY_ID_FOR_COMPANY as SQLITE_GET_COMPANY, + GET_BY_ID_FOR_AGENT as SQLITE_GET_AGENT, +} from '../../providers/sqlite3/queries/printerjob.js'; +import resolveSelectedCompanyAccess from '../companyuser/resolveSelectedCompanyAccess.js'; +import makeRunner from '../runner.js'; +import { shape } from './_shared.js'; + +const VALID_STATES = new Set(['pending', 'processing', 'completed', 'cancelled', 'failed']); + +export default function updateState(provider) { + const access = resolveSelectedCompanyAccess(provider); + const isSqlite = provider?.dialect === 'sqlite3'; + const updateCompanySql = isSqlite ? SQLITE_UPDATE_COMPANY : MSSQL_UPDATE_COMPANY; + const updateAgentSql = isSqlite ? SQLITE_UPDATE_AGENT : MSSQL_UPDATE_AGENT; + const getCompanySql = isSqlite ? SQLITE_GET_COMPANY : MSSQL_GET_COMPANY; + const getAgentSql = isSqlite ? SQLITE_GET_AGENT : MSSQL_GET_AGENT; + + /** + * Updates a printer job's state ("processing", "completed", "cancelled", etc.). + * + * @param {number|{agentId: number}} auth - User's googleAuthId or { agentId }. + * @param {number} id - Printer job ID. + * @param {string} state - New state. + * @param {object} [tx] - Optional transaction. + * @returns {Promise} Updated printer job. + */ + return async (auth, id, state, tx) => { + const jId = Number(id); + if (!Number.isInteger(jId) || jId <= 0) { + throw new Error('Printerjob not found'); + } + + const normalizedState = typeof state === 'string' ? state.trim().toLowerCase() : ''; + if (!VALID_STATES.has(normalizedState)) { + throw new Error(`Invalid printerjob state: ${state}`); + } + + const runner = makeRunner(provider, tx); + + if (typeof auth === 'object' && auth !== null && auth.agentId != null) { + const aId = Number(auth.agentId); + if (!Number.isInteger(aId) || aId <= 0) throw new Error('Not authorized'); + + await runner.query(updateAgentSql, { id: jId, agentId: aId, state: normalizedState }); + const fetched = await runner.query(getAgentSql, { id: jId, agentId: aId }); + if (fetched.rows.length === 0) throw new Error('Printerjob not found'); + return shape(fetched.rows[0]); + } + + const { companyId } = await access(auth, runner); + await runner.query(updateCompanySql, { id: jId, companyId, state: normalizedState }); + const fetched = await runner.query(getCompanySql, { id: jId, companyId }); + if (fetched.rows.length === 0) throw new Error('Printerjob not found'); + return shape(fetched.rows[0]); + }; +} +