feat: import Seed (mycantaloupe.com) data — 1,655 assets enriched
- Added import_seed.py — reads Machine List(8).xlsx, matches by Asset ID → machine_id
- 51 new columns on assets table (route, barcode, sales, contact info, etc.)
- seed_data table with full 62-field JSON per asset for detail enrichment
- Seed data card in frontend asset detail view
- GET /api/assets/{id} now returns result.seed and result.seed_imported_at
- GPS columns from Seed ignored per request
- 1,654 matched, 199 unmatched (Seed-only assets not in MSFS)
Refs #47
This commit is contained in:
@@ -1315,6 +1315,12 @@
|
||||
<div id="detailMsfsFields"></div>
|
||||
</div>
|
||||
|
||||
<!-- Seed Data (mycantaloupe.com enrichment) -->
|
||||
<div class="card" id="detailSeedCard" style="display:none;">
|
||||
<div class="card-title">🌱 Cantaloupe/Seed Data</div>
|
||||
<div id="detailSeedFields"></div>
|
||||
</div>
|
||||
|
||||
<!-- Check-in History -->
|
||||
<div class="card">
|
||||
<div class="card-title">Check-in History <span id="checkinCount" style="color:var(--accent2);"></span></div>
|
||||
@@ -3834,6 +3840,66 @@
|
||||
msfsCard.style.display = 'none';
|
||||
}
|
||||
|
||||
// Seed Data (Cantaloupe/mycantaloupe.com enrichment)
|
||||
const seedCard = document.getElementById('detailSeedCard');
|
||||
const seedFields = document.getElementById('detailSeedFields');
|
||||
if (a.seed) {
|
||||
const s = a.seed;
|
||||
// Define which Seed fields to show and in what order
|
||||
const seedRows = [
|
||||
df('Device', s.Device, true),
|
||||
df('Type', s.Type),
|
||||
df('Class', s.Class),
|
||||
df('Asset Family', s['Asset Family']),
|
||||
df('Customer', s.Customer),
|
||||
df('Management Company', s['Management Company']),
|
||||
df('Branch', s.Branch),
|
||||
df('Route', s.Route),
|
||||
df('Subroute', s.Subroute),
|
||||
df('Location', s.Location),
|
||||
df('Location Code', s['Location Code']),
|
||||
df('Customer Code', s['Customer Code']),
|
||||
df('Place', s.Place),
|
||||
df('Barcode', s.Barcode, true),
|
||||
df('Serial #', s['Serial Number'], true),
|
||||
df('Valid Address', s['Valid Address']),
|
||||
df('Business Type', s['Business Type']),
|
||||
df('Primary Consumer', s['Primary Consumer Type']),
|
||||
df('Machine Branding', s['Machine Branding']),
|
||||
df('Has Cashless', s['Has Cashless'] ? '✅ Yes' : '❌ No'),
|
||||
df('Non-Revenue', s['Non-Revenue']),
|
||||
df('Phone', s.Phone),
|
||||
df('Email', s.Email),
|
||||
df('Fax', s.Fax),
|
||||
df('Alerts', s.Alerts),
|
||||
df('Coil Alerts', s['Coil Alerts']),
|
||||
df('Product Alerts', s['Product Alerts']),
|
||||
df('Daily Avg Sales', s['Daily Average Sales'] != null ? '$' + parseFloat(s['Daily Average Sales']).toFixed(2) : null),
|
||||
df('Monthly Sales', s['Monthly Sales'] != null ? '$' + parseFloat(s['Monthly Sales']).toFixed(2) : null),
|
||||
df('Yearly Sales', s['Yearly Sales'] != null ? '$' + parseFloat(s['Yearly Sales']).toFixed(2) : null),
|
||||
df('Last Restock', s['Last Restock']),
|
||||
df('Days Since Restock', s['Days Since Restock']),
|
||||
df('Last Contact', s['Last Contact Time']),
|
||||
df('Last DEX Report', s['Last Dex Report Time']),
|
||||
df('Last Inventory', s['Last Inventory']),
|
||||
df('Prepick Group', s['Prepick Group']),
|
||||
df('Added Date', s['Added Date']),
|
||||
df('Status', s.Status),
|
||||
df('Make', s.Make),
|
||||
df('Model', s.Model),
|
||||
df('State', s.State),
|
||||
df('Postal Code', s['Postal Code']),
|
||||
].filter(Boolean).join('');
|
||||
if (seedRows) {
|
||||
seedFields.innerHTML = seedRows;
|
||||
seedCard.style.display = 'block';
|
||||
} else {
|
||||
seedCard.style.display = 'none';
|
||||
}
|
||||
} else {
|
||||
seedCard.style.display = 'none';
|
||||
}
|
||||
|
||||
await loadCheckinHistory(id);
|
||||
} catch (e) {
|
||||
showToast(e.message, true);
|
||||
|
||||
Reference in New Issue
Block a user