@alizadefaig/pos-server 1.2.25 → 1.2.27
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/app/core/Context.js +170 -0
- package/app/core/auth/index.js +133 -0
- package/app/core/auth/license.js +45 -0
- package/app/core/auth/posMode.js +260 -0
- package/app/core/auth/sanctum.js +209 -0
- package/app/core/auth/scope.js +231 -0
- package/app/core/emitter.js +67 -0
- package/app/core/errors.js +18 -0
- package/app/core/guard.js +54 -0
- package/app/core/idempotency.js +196 -0
- package/app/core/index.js +374 -0
- package/app/core/models/Order.js +176 -0
- package/app/core/models/OrderProduct.js +249 -0
- package/app/core/repositories/EventRepository.js +51 -0
- package/app/core/repositories/OrderProductRepository.js +225 -0
- package/app/core/repositories/OrderRepository.js +274 -0
- package/app/core/repositories/OutboxRepository.js +52 -0
- package/app/core/repositories/PaymentRepository.js +43 -0
- package/app/core/repositories/PrinterRepository.js +179 -0
- package/app/core/repositories/ProductRelations.js +148 -0
- package/app/core/repositories/SnapshotRepository.js +197 -0
- package/app/core/resources/HallResource.js +54 -0
- package/app/core/resources/OrderResource.js +177 -0
- package/app/core/resources/ProductResource.js +186 -0
- package/app/core/resources/ShiftResource.js +42 -0
- package/app/core/resources/TeamResource.js +85 -0
- package/app/core/resources/casts.js +55 -0
- package/app/core/services/AuthService.js +468 -0
- package/app/core/services/InitService.js +277 -0
- package/app/core/services/KdsService.js +639 -0
- package/app/core/services/OrderProductService.js +309 -0
- package/app/core/services/OrderService.js +903 -0
- package/app/core/services/PrintService.js +715 -0
- package/app/core/services/PrinterAdminService.js +324 -0
- package/app/core/services/ReportService.js +581 -0
- package/app/core/services/ShiftService.js +199 -0
- package/app/core/services/StorageService.js +425 -0
- package/app/core/services/TelegramService.js +162 -0
- package/app/core/support/db.js +48 -0
- package/app/core/support/ids.js +9 -0
- package/app/core/support/imageData.js +135 -0
- package/app/core/support/images.js +92 -0
- package/app/core/support/lanHandoff.js +105 -0
- package/app/core/support/lang.js +98 -0
- package/app/core/support/lock.js +63 -0
- package/app/core/support/num.js +51 -0
- package/app/core/support/php.js +125 -0
- package/app/core/support/pin.js +34 -0
- package/app/core/support/receipts.js +278 -0
- package/app/core/support/rows.js +38 -0
- package/app/core/support/str.js +31 -0
- package/app/core/support/teamSettings.js +77 -0
- package/app/core/support/telegram.js +253 -0
- package/app/core/support/telegramReport.js +143 -0
- package/app/core/support/time.js +58 -0
- package/app/core/support/tz.js +80 -0
- package/app/core/support/unlock.js +113 -0
- package/app/core/timers.js +151 -0
- package/app/db/Database.js +197 -1
- package/app/db/Migrator.js +45 -0
- package/app/db/driver.js +248 -0
- package/app/db/migrations/001_snapshot.sql +214 -0
- package/app/db/migrations/002_orders.sql +190 -0
- package/app/db/migrations/003_idempotency.sql +18 -0
- package/app/db/migrations/004_shifts.sql +21 -0
- package/app/db/migrations/005_printers.sql +43 -0
- package/app/db/migrations/006_sync.sql +136 -0
- package/app/db/migrations/007_printer_mac.sql +4 -0
- package/app/db/migrations/008_kds.sql +94 -0
- package/app/db/migrations/009_product_categories.sql +20 -0
- package/app/db/migrations/010_printers_sync.sql +43 -0
- package/app/db/migrations/011_team_logos.sql +13 -0
- package/app/db/migrations/012_category_workshop.sql +4 -0
- package/app/monitor.js +185 -0
- package/app/printers/address.js +348 -0
- package/app/proxy.js +20 -3
- package/app/relay/masters.js +173 -0
- package/app/security.js +218 -0
- package/app/socket/auth.js +160 -0
- package/app/socket/request.js +87 -0
- package/app/socket/server.js +121 -19
- package/app/sync/bridge.js +162 -0
- package/app/sync/cloud.js +120 -0
- package/app/sync/config.js +185 -0
- package/app/sync/discovery.js +546 -0
- package/app/sync/index.js +1644 -0
- package/app/sync/kv.js +26 -0
- package/app/sync/lan.js +139 -0
- package/app/sync/lanDevices.js +177 -0
- package/app/sync/lanFront.js +431 -0
- package/app/sync/mirror.js +236 -0
- package/app/sync/push.js +324 -0
- package/app/sync/reconcile.js +161 -0
- package/app/sync/signal.js +202 -0
- package/app/sync/snapshot.js +189 -0
- package/app/sync/store.js +234 -0
- package/app/tcp.js +11 -2
- package/package.json +6 -3
- package/routes/common/api/auth.js +38 -0
- package/routes/common/api/kds.js +37 -0
- package/routes/common/api/orderProducts.js +81 -0
- package/routes/common/api/orders.js +176 -0
- package/routes/common/api/printersCore.js +35 -0
- package/routes/common/api/reports.js +29 -0
- package/routes/common/socket.js +15 -35
- package/routes/common/tax.js +35 -7
- package/routes/desktop/api/offline.js +146 -0
- package/routes/desktop/api/printers.js +288 -5
- package/routes/desktop/local.js +130 -0
- package/routes/server/api/login.js +37 -0
- package/routes/server/api/platform.js +31 -0
- package/routes/server/api/shifts.js +25 -0
- package/routes/server/master.js +24 -0
- package/server.js +111 -4
- package/utils.js +6 -0
- package/tests/usbPrinterTest.js +0 -20
|
@@ -0,0 +1,324 @@
|
|
|
1
|
+
const {HttpError} = require('../errors');
|
|
2
|
+
const {uuid} = require('../support/ids');
|
|
3
|
+
const {nowSql} = require('../support/time');
|
|
4
|
+
const {toBool} = require('../support/num');
|
|
5
|
+
const {castRow} = require('../support/db');
|
|
6
|
+
const {phpTruthy} = require('../support/receipts');
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Порт App\Http\Controllers\Api\Platform\PrinterController: настройка принтеров терминала
|
|
10
|
+
* (update / list / delete / get / syncAddress). Печать — отдельно, в PrintService.
|
|
11
|
+
*
|
|
12
|
+
* Без облака касса должна уметь назначить принтер цеху, сменить принтер счёта или подхватить
|
|
13
|
+
* новый IP принтера: иначе замена принтера посреди смены останавливает кухню до возвращения
|
|
14
|
+
* интернета. Локальные правки помечаются грязными триггерами 010 и уезжают в облако push'ем
|
|
15
|
+
* (sync/push.js), облачные приходят снапшотом.
|
|
16
|
+
*
|
|
17
|
+
* Семантика запросов — как у Eloquent:
|
|
18
|
+
* - Printer::query() не видит soft-deleted — у printers deleted_at есть в обеих схемах;
|
|
19
|
+
* - у product_workshops и terminals в локальном снапшоте deleted_at нет (удалённое туда не
|
|
20
|
+
* приходит), поэтому условие на них — только под Postgres;
|
|
21
|
+
* - where([... => null]) — это IS NULL;
|
|
22
|
+
* - Builder::update() и SoftDeletes ставят updated_at.
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
// FILTER_VALIDATE_IP + FILTER_FLAG_IPV4: четыре октета 0..255 без ведущих нулей.
|
|
26
|
+
const OCTET = '(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)';
|
|
27
|
+
const IPV4 = new RegExp(`^${OCTET}(?:\\.${OCTET}){3}$`);
|
|
28
|
+
|
|
29
|
+
// Printer::normalizeMac: разделитель один на весь адрес, октет из 1–2 знаков.
|
|
30
|
+
const MAC = /^\s*([0-9a-f]{1,2})([:-])([0-9a-f]{1,2})(?:\2([0-9a-f]{1,2})){4}\s*$/i;
|
|
31
|
+
|
|
32
|
+
const normalizeMac = (value) => {
|
|
33
|
+
if (typeof value !== 'string' || !MAC.test(value)) {
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
const mac = value.trim().split(/[:-]/).map((octet) => octet.toLowerCase().padStart(2, '0')).join(':');
|
|
37
|
+
return mac === '00:00:00:00:00:00' || mac === 'ff:ff:ff:ff:ff:ff' ? null : mac;
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
// $request->boolean(): FILTER_VALIDATE_BOOLEAN — true только для 1/'1'/'true'/'on'/'yes'.
|
|
41
|
+
const requestBoolean = (value) => {
|
|
42
|
+
if (typeof value === 'boolean') {
|
|
43
|
+
return value;
|
|
44
|
+
}
|
|
45
|
+
if (typeof value === 'number') {
|
|
46
|
+
return value === 1;
|
|
47
|
+
}
|
|
48
|
+
if (typeof value === 'string') {
|
|
49
|
+
return ['1', 'true', 'on', 'yes'].includes(value.trim().toLowerCase());
|
|
50
|
+
}
|
|
51
|
+
return false;
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
// Идентификаторы USB приходят из скана числами, а лежат строками. Postgres не сравнит varchar с
|
|
55
|
+
// integer-параметром, поэтому в запрос уходит строка.
|
|
56
|
+
const asText = (value) => (value === null || value === undefined ? null : String(value));
|
|
57
|
+
|
|
58
|
+
// where(column, value) у Eloquent: null → IS NULL, иначе обычное равенство — с индексом в Postgres
|
|
59
|
+
// (IS NOT DISTINCT FROM индекс не использует).
|
|
60
|
+
const eq = (column, value) => (value === null || value === undefined
|
|
61
|
+
? {sql: ` AND ${column} IS NULL`, params: []}
|
|
62
|
+
: {sql: ` AND ${column} = ?`, params: [value]});
|
|
63
|
+
|
|
64
|
+
const where = (...parts) => ({
|
|
65
|
+
sql: parts.map((part) => part.sql).join(''),
|
|
66
|
+
params: parts.flatMap((part) => part.params),
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
// Сравнение значения из запроса с колонкой «по смыслу» — как isDirty у Eloquent для флагов
|
|
70
|
+
// (true и 1 — одно и то же, иначе каждое сохранение без изменений писало бы строку и гнало push).
|
|
71
|
+
const sameValue = (column, current, next) => {
|
|
72
|
+
if (['bill', 'bell', 'cashdraw'].includes(column)) {
|
|
73
|
+
return current !== null && current !== undefined && next !== null && next !== undefined
|
|
74
|
+
? toBool(current) === toBool(next)
|
|
75
|
+
: current === next;
|
|
76
|
+
}
|
|
77
|
+
return (current ?? null) === (next ?? null);
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
class PrinterAdminService {
|
|
81
|
+
constructor({db, context}) {
|
|
82
|
+
this.db = db;
|
|
83
|
+
this.context = context;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
notDeleted(alias = null) {
|
|
87
|
+
return this.db.dialect === 'pgsql' ? ` AND ${alias ? `${alias}.` : ''}deleted_at IS NULL` : '';
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/** DB::table('product_workshops')->where(team_id, deleted_at = null)->get(). */
|
|
91
|
+
async workshops() {
|
|
92
|
+
const rows = await this.db.all(
|
|
93
|
+
`SELECT * FROM product_workshops WHERE team_id = ?${this.notDeleted()}`,
|
|
94
|
+
[this.context.teamId]
|
|
95
|
+
);
|
|
96
|
+
return rows.map((row) => {
|
|
97
|
+
const {synced_at: ignored, ...rest} = row;
|
|
98
|
+
return rest;
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/** pluck('print_ticket', 'product_workshop_id'): объект, пустой → [] (пустая коллекция в JSON). */
|
|
103
|
+
async workshopSettings(printerId, db = this.db) {
|
|
104
|
+
const rows = await db.all(
|
|
105
|
+
'SELECT product_workshop_id, print_ticket FROM printer_workshop WHERE printer_id = ?',
|
|
106
|
+
[printerId]
|
|
107
|
+
);
|
|
108
|
+
if (!rows.length) {
|
|
109
|
+
return [];
|
|
110
|
+
}
|
|
111
|
+
const settings = {};
|
|
112
|
+
for (const row of rows) {
|
|
113
|
+
settings[row.product_workshop_id] = toBool(row.print_ticket);
|
|
114
|
+
}
|
|
115
|
+
return settings;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Условие поиска сетевого принтера: с MAC — строка с тем же MAC или строка без MAC на этом
|
|
120
|
+
* IP (записанная до привязки), MAC-строка первой; без MAC — по IP.
|
|
121
|
+
*/
|
|
122
|
+
lanMatch(ip, mac) {
|
|
123
|
+
if (!mac) {
|
|
124
|
+
return {...eq('ip_address', asText(ip)), order: ''};
|
|
125
|
+
}
|
|
126
|
+
const byIp = eq('ip_address', asText(ip));
|
|
127
|
+
return {
|
|
128
|
+
sql: ` AND (mac_address = ? OR (mac_address IS NULL${byIp.sql}))`,
|
|
129
|
+
params: [mac, ...byIp.params],
|
|
130
|
+
order: ' ORDER BY (mac_address IS NULL)',
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
async update(params) {
|
|
135
|
+
const terminalId = this.context.terminalId;
|
|
136
|
+
const type = params.type ?? null;
|
|
137
|
+
const productId = params.product_id ?? null;
|
|
138
|
+
const vendorId = params.vendor_id ?? null;
|
|
139
|
+
const ip = params.ip_address ?? null;
|
|
140
|
+
const mac = type === 'LAN' ? normalizeMac(params.mac_address) : null;
|
|
141
|
+
const cashdraw = requestBoolean(params.cashdraw);
|
|
142
|
+
|
|
143
|
+
return this.db.transaction(async (db) => {
|
|
144
|
+
let filter = eq('terminal_id', terminalId);
|
|
145
|
+
let order = '';
|
|
146
|
+
if (type === 'USB') {
|
|
147
|
+
filter = where(filter, eq('product_id', asText(productId)), eq('vendor_id', asText(vendorId)));
|
|
148
|
+
} else if (type === 'LAN') {
|
|
149
|
+
const match = this.lanMatch(ip, mac);
|
|
150
|
+
filter = where(filter, match);
|
|
151
|
+
order = match.order;
|
|
152
|
+
}
|
|
153
|
+
const existing = await db.get(
|
|
154
|
+
`SELECT * FROM printers WHERE deleted_at IS NULL${filter.sql}${order} LIMIT 1`,
|
|
155
|
+
filter.params
|
|
156
|
+
);
|
|
157
|
+
|
|
158
|
+
const changes = {
|
|
159
|
+
name: params.name ?? null,
|
|
160
|
+
// Laravel пишет bill/bell как пришли. null в NOT NULL-колонку уронил бы запрос,
|
|
161
|
+
// поэтому непереданный флаг оставляем прежним (у новой строки — DEFAULT).
|
|
162
|
+
...(params.bill === undefined || params.bill === null ? {} : {bill: params.bill}),
|
|
163
|
+
...(params.bell === undefined || params.bell === null ? {} : {bell: params.bell}),
|
|
164
|
+
cashdraw,
|
|
165
|
+
...(mac ? {ip_address: asText(ip), mac_address: mac} : {}),
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
const now = nowSql();
|
|
169
|
+
let printerId;
|
|
170
|
+
if (existing) {
|
|
171
|
+
printerId = existing.id;
|
|
172
|
+
const dirty = {};
|
|
173
|
+
for (const [column, value] of Object.entries(changes)) {
|
|
174
|
+
if (!sameValue(column, existing[column], value)) {
|
|
175
|
+
dirty[column] = value;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
if (Object.keys(dirty).length) {
|
|
179
|
+
await db.update('printers', printerId, castRow({...dirty, updated_at: now}));
|
|
180
|
+
}
|
|
181
|
+
} else {
|
|
182
|
+
printerId = uuid();
|
|
183
|
+
await db.insert('printers', castRow({
|
|
184
|
+
id: printerId,
|
|
185
|
+
ip_address: asText(ip),
|
|
186
|
+
mac_address: mac,
|
|
187
|
+
type,
|
|
188
|
+
vendor_id: asText(vendorId),
|
|
189
|
+
product_id: asText(productId),
|
|
190
|
+
terminal_id: terminalId,
|
|
191
|
+
team_id: this.context.teamId,
|
|
192
|
+
company_id: this.context.companyId,
|
|
193
|
+
...changes,
|
|
194
|
+
created_at: now,
|
|
195
|
+
updated_at: now,
|
|
196
|
+
}));
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// BelongsToMany::sync со списком строк: ключи списка — 0..n, поэтому текущие связи
|
|
200
|
+
// уходят все, а строки вставляются заново. То есть полная замена цехов принтера.
|
|
201
|
+
const settings = params.workshop_settings;
|
|
202
|
+
if (phpTruthy(settings) && typeof settings === 'object') {
|
|
203
|
+
await db.run('DELETE FROM printer_workshop WHERE printer_id = ?', [printerId]);
|
|
204
|
+
for (const [workshopId, printTicket] of Object.entries(settings)) {
|
|
205
|
+
await db.run(
|
|
206
|
+
'INSERT INTO printer_workshop (printer_id, product_workshop_id, print_ticket) VALUES (?, ?, ?)',
|
|
207
|
+
[printerId, workshopId, castRow({print_ticket: printTicket}).print_ticket]
|
|
208
|
+
);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
return true;
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
async list() {
|
|
216
|
+
const workshops = await this.workshops();
|
|
217
|
+
const filter = eq('terminal_id', this.context.terminalId);
|
|
218
|
+
const rows = await this.db.all(`SELECT * FROM printers WHERE deleted_at IS NULL${filter.sql}`, filter.params);
|
|
219
|
+
const printers = [];
|
|
220
|
+
for (const printer of rows) {
|
|
221
|
+
printers.push({
|
|
222
|
+
id: printer.id,
|
|
223
|
+
type: printer.type,
|
|
224
|
+
ip_address: printer.ip_address,
|
|
225
|
+
mac_address: printer.mac_address ?? null,
|
|
226
|
+
vendor_id: printer.vendor_id,
|
|
227
|
+
product_id: printer.product_id,
|
|
228
|
+
name: printer.name,
|
|
229
|
+
bill: toBool(printer.bill),
|
|
230
|
+
bell: toBool(printer.bell),
|
|
231
|
+
cashdraw: toBool(printer.cashdraw),
|
|
232
|
+
workshop_settings: await this.workshopSettings(printer.id),
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
return {workshops, printers};
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
async delete(params) {
|
|
239
|
+
return this.db.transaction(async (db) => {
|
|
240
|
+
const filter = where(eq('terminal_id', this.context.terminalId), eq('id', asText(params.id)));
|
|
241
|
+
const printer = await db.get(`SELECT id FROM printers WHERE deleted_at IS NULL${filter.sql} LIMIT 1`, filter.params);
|
|
242
|
+
if (printer) {
|
|
243
|
+
await db.run('DELETE FROM printer_workshop WHERE printer_id = ?', [printer.id]);
|
|
244
|
+
const now = nowSql();
|
|
245
|
+
await db.update('printers', printer.id, {deleted_at: now, updated_at: now});
|
|
246
|
+
}
|
|
247
|
+
return true;
|
|
248
|
+
});
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
async syncAddress(params) {
|
|
252
|
+
const mac = normalizeMac(params.mac_address);
|
|
253
|
+
const ip = params.ip_address;
|
|
254
|
+
if (!mac || typeof ip !== 'string' || !IPV4.test(ip)) {
|
|
255
|
+
throw new HttpError(422, {success: false, message: 'Invalid printer address'});
|
|
256
|
+
}
|
|
257
|
+
const now = nowSql();
|
|
258
|
+
|
|
259
|
+
const own = eq('terminal_id', this.context.terminalId);
|
|
260
|
+
const bound = await this.db.run(
|
|
261
|
+
`UPDATE printers SET mac_address = ?, updated_at = ?
|
|
262
|
+
WHERE deleted_at IS NULL${own.sql} AND type = 'LAN' AND mac_address IS NULL AND ip_address = ?`,
|
|
263
|
+
[mac, now, ...own.params, ip]
|
|
264
|
+
);
|
|
265
|
+
|
|
266
|
+
const company = eq('company_id', this.context.companyId);
|
|
267
|
+
const moved = await this.db.run(
|
|
268
|
+
`UPDATE printers SET ip_address = ?, updated_at = ?
|
|
269
|
+
WHERE deleted_at IS NULL
|
|
270
|
+
AND terminal_id IN (SELECT id FROM terminals WHERE 1 = 1${company.sql}${this.notDeleted()})
|
|
271
|
+
AND type = 'LAN' AND mac_address = ? AND ip_address != ?`,
|
|
272
|
+
[ip, now, ...company.params, mac, ip]
|
|
273
|
+
);
|
|
274
|
+
|
|
275
|
+
return {success: true, bound: bound?.changes ?? 0, moved: moved?.changes ?? 0};
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* Скан локального Node (params.printers) + сохранённые настройки: по каждому найденному
|
|
280
|
+
* принтеру — цеха, счёт, звонок и имя из БД. Не найден — все цеха выключены.
|
|
281
|
+
*/
|
|
282
|
+
async get(params) {
|
|
283
|
+
const scanned = Array.isArray(params.printers) ? params.printers
|
|
284
|
+
: (params.printers && typeof params.printers === 'object' ? Object.values(params.printers) : []);
|
|
285
|
+
const workshops = await this.workshops();
|
|
286
|
+
const printers = [];
|
|
287
|
+
for (const item of scanned) {
|
|
288
|
+
const printerData = {...(item || {})};
|
|
289
|
+
const mac = printerData.type === 'LAN' ? normalizeMac(printerData.mac_address) : null;
|
|
290
|
+
|
|
291
|
+
const match = this.lanMatch(printerData.ip_address, mac);
|
|
292
|
+
const filter = where(
|
|
293
|
+
eq('product_id', asText(printerData.product_id)),
|
|
294
|
+
eq('vendor_id', asText(printerData.vendor_id)),
|
|
295
|
+
eq('terminal_id', this.context.terminalId),
|
|
296
|
+
eq('type', asText(printerData.type)),
|
|
297
|
+
match
|
|
298
|
+
);
|
|
299
|
+
const printer = await this.db.get(
|
|
300
|
+
`SELECT * FROM printers WHERE deleted_at IS NULL${filter.sql}${match.order} LIMIT 1`,
|
|
301
|
+
filter.params
|
|
302
|
+
);
|
|
303
|
+
|
|
304
|
+
const workshopSettings = {};
|
|
305
|
+
for (const workshop of workshops) {
|
|
306
|
+
workshopSettings[workshop.id] = false;
|
|
307
|
+
}
|
|
308
|
+
printerData.workshop_settings = workshops.length ? workshopSettings : [];
|
|
309
|
+
printerData.bill = false;
|
|
310
|
+
printerData.bell = false;
|
|
311
|
+
if (printer) {
|
|
312
|
+
printerData.bill = toBool(printer.bill);
|
|
313
|
+
printerData.bell = toBool(printer.bell);
|
|
314
|
+
printerData.name = printer.name;
|
|
315
|
+
printerData.workshop_settings = await this.workshopSettings(printer.id);
|
|
316
|
+
}
|
|
317
|
+
printers.push(printerData);
|
|
318
|
+
}
|
|
319
|
+
return {workshops, printers};
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
module.exports = PrinterAdminService;
|
|
324
|
+
module.exports.normalizeMac = normalizeMac;
|