diff --git a/server.py b/server.py index 7b109f1..7de21ff 100644 --- a/server.py +++ b/server.py @@ -1292,6 +1292,69 @@ def get_asset(asset_id: int): "work_orders": msfs.get("work_orders", []), } + # Enrich with related work orders from extraction DB (live query) + try: + ext = _get_extraction_db() + if ext: + cur_ext = ext.cursor() + asset_name = result.get("name", "") + asset_company = result.get("company", "") + related_wos = [] + + # Match by customer asset name + if asset_name: + cur_ext.execute( + """SELECT msdyn_name, "msdyn_serviceaccount!name" AS account_name, + msdyn_systemstatus, msdyn_datewindowstart, + "msdyn_functionallocation!name" AS functional_location, + msdyn_workordersummary + FROM msdyn_workorder + WHERE "msdyn_customerasset!name" = ? + ORDER BY msdyn_datewindowstart DESC + LIMIT 20""", + (asset_name,), + ) + for r in cur_ext.fetchall(): + related_wos.append({ + "name": r["msdyn_name"], + "account_name": r["account_name"], + "status_code": str(r["msdyn_systemstatus"]), + "status": _status_label(r["msdyn_systemstatus"]), + "functional_location": r["functional_location"], + "summary": r["msdyn_workordersummary"], + "datewindow_start": r["msdyn_datewindowstart"], + }) + + # Also match by company name (account name) + if asset_company and not related_wos: + cur_ext.execute( + """SELECT msdyn_name, "msdyn_serviceaccount!name" AS account_name, + msdyn_systemstatus, msdyn_datewindowstart, + "msdyn_functionallocation!name" AS functional_location, + msdyn_workordersummary + FROM msdyn_workorder + WHERE "msdyn_serviceaccount!name" = ? + ORDER BY msdyn_datewindowstart DESC + LIMIT 20""", + (asset_company,), + ) + for r in cur_ext.fetchall(): + related_wos.append({ + "name": r["msdyn_name"], + "account_name": r["account_name"], + "status_code": str(r["msdyn_systemstatus"]), + "status": _status_label(r["msdyn_systemstatus"]), + "functional_location": r["functional_location"], + "summary": r["msdyn_workordersummary"], + "datewindow_start": r["msdyn_datewindowstart"], + }) + + if related_wos: + result["related_work_orders"] = related_wos + ext.close() + except Exception: + pass # Extraction DB might not be available, silently skip + # Enrich with Seed data (from mycantaloupe.com) seed_row = conn.execute( "SELECT raw_data, imported_at FROM seed_data WHERE asset_id = ?", @@ -3585,6 +3648,7 @@ async def workorders_list( q: str = Query("", description="Search term"), status: str = Query("", description="Comma-separated status codes to include"), tech: str = Query("", description="Comma-separated technician names"), + location: str = Query("", description="Filter by city, functional location, or account name"), date_from: str = Query("", description="ISO date filter start (inclusive)"), date_to: str = Query("", description="ISO date filter end (inclusive)"), date_field: str = Query("createdon", description="Date field to filter on: createdon, msdyn_completedon"), @@ -3642,6 +3706,14 @@ async def workorders_list( where_clauses.append("(" + " OR ".join(tech_conditions) + ")") params.extend(tech_names * 4) + # Location filter โ€” matches city, functional location, or account name + if location: + like = f"%{location}%" + where_clauses.append( + "(a.address1_city LIKE ? OR w.\"msdyn_functionallocation!name\" LIKE ? OR w.\"msdyn_serviceaccount!name\" LIKE ?)" + ) + params.extend([like, like, like]) + # Date range filter if date_from: where_clauses.append(f"w.{date_field} >= ?") diff --git a/static/index.html b/static/index.html index 93ea5b5..a93280a 100644 --- a/static/index.html +++ b/static/index.html @@ -1492,6 +1492,12 @@
+ + +
Check-in History
@@ -5451,6 +5457,41 @@ seedCard.style.display = 'none'; } + // Related Work Orders from extraction DB + const rwoCard = document.getElementById('detailRelatedWOCard'); + const rwoList = document.getElementById('detailRelatedWOList'); + if (a.related_work_orders && a.related_work_orders.length) { + rwoCard.style.display = 'block'; + rwoList.innerHTML = a.related_work_orders.map(function(wo) { + var statusColor = wo.status_code === '690970003' ? '#4ade80' : + wo.status_code === '690970004' ? '#4ade80' : + wo.status_code === '690970001' ? '#5b6ef7' : + wo.status_code === '690970002' ? '#fbbf24' : '#8b8fa3'; + var statusIcon = wo.status_code === '690970003' ? 'โœ…' : + wo.status_code === '690970004' ? 'โœ…' : + wo.status_code === '690970001' ? '๐Ÿ“…' : + wo.status_code === '690970002' ? '๐Ÿ”ง' : '๐Ÿ“‹'; + var locParts = []; + if (wo.functional_location) locParts.push(esc(wo.functional_location)); + if (wo.account_name) locParts.push(esc(wo.account_name)); + var dateStr = ''; + if (wo.datewindow_start) { + try { var d = new Date(wo.datewindow_start); dateStr = d.toLocaleDateString('en-US', {month:'short', day:'numeric'}); } catch(e) {} + } + return '
' + + '
' + + '' + statusIcon + ' ' + esc(wo.name) + '' + + '' + esc(wo.status) + '' + + '
' + + (locParts.length ? '
๐Ÿ“ ' + locParts.join(' ยท ') + '
' : '') + + (dateStr ? '
๐Ÿ“… ' + dateStr + '
' : '') + + (wo.summary ? '
' + esc(wo.summary) + '
' : '') + + '
'; + }).join(''); + } else { + rwoCard.style.display = 'none'; + } + await loadCheckinHistory(id); } catch (e) { showToast(e.message, true);