diff --git a/plugins/printers/api/asset_routes.py b/plugins/printers/api/asset_routes.py index d45401b..6ad1c21 100644 --- a/plugins/printers/api/asset_routes.py +++ b/plugins/printers/api/asset_routes.py @@ -364,12 +364,29 @@ UNIVERSAL_INSTALL_VENDORS = frozenset({'HP', 'Xerox'}) def _batch_base_url(): """Base URL the generated .bat downloads installers from. Prefer the - configured site_base_url (already includes the /shopdb mount); fall back to - the request root so a site that never set it still produces a usable batch.""" + configured site_base_url (it already includes scheme + the /shopdb mount). + + Fallback matters: behind IIS the app sees http on a loopback port and its + url_root drops the mount, so a naive request.url_root yields a broken + http://127.0.0.1/installers/... URL. Rebuild from the forwarded Host + the + mount (script_root) and force https instead.""" base = (Setting.get('site_base_url') or '').strip().rstrip('/') if base: return base - return request.url_root.rstrip('/') + host = request.headers.get('X-Forwarded-Host') or request.host + root = (request.script_root or '').rstrip('/') + return 'https://%s%s' % (host, root) + + +def _printer_vendor(printer): + """Vendor name for install grouping. The legacy import sets the printer's + model but not its direct vendorid, so resolve via the model's vendor (as the + classic installprinter.asp did) when the printer has no direct vendor.""" + if printer.vendor: + return (printer.vendor.vendor or '').strip() + if printer.model and printer.model.vendor: + return (printer.model.vendor.vendor or '').strip() + return '' def _installer_url(installpath, base): @@ -429,7 +446,7 @@ def printer_install_batch(): name = _install_name(printer, asset) if not name: continue - vendor = (printer.vendor.vendor if printer.vendor else '').strip() + vendor = _printer_vendor(printer) installpath = (printer.installpath or '').strip() if vendor in UNIVERSAL_INSTALL_VENDORS: universal.append(name) diff --git a/tests/test_plugins/test_printer_install_batch.py b/tests/test_plugins/test_printer_install_batch.py index 7d71356..e0c5eb6 100644 --- a/tests/test_plugins/test_printer_install_batch.py +++ b/tests/test_plugins/test_printer_install_batch.py @@ -72,6 +72,33 @@ def test_install_batch_groups_universal_specific_and_manual( assert 'c350navi.zip' not in bat # never handed to the runner +def test_install_batch_resolves_vendor_via_model( + client, db, auth_headers, printer_assettype): + """A printer with no direct vendorid still groups as universal when its + MODEL's vendor is HP/Xerox (the import sets the model, not the printer's + vendorid) - the bug that dumped every prod printer into 'manual (unknown)'.""" + from shopdb.core.models import Vendor, Model + + xerox = Vendor(vendor='Xerox') + db.session.add(xerox) + db.session.commit() + model = Model(modelnumber='VersaLink C7125', vendorid=xerox.vendorid) + db.session.add(model) + db.session.commit() + + # No vendorid on the printer - only the model ties it to Xerox. + pid = _make_printer(client, auth_headers, assetnumber='SpoolsInspection-Xerox', + hostname='wjprn07', modelnumberid=model.modelnumberid) + + resp = client.get('/api/printers/install-batch?printerids=%d' % pid, + headers=auth_headers) + assert resp.status_code == 200 + bat = resp.get_data(as_text=True) + assert 'PrinterInstaller.exe' in bat + assert '/PRINTER="SpoolsInspection-Xerox"' in bat + assert 'MANUAL' not in bat + + def test_install_batch_requires_ids(client, db, auth_headers, printer_assettype): resp = client.get('/api/printers/install-batch', headers=auth_headers) assert resp.status_code == 400