diff --git a/src/api/index.js b/src/api/index.js
index 15fca75..2a39d9c 100644
--- a/src/api/index.js
+++ b/src/api/index.js
@@ -17,6 +17,7 @@ export { posterminal } from './posterminal.js';
export { printer } from './printer.js';
export { printeragent } from './printeragent.js';
export { agentprinter } from './agentprinter.js';
+export { printerjob } from './printerjob.js';
export { propertyname } from './propertyname.js';
export { quote } from './quote.js';
export { retoure } from './retoure.js';
diff --git a/src/api/printerjob.js b/src/api/printerjob.js
new file mode 100644
index 0000000..44d25a7
--- /dev/null
+++ b/src/api/printerjob.js
@@ -0,0 +1,100 @@
+import { http } from './http.js';
+
+export const printerjob = {
+ /**
+ * POST /api/zpljob
+ * @param {{ agentprinterId: number, data: string, copiescount?: number }} payload
+ * → { printerjob: PrinterJob }
+ */
+ createZplJob({ agentprinterId, data, copiescount = 1 }) {
+ return http('/api/zpljob', {
+ method: 'POST',
+ body: {
+ agentprinterId: Number(agentprinterId),
+ data: String(data),
+ copiescount: Number(copiescount) || 1,
+ },
+ });
+ },
+
+ /**
+ * Sends a minimal ZPL test label to a specific agent printer.
+ * @param {number} agentprinterId
+ * @param {{ model?: string }} [options]
+ * → { printerjob: PrinterJob }
+ */
+ sendTestZpl(agentprinterId, { model = 'Zebra' } = {}) {
+ const timeStr = new Date().toLocaleTimeString('de-DE');
+ const safeModel = String(model || 'Drucker').replace(/[\^~]/g, '');
+ const zpl = [
+ '^XA',
+ '^FO40,40^ADN,36,20^FDTestdruck OK^FS',
+ `^FO40,85^ADN,18,10^FD${safeModel}^FS`,
+ `^FO40,115^ADN,18,10^FDID: ${agentprinterId} - ${timeStr}^FS`,
+ '^XZ',
+ ].join('\n');
+ return this.createZplJob({
+ agentprinterId,
+ data: zpl,
+ copiescount: 1,
+ });
+ },
+
+ /**
+ * GET /api/printerjob
+ * @param {{ agentprinterId?: number, state?: string }} [query]
+ * → { printerjobs: PrinterJob[] }
+ */
+ list(query = {}) {
+ const params = new URLSearchParams();
+ if (query.agentprinterId != null) params.set('agentprinterId', String(query.agentprinterId));
+ if (query.state != null) params.set('state', String(query.state));
+ const qs = params.toString();
+ return http(qs ? `/api/printerjob?${qs}` : '/api/printerjob');
+ },
+
+ /**
+ * GET /api/printerjob/:id
+ * → { printerjob: PrinterJob }
+ */
+ get(id) {
+ return http(`/api/printerjob/${id}`);
+ },
+
+ /**
+ * POST /api/printerjob
+ * @param {{ agentprinterId: number, type?: 'zpl'|'pdf'|'epos', data: string }} payload
+ * → { printerjob: PrinterJob }
+ */
+ create({ agentprinterId, type = 'zpl', data }) {
+ return http('/api/printerjob', {
+ method: 'POST',
+ body: {
+ agentprinterId: Number(agentprinterId),
+ type,
+ data,
+ },
+ });
+ },
+
+ /**
+ * POST /api/printerjob/:id/cancel
+ * → { success: true, printerjob: PrinterJob }
+ */
+ cancel(id) {
+ return http(`/api/printerjob/${id}/cancel`, {
+ method: 'POST',
+ });
+ },
+
+ /**
+ * DELETE /api/printerjob/:id
+ * → { success: true, deletedJob: PrinterJob }
+ */
+ delete(id) {
+ return http(`/api/printerjob/${id}`, {
+ method: 'DELETE',
+ });
+ },
+};
+
diff --git a/src/automats/companyAutomat.js b/src/automats/companyAutomat.js
index 8ce2aca..bfdb529 100644
--- a/src/automats/companyAutomat.js
+++ b/src/automats/companyAutomat.js
@@ -28,6 +28,7 @@ import {
printer as printerApi,
printeragent as printeragentApi,
agentprinter as agentprinterApi,
+ printerjob as printerjobApi,
supplier as supplierApi,
unit as unitApi,
warehouse as warehouseApi,
@@ -876,6 +877,14 @@ const actions = {
});
},
+ async sendTestPrint(agentprinterId, options = {}) {
+ return printerjobApi.sendTestZpl(agentprinterId, options);
+ },
+
+ async createZplJob(payload) {
+ return printerjobApi.createZplJob(payload);
+ },
+
async loadLivePrinters() {
const companyId =
authAutomat.state.user?.selectedCompanyId ?? companyAutomat.state.current?.id;
diff --git a/src/components/settings/PrinterAgentSettingsPanel.jsx b/src/components/settings/PrinterAgentSettingsPanel.jsx
index ca1afba..df5b64b 100644
--- a/src/components/settings/PrinterAgentSettingsPanel.jsx
+++ b/src/components/settings/PrinterAgentSettingsPanel.jsx
@@ -120,7 +120,14 @@ class AgentQrCode extends PureComponent {
}
function renderPrinterDiagnostics(printer, live) {
- if (!live) {
+ const isOnline = printer?.online ?? (live ? !live.unreachable && live.status !== 'offline' : false);
+ const isOpen = printer?.open ?? (live?.errors?.headOpen || live?.headOpen || false);
+ const isPaused = printer?.pause ?? (live?.errors?.pause || live?.pause || false);
+ const isError = printer?.error ?? (live?.errors?.other || live?.error || false);
+ const isUnreachable = !isOnline || (live?.errors?.unreachable || false);
+ const labelLength = printer?.labelLength ?? live?.labelLength;
+
+ if (!isOnline) {
return (
,
);
}
- if (errors.pause) {
+ if (isPaused) {
errorChips.push(
,
);
}
- if (errors.unreachable) {
+ if (isUnreachable) {
errorChips.push(
,
);
}
- if (errors.other) {
+ if (isError) {
errorChips.push(
- {live.labelLength != null ? (
+ {labelLength != null ? (
)}
- {/* Live connection state */}
- {clientState ? (
-
- ) : null}
-
{/* Agent version */}
{agent.agentversion ? (
@@ -931,6 +945,30 @@ export default class PrinterAgentSettingsPanel extends PureComponent {
+ {/* Test print button */}
+
+
+ }
+ disabled={testingPrinterId === p.id}
+ onClick={() => void this.handleTestPrint(p)}
+ sx={{
+ textTransform: 'none',
+ fontSize: '0.75rem',
+ py: 0.25,
+ px: 1,
+ minHeight: 26,
+ fontWeight: 600,
+ }}
+ >
+ {testingPrinterId === p.id ? 'Druckt…' : 'Testdruck'}
+
+
+
+
{/* Enabled toggle switch */}