71 lines
2.8 KiB
Python
71 lines
2.8 KiB
Python
"""
|
|
Suggests which serial-number fields are probably relevant for a ticket,
|
|
based on keywords in its line items (e.g. an item name containing
|
|
"Laptop" suggests Laptop Serial Number + Laptop Asset Tag). This is a
|
|
starting point staff can edit, not a hard schema - device types and
|
|
their field sets vary by company and grow over time, so the mapping
|
|
lives in a setting (DEVICE_FIELD_SUGGESTIONS), not code.
|
|
|
|
If a ticket has two line items that both match "phone" (e.g. a
|
|
termination processing two lines), the second gets numbered - "Phone
|
|
IMEI (2)" - rather than colliding with the first.
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
from typing import List
|
|
|
|
from app import config
|
|
|
|
|
|
def parse_device_field_suggestions(raw: str) -> dict[str, list[str]]:
|
|
"""'keyword:Field One|Field Two,keyword2:Field Three' -> {keyword: [fields]}"""
|
|
mapping: dict[str, list[str]] = {}
|
|
for group in (raw or "").split(","):
|
|
group = group.strip()
|
|
if not group or ":" not in group:
|
|
continue
|
|
keyword, _, fields_str = group.partition(":")
|
|
keyword = keyword.strip().lower()
|
|
fields = [f.strip() for f in fields_str.split("|") if f.strip()]
|
|
if keyword and fields:
|
|
mapping[keyword] = fields
|
|
return mapping
|
|
|
|
|
|
def get_device_field_suggestions() -> dict[str, list[str]]:
|
|
return parse_device_field_suggestions(
|
|
config.get("DEVICE_FIELD_SUGGESTIONS", config.DEFAULT_DEVICE_FIELD_SUGGESTIONS)
|
|
)
|
|
|
|
|
|
def suggest_serial_fields(line_items: List[dict]) -> List[str]:
|
|
"""
|
|
line_items: [{"sku": "OK401", "item_name": "Laptop - Dell Latitude..."}]
|
|
Returns suggested field labels, in the order their matching keyword
|
|
was found, numbered on repeat matches (e.g. two phone line items ->
|
|
"Phone IMEI (1)", "Phone IMEI (2)", ...).
|
|
|
|
Line items describing a shipping/return-label deliverable (e.g.
|
|
"Shipping - Return Label Laptop") are skipped - they name a device
|
|
type in passing but aren't an actual physical device to serialize,
|
|
and would otherwise double-count against the real device line item.
|
|
"""
|
|
keyword_map = get_device_field_suggestions()
|
|
suggestions: List[str] = []
|
|
match_counts: dict[str, int] = {}
|
|
|
|
for item in line_items or []:
|
|
item_name = item.get("item_name", "") or ""
|
|
if item_name.strip().lower().startswith("shipping"):
|
|
continue
|
|
|
|
haystack = f"{item_name} {item.get('sku', '')}".lower()
|
|
for keyword, fields in keyword_map.items():
|
|
if keyword in haystack:
|
|
match_counts[keyword] = match_counts.get(keyword, 0) + 1
|
|
occurrence = match_counts[keyword]
|
|
suffix = f" ({occurrence})" if occurrence > 1 else ""
|
|
suggestions.extend(f"{field}{suffix}" for field in fields)
|
|
|
|
return suggestions
|