This commit is contained in:
seb
2026-10-31 21:19:13 +01:00
parent 6719cf9d51
commit f6eb31b70d
4 changed files with 169 additions and 21 deletions

View File

@@ -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';

100
src/api/printerjob.js Normal file
View File

@@ -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',
});
},
};

View File

@@ -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;

View File

@@ -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 (
<Chip
label="Offline"
@@ -132,9 +139,8 @@ function renderPrinterDiagnostics(printer, live) {
);
}
const errors = live.errors || {};
const errorChips = [];
if (errors.headOpen) {
if (isOpen) {
errorChips.push(
<Chip
key="headOpen"
@@ -145,7 +151,7 @@ function renderPrinterDiagnostics(printer, live) {
/>,
);
}
if (errors.pause) {
if (isPaused) {
errorChips.push(
<Chip
key="pause"
@@ -156,7 +162,7 @@ function renderPrinterDiagnostics(printer, live) {
/>,
);
}
if (errors.unreachable) {
if (isUnreachable) {
errorChips.push(
<Chip
key="unreachable"
@@ -167,7 +173,7 @@ function renderPrinterDiagnostics(printer, live) {
/>,
);
}
if (errors.other) {
if (isError) {
errorChips.push(
<Chip
key="other"
@@ -192,9 +198,9 @@ function renderPrinterDiagnostics(printer, live) {
variant="outlined"
sx={{ fontSize: '0.7rem', height: 20, fontWeight: 600 }}
/>
{live.labelLength != null ? (
{labelLength != null ? (
<Chip
label={`Etikett: ${live.labelLength} px`}
label={`Etikett: ${labelLength} px`}
size="small"
variant="outlined"
sx={{ fontSize: '0.7rem', height: 20 }}
@@ -227,6 +233,7 @@ export default class PrinterAgentSettingsPanel extends PureComponent {
editingId: null,
editNameDraft: '',
sortDrafts: {},
testingPrinterId: null,
};
this.handleRetry = this.handleRetry.bind(this);
this.handleCreateAgent = this.handleCreateAgent.bind(this);
@@ -241,6 +248,7 @@ export default class PrinterAgentSettingsPanel extends PureComponent {
this.handleTogglePrinterEnabled = this.handleTogglePrinterEnabled.bind(this);
this.handleMovePrinter = this.handleMovePrinter.bind(this);
this.handleDeletePrinter = this.handleDeletePrinter.bind(this);
this.handleTestPrint = this.handleTestPrint.bind(this);
}
componentDidMount() {
@@ -467,6 +475,24 @@ export default class PrinterAgentSettingsPanel extends PureComponent {
}
}
async handleTestPrint(printer) {
if (!printer?.id) return;
this.setState({ testingPrinterId: printer.id });
try {
await companyAutomat.actions.sendTestPrint(printer.id, {
model: printer.model || printer.type || 'Zebra',
});
enqueueSnackbar(
`Testdruck für ${printer.model || 'Drucker'} (#${printer.id}) an Drucker-Warteschlange gesendet.`,
{ variant: 'success' },
);
} catch (err) {
notifyError(err);
} finally {
this.setState({ testingPrinterId: null });
}
}
render() {
const {
error,
@@ -479,6 +505,7 @@ export default class PrinterAgentSettingsPanel extends PureComponent {
patchingAgentId,
editingId,
editNameDraft,
testingPrinterId,
} = this.state;
if (error && printeragents.length === 0) {
@@ -564,8 +591,6 @@ export default class PrinterAgentSettingsPanel extends PureComponent {
const isEditing = editingId === agent.id;
const isPaired = agent.pairedAt != null;
const isEnabled = agent.enabled !== false;
const clientState = agent.clientstate;
const isOnline = clientState === 'online';
const pairedDateText = formatDateTime(agent.pairedAt);
const lastseenText = formatDateTime(agent.lastseenAt) ?? 'Nie';
const updatedText = formatDateTime(agent.updatedAt) ?? '—';
@@ -700,17 +725,6 @@ export default class PrinterAgentSettingsPanel extends PureComponent {
/>
)}
{/* Live connection state */}
{clientState ? (
<Chip
label={isOnline ? 'Online' : 'Offline'}
size="small"
variant="outlined"
color={isOnline ? 'success' : 'default'}
sx={{ fontSize: '0.75rem', fontWeight: 600 }}
/>
) : null}
{/* Agent version */}
{agent.agentversion ? (
<Tooltip title="Agent Software-Version">
@@ -931,6 +945,30 @@ export default class PrinterAgentSettingsPanel extends PureComponent {
</Box>
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
{/* Test print button */}
<Tooltip title="Test-Etikett an Drucker senden (ZPL)">
<span>
<Button
size="small"
variant="outlined"
color="primary"
startIcon={<PrintOutlinedIcon sx={{ fontSize: 15 }} />}
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'}
</Button>
</span>
</Tooltip>
{/* Enabled toggle switch */}
<FormControlLabel
control={