From 62f4a4221028b1a761ff1766a8a0c6afa4458a0b Mon Sep 17 00:00:00 2001 From: cproudlock Date: Mon, 17 Aug 2026 14:10:08 -0400 Subject: [PATCH] fix: page past the 100-row cap in batch label sheets and asset pickers Follow-up to the application-picker fix. Three of these were already wrong on data that exists today, not merely latent. Batch label printing is the worst of them: AssetLabelBatch asked for 500 machines or PCs, got 100, and printed a sheet that looked complete. With 262 machines and 290 PCs in the catalogue that is a physically short run with no error anywhere - the operator finds out at the label printer, or later at the bay with no label on it. PrinterQRBatch, USBLabelBatch and PrintedPartsLabels had the same shape and are fixed alongside it, before their tables cross 100 too. MachineForm's "controls" PC dropdown offered the first 100 of 290, so a machine could not be linked to a PC sorting late in the list. NetworkDeviceForm had it for models, which are already past 100 - and the same file already called modelsApi.listAll() correctly two lines away. Adds listAll() to the machines, computers, printers, network, measuring-tools, USB and printed-parts APIs, all delegating to fetchAllPages(). Still outstanding: callers of vendors, locations, business units and the type catalogues that ask for more than 100. Those tables are all well under the cap today, so they are correct for now and wrong the day they are not. --- frontend/src/api/index.js | 47 +++++++++++++++++++ frontend/src/views/print/AssetLabelBatch.vue | 6 ++- .../machines/frontend/views/MachineForm.vue | 2 +- .../frontend/views/NetworkDeviceForm.vue | 5 +- .../frontend/views/PrintedPartsLabels.vue | 5 +- .../frontend/views/PrinterQRBatch.vue | 5 +- plugins/usb/frontend/views/USBLabelBatch.vue | 5 +- 7 files changed, 64 insertions(+), 11 deletions(-) diff --git a/frontend/src/api/index.js b/frontend/src/api/index.js index ea104d7..3592d72 100644 --- a/frontend/src/api/index.js +++ b/frontend/src/api/index.js @@ -101,6 +101,13 @@ export const machinesApi = { list(params = {}) { return api.get('/machines', { params }) }, + // Every machine, paged past the backend's 100-row cap. Batch label printing + // and "pick any record" dropdowns must use this: list() with a large + // perpage is clamped to 100 and still returns a success response, so + // the tail simply goes missing. + listAll(params = {}) { + return fetchAllPages('/machines', params) + }, get(id) { return api.get(`/machines/${id}`) }, @@ -144,6 +151,13 @@ export const computersApi = { list(params = {}) { return api.get('/computers', { params }) }, + // Every PC, paged past the backend's 100-row cap. Batch label printing + // and "pick any record" dropdowns must use this: list() with a large + // perpage is clamped to 100 and still returns a success response, so + // the tail simply goes missing. + listAll(params = {}) { + return fetchAllPages('/computers', params) + }, displayKiosks() { return api.get('/computers/display-kiosks') }, @@ -292,6 +306,13 @@ export const printersApi = { list(params = {}) { return api.get('/printers', { params }) }, + // Every printer, paged past the backend's 100-row cap. Batch label printing + // and "pick any record" dropdowns must use this: list() with a large + // perpage is clamped to 100 and still returns a success response, so + // the tail simply goes missing. + listAll(params = {}) { + return fetchAllPages('/printers', params) + }, get(id) { return api.get(`/printers/${id}`) }, @@ -732,6 +753,13 @@ export const usbApi = { list(params = {}) { return api.get('/usb', { params }) }, + // Every USB device, paged past the backend's 100-row cap. Batch label printing + // and "pick any record" dropdowns must use this: list() with a large + // perpage is clamped to 100 and still returns a success response, so + // the tail simply goes missing. + listAll(params = {}) { + return fetchAllPages('/usb', params) + }, get(id) { return api.get(`/usb/${id}`) }, @@ -1065,6 +1093,13 @@ export const networkApi = { list(params = {}) { return api.get('/network', { params }) }, + // Every network device, paged past the backend's 100-row cap. Batch label + // printing and "pick any record" dropdowns must use this: list() with a + // large perpage is clamped to 100 and still returns a success response, so + // the tail simply goes missing. + listAll(params = {}) { + return fetchAllPages('/network', params) + }, get(id) { return api.get(`/network/${id}`) }, @@ -1211,6 +1246,13 @@ export const measuringtoolsApi = { list(params = {}) { return api.get('/measuringtools', { params }) }, + // Every measuring tool, paged past the backend's 100-row cap. Batch label printing + // and "pick any record" dropdowns must use this: list() with a large + // perpage is clamped to 100 and still returns a success response, so + // the tail simply goes missing. + listAll(params = {}) { + return fetchAllPages('/measuringtools', params) + }, get(id) { return api.get(`/measuringtools/${id}`) }, @@ -1254,6 +1296,11 @@ export const printedpartsApi = { list(params = {}) { return api.get('/printedparts/items', { params }) }, + // Every printed item, paged past the backend's 100-row cap. The label sheet + // must print the whole selection, not the first page of it. + listAll(params = {}) { + return fetchAllPages('/printedparts/items', params) + }, get(printeditemid) { return api.get(`/printedparts/items/${printeditemid}`) }, diff --git a/frontend/src/views/print/AssetLabelBatch.vue b/frontend/src/views/print/AssetLabelBatch.vue index 8ecd28b..96ec4d1 100644 --- a/frontend/src/views/print/AssetLabelBatch.vue +++ b/frontend/src/views/print/AssetLabelBatch.vue @@ -295,8 +295,10 @@ onMounted(async () => { codetype.value = (await getSetting('label_default_codetype', 'qr')) === 'barcode' ? 'barcode' : 'qr' encodes.value = await resolveDefaultEncodes(assettype) try { - const response = await config.api.list({ perpage: 500 }) - assets.value = response.data.data || [] + // listAll, not list: perpage is clamped to 100 server-side, so a batch + // sheet built from list() silently omitted every asset past the first + // 100 and printed a short run that looked complete. + assets.value = await config.api.listAll() } catch (err) { console.error('Error loading assets:', err) } finally { diff --git a/plugins/machines/frontend/views/MachineForm.vue b/plugins/machines/frontend/views/MachineForm.vue index 3d499ed..198ce16 100644 --- a/plugins/machines/frontend/views/MachineForm.vue +++ b/plugins/machines/frontend/views/MachineForm.vue @@ -469,7 +469,7 @@ onMounted(async () => { locationsApi.list({ perpage: 500 }), modelsApi.listAll(), // backend caps perpage at 100; page through all businessunitsApi.list({ perpage: 500 }), - computersApi.list({ perpage: 500 }), + computersApi.listAll(), // backend caps perpage at 100; page through all assetsApi.types.list() // Used for relationship types, will fix below ]) diff --git a/plugins/network/frontend/views/NetworkDeviceForm.vue b/plugins/network/frontend/views/NetworkDeviceForm.vue index d9bccd1..c53b7a9 100644 --- a/plugins/network/frontend/views/NetworkDeviceForm.vue +++ b/plugins/network/frontend/views/NetworkDeviceForm.vue @@ -428,8 +428,9 @@ async function loadVendors() { try { const response = await vendorsApi.list({ perpage: 100 }) vendors.value = response.data.data || [] - const modelResponse = await modelsApi.list({ perpage: 500 }) - models.value = modelResponse.data.data || [] + // listAll: the models catalogue is already past the backend's 100-row + // cap, so list() left the tail of it unselectable. + models.value = await modelsApi.listAll() } catch (err) { console.error('Error loading vendors:', err) } diff --git a/plugins/printedparts/frontend/views/PrintedPartsLabels.vue b/plugins/printedparts/frontend/views/PrintedPartsLabels.vue index 73cdb91..88f7431 100644 --- a/plugins/printedparts/frontend/views/PrintedPartsLabels.vue +++ b/plugins/printedparts/frontend/views/PrintedPartsLabels.vue @@ -78,8 +78,9 @@ function labelText(item) { onMounted(async () => { try { - const response = await printedpartsApi.list({ perpage: 500 }) - items.value = response.data.data || [] + // listAll: perpage is clamped to 100, and a label sheet must cover every + // item, not the first page of them. + items.value = await printedpartsApi.listAll() // ?item= preselects one part (the Detail-page print button) const preselect = new URLSearchParams(window.location.search).get('item') if (preselect) { diff --git a/plugins/printers/frontend/views/PrinterQRBatch.vue b/plugins/printers/frontend/views/PrinterQRBatch.vue index ec7d780..2a09231 100644 --- a/plugins/printers/frontend/views/PrinterQRBatch.vue +++ b/plugins/printers/frontend/views/PrinterQRBatch.vue @@ -91,8 +91,9 @@ const pages = computed(() => { onMounted(async () => { try { - const response = await printersApi.list({ perpage: 500 }) - printers.value = response.data.data || [] + // listAll: perpage is clamped to 100, and a batch sheet must cover every + // printer, not the first page of them. + printers.value = await printersApi.listAll() } catch (error) { console.error('Error loading printers:', error) } finally { diff --git a/plugins/usb/frontend/views/USBLabelBatch.vue b/plugins/usb/frontend/views/USBLabelBatch.vue index ed0110c..a43576a 100644 --- a/plugins/usb/frontend/views/USBLabelBatch.vue +++ b/plugins/usb/frontend/views/USBLabelBatch.vue @@ -145,8 +145,9 @@ const sheetPages = computed(() => { onMounted(async () => { labelStyle.value = (await getSetting('usb_label_style', 'barcode')) === 'qr' ? 'qr' : 'barcode' try { - const response = await usbApi.list({ perpage: 500 }) - devices.value = response.data.data || [] + // listAll: perpage is clamped to 100, and a batch sheet must cover every + // device, not the first page of them. + devices.value = await usbApi.listAll() } catch (error) { console.error('Error loading USB devices:', error) } finally {