Compare commits
5 Commits
c7d1b1a5d2
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
26f21cd7ac | ||
|
|
d0a33b1f82 | ||
|
|
55f04de8a8 | ||
|
|
76ea6642f4 | ||
|
|
f612c87b6e |
119
API.agentprinter.md
Normal file
119
API.agentprinter.md
Normal 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 or globally across the user's selected company.
|
||||
|
||||
**Parameters**
|
||||
|
||||
| Name | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| `googleAuthId` | `number` | The id of the Google auth user. |
|
||||
| `input` | `object` | |
|
||||
| `[input.printeragentId]` | `number` | Optional printer agent id. If omitted, reorders all printers in company. |
|
||||
| `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}>`
|
||||
86
API.autoinvite.md
Normal file
86
API.autoinvite.md
Normal file
@@ -0,0 +1,86 @@
|
||||
# autoinvite
|
||||
|
||||
This document is auto-generated from the JSDoc comments in `src/store` by running `npm run api-doc`.
|
||||
|
||||
## `autoinvite.add`
|
||||
|
||||
*Source: `src/store/autoinvite/add.js`*
|
||||
|
||||
Registers an email for automatic company membership. When that email is registered (`google_auth.add`) the invitation is consumed: it is removed from the list and the new user is added to the company with the given role.
|
||||
|
||||
**Parameters**
|
||||
|
||||
| Name | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| `googleAuthId` | `number` | The id of the acting Google auth user (must be admin of the selected company). |
|
||||
| `input` | `{email: string, role?: 'admin'|'user'}` | The email to invite and the role (default 'user'). |
|
||||
| `[tx]` | `object` | Optional transaction to run within. |
|
||||
|
||||
**Returns**
|
||||
|
||||
`Promise<{id: number, email: string, role: 'admin'|'user'}>` — The created invitation.
|
||||
|
||||
**Throws**
|
||||
|
||||
`Error` — 'Not authorized', 'Invalid email', 'Invalid role', 'User already registered', 'Invitation already exists'
|
||||
|
||||
**Example**
|
||||
|
||||
```js
|
||||
await store.autoinvite.add(42, { email: 'new@example.com', role: 'user' });
|
||||
```
|
||||
|
||||
## `autoinvite.list`
|
||||
|
||||
*Source: `src/store/autoinvite/list.js`*
|
||||
|
||||
Lists the pending auto-invitations of the admin's selected company.
|
||||
|
||||
**Parameters**
|
||||
|
||||
| Name | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| `googleAuthId` | `number` | The id of the Google auth user (must be admin of the selected company). |
|
||||
| `[tx]` | `object` | Optional transaction to run within. |
|
||||
|
||||
**Returns**
|
||||
|
||||
`Promise<Array<{id: number, email: string, role: 'admin'|'user', createdAt: string}>>` — The invitations ordered by email.
|
||||
|
||||
**Throws**
|
||||
|
||||
`Error` — 'Not authorized' / 'No company selected'
|
||||
|
||||
**Example**
|
||||
|
||||
```js
|
||||
const invites = await store.autoinvite.list(42);
|
||||
```
|
||||
|
||||
## `autoinvite.remove`
|
||||
|
||||
*Source: `src/store/autoinvite/remove.js`*
|
||||
|
||||
Deletes a pending auto-invitation of the admin's selected company.
|
||||
|
||||
**Parameters**
|
||||
|
||||
| Name | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| `googleAuthId` | `number` | The id of the acting Google auth user (must be admin of the selected company). |
|
||||
| `id` | `number` | The invitation id. |
|
||||
| `[tx]` | `object` | Optional transaction to run within. |
|
||||
|
||||
**Returns**
|
||||
|
||||
`Promise<void>`
|
||||
|
||||
**Throws**
|
||||
|
||||
`Error` — 'Not authorized', 'Invitation not found'
|
||||
|
||||
**Example**
|
||||
|
||||
```js
|
||||
await store.autoinvite.remove(42, 3);
|
||||
```
|
||||
@@ -6,7 +6,7 @@ This document is auto-generated from the JSDoc comments in `src/store` by runnin
|
||||
|
||||
*Source: `src/store/companyuser/add.js`*
|
||||
|
||||
Adds a company user linking a company to a Google auth account.
|
||||
Adds a company user linking a company to a Google auth account. No admin check: this is the low-level link primitive (company creation, invitations). Use `companyuser.addByEmail` for the admin-guarded flow.
|
||||
|
||||
**Parameters**
|
||||
|
||||
@@ -14,19 +14,52 @@ Adds a company user linking a company to a Google auth account.
|
||||
| --- | --- | --- |
|
||||
| `companyId` | `number` | The id of the company. |
|
||||
| `googleAuthId` | `number` | The id of the Google auth account. |
|
||||
| `[role='user']` | `'admin'|'user'` | The role within the company. |
|
||||
| `[tx]` | `object` | Optional transaction to run within. |
|
||||
|
||||
**Returns**
|
||||
|
||||
`Promise<number>` — The id of the created company user link.
|
||||
|
||||
**Throws**
|
||||
|
||||
`Error` — 'Invalid role' if the role is not 'admin' or 'user'.
|
||||
|
||||
**Example**
|
||||
|
||||
```js
|
||||
const id = await store.companyuser.add(1, 42);
|
||||
const id = await store.companyuser.add(1, 42, 'admin');
|
||||
// => 7
|
||||
```
|
||||
|
||||
## `companyuser.addByEmail`
|
||||
|
||||
*Source: `src/store/companyuser/addByEmail.js`*
|
||||
|
||||
Adds an existing Google auth user to the admin's selected company. The added user can then select the company as their own selected company. For emails that are not registered yet use `autoinvite.add`.
|
||||
|
||||
**Parameters**
|
||||
|
||||
| Name | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| `googleAuthId` | `number` | The id of the acting Google auth user (must be admin of the selected company). |
|
||||
| `input` | `{email: string, role?: 'admin'|'user'}` | Email of the existing user and role (default 'user'). |
|
||||
| `[tx]` | `object` | Optional transaction to run within. |
|
||||
|
||||
**Returns**
|
||||
|
||||
`Promise<{id: number, googleAuthId: number, email: string, picture: string|null, role: 'admin'|'user'}>` — The created company user.
|
||||
|
||||
**Throws**
|
||||
|
||||
`Error` — 'Not authorized', 'Invalid email', 'Invalid role', 'User not found', 'User already in company'
|
||||
|
||||
**Example**
|
||||
|
||||
```js
|
||||
const member = await store.companyuser.addByEmail(42, { email: 'new@example.com', role: 'user' });
|
||||
```
|
||||
|
||||
## `companyuser.delete`
|
||||
|
||||
*Source: `src/store/companyuser/delete.js`*
|
||||
@@ -73,3 +106,134 @@ Returns the company user ids associated with the given email address.
|
||||
const ids = await store.companyuser.getByEmail('user@example.com');
|
||||
// => [1, 2, 3]
|
||||
```
|
||||
|
||||
## `companyuser.list`
|
||||
|
||||
*Source: `src/store/companyuser/list.js`*
|
||||
|
||||
Lists the members of the admin's selected company.
|
||||
|
||||
**Parameters**
|
||||
|
||||
| Name | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| `googleAuthId` | `number` | The id of the Google auth user (must be admin of the selected company). |
|
||||
| `[tx]` | `object` | Optional transaction to run within. |
|
||||
|
||||
**Returns**
|
||||
|
||||
`Promise<Array<{id: number, googleAuthId: number, email: string, picture: string|null, role: 'admin'|'user'}>>` — The company users ordered by email.
|
||||
|
||||
**Throws**
|
||||
|
||||
`Error` — 'Not authorized' / 'No company selected'
|
||||
|
||||
**Example**
|
||||
|
||||
```js
|
||||
const members = await store.companyuser.list(42);
|
||||
```
|
||||
|
||||
## `companyuser.remove`
|
||||
|
||||
*Source: `src/store/companyuser/remove.js`*
|
||||
|
||||
Removes a member from the admin's selected company. History entries keep existing but lose their author link; if the removed user had this company selected, their selection is cleared.
|
||||
|
||||
**Parameters**
|
||||
|
||||
| Name | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| `googleAuthId` | `number` | The id of the acting Google auth user (must be admin of the selected company). |
|
||||
| `id` | `number` | The company user id to remove. |
|
||||
| `[tx]` | `object` | Optional transaction to run within. |
|
||||
|
||||
**Returns**
|
||||
|
||||
`Promise<{id: number, googleAuthId: number}>` — The removed company user.
|
||||
|
||||
**Throws**
|
||||
|
||||
`Error` — 'Not authorized', 'Company user not found', 'Cannot remove the last admin'
|
||||
|
||||
**Example**
|
||||
|
||||
```js
|
||||
await store.companyuser.remove(42, 7);
|
||||
```
|
||||
|
||||
## `companyuser.requireAdmin`
|
||||
|
||||
*Source: `src/store/companyuser/requireAdmin.js`*
|
||||
|
||||
Resolves the selected company and requires the user to be its admin.
|
||||
|
||||
**Parameters**
|
||||
|
||||
| Name | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| `googleAuthId` | `number` | |
|
||||
| `[tx]` | `object` | |
|
||||
|
||||
**Returns**
|
||||
|
||||
`Promise<{companyId: number, companyuserId: number, role: string}>`
|
||||
|
||||
**Throws**
|
||||
|
||||
`Error` — 'Not authorized' if the user is not an admin of the selected company.
|
||||
|
||||
## `companyuser.roles`
|
||||
|
||||
*Source: `src/store/companyuser/roles.js`*
|
||||
|
||||
Validates a company user role.
|
||||
|
||||
**Parameters**
|
||||
|
||||
| Name | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| `role` | `unknown` | |
|
||||
|
||||
**Returns**
|
||||
|
||||
`'admin'|'user'`
|
||||
|
||||
**Throws**
|
||||
|
||||
`Error` — 'Invalid role'
|
||||
|
||||
## `companyuser.setRole`
|
||||
|
||||
*Source: `src/store/companyuser/setRole.js`*
|
||||
|
||||
Changes the role of a member of the admin's selected company.
|
||||
|
||||
**Parameters**
|
||||
|
||||
| Name | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| `googleAuthId` | `number` | The id of the acting Google auth user (must be admin of the selected company). |
|
||||
| `id` | `number` | The company user id. |
|
||||
| `role` | `'admin'|'user'` | The new role. |
|
||||
| `[tx]` | `object` | Optional transaction to run within. |
|
||||
|
||||
**Returns**
|
||||
|
||||
`Promise<{id: number, googleAuthId: number, email: string, picture: string|null, role: 'admin'|'user'}>` — The updated company user.
|
||||
|
||||
**Throws**
|
||||
|
||||
`Error` — 'Not authorized', 'Invalid role', 'Company user not found', 'Cannot demote the last admin'
|
||||
|
||||
**Example**
|
||||
|
||||
```js
|
||||
await store.companyuser.setRole(42, 7, 'admin');
|
||||
```
|
||||
|
||||
## `companyuser.withTransaction`
|
||||
|
||||
*Source: `src/store/companyuser/withTransaction.js`*
|
||||
|
||||
Runs `fn(runner)` inside the given transaction, a new one, or (providers without transaction support) directly.
|
||||
|
||||
@@ -6,7 +6,7 @@ This document is auto-generated from the JSDoc comments in `src/store` by runnin
|
||||
|
||||
*Source: `src/store/google_auth/add.js`*
|
||||
|
||||
Adds a new allowed Google auth email.
|
||||
Adds a new allowed Google auth email (registration). Pending `autoinvite` rows for this email are consumed in the same transaction: each is deleted and turned into a `companyuser` with the invited role.
|
||||
|
||||
**Parameters**
|
||||
|
||||
@@ -144,7 +144,7 @@ await store.google_auth.setProfile(42, { picture: 'url', pictureBinary: buffer,
|
||||
|
||||
*Source: `src/store/google_auth/setSelectedCompany.js`*
|
||||
|
||||
Sets the currently selected company for a Google auth user.
|
||||
Sets the currently selected company for a Google auth user. The user must be a member (`companyuser`) of the company.
|
||||
|
||||
**Parameters**
|
||||
|
||||
@@ -157,6 +157,10 @@ Sets the currently selected company for a Google auth user.
|
||||
|
||||
`Promise<void>`
|
||||
|
||||
**Throws**
|
||||
|
||||
`Error` — 'Not authorized' if the user is not a member of the company.
|
||||
|
||||
**Example**
|
||||
|
||||
```js
|
||||
|
||||
26
API.md
26
API.md
@@ -82,9 +82,16 @@ The store is exposed as a `LibDashStore` instance (see `src/index.js`). Each met
|
||||
## Table of Contents
|
||||
|
||||
- [companyuser](API.companyuser.md)
|
||||
- `add(companyId: number, googleAuthId: number, tx?: object): Promise<number>`
|
||||
- `add(companyId: number, googleAuthId: number, role?: 'admin'|'user', tx?: object): Promise<number>`
|
||||
- `addByEmail(googleAuthId: number, input: {email: string, role?: 'admin'|'user'}, tx?: object): Promise<{id: number, googleAuthId: number, email: string, picture: string|null, role: 'admin'|'user'}>`
|
||||
- `delete(id: number, tx?: object): Promise<void>`
|
||||
- `getByEmail(email: string, tx?: object): Promise<Array<number>>`
|
||||
- `list(googleAuthId: number, tx?: object): Promise<Array<{id: number, googleAuthId: number, email: string, picture: string|null, role: 'admin'|'user'}>>`
|
||||
- `remove(googleAuthId: number, id: number, tx?: object): Promise<{id: number, googleAuthId: number}>`
|
||||
- `requireAdmin(googleAuthId: number, tx?: object): Promise<{companyId: number, companyuserId: number, role: string}>`
|
||||
- `roles(role: unknown): 'admin'|'user'`
|
||||
- `setRole(googleAuthId: number, id: number, role: 'admin'|'user', tx?: object): Promise<{id: number, googleAuthId: number, email: string, picture: string|null, role: 'admin'|'user'}>`
|
||||
- `withTransaction(): void`
|
||||
- [company](API.company.md)
|
||||
- `addHistory(googleAuthId: number, input: {text?: string}, tx?: object): Promise<{id: number, companyuserId: number|null, date: string, text: string}>`
|
||||
- `create(input: {iso639: string, name: string, taxrate: number, reducedtaxrate: number, googleAuthId: number}, tx?: object): Promise<{id: number, rootCategoryId: number, languageId: number, taxrateId: number, reducedtaxrateId: number, taxfreerateId: number, posCustomerGroupId: number}>`
|
||||
@@ -344,6 +351,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)
|
||||
@@ -351,6 +365,10 @@ The store is exposed as a `LibDashStore` instance (see `src/index.js`). Each met
|
||||
- `setItemsByArticleSku(googleAuthId: number, sku: string, items?: Array<{articleId?: number, sku?: string, count?: number, sort?: number}>, tx?: object): Promise<Array<{id: number, articleId: number, sku: string, name: string|null, count: number, sort: number}>>`
|
||||
- [articlewarehousesummary](API.articlewarehousesummary.md)
|
||||
- `syncSummary(): void`
|
||||
- [autoinvite](API.autoinvite.md)
|
||||
- `add(googleAuthId: number, input: {email: string, role?: 'admin'|'user'}, tx?: object): Promise<{id: number, email: string, role: 'admin'|'user'}>`
|
||||
- `list(googleAuthId: number, tx?: object): Promise<Array<{id: number, email: string, role: 'admin'|'user', createdAt: string}>>`
|
||||
- `remove(googleAuthId: number, id: number, tx?: object): Promise<void>`
|
||||
- [carrier](API.carrier.md)
|
||||
- `create(googleAuthId: number, input: {name: string, sort?: number, hasPackages?: boolean, isReverse?: boolean}, tx?: object): Promise<{id: number, name: string, sort: number, hasPackages: boolean, isReverse: boolean}>`
|
||||
- `delete(googleAuthId: number, id: number, tx?: object): Promise<void>`
|
||||
@@ -414,6 +432,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<object>`
|
||||
- `delete(googleAuthId: number, id: number, tx?: object): Promise<object>`
|
||||
- `getById(auth: number|{agentId: number}, id: number, tx?: object): Promise<object|null>`
|
||||
- `list(auth: number|{agentId: number}, filters?: {agentprinterId?: number, state?: string}, tx?: object): Promise<Array<object>>`
|
||||
- `updateState(auth: number|{agentId: number}, id: number, state: string, tx?: object): Promise<object>`
|
||||
- [retoure](API.retoure.md)
|
||||
- `create(googleAuthId: number, body: {number?: string, date: string, entityId?: number, externalNumber?: string, lines?: Array<object>}, tx?: object): Promise<object>`
|
||||
- `createShipment(googleAuthId: number, retoureId: number, input: object, tx?: object): Promise<object>`
|
||||
|
||||
100
API.printerjob.md
Normal file
100
API.printerjob.md
Normal file
@@ -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<object>` — 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<object>` — 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<object|null>`
|
||||
|
||||
## `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<Array<object>>`
|
||||
|
||||
## `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<object>` — Updated printer job.
|
||||
328
schema-graph.dot
328
schema-graph.dot
File diff suppressed because one or more lines are too long
7281
schema-graph.svg
7281
schema-graph.svg
File diff suppressed because it is too large
Load Diff
|
Before Width: | Height: | Size: 441 KiB After Width: | Height: | Size: 459 KiB |
92
scripts/test-companyuser-roles.js
Normal file
92
scripts/test-companyuser-roles.js
Normal file
@@ -0,0 +1,92 @@
|
||||
import assert from 'node:assert/strict';
|
||||
import { execFileSync } from 'node:child_process';
|
||||
import { mkdtempSync, rmSync } from 'node:fs';
|
||||
import { tmpdir } from 'node:os';
|
||||
import { join } from 'node:path';
|
||||
import LibDashStore from '../src/index.js';
|
||||
import { BetterSqlite3Provider } from '../src/providers/sqlite3/index.js';
|
||||
|
||||
async function open(tempDir) {
|
||||
const provider = new BetterSqlite3Provider({ filename: join(tempDir, 'test.db'), storageDir: join(tempDir, 'storage') });
|
||||
await provider.initialize();
|
||||
return { provider, store: await new LibDashStore(provider) };
|
||||
}
|
||||
|
||||
const tempDir = mkdtempSync(join(tmpdir(), 'test-cu-roles-'));
|
||||
try {
|
||||
console.log('--- Testing companyuser roles + autoinvite ---');
|
||||
let { provider, store } = await open(tempDir);
|
||||
|
||||
const adminId = await store.google_auth.add('admin@example.com');
|
||||
const userId = await store.google_auth.add('user@example.com');
|
||||
const company = await store.company.create({ name: 'Roles Corp', iso639: 'de', taxrate: 19, reducedtaxrate: 7, googleAuthId: adminId });
|
||||
await store.google_auth.setSelectedCompany(adminId, company.id);
|
||||
|
||||
// creator is admin
|
||||
let members = await store.companyuser.list(adminId);
|
||||
assert.deepEqual(members.map((m) => [m.email, m.role]), [['admin@example.com', 'admin']]);
|
||||
assert.equal((await store.company.list(adminId))[0].role, 'admin');
|
||||
|
||||
// non-member cannot select the company
|
||||
await assert.rejects(store.google_auth.setSelectedCompany(userId, company.id), /Not authorized/);
|
||||
|
||||
// admin adds existing user
|
||||
await assert.rejects(store.companyuser.addByEmail(adminId, { email: 'nobody@example.com' }), /User not found/);
|
||||
await assert.rejects(store.companyuser.addByEmail(adminId, { email: 'user@example.com', role: 'root' }), /Invalid role/);
|
||||
const added = await store.companyuser.addByEmail(adminId, { email: 'User@Example.com' });
|
||||
assert.equal(added.role, 'user');
|
||||
await assert.rejects(store.companyuser.addByEmail(adminId, { email: 'user@example.com' }), /already in company/);
|
||||
|
||||
// user can now select it, but is not admin
|
||||
await store.google_auth.setSelectedCompany(userId, company.id);
|
||||
await assert.rejects(store.companyuser.list(userId), /Not authorized/);
|
||||
await assert.rejects(store.companyuser.addByEmail(userId, { email: 'admin@example.com' }), /Not authorized/);
|
||||
await assert.rejects(store.companyuser.setRole(userId, added.id, 'admin'), /Not authorized/);
|
||||
await assert.rejects(store.companyuser.remove(userId, added.id), /Not authorized/);
|
||||
await assert.rejects(store.autoinvite.add(userId, { email: 'x@example.com' }), /Not authorized/);
|
||||
|
||||
// last admin guard
|
||||
const adminCu = members[0].id;
|
||||
await assert.rejects(store.companyuser.setRole(adminId, adminCu, 'user'), /last admin/);
|
||||
await assert.rejects(store.companyuser.remove(adminId, adminCu), /last admin/);
|
||||
await store.companyuser.setRole(adminId, added.id, 'admin');
|
||||
await store.companyuser.setRole(adminId, adminCu, 'user');
|
||||
await store.companyuser.setRole(userId, adminCu, 'admin');
|
||||
await store.companyuser.setRole(userId, added.id, 'user');
|
||||
await assert.rejects(store.companyuser.setRole(adminId, 99999, 'user'), /Company user not found/);
|
||||
|
||||
// autoinvite
|
||||
await assert.rejects(store.autoinvite.add(adminId, { email: 'user@example.com' }), /already registered/);
|
||||
await assert.rejects(store.autoinvite.add(adminId, { email: 'bad' }), /Invalid email/);
|
||||
await store.autoinvite.add(adminId, { email: 'New@Example.com', role: 'admin' });
|
||||
await assert.rejects(store.autoinvite.add(adminId, { email: 'new@example.com' }), /already exists/);
|
||||
await store.autoinvite.add(adminId, { email: 'gone@example.com' });
|
||||
assert.equal((await store.autoinvite.list(adminId)).length, 2);
|
||||
await store.autoinvite.remove(adminId, (await store.autoinvite.list(adminId)).find((i) => i.email === 'gone@example.com').id);
|
||||
await assert.rejects(store.autoinvite.remove(adminId, 99999), /Invitation not found/);
|
||||
|
||||
const newId = await store.google_auth.add('new@example.com');
|
||||
assert.deepEqual(await store.autoinvite.list(adminId), []);
|
||||
await store.google_auth.setSelectedCompany(newId, company.id);
|
||||
members = await store.companyuser.list(adminId);
|
||||
assert.equal(members.find((m) => m.email === 'new@example.com').role, 'admin');
|
||||
|
||||
// remove clears selection
|
||||
const userCu = members.find((m) => m.email === 'user@example.com');
|
||||
await store.companyuser.remove(adminId, userCu.id);
|
||||
assert.equal((await store.google_auth.getById(userId)).selectedCompanyId, null);
|
||||
|
||||
// downgrade → upgrade through the migration engine
|
||||
await provider.close?.();
|
||||
const env = { ...process.env, DB_PROVIDER: 'sqlite', SQLITE_PATH: join(tempDir, 'test.db') };
|
||||
execFileSync('node', ['scripts/downgrade.js', '5'], { env, stdio: 'inherit' });
|
||||
({ provider, store } = await open(tempDir).catch((e) => { throw e; }));
|
||||
// opening upgrades to latest again; roles reset to admin for all members
|
||||
const rows = (await provider.query('SELECT role FROM companyuser')).rows;
|
||||
assert.ok(rows.length > 0 && rows.every((r) => r.role === 'admin'));
|
||||
assert.deepEqual((await provider.query('SELECT * FROM autoinvite')).rows, []);
|
||||
await provider.close?.();
|
||||
console.log('OK');
|
||||
} finally {
|
||||
rmSync(tempDir, { recursive: true, force: true });
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
45
src/index.js
45
src/index.js
@@ -1,6 +1,13 @@
|
||||
import companyuserGetByEmail from './store/companyuser/getByEmail.js';
|
||||
import companyuserAdd from './store/companyuser/add.js';
|
||||
import companyuserDelete from './store/companyuser/delete.js';
|
||||
import companyuserList from './store/companyuser/list.js';
|
||||
import companyuserAddByEmail from './store/companyuser/addByEmail.js';
|
||||
import companyuserSetRole from './store/companyuser/setRole.js';
|
||||
import companyuserRemove from './store/companyuser/remove.js';
|
||||
import autoinviteAdd from './store/autoinvite/add.js';
|
||||
import autoinviteList from './store/autoinvite/list.js';
|
||||
import autoinviteRemove from './store/autoinvite/remove.js';
|
||||
import companyCreate from './store/company/create.js';
|
||||
import companyUpdate from './store/company/update.js';
|
||||
import companyDelete from './store/company/delete.js';
|
||||
@@ -209,6 +216,17 @@ 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 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';
|
||||
@@ -323,6 +341,16 @@ export default class LibDashStore {
|
||||
getByEmail: companyuserGetByEmail(provider),
|
||||
add: companyuserAdd(provider),
|
||||
delete: companyuserDelete(provider),
|
||||
list: companyuserList(provider),
|
||||
addByEmail: companyuserAddByEmail(provider),
|
||||
setRole: companyuserSetRole(provider),
|
||||
remove: companyuserRemove(provider),
|
||||
};
|
||||
|
||||
this.autoinvite = {
|
||||
add: autoinviteAdd(provider),
|
||||
list: autoinviteList(provider),
|
||||
remove: autoinviteRemove(provider),
|
||||
};
|
||||
|
||||
this.company = {
|
||||
@@ -658,6 +686,23 @@ 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.printerjob = {
|
||||
create: printerjobCreate(provider),
|
||||
getById: printerjobGetById(provider),
|
||||
list: printerjobList(provider),
|
||||
updateState: printerjobUpdateState(provider),
|
||||
delete: printerjobDelete(provider),
|
||||
};
|
||||
|
||||
this.modelconversation = {
|
||||
create: modelconversationCreate(provider),
|
||||
get: modelconversationGet(provider),
|
||||
|
||||
38
src/providers/mssql/downgrade/4_to_3.js
Normal file
38
src/providers/mssql/downgrade/4_to_3.js
Normal 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);
|
||||
}
|
||||
|
||||
13
src/providers/mssql/downgrade/5_to_4.js
Normal file
13
src/providers/mssql/downgrade/5_to_4.js
Normal file
@@ -0,0 +1,13 @@
|
||||
/**
|
||||
* Schema 5 → 4 (MSSQL):
|
||||
* - Drops printerjob table
|
||||
* @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(5);
|
||||
|
||||
await query(`DROP TABLE IF EXISTS printerjob;`);
|
||||
|
||||
await setVersion(4);
|
||||
}
|
||||
|
||||
18
src/providers/mssql/downgrade/6_to_5.js
Normal file
18
src/providers/mssql/downgrade/6_to_5.js
Normal file
@@ -0,0 +1,18 @@
|
||||
/**
|
||||
* Schema 6 → 5 (MSSQL):
|
||||
* - Drops autoinvite table
|
||||
* - Drops companyuser.role
|
||||
* @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(6);
|
||||
|
||||
await query(`DROP TABLE IF EXISTS autoinvite;`);
|
||||
|
||||
await query(`
|
||||
ALTER TABLE companyuser DROP CONSTRAINT CK_companyuser_role, DF_companyuser_role;
|
||||
`);
|
||||
await query(`ALTER TABLE companyuser DROP COLUMN role;`);
|
||||
|
||||
await setVersion(5);
|
||||
}
|
||||
@@ -28,6 +28,8 @@ export const TABLES = [
|
||||
'posterminalobituary',
|
||||
'posterminalcategory',
|
||||
'posterminal',
|
||||
'printerjob',
|
||||
'agentprinter',
|
||||
'printeragent',
|
||||
'printer',
|
||||
'numbers',
|
||||
|
||||
69
src/providers/mssql/queries/agentprinter.js
Normal file
69
src/providers/mssql/queries/agentprinter.js
Normal 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)
|
||||
`;
|
||||
43
src/providers/mssql/queries/autoinvite.js
Normal file
43
src/providers/mssql/queries/autoinvite.js
Normal file
@@ -0,0 +1,43 @@
|
||||
export const ADD = `
|
||||
INSERT INTO autoinvite (company_id, email, role)
|
||||
OUTPUT INSERTED.id
|
||||
VALUES (@companyId, @email, @role)
|
||||
`;
|
||||
|
||||
export const LIST_BY_COMPANY = `
|
||||
SELECT
|
||||
id,
|
||||
email,
|
||||
role,
|
||||
created_at AS createdAt
|
||||
FROM autoinvite
|
||||
WHERE company_id = @companyId
|
||||
ORDER BY email
|
||||
`;
|
||||
|
||||
export const LIST_BY_EMAIL = `
|
||||
SELECT
|
||||
id,
|
||||
company_id AS companyId,
|
||||
role
|
||||
FROM autoinvite
|
||||
WHERE email = @email
|
||||
ORDER BY id
|
||||
`;
|
||||
|
||||
export const GET_IN_COMPANY = `
|
||||
SELECT id
|
||||
FROM autoinvite
|
||||
WHERE id = @id AND company_id = @companyId
|
||||
`;
|
||||
|
||||
export const GET_BY_COMPANY_EMAIL = `
|
||||
SELECT id
|
||||
FROM autoinvite
|
||||
WHERE company_id = @companyId AND email = @email
|
||||
`;
|
||||
|
||||
export const DELETE = `
|
||||
DELETE FROM autoinvite
|
||||
WHERE id = @id
|
||||
`;
|
||||
@@ -55,7 +55,8 @@ SELECT
|
||||
l.iso639,
|
||||
tr.rate AS taxrate,
|
||||
rtr.rate AS reducedtaxrate,
|
||||
tfr.rate AS taxfreerate
|
||||
tfr.rate AS taxfreerate,
|
||||
cu.role AS role
|
||||
FROM company c
|
||||
INNER JOIN companyuser cu ON cu.company_id = c.id
|
||||
LEFT JOIN language l ON l.id = c.default_language_id
|
||||
|
||||
@@ -10,7 +10,8 @@ SELECT
|
||||
c.root_category_id AS rootCategoryId,
|
||||
c.default_language_id AS languageId,
|
||||
c.poscustomergroup_id AS posCustomerGroupId,
|
||||
cu.id AS companyuserId
|
||||
cu.id AS companyuserId,
|
||||
cu.role AS role
|
||||
FROM company c
|
||||
LEFT JOIN companyuser cu
|
||||
ON cu.company_id = c.id AND cu.google_auth_id = @googleAuthId
|
||||
@@ -18,12 +19,75 @@ WHERE c.id = @companyId
|
||||
`;
|
||||
|
||||
export const ADD = `
|
||||
INSERT INTO companyuser (google_auth_id, company_id)
|
||||
INSERT INTO companyuser (google_auth_id, company_id, role)
|
||||
OUTPUT INSERTED.id
|
||||
VALUES (@googleAuthId, @companyId)
|
||||
VALUES (@googleAuthId, @companyId, @role)
|
||||
`;
|
||||
|
||||
export const DELETE = `
|
||||
DELETE FROM companyuser
|
||||
WHERE id = @id
|
||||
`;
|
||||
|
||||
export const LIST_BY_COMPANY = `
|
||||
SELECT
|
||||
cu.id,
|
||||
cu.google_auth_id AS googleAuthId,
|
||||
ga.email,
|
||||
ga.picture,
|
||||
cu.role
|
||||
FROM companyuser cu
|
||||
INNER JOIN google_auth ga ON ga.id = cu.google_auth_id
|
||||
WHERE cu.company_id = @companyId
|
||||
ORDER BY ga.email
|
||||
`;
|
||||
|
||||
export const GET_IN_COMPANY = `
|
||||
SELECT
|
||||
cu.id,
|
||||
cu.google_auth_id AS googleAuthId,
|
||||
ga.email,
|
||||
ga.picture,
|
||||
cu.role
|
||||
FROM companyuser cu
|
||||
INNER JOIN google_auth ga ON ga.id = cu.google_auth_id
|
||||
WHERE cu.id = @id AND cu.company_id = @companyId
|
||||
`;
|
||||
|
||||
export const GET_MEMBERSHIP = `
|
||||
SELECT id, role
|
||||
FROM companyuser
|
||||
WHERE google_auth_id = @googleAuthId AND company_id = @companyId
|
||||
`;
|
||||
|
||||
export const COUNT_ADMINS = `
|
||||
SELECT COUNT(*) AS count
|
||||
FROM companyuser
|
||||
WHERE company_id = @companyId AND role = 'admin'
|
||||
`;
|
||||
|
||||
export const SET_ROLE = `
|
||||
UPDATE companyuser
|
||||
SET role = @role
|
||||
WHERE id = @id
|
||||
`;
|
||||
|
||||
export const CLEAR_ENTITYHISTORY_USER = `
|
||||
UPDATE entityhistory SET companyuser_id = NULL WHERE companyuser_id = @id
|
||||
`;
|
||||
|
||||
export const CLEAR_COMPANYHISTORY_USER = `
|
||||
UPDATE companyhistory SET companyuser_id = NULL WHERE companyuser_id = @id
|
||||
`;
|
||||
|
||||
export const IS_MEMBER = `
|
||||
SELECT id
|
||||
FROM companyuser
|
||||
WHERE google_auth_id = @googleAuthId AND company_id = @companyId
|
||||
`;
|
||||
|
||||
export const CLEAR_SELECTED_COMPANY = `
|
||||
UPDATE google_auth
|
||||
SET selected_company = NULL
|
||||
WHERE id = @googleAuthId AND selected_company = @companyId
|
||||
`;
|
||||
|
||||
@@ -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
|
||||
`;
|
||||
|
||||
|
||||
115
src/providers/mssql/queries/printerjob.js
Normal file
115
src/providers/mssql/queries/printerjob.js
Normal file
@@ -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
|
||||
)
|
||||
`;
|
||||
|
||||
@@ -866,6 +866,22 @@ END
|
||||
');
|
||||
`;
|
||||
|
||||
export const INIT_AUTOINVITE = `
|
||||
IF NOT EXISTS (SELECT * FROM sys.tables WHERE name = 'autoinvite')
|
||||
BEGIN
|
||||
CREATE TABLE autoinvite (
|
||||
id INT IDENTITY(1,1) PRIMARY KEY,
|
||||
company_id INT NOT NULL,
|
||||
email NVARCHAR(255) NOT NULL,
|
||||
role NVARCHAR(10) NOT NULL CONSTRAINT DF_autoinvite_role DEFAULT 'user',
|
||||
created_at DATETIME2 NOT NULL CONSTRAINT DF_autoinvite_created_at DEFAULT SYSUTCDATETIME(),
|
||||
CONSTRAINT CK_autoinvite_role CHECK (role IN ('admin', 'user')),
|
||||
CONSTRAINT UQ_autoinvite_company_email UNIQUE (company_id, email),
|
||||
CONSTRAINT FK_autoinvite_company FOREIGN KEY (company_id) REFERENCES company(id) ON DELETE CASCADE
|
||||
);
|
||||
END
|
||||
`;
|
||||
|
||||
export const INIT_COMPANYUSER = `
|
||||
IF NOT EXISTS (SELECT * FROM sys.tables WHERE name = 'companyuser')
|
||||
BEGIN
|
||||
@@ -873,6 +889,8 @@ BEGIN
|
||||
id INT IDENTITY(1,1) PRIMARY KEY,
|
||||
google_auth_id INT NOT NULL,
|
||||
company_id INT NOT NULL,
|
||||
role NVARCHAR(10) NOT NULL CONSTRAINT DF_companyuser_role DEFAULT 'user',
|
||||
CONSTRAINT CK_companyuser_role CHECK (role IN ('admin', 'user')),
|
||||
CONSTRAINT UQ_companyuser_google_auth_company UNIQUE (google_auth_id, company_id),
|
||||
CONSTRAINT FK_companyuser_google_auth FOREIGN KEY (google_auth_id) REFERENCES google_auth(id) ON DELETE CASCADE,
|
||||
CONSTRAINT FK_companyuser_company FOREIGN KEY (company_id) REFERENCES company(id) ON DELETE CASCADE
|
||||
@@ -1021,13 +1039,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 +1051,41 @@ 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_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
|
||||
@@ -1678,6 +1729,7 @@ export const SCHEMA_SCRIPTS = [
|
||||
INIT_GET_NEXT_NUMBER_WITH_PREFIX_AND_PADDING,
|
||||
INIT_PEEK_NEXT_NUMBER_WITH_PREFIX_AND_PADDING,
|
||||
INIT_COMPANYUSER,
|
||||
INIT_AUTOINVITE,
|
||||
INIT_MODELCONVERSATION,
|
||||
INIT_MODELMESSAGE,
|
||||
INIT_MODELVFS,
|
||||
@@ -1691,6 +1743,8 @@ export const SCHEMA_SCRIPTS = [
|
||||
INIT_POSTERMINALSYNCQUEUE,
|
||||
INIT_PRINTER,
|
||||
INIT_PRINTERAGENT,
|
||||
INIT_AGENTPRINTER,
|
||||
INIT_PRINTERJOB,
|
||||
INIT_LABELTEMPLATE,
|
||||
INIT_FILE,
|
||||
INIT_MANIFESTATTACHMENT,
|
||||
|
||||
31
src/providers/mssql/upgrade/3_to_4.js
Normal file
31
src/providers/mssql/upgrade/3_to_4.js
Normal 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);
|
||||
}
|
||||
|
||||
15
src/providers/mssql/upgrade/4_to_5.js
Normal file
15
src/providers/mssql/upgrade/4_to_5.js
Normal file
@@ -0,0 +1,15 @@
|
||||
import { INIT_PRINTERJOB } from '../schema.js';
|
||||
|
||||
/**
|
||||
* Schema 4 → 5 (MSSQL):
|
||||
* - Creates printerjob 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(4);
|
||||
|
||||
await query(INIT_PRINTERJOB);
|
||||
|
||||
await setVersion(5);
|
||||
}
|
||||
|
||||
25
src/providers/mssql/upgrade/5_to_6.js
Normal file
25
src/providers/mssql/upgrade/5_to_6.js
Normal file
@@ -0,0 +1,25 @@
|
||||
import { INIT_AUTOINVITE } from '../schema.js';
|
||||
|
||||
/**
|
||||
* Schema 5 → 6 (MSSQL):
|
||||
* - Adds companyuser.role ('admin' | 'user'); existing members become 'admin'
|
||||
* (they had full access before roles existed)
|
||||
* - Creates autoinvite 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(5);
|
||||
|
||||
await query(`
|
||||
ALTER TABLE companyuser ADD
|
||||
role NVARCHAR(10) NOT NULL CONSTRAINT DF_companyuser_role DEFAULT 'user';
|
||||
`);
|
||||
await query(`
|
||||
ALTER TABLE companyuser ADD CONSTRAINT CK_companyuser_role CHECK (role IN ('admin', 'user'));
|
||||
`);
|
||||
await query(`UPDATE companyuser SET role = 'admin';`);
|
||||
|
||||
await query(INIT_AUTOINVITE);
|
||||
|
||||
await setVersion(6);
|
||||
}
|
||||
28
src/providers/sqlite3/downgrade/4_to_3.js
Normal file
28
src/providers/sqlite3/downgrade/4_to_3.js
Normal 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);
|
||||
}
|
||||
|
||||
13
src/providers/sqlite3/downgrade/5_to_4.js
Normal file
13
src/providers/sqlite3/downgrade/5_to_4.js
Normal file
@@ -0,0 +1,13 @@
|
||||
/**
|
||||
* Schema 5 → 4 (SQLite):
|
||||
* - Drops printerjob table
|
||||
* @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(5);
|
||||
|
||||
exec(`DROP TABLE IF EXISTS printerjob;`);
|
||||
|
||||
await setVersion(4);
|
||||
}
|
||||
|
||||
15
src/providers/sqlite3/downgrade/6_to_5.js
Normal file
15
src/providers/sqlite3/downgrade/6_to_5.js
Normal file
@@ -0,0 +1,15 @@
|
||||
/**
|
||||
* Schema 6 → 5 (SQLite):
|
||||
* - Drops autoinvite table
|
||||
* - Drops companyuser.role
|
||||
* @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(6);
|
||||
|
||||
exec(`DROP TABLE IF EXISTS autoinvite;`);
|
||||
|
||||
exec(`ALTER TABLE companyuser DROP COLUMN role;`);
|
||||
|
||||
await setVersion(5);
|
||||
}
|
||||
15
src/providers/sqlite3/queries/agentprinter.js
Normal file
15
src/providers/sqlite3/queries/agentprinter.js
Normal 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;
|
||||
|
||||
5
src/providers/sqlite3/queries/autoinvite.js
Normal file
5
src/providers/sqlite3/queries/autoinvite.js
Normal file
@@ -0,0 +1,5 @@
|
||||
import * as source from '../../mssql/queries/autoinvite.js';
|
||||
import { translateQueries } from './translate.js';
|
||||
|
||||
const queries = translateQueries(source, 'autoinvite');
|
||||
export const { ADD, LIST_BY_COMPANY, LIST_BY_EMAIL, GET_IN_COMPANY, GET_BY_COMPANY_EMAIL, DELETE } = queries;
|
||||
@@ -2,4 +2,18 @@ import * as source from '../../mssql/queries/companyuser.js';
|
||||
import { translateQueries } from './translate.js';
|
||||
|
||||
const queries = translateQueries(source, 'companyuser');
|
||||
export const { ADD, DELETE, GET_BY_EMAIL, GET_COMPANY_ACCESS } = queries;
|
||||
export const {
|
||||
ADD,
|
||||
DELETE,
|
||||
GET_BY_EMAIL,
|
||||
GET_COMPANY_ACCESS,
|
||||
LIST_BY_COMPANY,
|
||||
GET_IN_COMPANY,
|
||||
GET_MEMBERSHIP,
|
||||
COUNT_ADMINS,
|
||||
SET_ROLE,
|
||||
CLEAR_ENTITYHISTORY_USER,
|
||||
CLEAR_COMPANYHISTORY_USER,
|
||||
IS_MEMBER,
|
||||
CLEAR_SELECTED_COMPANY,
|
||||
} = queries;
|
||||
|
||||
18
src/providers/sqlite3/queries/printerjob.js
Normal file
18
src/providers/sqlite3/queries/printerjob.js
Normal file
@@ -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;
|
||||
|
||||
@@ -512,11 +512,24 @@ CREATE TABLE IF NOT EXISTS numbers (
|
||||
);
|
||||
`;
|
||||
|
||||
export const INIT_AUTOINVITE = `
|
||||
CREATE TABLE IF NOT EXISTS autoinvite (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
company_id INTEGER NOT NULL,
|
||||
email TEXT NOT NULL COLLATE NOCASE,
|
||||
role TEXT NOT NULL DEFAULT 'user' CHECK (role IN ('admin', 'user')),
|
||||
created_at TEXT NOT NULL DEFAULT (${UTC_NOW}),
|
||||
UNIQUE (company_id, email),
|
||||
FOREIGN KEY (company_id) REFERENCES company(id) ON DELETE CASCADE
|
||||
);
|
||||
`;
|
||||
|
||||
export const INIT_COMPANYUSER = `
|
||||
CREATE TABLE IF NOT EXISTS companyuser (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
google_auth_id INTEGER NOT NULL,
|
||||
company_id INTEGER NOT NULL,
|
||||
role TEXT NOT NULL DEFAULT 'user' CHECK (role IN ('admin', 'user')),
|
||||
UNIQUE (google_auth_id, company_id),
|
||||
FOREIGN KEY (google_auth_id) REFERENCES google_auth(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (company_id) REFERENCES company(id) ON DELETE CASCADE
|
||||
@@ -700,13 +713,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 +726,38 @@ 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_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,
|
||||
@@ -1236,6 +1279,7 @@ export const SCHEMA_SCRIPTS = [
|
||||
INIT_LEDGER,
|
||||
INIT_NUMBERS,
|
||||
INIT_COMPANYUSER,
|
||||
INIT_AUTOINVITE,
|
||||
INIT_MODELCONVERSATION,
|
||||
INIT_MODELMESSAGE,
|
||||
INIT_MODELVFS,
|
||||
@@ -1249,6 +1293,8 @@ export const SCHEMA_SCRIPTS = [
|
||||
INIT_POSTERMINALSYNCQUEUE,
|
||||
INIT_PRINTER,
|
||||
INIT_PRINTERAGENT,
|
||||
INIT_AGENTPRINTER,
|
||||
INIT_PRINTERJOB,
|
||||
INIT_LABELTEMPLATE,
|
||||
INIT_FILE,
|
||||
INIT_MANIFESTATTACHMENT,
|
||||
|
||||
30
src/providers/sqlite3/upgrade/3_to_4.js
Normal file
30
src/providers/sqlite3/upgrade/3_to_4.js
Normal 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);
|
||||
}
|
||||
|
||||
15
src/providers/sqlite3/upgrade/4_to_5.js
Normal file
15
src/providers/sqlite3/upgrade/4_to_5.js
Normal file
@@ -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<void>, setVersion: (v: number) => Promise<void> }} ctx
|
||||
*/
|
||||
export default async function upgrade({ exec, assertVersion, setVersion }) {
|
||||
await assertVersion(4);
|
||||
|
||||
exec(INIT_PRINTERJOB);
|
||||
|
||||
await setVersion(5);
|
||||
}
|
||||
|
||||
19
src/providers/sqlite3/upgrade/5_to_6.js
Normal file
19
src/providers/sqlite3/upgrade/5_to_6.js
Normal file
@@ -0,0 +1,19 @@
|
||||
import { INIT_AUTOINVITE } from '../schema.js';
|
||||
|
||||
/**
|
||||
* Schema 5 → 6 (SQLite):
|
||||
* - Adds companyuser.role ('admin' | 'user'); existing members become 'admin'
|
||||
* (they had full access before roles existed)
|
||||
* - Creates autoinvite 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(5);
|
||||
|
||||
exec(`ALTER TABLE companyuser ADD COLUMN role TEXT NOT NULL DEFAULT 'user' CHECK (role IN ('admin', 'user'));`);
|
||||
exec(`UPDATE companyuser SET role = 'admin';`);
|
||||
|
||||
exec(INIT_AUTOINVITE);
|
||||
|
||||
await setVersion(6);
|
||||
}
|
||||
16
src/store/agentprinter/_shared.js
Normal file
16
src/store/agentprinter/_shared.js
Normal 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}';
|
||||
|
||||
35
src/store/agentprinter/delete.js
Normal file
35
src/store/agentprinter/delete.js
Normal 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 };
|
||||
};
|
||||
}
|
||||
|
||||
55
src/store/agentprinter/findOrCreate.js
Normal file
55
src/store/agentprinter/findOrCreate.js
Normal 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]) };
|
||||
};
|
||||
}
|
||||
|
||||
36
src/store/agentprinter/getById.js
Normal file
36
src/store/agentprinter/getById.js
Normal 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;
|
||||
};
|
||||
}
|
||||
|
||||
41
src/store/agentprinter/list.js
Normal file
41
src/store/agentprinter/list.js
Normal 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);
|
||||
};
|
||||
}
|
||||
|
||||
68
src/store/agentprinter/order.js
Normal file
68
src/store/agentprinter/order.js
Normal file
@@ -0,0 +1,68 @@
|
||||
import {
|
||||
LIST_BY_COMPANY as MSSQL_LIST_BY_COMPANY,
|
||||
LIST_BY_PRINTERAGENT as MSSQL_LIST_BY_AGENT,
|
||||
UPDATE as MSSQL_UPDATE,
|
||||
} from '../../providers/mssql/queries/agentprinter.js';
|
||||
import {
|
||||
LIST_BY_COMPANY as SQLITE_LIST_BY_COMPANY,
|
||||
LIST_BY_PRINTERAGENT as SQLITE_LIST_BY_AGENT,
|
||||
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 listCompanySql = isSqlite ? SQLITE_LIST_BY_COMPANY : MSSQL_LIST_BY_COMPANY;
|
||||
const listAgentSql = isSqlite ? SQLITE_LIST_BY_AGENT : MSSQL_LIST_BY_AGENT;
|
||||
const updateSql = isSqlite ? SQLITE_UPDATE : MSSQL_UPDATE;
|
||||
|
||||
/**
|
||||
* Reorders agent printers for a given printer agent or globally across the user's selected company.
|
||||
*
|
||||
* @param {number} googleAuthId - The id of the Google auth user.
|
||||
* @param {object} input
|
||||
* @param {number} [input.printeragentId] - Optional printer agent id. If omitted, reorders all printers in company.
|
||||
* @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 hasAgent = input.printeragentId != null;
|
||||
let pId;
|
||||
if (hasAgent) {
|
||||
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 = hasAgent
|
||||
? await runner.query(listAgentSql, { companyId, printeragentId: pId })
|
||||
: await runner.query(listCompanySql, { companyId });
|
||||
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) };
|
||||
};
|
||||
}
|
||||
|
||||
63
src/store/agentprinter/update.js
Normal file
63
src/store/agentprinter/update.js
Normal 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]);
|
||||
};
|
||||
}
|
||||
|
||||
46
src/store/autoinvite/add.js
Normal file
46
src/store/autoinvite/add.js
Normal file
@@ -0,0 +1,46 @@
|
||||
import { ADD as MSSQL_ADD, GET_BY_COMPANY_EMAIL as MSSQL_EXISTS } from '../../providers/mssql/queries/autoinvite.js';
|
||||
import { ADD as SQLITE_ADD, GET_BY_COMPANY_EMAIL as SQLITE_EXISTS } from '../../providers/sqlite3/queries/autoinvite.js';
|
||||
import { GET_BY_EMAIL as MSSQL_GA_BY_EMAIL } from '../../providers/mssql/queries/google_auth.js';
|
||||
import { GET_BY_EMAIL as SQLITE_GA_BY_EMAIL } from '../../providers/sqlite3/queries/google_auth.js';
|
||||
import requireAdmin from '../companyuser/requireAdmin.js';
|
||||
import withTransaction from '../companyuser/withTransaction.js';
|
||||
import { assertRole, normalizeEmail } from '../companyuser/roles.js';
|
||||
|
||||
export default function add(provider) {
|
||||
const isSqlite = provider?.dialect === 'sqlite3';
|
||||
const addSql = isSqlite ? SQLITE_ADD : MSSQL_ADD;
|
||||
const existsSql = isSqlite ? SQLITE_EXISTS : MSSQL_EXISTS;
|
||||
const gaByEmailSql = isSqlite ? SQLITE_GA_BY_EMAIL : MSSQL_GA_BY_EMAIL;
|
||||
const admin = requireAdmin(provider);
|
||||
|
||||
/**
|
||||
* Registers an email for automatic company membership. When that email is
|
||||
* registered (`google_auth.add`) the invitation is consumed: it is removed from
|
||||
* the list and the new user is added to the company with the given role.
|
||||
*
|
||||
* @param {number} googleAuthId - The id of the acting Google auth user (must be admin of the selected company).
|
||||
* @param {{email: string, role?: 'admin'|'user'}} input - The email to invite and the role (default 'user').
|
||||
* @param {object} [tx] - Optional transaction to run within.
|
||||
* @returns {Promise<{id: number, email: string, role: 'admin'|'user'}>} The created invitation.
|
||||
* @throws {Error} 'Not authorized', 'Invalid email', 'Invalid role', 'User already registered', 'Invitation already exists'
|
||||
*
|
||||
* @example
|
||||
* await store.autoinvite.add(42, { email: 'new@example.com', role: 'user' });
|
||||
*/
|
||||
return async (googleAuthId, input = {}, tx) =>
|
||||
withTransaction(provider, tx, async (runner) => {
|
||||
const { companyId } = await admin(googleAuthId, runner);
|
||||
const email = normalizeEmail(input.email);
|
||||
const role = assertRole(input.role ?? 'user');
|
||||
|
||||
if ((await runner.query(gaByEmailSql, { email })).rows[0]) {
|
||||
throw new Error('User already registered');
|
||||
}
|
||||
if ((await runner.query(existsSql, { companyId, email })).rows[0]) {
|
||||
throw new Error('Invitation already exists');
|
||||
}
|
||||
|
||||
const result = await runner.query(addSql, { companyId, email, role });
|
||||
return { id: result.rows[0]?.id ?? result.insertId, email, role };
|
||||
});
|
||||
}
|
||||
26
src/store/autoinvite/list.js
Normal file
26
src/store/autoinvite/list.js
Normal file
@@ -0,0 +1,26 @@
|
||||
import { LIST_BY_COMPANY as MSSQL_LIST } from '../../providers/mssql/queries/autoinvite.js';
|
||||
import { LIST_BY_COMPANY as SQLITE_LIST } from '../../providers/sqlite3/queries/autoinvite.js';
|
||||
import makeRunner from '../runner.js';
|
||||
import requireAdmin from '../companyuser/requireAdmin.js';
|
||||
|
||||
export default function list(provider) {
|
||||
const sql = provider?.dialect === 'sqlite3' ? SQLITE_LIST : MSSQL_LIST;
|
||||
const admin = requireAdmin(provider);
|
||||
|
||||
/**
|
||||
* Lists the pending auto-invitations of the admin's selected company.
|
||||
*
|
||||
* @param {number} googleAuthId - The id of the Google auth user (must be admin of the selected company).
|
||||
* @param {object} [tx] - Optional transaction to run within.
|
||||
* @returns {Promise<Array<{id: number, email: string, role: 'admin'|'user', createdAt: string}>>} The invitations ordered by email.
|
||||
* @throws {Error} 'Not authorized' / 'No company selected'
|
||||
*
|
||||
* @example
|
||||
* const invites = await store.autoinvite.list(42);
|
||||
*/
|
||||
return async (googleAuthId, tx) => {
|
||||
const { companyId } = await admin(googleAuthId, tx);
|
||||
const result = await makeRunner(provider, tx).query(sql, { companyId });
|
||||
return result.rows;
|
||||
};
|
||||
}
|
||||
32
src/store/autoinvite/remove.js
Normal file
32
src/store/autoinvite/remove.js
Normal file
@@ -0,0 +1,32 @@
|
||||
import { DELETE as MSSQL_DELETE, GET_IN_COMPANY as MSSQL_GET } from '../../providers/mssql/queries/autoinvite.js';
|
||||
import { DELETE as SQLITE_DELETE, GET_IN_COMPANY as SQLITE_GET } from '../../providers/sqlite3/queries/autoinvite.js';
|
||||
import requireAdmin from '../companyuser/requireAdmin.js';
|
||||
import withTransaction from '../companyuser/withTransaction.js';
|
||||
|
||||
export default function remove(provider) {
|
||||
const isSqlite = provider?.dialect === 'sqlite3';
|
||||
const getSql = isSqlite ? SQLITE_GET : MSSQL_GET;
|
||||
const deleteSql = isSqlite ? SQLITE_DELETE : MSSQL_DELETE;
|
||||
const admin = requireAdmin(provider);
|
||||
|
||||
/**
|
||||
* Deletes a pending auto-invitation of the admin's selected company.
|
||||
*
|
||||
* @param {number} googleAuthId - The id of the acting Google auth user (must be admin of the selected company).
|
||||
* @param {number} id - The invitation id.
|
||||
* @param {object} [tx] - Optional transaction to run within.
|
||||
* @returns {Promise<void>}
|
||||
* @throws {Error} 'Not authorized', 'Invitation not found'
|
||||
*
|
||||
* @example
|
||||
* await store.autoinvite.remove(42, 3);
|
||||
*/
|
||||
return async (googleAuthId, id, tx) =>
|
||||
withTransaction(provider, tx, async (runner) => {
|
||||
const { companyId } = await admin(googleAuthId, runner);
|
||||
if (!(await runner.query(getSql, { id, companyId })).rows[0]) {
|
||||
throw new Error('Invitation not found');
|
||||
}
|
||||
await runner.query(deleteSql, { id });
|
||||
});
|
||||
}
|
||||
@@ -89,9 +89,11 @@ export default function create(provider) {
|
||||
companyId,
|
||||
});
|
||||
|
||||
// The creator becomes the company's admin.
|
||||
await runner.query(addCompanyUserSql, {
|
||||
companyId,
|
||||
googleAuthId,
|
||||
role: 'admin',
|
||||
});
|
||||
|
||||
return {
|
||||
|
||||
@@ -1,26 +1,33 @@
|
||||
import { ADD as MSSQL_ADD } from '../../providers/mssql/queries/companyuser.js';
|
||||
import { ADD as SQLITE_ADD } from '../../providers/sqlite3/queries/companyuser.js';
|
||||
import makeRunner from '../runner.js';
|
||||
import { assertRole } from './roles.js';
|
||||
|
||||
export default function add(provider) {
|
||||
const sql = provider?.dialect === 'sqlite3' ? SQLITE_ADD : MSSQL_ADD;
|
||||
|
||||
/**
|
||||
* Adds a company user linking a company to a Google auth account.
|
||||
* No admin check: this is the low-level link primitive (company creation, invitations).
|
||||
* Use `companyuser.addByEmail` for the admin-guarded flow.
|
||||
*
|
||||
* @param {number} companyId - The id of the company.
|
||||
* @param {number} googleAuthId - The id of the Google auth account.
|
||||
* @param {'admin'|'user'} [role='user'] - The role within the company.
|
||||
* @param {object} [tx] - Optional transaction to run within.
|
||||
* @returns {Promise<number>} The id of the created company user link.
|
||||
* @throws {Error} 'Invalid role' if the role is not 'admin' or 'user'.
|
||||
*
|
||||
* @example
|
||||
* const id = await store.companyuser.add(1, 42);
|
||||
* const id = await store.companyuser.add(1, 42, 'admin');
|
||||
* // => 7
|
||||
*/
|
||||
return async (companyId, googleAuthId, tx) => {
|
||||
return async (companyId, googleAuthId, role = 'user', tx) => {
|
||||
assertRole(role);
|
||||
const result = await makeRunner(provider, tx).query(sql, {
|
||||
companyId,
|
||||
googleAuthId,
|
||||
role,
|
||||
});
|
||||
|
||||
return result.rows[0]?.id ?? result.insertId;
|
||||
|
||||
46
src/store/companyuser/addByEmail.js
Normal file
46
src/store/companyuser/addByEmail.js
Normal file
@@ -0,0 +1,46 @@
|
||||
import { ADD as MSSQL_ADD, IS_MEMBER as MSSQL_IS_MEMBER } from '../../providers/mssql/queries/companyuser.js';
|
||||
import { ADD as SQLITE_ADD, IS_MEMBER as SQLITE_IS_MEMBER } from '../../providers/sqlite3/queries/companyuser.js';
|
||||
import { GET_BY_EMAIL as MSSQL_GA_BY_EMAIL } from '../../providers/mssql/queries/google_auth.js';
|
||||
import { GET_BY_EMAIL as SQLITE_GA_BY_EMAIL } from '../../providers/sqlite3/queries/google_auth.js';
|
||||
import requireAdmin from './requireAdmin.js';
|
||||
import withTransaction from './withTransaction.js';
|
||||
import { assertRole, normalizeEmail } from './roles.js';
|
||||
|
||||
export default function addByEmail(provider) {
|
||||
const isSqlite = provider?.dialect === 'sqlite3';
|
||||
const addSql = isSqlite ? SQLITE_ADD : MSSQL_ADD;
|
||||
const isMemberSql = isSqlite ? SQLITE_IS_MEMBER : MSSQL_IS_MEMBER;
|
||||
const gaByEmailSql = isSqlite ? SQLITE_GA_BY_EMAIL : MSSQL_GA_BY_EMAIL;
|
||||
const admin = requireAdmin(provider);
|
||||
|
||||
/**
|
||||
* Adds an existing Google auth user to the admin's selected company.
|
||||
* The added user can then select the company as their own selected company.
|
||||
* For emails that are not registered yet use `autoinvite.add`.
|
||||
*
|
||||
* @param {number} googleAuthId - The id of the acting Google auth user (must be admin of the selected company).
|
||||
* @param {{email: string, role?: 'admin'|'user'}} input - Email of the existing user and role (default 'user').
|
||||
* @param {object} [tx] - Optional transaction to run within.
|
||||
* @returns {Promise<{id: number, googleAuthId: number, email: string, picture: string|null, role: 'admin'|'user'}>} The created company user.
|
||||
* @throws {Error} 'Not authorized', 'Invalid email', 'Invalid role', 'User not found', 'User already in company'
|
||||
*
|
||||
* @example
|
||||
* const member = await store.companyuser.addByEmail(42, { email: 'new@example.com', role: 'user' });
|
||||
*/
|
||||
return async (googleAuthId, input = {}, tx) =>
|
||||
withTransaction(provider, tx, async (runner) => {
|
||||
const { companyId } = await admin(googleAuthId, runner);
|
||||
const email = normalizeEmail(input.email);
|
||||
const role = assertRole(input.role ?? 'user');
|
||||
|
||||
const auth = (await runner.query(gaByEmailSql, { email })).rows[0];
|
||||
if (!auth) throw new Error('User not found');
|
||||
|
||||
const existing = (await runner.query(isMemberSql, { googleAuthId: auth.id, companyId })).rows[0];
|
||||
if (existing) throw new Error('User already in company');
|
||||
|
||||
const result = await runner.query(addSql, { companyId, googleAuthId: auth.id, role });
|
||||
const id = result.rows[0]?.id ?? result.insertId;
|
||||
return { id, googleAuthId: auth.id, email: auth.email, picture: auth.picture ?? null, role };
|
||||
});
|
||||
}
|
||||
26
src/store/companyuser/list.js
Normal file
26
src/store/companyuser/list.js
Normal file
@@ -0,0 +1,26 @@
|
||||
import { LIST_BY_COMPANY as MSSQL_LIST } from '../../providers/mssql/queries/companyuser.js';
|
||||
import { LIST_BY_COMPANY as SQLITE_LIST } from '../../providers/sqlite3/queries/companyuser.js';
|
||||
import makeRunner from '../runner.js';
|
||||
import requireAdmin from './requireAdmin.js';
|
||||
|
||||
export default function list(provider) {
|
||||
const sql = provider?.dialect === 'sqlite3' ? SQLITE_LIST : MSSQL_LIST;
|
||||
const admin = requireAdmin(provider);
|
||||
|
||||
/**
|
||||
* Lists the members of the admin's selected company.
|
||||
*
|
||||
* @param {number} googleAuthId - The id of the Google auth user (must be admin of the selected company).
|
||||
* @param {object} [tx] - Optional transaction to run within.
|
||||
* @returns {Promise<Array<{id: number, googleAuthId: number, email: string, picture: string|null, role: 'admin'|'user'}>>} The company users ordered by email.
|
||||
* @throws {Error} 'Not authorized' / 'No company selected'
|
||||
*
|
||||
* @example
|
||||
* const members = await store.companyuser.list(42);
|
||||
*/
|
||||
return async (googleAuthId, tx) => {
|
||||
const { companyId } = await admin(googleAuthId, tx);
|
||||
const result = await makeRunner(provider, tx).query(sql, { companyId });
|
||||
return result.rows;
|
||||
};
|
||||
}
|
||||
63
src/store/companyuser/remove.js
Normal file
63
src/store/companyuser/remove.js
Normal file
@@ -0,0 +1,63 @@
|
||||
import {
|
||||
CLEAR_COMPANYHISTORY_USER as MSSQL_CLEAR_COMPANYHISTORY,
|
||||
CLEAR_ENTITYHISTORY_USER as MSSQL_CLEAR_ENTITYHISTORY,
|
||||
CLEAR_SELECTED_COMPANY as MSSQL_CLEAR_SELECTED,
|
||||
COUNT_ADMINS as MSSQL_COUNT_ADMINS,
|
||||
DELETE as MSSQL_DELETE,
|
||||
GET_IN_COMPANY as MSSQL_GET,
|
||||
} from '../../providers/mssql/queries/companyuser.js';
|
||||
import {
|
||||
CLEAR_COMPANYHISTORY_USER as SQLITE_CLEAR_COMPANYHISTORY,
|
||||
CLEAR_ENTITYHISTORY_USER as SQLITE_CLEAR_ENTITYHISTORY,
|
||||
CLEAR_SELECTED_COMPANY as SQLITE_CLEAR_SELECTED,
|
||||
COUNT_ADMINS as SQLITE_COUNT_ADMINS,
|
||||
DELETE as SQLITE_DELETE,
|
||||
GET_IN_COMPANY as SQLITE_GET,
|
||||
} from '../../providers/sqlite3/queries/companyuser.js';
|
||||
import requireAdmin from './requireAdmin.js';
|
||||
import withTransaction from './withTransaction.js';
|
||||
|
||||
export default function remove(provider) {
|
||||
const isSqlite = provider?.dialect === 'sqlite3';
|
||||
const pick = (mssql, sqlite) => (isSqlite ? sqlite : mssql);
|
||||
const getSql = pick(MSSQL_GET, SQLITE_GET);
|
||||
const countSql = pick(MSSQL_COUNT_ADMINS, SQLITE_COUNT_ADMINS);
|
||||
const deleteSql = pick(MSSQL_DELETE, SQLITE_DELETE);
|
||||
const clearEntityHistorySql = pick(MSSQL_CLEAR_ENTITYHISTORY, SQLITE_CLEAR_ENTITYHISTORY);
|
||||
const clearCompanyHistorySql = pick(MSSQL_CLEAR_COMPANYHISTORY, SQLITE_CLEAR_COMPANYHISTORY);
|
||||
const clearSelectedSql = pick(MSSQL_CLEAR_SELECTED, SQLITE_CLEAR_SELECTED);
|
||||
const admin = requireAdmin(provider);
|
||||
|
||||
/**
|
||||
* Removes a member from the admin's selected company. History entries keep
|
||||
* existing but lose their author link; if the removed user had this company
|
||||
* selected, their selection is cleared.
|
||||
*
|
||||
* @param {number} googleAuthId - The id of the acting Google auth user (must be admin of the selected company).
|
||||
* @param {number} id - The company user id to remove.
|
||||
* @param {object} [tx] - Optional transaction to run within.
|
||||
* @returns {Promise<{id: number, googleAuthId: number}>} The removed company user.
|
||||
* @throws {Error} 'Not authorized', 'Company user not found', 'Cannot remove the last admin'
|
||||
*
|
||||
* @example
|
||||
* await store.companyuser.remove(42, 7);
|
||||
*/
|
||||
return async (googleAuthId, id, tx) =>
|
||||
withTransaction(provider, tx, async (runner) => {
|
||||
const { companyId } = await admin(googleAuthId, runner);
|
||||
|
||||
const member = (await runner.query(getSql, { id, companyId })).rows[0];
|
||||
if (!member) throw new Error('Company user not found');
|
||||
|
||||
if (member.role === 'admin') {
|
||||
const { count } = (await runner.query(countSql, { companyId })).rows[0];
|
||||
if (Number(count) <= 1) throw new Error('Cannot remove the last admin');
|
||||
}
|
||||
|
||||
await runner.query(clearEntityHistorySql, { id });
|
||||
await runner.query(clearCompanyHistorySql, { id });
|
||||
await runner.query(clearSelectedSql, { googleAuthId: member.googleAuthId, companyId });
|
||||
await runner.query(deleteSql, { id });
|
||||
return { id: member.id, googleAuthId: member.googleAuthId };
|
||||
});
|
||||
}
|
||||
20
src/store/companyuser/requireAdmin.js
Normal file
20
src/store/companyuser/requireAdmin.js
Normal file
@@ -0,0 +1,20 @@
|
||||
import resolveSelectedCompanyAccess from './resolveSelectedCompanyAccess.js';
|
||||
|
||||
export default function requireAdmin(provider) {
|
||||
const access = resolveSelectedCompanyAccess(provider);
|
||||
|
||||
/**
|
||||
* Resolves the selected company and requires the user to be its admin.
|
||||
* @param {number} googleAuthId
|
||||
* @param {object} [tx]
|
||||
* @returns {Promise<{companyId: number, companyuserId: number, role: string}>}
|
||||
* @throws {Error} 'Not authorized' if the user is not an admin of the selected company.
|
||||
*/
|
||||
return async (googleAuthId, tx) => {
|
||||
const ctx = await access(googleAuthId, tx);
|
||||
if (ctx.role !== 'admin') {
|
||||
throw new Error('Not authorized');
|
||||
}
|
||||
return ctx;
|
||||
};
|
||||
}
|
||||
@@ -30,6 +30,7 @@ export default function resolveCompanyAccess(provider) {
|
||||
languageId: row.languageId,
|
||||
posCustomerGroupId: row.posCustomerGroupId ?? null,
|
||||
companyuserId: row.companyuserId,
|
||||
role: row.role,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
28
src/store/companyuser/roles.js
Normal file
28
src/store/companyuser/roles.js
Normal file
@@ -0,0 +1,28 @@
|
||||
export const COMPANY_USER_ROLES = ['admin', 'user'];
|
||||
|
||||
/**
|
||||
* Validates a company user role.
|
||||
* @param {unknown} role
|
||||
* @returns {'admin'|'user'}
|
||||
* @throws {Error} 'Invalid role'
|
||||
*/
|
||||
export function assertRole(role) {
|
||||
if (!COMPANY_USER_ROLES.includes(role)) {
|
||||
throw new Error('Invalid role');
|
||||
}
|
||||
return role;
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalizes and validates an email address.
|
||||
* @param {unknown} email
|
||||
* @returns {string} lower-cased, trimmed email
|
||||
* @throws {Error} 'Invalid email'
|
||||
*/
|
||||
export function normalizeEmail(email) {
|
||||
const value = typeof email === 'string' ? email.trim().toLowerCase() : '';
|
||||
if (!value || !value.includes('@')) {
|
||||
throw new Error('Invalid email');
|
||||
}
|
||||
return value;
|
||||
}
|
||||
51
src/store/companyuser/setRole.js
Normal file
51
src/store/companyuser/setRole.js
Normal file
@@ -0,0 +1,51 @@
|
||||
import {
|
||||
COUNT_ADMINS as MSSQL_COUNT_ADMINS,
|
||||
GET_IN_COMPANY as MSSQL_GET,
|
||||
SET_ROLE as MSSQL_SET_ROLE,
|
||||
} from '../../providers/mssql/queries/companyuser.js';
|
||||
import {
|
||||
COUNT_ADMINS as SQLITE_COUNT_ADMINS,
|
||||
GET_IN_COMPANY as SQLITE_GET,
|
||||
SET_ROLE as SQLITE_SET_ROLE,
|
||||
} from '../../providers/sqlite3/queries/companyuser.js';
|
||||
import requireAdmin from './requireAdmin.js';
|
||||
import withTransaction from './withTransaction.js';
|
||||
import { assertRole } from './roles.js';
|
||||
|
||||
export default function setRole(provider) {
|
||||
const isSqlite = provider?.dialect === 'sqlite3';
|
||||
const getSql = isSqlite ? SQLITE_GET : MSSQL_GET;
|
||||
const countSql = isSqlite ? SQLITE_COUNT_ADMINS : MSSQL_COUNT_ADMINS;
|
||||
const setSql = isSqlite ? SQLITE_SET_ROLE : MSSQL_SET_ROLE;
|
||||
const admin = requireAdmin(provider);
|
||||
|
||||
/**
|
||||
* Changes the role of a member of the admin's selected company.
|
||||
*
|
||||
* @param {number} googleAuthId - The id of the acting Google auth user (must be admin of the selected company).
|
||||
* @param {number} id - The company user id.
|
||||
* @param {'admin'|'user'} role - The new role.
|
||||
* @param {object} [tx] - Optional transaction to run within.
|
||||
* @returns {Promise<{id: number, googleAuthId: number, email: string, picture: string|null, role: 'admin'|'user'}>} The updated company user.
|
||||
* @throws {Error} 'Not authorized', 'Invalid role', 'Company user not found', 'Cannot demote the last admin'
|
||||
*
|
||||
* @example
|
||||
* await store.companyuser.setRole(42, 7, 'admin');
|
||||
*/
|
||||
return async (googleAuthId, id, role, tx) =>
|
||||
withTransaction(provider, tx, async (runner) => {
|
||||
const { companyId } = await admin(googleAuthId, runner);
|
||||
assertRole(role);
|
||||
|
||||
const member = (await runner.query(getSql, { id, companyId })).rows[0];
|
||||
if (!member) throw new Error('Company user not found');
|
||||
|
||||
if (member.role === 'admin' && role !== 'admin') {
|
||||
const { count } = (await runner.query(countSql, { companyId })).rows[0];
|
||||
if (Number(count) <= 1) throw new Error('Cannot demote the last admin');
|
||||
}
|
||||
|
||||
await runner.query(setSql, { id, role });
|
||||
return { id: member.id, googleAuthId: member.googleAuthId, email: member.email, picture: member.picture ?? null, role };
|
||||
});
|
||||
}
|
||||
11
src/store/companyuser/withTransaction.js
Normal file
11
src/store/companyuser/withTransaction.js
Normal file
@@ -0,0 +1,11 @@
|
||||
import makeRunner from '../runner.js';
|
||||
|
||||
/**
|
||||
* Runs `fn(runner)` inside the given transaction, a new one, or (providers
|
||||
* without transaction support) directly.
|
||||
*/
|
||||
export default async function withTransaction(provider, tx, fn) {
|
||||
if (tx) return fn(makeRunner(provider, tx));
|
||||
if (typeof provider?.transaction === 'function') return provider.transaction(fn);
|
||||
return fn(makeRunner(provider));
|
||||
}
|
||||
@@ -1,10 +1,27 @@
|
||||
import { ADD as MSSQL_ADD } from '../../providers/mssql/queries/google_auth.js';
|
||||
import { ADD as SQLITE_ADD } from '../../providers/sqlite3/queries/google_auth.js';
|
||||
import {
|
||||
DELETE as MSSQL_DELETE_INVITE,
|
||||
LIST_BY_EMAIL as MSSQL_INVITES,
|
||||
} from '../../providers/mssql/queries/autoinvite.js';
|
||||
import {
|
||||
DELETE as SQLITE_DELETE_INVITE,
|
||||
LIST_BY_EMAIL as SQLITE_INVITES,
|
||||
} from '../../providers/sqlite3/queries/autoinvite.js';
|
||||
import { ADD as MSSQL_ADD_COMPANYUSER } from '../../providers/mssql/queries/companyuser.js';
|
||||
import { ADD as SQLITE_ADD_COMPANYUSER } from '../../providers/sqlite3/queries/companyuser.js';
|
||||
import withTransaction from '../companyuser/withTransaction.js';
|
||||
|
||||
export default function add(provider) {
|
||||
const sql = provider.dialect === 'sqlite3' ? SQLITE_ADD : MSSQL_ADD;
|
||||
const isSqlite = provider.dialect === 'sqlite3';
|
||||
const sql = isSqlite ? SQLITE_ADD : MSSQL_ADD;
|
||||
const invitesSql = isSqlite ? SQLITE_INVITES : MSSQL_INVITES;
|
||||
const deleteInviteSql = isSqlite ? SQLITE_DELETE_INVITE : MSSQL_DELETE_INVITE;
|
||||
const addCompanyUserSql = isSqlite ? SQLITE_ADD_COMPANYUSER : MSSQL_ADD_COMPANYUSER;
|
||||
/**
|
||||
* Adds a new allowed Google auth email.
|
||||
* Adds a new allowed Google auth email (registration). Pending
|
||||
* `autoinvite` rows for this email are consumed in the same transaction:
|
||||
* each is deleted and turned into a `companyuser` with the invited role.
|
||||
*
|
||||
* @param {string} email - The email address to allow.
|
||||
* @returns {Promise<number>} The id of the created Google auth row.
|
||||
@@ -13,8 +30,20 @@ export default function add(provider) {
|
||||
* const id = await store.google_auth.add('user@example.com');
|
||||
* // => 1
|
||||
*/
|
||||
return async (email) => {
|
||||
const result = await provider.query(sql, { email });
|
||||
return result.rows[0]?.id ?? result.insertId;
|
||||
};
|
||||
return async (email) =>
|
||||
withTransaction(provider, null, async (runner) => {
|
||||
const result = await runner.query(sql, { email });
|
||||
const googleAuthId = result.rows[0]?.id ?? result.insertId;
|
||||
|
||||
const invites = (await runner.query(invitesSql, { email: String(email).trim().toLowerCase() })).rows;
|
||||
for (const invite of invites) {
|
||||
await runner.query(addCompanyUserSql, {
|
||||
companyId: invite.companyId,
|
||||
googleAuthId,
|
||||
role: invite.role,
|
||||
});
|
||||
await runner.query(deleteInviteSql, { id: invite.id });
|
||||
}
|
||||
return googleAuthId;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,19 +1,29 @@
|
||||
import { SET_SELECTED_COMPANY as MSSQL_SET } from '../../providers/mssql/queries/google_auth.js';
|
||||
import { SET_SELECTED_COMPANY as SQLITE_SET } from '../../providers/sqlite3/queries/google_auth.js';
|
||||
import { IS_MEMBER as MSSQL_IS_MEMBER } from '../../providers/mssql/queries/companyuser.js';
|
||||
import { IS_MEMBER as SQLITE_IS_MEMBER } from '../../providers/sqlite3/queries/companyuser.js';
|
||||
|
||||
export default function setSelectedCompany(provider) {
|
||||
const sql = provider.dialect === 'sqlite3' ? SQLITE_SET : MSSQL_SET;
|
||||
const isSqlite = provider.dialect === 'sqlite3';
|
||||
const sql = isSqlite ? SQLITE_SET : MSSQL_SET;
|
||||
const isMemberSql = isSqlite ? SQLITE_IS_MEMBER : MSSQL_IS_MEMBER;
|
||||
/**
|
||||
* Sets the currently selected company for a Google auth user.
|
||||
* The user must be a member (`companyuser`) of the company.
|
||||
*
|
||||
* @param {number} googleAuthId - The id of the Google auth user.
|
||||
* @param {number} companyId - The id of the company to select.
|
||||
* @returns {Promise<void>}
|
||||
* @throws {Error} 'Not authorized' if the user is not a member of the company.
|
||||
*
|
||||
* @example
|
||||
* await store.google_auth.setSelectedCompany(42, 1);
|
||||
*/
|
||||
return async (googleAuthId, companyId) => {
|
||||
if (companyId != null) {
|
||||
const member = await provider.query(isMemberSql, { googleAuthId, companyId });
|
||||
if (!member.rows[0]) throw new Error('Not authorized');
|
||||
}
|
||||
await provider.query(sql, { googleAuthId, companyId });
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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}';
|
||||
|
||||
@@ -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]);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
});
|
||||
|
||||
|
||||
@@ -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 });
|
||||
|
||||
14
src/store/printerjob/_shared.js
Normal file
14
src/store/printerjob/_shared.js
Normal file
@@ -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),
|
||||
};
|
||||
}
|
||||
|
||||
71
src/store/printerjob/create.js
Normal file
71
src/store/printerjob/create.js
Normal file
@@ -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<object>} 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;
|
||||
};
|
||||
}
|
||||
|
||||
46
src/store/printerjob/delete.js
Normal file
46
src/store/printerjob/delete.js
Normal file
@@ -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<object>} 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;
|
||||
};
|
||||
}
|
||||
|
||||
49
src/store/printerjob/getById.js
Normal file
49
src/store/printerjob/getById.js
Normal file
@@ -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<object|null>}
|
||||
*/
|
||||
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;
|
||||
};
|
||||
}
|
||||
|
||||
74
src/store/printerjob/list.js
Normal file
74
src/store/printerjob/list.js
Normal file
@@ -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<Array<object>>}
|
||||
*/
|
||||
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;
|
||||
};
|
||||
}
|
||||
|
||||
66
src/store/printerjob/updateState.js
Normal file
66
src/store/printerjob/updateState.js
Normal file
@@ -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<object>} 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]);
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user