Add collector PC->printer links and searchable custom fields
All checks were successful
CI / backend (push) Successful in 1m24s
CI / naming (push) Successful in 1s
CI / frontend (push) Successful in 7s

Collector: the computers collector schema gains defaultprinter and
printers; apply_collector_payload resolves each reported identifier to
a printer asset (windowsname/hostname/sharename/assetnumber/IP,
first-hit case-insensitive) and idempotently syncs relationships -
defaultprinter (directional) for the default, connectedto for the
rest. Collector-created rows are tagged so a re-report archives dropped
links while manual relationships are never touched; unresolved
identifiers warn instead of failing. Both PC and printer detail pages
show the links via the shared relationships card (no frontend change).
GE-Enforce Win32_Printer collection snippet documented.

Searchable custom fields: a per-field searchable flag (migration 7d24);
global search matches custom-field values on flagged active fields and
routes each hit to the asset detail page, reusing the existing
(type,id) dedupe and search_<type>_enabled domain filter. Searchable
toggle on the Custom Fields settings page.

822 tests pass; both verified live.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
cproudlock
2026-07-12 14:01:20 -04:00
parent 9daf1578a7
commit 275224822e
12 changed files with 664 additions and 6 deletions

View File

@@ -33,6 +33,8 @@ class CustomField(db.Model):
showonform = db.Column(db.Boolean, nullable=False, server_default='1')
sortorder = db.Column(db.Integer, nullable=False, server_default='0')
isactive = db.Column(db.Boolean, nullable=False, server_default='1')
# When true, this field's values are matched by global search.
searchable = db.Column(db.Boolean, nullable=False, server_default='0')
__table_args__ = (
db.UniqueConstraint('assettypeid', 'fieldkey', name='uq_customfield_type_key'),
@@ -57,6 +59,7 @@ class CustomField(db.Model):
'showonform': bool(self.showonform),
'sortorder': self.sortorder,
'isactive': bool(self.isactive),
'searchable': bool(self.searchable),
}