This commit is contained in:
seb
2026-10-31 20:28:39 +01:00
parent c7d1b1a5d2
commit f612c87b6e
25 changed files with 757 additions and 40 deletions

119
API.agentprinter.md Normal file
View File

@@ -0,0 +1,119 @@
# agentprinter
This document is auto-generated from the JSDoc comments in `src/store` by running `npm run api-doc`.
## `agentprinter.delete`
*Source: `src/store/agentprinter/delete.js`*
Deletes an agent printer by id for the user's selected company.
**Parameters**
| Name | Type | Description |
| --- | --- | --- |
| `googleAuthId` | `number` | The id of the Google auth user. |
| `id` | `number` | The agent printer id. |
| `[tx]` | `object` | Optional transaction to run within. |
**Returns**
`Promise<{id: number}>`
## `agentprinter.findOrCreate`
*Source: `src/store/agentprinter/findOrCreate.js`*
Finds or creates an agentprinter by (printeragentId, type, model, serial) combo.
**Parameters**
| Name | Type | Description |
| --- | --- | --- |
| `printeragentId` | `number` | The parent printer agent id. |
| `input` | `object` | |
| `input.type` | `string` | Printer type ("zpl", "esc/pos", etc.). |
| `[input.model]` | `string|null` | Discovered model name. |
| `[input.serial]` | `string|null` | Discovered serial number. |
| `[tx]` | `object` | Optional transaction to run within. |
**Returns**
`Promise<{created: boolean, agentprinter: {id: number, printeragentId: number, type: string, model: string|null, serial: string|null, enabled: boolean, sort: number}}>`
## `agentprinter.getById`
*Source: `src/store/agentprinter/getById.js`*
Retrieves an agent printer by id, scoped to the user's selected company.
**Parameters**
| Name | Type | Description |
| --- | --- | --- |
| `googleAuthId` | `number` | The id of the Google auth user. |
| `id` | `number` | The agent printer id. |
| `[tx]` | `object` | Optional transaction to run within. |
**Returns**
`Promise<{id: number, printeragentId: number, type: string, model: string|null, serial: string|null, enabled: boolean, sort: number}|null>`
## `agentprinter.list`
*Source: `src/store/agentprinter/list.js`*
Lists agent printers of the user's selected company, optionally filtered by printer agent.
**Parameters**
| Name | Type | Description |
| --- | --- | --- |
| `googleAuthId` | `number` | The id of the Google auth user. |
| `[printeragentId]` | `number` | Optional printer agent id filter. |
| `[tx]` | `object` | Optional transaction to run within. |
**Returns**
`Promise<Array<{id: number, printeragentId: number, type: string, model: string|null, serial: string|null, enabled: boolean, sort: number}>>`
## `agentprinter.order`
*Source: `src/store/agentprinter/order.js`*
Reorders agent printers for a given printer agent within the user's selected company.
**Parameters**
| Name | Type | Description |
| --- | --- | --- |
| `googleAuthId` | `number` | The id of the Google auth user. |
| `input` | `object` | |
| `input.printeragentId` | `number` | The printer agent id. |
| `input.agentprinterIds` | `number[]` | Complete ordered list of agent printer IDs. |
| `[tx]` | `object` | Optional transaction to run within. |
**Returns**
`Promise<{agentprinterIds: number[]}>`
## `agentprinter.update`
*Source: `src/store/agentprinter/update.js`*
Updates enabled and/or sort of an agent printer for the user's selected company.
**Parameters**
| Name | Type | Description |
| --- | --- | --- |
| `googleAuthId` | `number` | The id of the Google auth user. |
| `input` | `object` | |
| `input.id` | `number` | Agent printer id. |
| `[input.enabled]` | `boolean` | Whether the printer is enabled. |
| `[input.sort]` | `number` | Display order integer. |
| `[tx]` | `object` | Optional transaction to run within. |
**Returns**
`Promise<{id: number, printeragentId: number, type: string, model: string|null, serial: string|null, enabled: boolean, sort: number}>`

7
API.md
View File

@@ -344,6 +344,13 @@ The store is exposed as a `LibDashStore` instance (see `src/index.js`). Each met
- `create(googleAuthId: number, input: {type: string, name: string}, tx?: object): Promise<{id: number, companyId: number, type: string, name: string}>`
- `delete(googleAuthId: number, id: number, tx?: object): Promise<void>`
- `list(googleAuthId: number, tx?: object): Promise<Array<{id: number, companyId: number, type: string, name: string}>>`
- [agentprinter](API.agentprinter.md)
- `delete(googleAuthId: number, id: number, tx?: object): Promise<{id: number}>`
- `findOrCreate(printeragentId: number, input: {type: string, model?: string|null, serial?: string|null}, tx?: object): Promise<{created: boolean, agentprinter: {id: number, printeragentId: number, type: string, model: string|null, serial: string|null, enabled: boolean, sort: number}}>`
- `getById(googleAuthId: number, id: number, tx?: object): Promise<{id: number, printeragentId: number, type: string, model: string|null, serial: string|null, enabled: boolean, sort: number}|null>`
- `list(googleAuthId: number, printeragentId?: number, tx?: object): Promise<Array<{id: number, printeragentId: number, type: string, model: string|null, serial: string|null, enabled: boolean, sort: number}>>`
- `order(googleAuthId: number, input: {printeragentId: number, agentprinterIds: number[]}, tx?: object): Promise<{agentprinterIds: number[]}>`
- `update(googleAuthId: number, input: {id: number, enabled?: boolean, sort?: number}, tx?: object): Promise<{id: number, printeragentId: number, type: string, model: string|null, serial: string|null, enabled: boolean, sort: number}>`
- [articlehistory](API.articlehistory.md)
- `record(): void`
- [articlelist](API.articlelist.md)

View File

@@ -34,42 +34,78 @@ try {
// pair
const paired = await store.printeragent.pair(a.id, {
token: 'test-token-123',
clientserial: 'SN12345',
clientmodel: 'Epson TM-T88VI',
agentversion: '1.0.0',
});
assert.equal(paired.token, 'test-token-123');
assert.equal(paired.clientserial, 'SN12345');
assert.equal(paired.clientmodel, 'Epson TM-T88VI');
assert.equal(paired.agentversion, '1.0.0');
assert.equal(paired.pairingpin, null);
assert.equal(paired.enabled, true);
assert.ok(paired.pairedAt);
// findByToken
const foundByToken = await store.printeragent.findByToken('test-token-123');
assert.equal(foundByToken.id, a.id);
assert.equal(foundByToken.clientmodel, 'Epson TM-T88VI');
assert.equal(foundByToken.enabled, true);
assert.equal(await store.printeragent.findByToken('invalid-token'), null);
// updateState
const withState = await store.printeragent.updateState(a.id, { online: true, paper: 'ok' });
assert.equal(withState.clientstate, JSON.stringify({ online: true, paper: 'ok' }));
// touchLastseen
await store.printeragent.touchLastseen(a.id, '1.0.1');
const afterTouch = await store.printeragent.findByToken('test-token-123');
assert.equal(afterTouch.agentversion, '1.0.1');
// update name and sort
const b = await store.printeragent.update(googleAuthId, { id: a.id, name: 'Kitchen', sort: 1 });
// update name, sort, and enabled
const b = await store.printeragent.update(googleAuthId, { id: a.id, name: 'Kitchen', sort: 1, enabled: false });
assert.equal(b.name, 'Kitchen');
assert.equal(b.sort, 1);
assert.equal(b.enabled, false);
// list
const list = await store.printeragent.list(googleAuthId);
assert.equal(list.length, 1);
assert.equal(list[0].name, 'Kitchen');
assert.equal(list[0].sort, 1);
assert.equal(list[0].enabled, false);
// test agentprinter findOrCreate
const ap1 = await store.agentprinter.findOrCreate(a.id, { type: 'zpl', model: 'ZD410-200dpi', serial: '50J203002218' });
assert.equal(ap1.created, true);
assert.equal(ap1.agentprinter.type, 'zpl');
assert.equal(ap1.agentprinter.model, 'ZD410-200dpi');
assert.equal(ap1.agentprinter.serial, '50J203002218');
assert.equal(ap1.agentprinter.enabled, true);
// second call with same combo returns existing
const ap1Again = await store.agentprinter.findOrCreate(a.id, { type: 'zpl', model: 'ZD410-200dpi', serial: '50J203002218' });
assert.equal(ap1Again.created, false);
assert.equal(ap1Again.agentprinter.id, ap1.agentprinter.id);
// create second printer
const ap2 = await store.agentprinter.findOrCreate(a.id, { type: 'esc/pos', model: 'TM-T88VI', serial: 'SN999' });
assert.equal(ap2.created, true);
// list agentprinters
const apList = await store.agentprinter.list(googleAuthId, a.id);
assert.equal(apList.length, 2);
// getById
const apGet = await store.agentprinter.getById(googleAuthId, ap1.agentprinter.id);
assert.equal(apGet.serial, '50J203002218');
// update agentprinter
const apUpdated = await store.agentprinter.update(googleAuthId, { id: ap1.agentprinter.id, enabled: false, sort: 10 });
assert.equal(apUpdated.enabled, false);
assert.equal(apUpdated.sort, 10);
// order agentprinters
const ordered = await store.agentprinter.order(googleAuthId, {
printeragentId: a.id,
agentprinterIds: [ap2.agentprinter.id, ap1.agentprinter.id],
});
assert.deepEqual(ordered.agentprinterIds, [ap2.agentprinter.id, ap1.agentprinter.id]);
// delete agentprinter
await store.agentprinter.delete(googleAuthId, ap2.agentprinter.id);
assert.equal((await store.agentprinter.list(googleAuthId, a.id)).length, 1);
// delete
await store.printeragent.delete(googleAuthId, a.id);

View File

@@ -209,6 +209,12 @@ import printeragentFindByToken from './store/printeragent/findByToken.js';
import printeragentPair from './store/printeragent/pair.js';
import printeragentUpdateState from './store/printeragent/updateState.js';
import printeragentTouchLastseen from './store/printeragent/touchLastseen.js';
import agentprinterList from './store/agentprinter/list.js';
import agentprinterGetById from './store/agentprinter/getById.js';
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 modelconversationCreate from './store/modelconversation/create.js';
import modelconversationGet from './store/modelconversation/get.js';
import modelconversationList from './store/modelconversation/list.js';
@@ -658,6 +664,15 @@ export default class LibDashStore {
touchLastseen: printeragentTouchLastseen(provider),
};
this.agentprinter = {
list: agentprinterList(provider),
getById: agentprinterGetById(provider),
findOrCreate: agentprinterFindOrCreate(provider),
update: agentprinterUpdate(provider),
order: agentprinterOrder(provider),
delete: agentprinterDelete(provider),
};
this.modelconversation = {
create: modelconversationCreate(provider),
get: modelconversationGet(provider),

View File

@@ -0,0 +1,38 @@
/**
* Schema 4 → 3 (MSSQL):
* - Drops agentprinter table
* - Removes enabled from printeragent
* - Restores clientserial, clientmodel, clientstate in printeragent
* @param {{ query: (sql: string, params?: object) => Promise<unknown>, assertVersion: (v: number) => Promise<void>, setVersion: (v: number) => Promise<void> }} ctx
*/
export default async function downgrade({ query, assertVersion, setVersion }) {
await assertVersion(4);
await query(`DROP TABLE IF EXISTS agentprinter;`);
await query(`
IF OBJECT_ID(N'dbo.printeragent', N'U') IS NOT NULL
BEGIN
IF COL_LENGTH('printeragent', 'enabled') IS NOT NULL
BEGIN
DECLARE @defName NVARCHAR(255);
SELECT @defName = dc.name
FROM sys.default_constraints dc
JOIN sys.columns c ON c.default_object_id = dc.object_id
WHERE dc.parent_object_id = OBJECT_ID(N'dbo.printeragent') AND c.name = 'enabled';
IF @defName IS NOT NULL
EXEC('ALTER TABLE printeragent DROP CONSTRAINT ' + @defName);
ALTER TABLE printeragent DROP COLUMN enabled;
END
IF COL_LENGTH('printeragent', 'clientserial') IS NULL
ALTER TABLE printeragent ADD clientserial NVARCHAR(255) NULL;
IF COL_LENGTH('printeragent', 'clientmodel') IS NULL
ALTER TABLE printeragent ADD clientmodel NVARCHAR(255) NULL;
IF COL_LENGTH('printeragent', 'clientstate') IS NULL
ALTER TABLE printeragent ADD clientstate NVARCHAR(255) NULL;
END
`);
await setVersion(3);
}

View File

@@ -28,6 +28,7 @@ export const TABLES = [
'posterminalobituary',
'posterminalcategory',
'posterminal',
'agentprinter',
'printeragent',
'printer',
'numbers',

View File

@@ -0,0 +1,69 @@
export const AGENTPRINTER_COLUMNS = `
ap.id,
ap.printeragent_id AS printeragentId,
ap.type,
ap.model,
ap.serial,
ap.enabled,
ap.sort
`;
export const LIST_BY_COMPANY = `
SELECT
${AGENTPRINTER_COLUMNS}
FROM agentprinter ap
JOIN printeragent pa ON pa.id = ap.printeragent_id
WHERE pa.company_id = @companyId
ORDER BY ap.sort, ap.id
`;
export const LIST_BY_PRINTERAGENT = `
SELECT
${AGENTPRINTER_COLUMNS}
FROM agentprinter ap
JOIN printeragent pa ON pa.id = ap.printeragent_id
WHERE ap.printeragent_id = @printeragentId AND pa.company_id = @companyId
ORDER BY ap.sort, ap.id
`;
export const GET_BY_ID = `
SELECT
${AGENTPRINTER_COLUMNS}
FROM agentprinter ap
JOIN printeragent pa ON pa.id = ap.printeragent_id
WHERE ap.id = @id AND pa.company_id = @companyId
`;
export const FIND_BY_COMBO = `
SELECT
${AGENTPRINTER_COLUMNS}
FROM agentprinter ap
WHERE ap.printeragent_id = @printeragentId
AND ap.type = @type
AND (ap.serial = @serial OR (ap.serial IS NULL AND @serial IS NULL))
AND (ap.model = @model OR (ap.model IS NULL AND @model IS NULL))
`;
export const CREATE = `
INSERT INTO agentprinter (printeragent_id, type, model, serial, enabled, sort)
OUTPUT
INSERTED.id,
INSERTED.printeragent_id AS printeragentId,
INSERTED.type,
INSERTED.model,
INSERTED.serial,
INSERTED.enabled,
INSERTED.sort
VALUES (@printeragentId, @type, @model, @serial, @enabled, @sort)
`;
export const UPDATE = `
UPDATE agentprinter
SET enabled = @enabled, sort = @sort
WHERE id = @id AND printeragent_id IN (SELECT id FROM printeragent WHERE company_id = @companyId)
`;
export const DELETE_BY_COMPANY_AND_ID = `
DELETE FROM agentprinter
WHERE id = @id AND printeragent_id IN (SELECT id FROM printeragent WHERE company_id = @companyId)
`;

View File

@@ -5,13 +5,11 @@ export const PRINTERAGENT_COLUMNS = `
token,
pairingpin,
paired_at AS pairedAt,
clientserial,
clientmodel,
clientstate,
agentversion,
lastseen_at AS lastseenAt,
updated_at AS updatedAt,
sort
sort,
enabled
`;
export const LIST_BY_COMPANY = `
@@ -55,8 +53,6 @@ UPDATE printeragent
SET token = @token,
pairingpin = NULL,
paired_at = @pairedAt,
clientserial = @clientserial,
clientmodel = @clientmodel,
agentversion = @agentversion,
lastseen_at = SYSUTCDATETIME(),
updated_at = SYSUTCDATETIME()
@@ -85,7 +81,7 @@ WHERE id = @id
`;
export const CREATE = `
INSERT INTO printeragent (company_id, name, pairingpin, sort)
INSERT INTO printeragent (company_id, name, pairingpin, sort, enabled)
OUTPUT
INSERTED.id,
INSERTED.company_id AS companyId,
@@ -93,19 +89,17 @@ OUTPUT
INSERTED.token,
INSERTED.pairingpin,
INSERTED.paired_at AS pairedAt,
INSERTED.clientserial,
INSERTED.clientmodel,
INSERTED.clientstate,
INSERTED.agentversion,
INSERTED.lastseen_at AS lastseenAt,
INSERTED.updated_at AS updatedAt,
INSERTED.sort
VALUES (@companyId, @name, @pairingpin, @sort)
INSERTED.sort,
INSERTED.enabled
VALUES (@companyId, @name, @pairingpin, @sort, @enabled)
`;
export const UPDATE = `
UPDATE printeragent
SET name = @name, sort = @sort, updated_at = SYSUTCDATETIME()
SET name = @name, sort = @sort, enabled = @enabled, updated_at = SYSUTCDATETIME()
WHERE id = @id AND company_id = @companyId
`;

View File

@@ -1021,13 +1021,11 @@ BEGIN
token NVARCHAR(255) NULL,
pairingpin NVARCHAR(50) NULL,
paired_at DATETIME2 NULL,
clientserial NVARCHAR(255) NULL,
clientmodel NVARCHAR(255) NULL,
clientstate NVARCHAR(255) NULL,
agentversion NVARCHAR(255) NULL,
lastseen_at DATETIME2 NULL,
updated_at DATETIME2 NOT NULL CONSTRAINT DF_printeragent_updated_at DEFAULT SYSUTCDATETIME(),
sort INT NOT NULL CONSTRAINT DF_printeragent_sort DEFAULT 0,
enabled BIT NOT NULL CONSTRAINT DF_printeragent_enabled DEFAULT 1,
CONSTRAINT FK_printeragent_company FOREIGN KEY (company_id) REFERENCES company(id) ON DELETE CASCADE
);
CREATE UNIQUE INDEX UQ_printeragent_token ON printeragent(token) WHERE token IS NOT NULL;
@@ -1035,6 +1033,23 @@ BEGIN
END
`;
export const INIT_AGENTPRINTER = `
IF NOT EXISTS (SELECT * FROM sys.tables WHERE name = 'agentprinter')
BEGIN
CREATE TABLE agentprinter (
id INT IDENTITY(1,1) PRIMARY KEY,
printeragent_id INT NOT NULL,
type NVARCHAR(50) NOT NULL,
model NVARCHAR(255) NULL,
serial NVARCHAR(255) NULL,
enabled BIT NOT NULL CONSTRAINT DF_agentprinter_enabled DEFAULT 1,
sort INT NOT NULL CONSTRAINT DF_agentprinter_sort DEFAULT 0,
CONSTRAINT FK_agentprinter_printeragent FOREIGN KEY (printeragent_id) REFERENCES printeragent(id) ON DELETE CASCADE
);
CREATE INDEX IX_agentprinter_printeragent_id ON agentprinter(printeragent_id);
END
`;
export const INIT_LABELTEMPLATE = `
IF NOT EXISTS (SELECT * FROM sys.tables WHERE name = 'labeltemplate')
BEGIN
@@ -1691,6 +1706,7 @@ export const SCHEMA_SCRIPTS = [
INIT_POSTERMINALSYNCQUEUE,
INIT_PRINTER,
INIT_PRINTERAGENT,
INIT_AGENTPRINTER,
INIT_LABELTEMPLATE,
INIT_FILE,
INIT_MANIFESTATTACHMENT,

View File

@@ -0,0 +1,31 @@
import { INIT_AGENTPRINTER } from '../schema.js';
/**
* Schema 3 → 4 (MSSQL):
* - Drops clientstate, clientmodel, clientserial from printeragent
* - Adds enabled to printeragent
* - Creates agentprinter table
* @param {{ query: (sql: string, params?: object) => Promise<unknown>, assertVersion: (v: number) => Promise<void>, setVersion: (v: number) => Promise<void> }} ctx
*/
export default async function upgrade({ query, assertVersion, setVersion }) {
await assertVersion(3);
await query(`
IF OBJECT_ID(N'dbo.printeragent', N'U') IS NOT NULL
BEGIN
IF COL_LENGTH('printeragent', 'clientstate') IS NOT NULL
ALTER TABLE printeragent DROP COLUMN clientstate;
IF COL_LENGTH('printeragent', 'clientmodel') IS NOT NULL
ALTER TABLE printeragent DROP COLUMN clientmodel;
IF COL_LENGTH('printeragent', 'clientserial') IS NOT NULL
ALTER TABLE printeragent DROP COLUMN clientserial;
IF COL_LENGTH('printeragent', 'enabled') IS NULL
ALTER TABLE printeragent ADD enabled BIT NOT NULL CONSTRAINT DF_printeragent_enabled DEFAULT 1;
END
`);
await query(INIT_AGENTPRINTER);
await setVersion(4);
}

View File

@@ -0,0 +1,28 @@
/**
* Schema 4 → 3 (SQLite):
* - Drops agentprinter table
* - Removes enabled from printeragent
* - Restores clientserial, clientmodel, clientstate in printeragent
* @param {{ exec: (sql: string) => void, assertVersion: (v: number) => Promise<void>, setVersion: (v: number) => Promise<void> }} ctx
*/
export default async function downgrade({ exec, assertVersion, setVersion }) {
await assertVersion(4);
exec(`DROP TABLE IF EXISTS agentprinter;`);
try {
exec(`ALTER TABLE printeragent DROP COLUMN enabled;`);
} catch {}
try {
exec(`ALTER TABLE printeragent ADD COLUMN clientserial TEXT;`);
} catch {}
try {
exec(`ALTER TABLE printeragent ADD COLUMN clientmodel TEXT;`);
} catch {}
try {
exec(`ALTER TABLE printeragent ADD COLUMN clientstate TEXT;`);
} catch {}
await setVersion(3);
}

View File

@@ -0,0 +1,15 @@
import * as source from '../../mssql/queries/agentprinter.js';
import { translateQueries } from './translate.js';
const queries = translateQueries(source, 'agentprinter');
export const {
AGENTPRINTER_COLUMNS,
CREATE,
DELETE_BY_COMPANY_AND_ID,
FIND_BY_COMBO,
GET_BY_ID,
LIST_BY_COMPANY,
LIST_BY_PRINTERAGENT,
UPDATE,
} = queries;

View File

@@ -700,13 +700,11 @@ CREATE TABLE IF NOT EXISTS printeragent (
token TEXT,
pairingpin TEXT,
paired_at TEXT,
clientserial TEXT,
clientmodel TEXT,
clientstate TEXT,
agentversion TEXT,
lastseen_at TEXT,
updated_at TEXT NOT NULL DEFAULT (${UTC_NOW}),
sort INTEGER NOT NULL DEFAULT 0,
enabled INTEGER NOT NULL DEFAULT 1,
FOREIGN KEY (company_id) REFERENCES company(id) ON DELETE CASCADE
);
CREATE UNIQUE INDEX IF NOT EXISTS UQ_printeragent_token
@@ -715,6 +713,21 @@ CREATE UNIQUE INDEX IF NOT EXISTS UQ_printeragent_pairingpin
ON printeragent(pairingpin) WHERE pairingpin IS NOT NULL;
`;
export const INIT_AGENTPRINTER = `
CREATE TABLE IF NOT EXISTS agentprinter (
id INTEGER PRIMARY KEY AUTOINCREMENT,
printeragent_id INTEGER NOT NULL,
type TEXT NOT NULL,
model TEXT,
serial TEXT,
enabled INTEGER NOT NULL DEFAULT 1,
sort INTEGER NOT NULL DEFAULT 0,
FOREIGN KEY (printeragent_id) REFERENCES printeragent(id) ON DELETE CASCADE
);
CREATE INDEX IF NOT EXISTS IX_agentprinter_printeragent_id
ON agentprinter(printeragent_id);
`;
export const INIT_LABELTEMPLATE = `
CREATE TABLE IF NOT EXISTS labeltemplate (
id INTEGER PRIMARY KEY AUTOINCREMENT,
@@ -1249,6 +1262,7 @@ export const SCHEMA_SCRIPTS = [
INIT_POSTERMINALSYNCQUEUE,
INIT_PRINTER,
INIT_PRINTERAGENT,
INIT_AGENTPRINTER,
INIT_LABELTEMPLATE,
INIT_FILE,
INIT_MANIFESTATTACHMENT,

View File

@@ -0,0 +1,30 @@
import { INIT_AGENTPRINTER } from '../schema.js';
/**
* Schema 3 → 4 (SQLite):
* - Drops clientstate, clientmodel, clientserial from printeragent
* - Adds enabled to printeragent
* - Creates agentprinter table
* @param {{ exec: (sql: string) => void, assertVersion: (v: number) => Promise<void>, setVersion: (v: number) => Promise<void> }} ctx
*/
export default async function upgrade({ exec, assertVersion, setVersion }) {
await assertVersion(3);
try {
exec(`ALTER TABLE printeragent DROP COLUMN clientstate;`);
} catch {}
try {
exec(`ALTER TABLE printeragent DROP COLUMN clientmodel;`);
} catch {}
try {
exec(`ALTER TABLE printeragent DROP COLUMN clientserial;`);
} catch {}
try {
exec(`ALTER TABLE printeragent ADD COLUMN enabled INTEGER NOT NULL DEFAULT 1;`);
} catch {}
exec(INIT_AGENTPRINTER);
await setVersion(4);
}

View File

@@ -0,0 +1,16 @@
export function shape(row) {
if (!row) return null;
return {
id: row.id,
printeragentId: row.printeragentId,
type: row.type,
model: row.model ?? null,
serial: row.serial ?? null,
enabled: Boolean(row.enabled),
sort: row.sort,
};
}
export const AGENTPRINTER_TYPEDEF =
'{id: number, printeragentId: number, type: string, model: string|null, serial: string|null, enabled: boolean, sort: number}';

View File

@@ -0,0 +1,35 @@
import {
DELETE_BY_COMPANY_AND_ID as MSSQL_DELETE,
} from '../../providers/mssql/queries/agentprinter.js';
import {
DELETE_BY_COMPANY_AND_ID as SQLITE_DELETE,
} from '../../providers/sqlite3/queries/agentprinter.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 an agent printer by id for the user's selected company.
*
* @param {number} googleAuthId - The id of the Google auth user.
* @param {number} id - The agent printer id.
* @param {object} [tx] - Optional transaction to run within.
* @returns {Promise<{id: number}>}
*/
return async (googleAuthId, id, tx) => {
const numericId = Number(id);
if (!Number.isInteger(numericId) || numericId <= 0) {
throw new Error('Agentprinter not found');
}
const runner = makeRunner(provider, tx);
const { companyId } = await access(googleAuthId, runner);
await runner.query(sql, { id: numericId, companyId });
return { id: numericId };
};
}

View File

@@ -0,0 +1,55 @@
import {
FIND_BY_COMBO as MSSQL_FIND,
CREATE as MSSQL_CREATE,
} from '../../providers/mssql/queries/agentprinter.js';
import {
FIND_BY_COMBO as SQLITE_FIND,
CREATE as SQLITE_CREATE,
} from '../../providers/sqlite3/queries/agentprinter.js';
import makeRunner from '../runner.js';
import { shape } from './_shared.js';
export default function findOrCreate(provider) {
const isSqlite = provider?.dialect === 'sqlite3';
const findSql = isSqlite ? SQLITE_FIND : MSSQL_FIND;
const createSql = isSqlite ? SQLITE_CREATE : MSSQL_CREATE;
/**
* Finds or creates an agentprinter by (printeragentId, type, model, serial) combo.
*
* @param {number} printeragentId - The parent printer agent id.
* @param {object} input
* @param {string} input.type - Printer type ("zpl", "esc/pos", etc.).
* @param {string|null} [input.model] - Discovered model name.
* @param {string|null} [input.serial] - Discovered serial number.
* @param {object} [tx] - Optional transaction to run within.
* @returns {Promise<{created: boolean, agentprinter: {id: number, printeragentId: number, type: string, model: string|null, serial: string|null, enabled: boolean, sort: number}}>}
*/
return async (printeragentId, input = {}, tx) => {
const pId = Number(printeragentId);
if (!Number.isInteger(pId) || pId <= 0) {
throw new Error('Printeragent not found');
}
const type = typeof input.type === 'string' && input.type.trim() ? input.type.trim().toLowerCase() : 'zpl';
const model = input.model != null && String(input.model).trim() ? String(input.model).trim() : null;
const serial = input.serial != null && String(input.serial).trim() ? String(input.serial).trim() : null;
const runner = makeRunner(provider, tx);
const existing = await runner.query(findSql, { printeragentId: pId, type, model, serial });
if (existing.rows.length > 0) {
return { created: false, agentprinter: shape(existing.rows[0]) };
}
const created = await runner.query(createSql, {
printeragentId: pId,
type,
model,
serial,
enabled: 1,
sort: 0,
});
return { created: true, agentprinter: shape(created.rows[0]) };
};
}

View File

@@ -0,0 +1,36 @@
import {
GET_BY_ID as MSSQL_GET,
} from '../../providers/mssql/queries/agentprinter.js';
import {
GET_BY_ID as SQLITE_GET,
} from '../../providers/sqlite3/queries/agentprinter.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 sql = provider?.dialect === 'sqlite3' ? SQLITE_GET : MSSQL_GET;
/**
* Retrieves an agent printer by id, scoped to the user's selected company.
*
* @param {number} googleAuthId - The id of the Google auth user.
* @param {number} id - The agent printer id.
* @param {object} [tx] - Optional transaction to run within.
* @returns {Promise<{id: number, printeragentId: number, type: string, model: string|null, serial: string|null, enabled: boolean, sort: number}|null>}
*/
return async (googleAuthId, id, tx) => {
const numericId = Number(id);
if (!Number.isInteger(numericId) || numericId <= 0) {
return null;
}
const runner = makeRunner(provider, tx);
const { companyId } = await access(googleAuthId, runner);
const result = await runner.query(sql, { id: numericId, companyId });
return result.rows[0] ? shape(result.rows[0]) : null;
};
}

View File

@@ -0,0 +1,41 @@
import {
LIST_BY_COMPANY as MSSQL_LIST_BY_COMPANY,
LIST_BY_PRINTERAGENT as MSSQL_LIST_BY_PRINTERAGENT,
} from '../../providers/mssql/queries/agentprinter.js';
import {
LIST_BY_COMPANY as SQLITE_LIST_BY_COMPANY,
LIST_BY_PRINTERAGENT as SQLITE_LIST_BY_PRINTERAGENT,
} from '../../providers/sqlite3/queries/agentprinter.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 listAgentSql = isSqlite ? SQLITE_LIST_BY_PRINTERAGENT : MSSQL_LIST_BY_PRINTERAGENT;
/**
* Lists agent printers of the user's selected company, optionally filtered by printer agent.
*
* @param {number} googleAuthId - The id of the Google auth user.
* @param {number} [printeragentId] - Optional printer agent id filter.
* @param {object} [tx] - Optional transaction to run within.
* @returns {Promise<Array<{id: number, printeragentId: number, type: string, model: string|null, serial: string|null, enabled: boolean, sort: number}>>}
*/
return async (googleAuthId, printeragentId, tx) => {
const runner = makeRunner(provider, tx);
const { companyId } = await access(googleAuthId, runner);
const aId = printeragentId != null ? Number(printeragentId) : null;
let result;
if (aId && Number.isInteger(aId) && aId > 0) {
result = await runner.query(listAgentSql, { companyId, printeragentId: aId });
} else {
result = await runner.query(listCompanySql, { companyId });
}
return result.rows.map(shape);
};
}

View File

@@ -0,0 +1,59 @@
import {
LIST_BY_PRINTERAGENT as MSSQL_LIST,
UPDATE as MSSQL_UPDATE,
} from '../../providers/mssql/queries/agentprinter.js';
import {
LIST_BY_PRINTERAGENT as SQLITE_LIST,
UPDATE as SQLITE_UPDATE,
} from '../../providers/sqlite3/queries/agentprinter.js';
import resolveSelectedCompanyAccess from '../companyuser/resolveSelectedCompanyAccess.js';
import makeRunner from '../runner.js';
export default function order(provider) {
const access = resolveSelectedCompanyAccess(provider);
const isSqlite = provider?.dialect === 'sqlite3';
const listSql = isSqlite ? SQLITE_LIST : MSSQL_LIST;
const updateSql = isSqlite ? SQLITE_UPDATE : MSSQL_UPDATE;
/**
* Reorders agent printers for a given printer agent within the user's selected company.
*
* @param {number} googleAuthId - The id of the Google auth user.
* @param {object} input
* @param {number} input.printeragentId - The printer agent id.
* @param {number[]} input.agentprinterIds - Complete ordered list of agent printer IDs.
* @param {object} [tx] - Optional transaction to run within.
* @returns {Promise<{agentprinterIds: number[]}>}
*/
return async (googleAuthId, input = {}, tx) => {
const pId = Number(input.printeragentId);
if (!Number.isInteger(pId) || pId <= 0) {
throw new Error('Printeragent not found');
}
const ids = input.agentprinterIds;
if (!Array.isArray(ids) || ids.length === 0 || ids.some((id) => !Number.isInteger(Number(id)) || Number(id) <= 0)) {
throw new Error('Invalid agentprinter order');
}
const runner = makeRunner(provider, tx);
const { companyId } = await access(googleAuthId, runner);
const existing = await runner.query(listSql, { companyId, printeragentId: pId });
const existingIds = new Set(existing.rows.map((r) => r.id));
if (ids.length !== existingIds.size || ids.some((id) => !existingIds.has(Number(id)))) {
throw new Error('Invalid agentprinter order');
}
for (let i = 0; i < ids.length; i++) {
const id = Number(ids[i]);
const current = existing.rows.find((r) => r.id === id);
const enabled = current ? (current.enabled ? 1 : 0) : 1;
await runner.query(updateSql, { id, companyId, sort: i + 1, enabled });
}
return { agentprinterIds: ids.map(Number) };
};
}

View File

@@ -0,0 +1,63 @@
import {
GET_BY_ID as MSSQL_GET,
UPDATE as MSSQL_UPDATE,
} from '../../providers/mssql/queries/agentprinter.js';
import {
GET_BY_ID as SQLITE_GET,
UPDATE as SQLITE_UPDATE,
} from '../../providers/sqlite3/queries/agentprinter.js';
import resolveSelectedCompanyAccess from '../companyuser/resolveSelectedCompanyAccess.js';
import makeRunner from '../runner.js';
import { shape } from './_shared.js';
export default function update(provider) {
const access = resolveSelectedCompanyAccess(provider);
const isSqlite = provider?.dialect === 'sqlite3';
const getSql = isSqlite ? SQLITE_GET : MSSQL_GET;
const updateSql = isSqlite ? SQLITE_UPDATE : MSSQL_UPDATE;
/**
* Updates enabled and/or sort of an agent printer for the user's selected company.
*
* @param {number} googleAuthId - The id of the Google auth user.
* @param {object} input
* @param {number} input.id - Agent printer id.
* @param {boolean} [input.enabled] - Whether the printer is enabled.
* @param {number} [input.sort] - Display order integer.
* @param {object} [tx] - Optional transaction to run within.
* @returns {Promise<{id: number, printeragentId: number, type: string, model: string|null, serial: string|null, enabled: boolean, sort: number}>}
*/
return async (googleAuthId, input = {}, tx) => {
const id = Number(input.id);
if (!Number.isInteger(id) || id <= 0) {
throw new Error('Agentprinter not found');
}
if (input.enabled === undefined && input.sort === undefined) {
throw new Error('Invalid agentprinter');
}
if (input.enabled !== undefined && typeof input.enabled !== 'boolean') {
throw new Error('Invalid agentprinter');
}
if (input.sort !== undefined && !Number.isInteger(input.sort)) {
throw new Error('Invalid agentprinter');
}
const runner = makeRunner(provider, tx);
const { companyId } = await access(googleAuthId, runner);
const existing = await runner.query(getSql, { id, companyId });
if (!existing.rows[0]) {
throw new Error('Agentprinter not found');
}
const sort = input.sort !== undefined ? input.sort : existing.rows[0].sort;
const enabled = input.enabled !== undefined ? (input.enabled ? 1 : 0) : (existing.rows[0].enabled ? 1 : 0);
await runner.query(updateSql, { id, companyId, sort, enabled });
const updated = await runner.query(getSql, { id, companyId });
return shape(updated.rows[0]);
};
}

View File

@@ -6,15 +6,13 @@ export function shape(row) {
token: row.token ?? null,
pairingpin: row.pairingpin ?? null,
pairedAt: row.pairedAt ?? null,
clientserial: row.clientserial ?? null,
clientmodel: row.clientmodel ?? null,
clientstate: row.clientstate ?? null,
agentversion: row.agentversion ?? null,
lastseenAt: row.lastseenAt ?? null,
updatedAt: row.updatedAt,
sort: row.sort,
enabled: Boolean(row.enabled),
};
}
export const PRINTERAGENT_TYPEDEF =
'{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}';
'{id: number, companyId: number, name: string, token: string|null, pairingpin: string|null, pairedAt: string|null, agentversion: string|null, lastseenAt: string|null, updatedAt: string, sort: number, enabled: boolean}';

View File

@@ -39,11 +39,12 @@ export default function create(provider) {
throw new Error('Invalid printeragent');
}
const sort = input.sort ?? 0;
const enabled = input.enabled !== undefined ? (input.enabled ? 1 : 0) : 1;
const runner = makeRunner(provider, tx);
const { companyId } = await access(googleAuthId, runner);
const result = await runner.query(sql, { companyId, name, pairingpin, sort });
const result = await runner.query(sql, { companyId, name, pairingpin, sort, enabled });
return shape(result.rows[0]);
};
}

View File

@@ -37,8 +37,6 @@ export default function pair(provider) {
throw new Error('Invalid printeragent');
}
const clientserial = input.clientserial ? String(input.clientserial).trim() : null;
const clientmodel = input.clientmodel ? String(input.clientmodel).trim() : null;
const agentversion = input.agentversion ? String(input.agentversion).trim() : null;
const pairedAt = input.pairedAt ?? new Date().toISOString();
@@ -47,8 +45,6 @@ export default function pair(provider) {
id: numericId,
token,
pairedAt,
clientserial,
clientmodel,
agentversion,
});

View File

@@ -36,7 +36,7 @@ export default function update(provider) {
if (!Number.isInteger(id) || id <= 0) {
throw new Error('Printeragent not found');
}
if (input.name === undefined && input.sort === undefined) {
if (input.name === undefined && input.sort === undefined && input.enabled === undefined) {
throw new Error('Invalid printeragent');
}
if (input.name !== undefined && (typeof input.name !== 'string' || !input.name.trim())) {
@@ -45,6 +45,9 @@ export default function update(provider) {
if (input.sort !== undefined && !Number.isInteger(input.sort)) {
throw new Error('Invalid printeragent');
}
if (input.enabled !== undefined && typeof input.enabled !== 'boolean') {
throw new Error('Invalid printeragent');
}
const runner = makeRunner(provider, tx);
const { companyId } = await access(googleAuthId, runner);
@@ -59,6 +62,7 @@ export default function update(provider) {
companyId,
name: input.name !== undefined ? input.name.trim() : existing.rows[0].name,
sort: input.sort !== undefined ? input.sort : existing.rows[0].sort,
enabled: input.enabled !== undefined ? (input.enabled ? 1 : 0) : existing.rows[0].enabled,
});
const row = await runner.query(getSql, { id, companyId });