feat: populate company/place/customer_name from MSFS extraction DB
- New script: scripts/populate_asset_company_place.py maps connect_id → account name (company/customer_name) + city (place) + address via extraction DB (msdyn_customerasset + account join) - 5,743/7,488 assets now have company + customer_name populated - 5,647/7,488 assets now have place (city) populated - Fix: SELECT COALESCE(c.name, a.customer_name) so stored customer_name isn't overridden to NULL when customer_id FK is missing - Bump SW v4→v5, version v3.1→v3.2 to force cache refresh
This commit is contained in:
+28
-6
@@ -1091,7 +1091,7 @@
|
||||
</button>
|
||||
</nav>
|
||||
<div class="drawer-footer">
|
||||
<div style="margin-bottom:6px;">Canteen Asset Tracker v3.0</div>
|
||||
<div style="margin-bottom:6px;">Canteen Asset Tracker v3.2</div>
|
||||
<div style="font-size:11px;line-height:1.5;margin-bottom:6px;">
|
||||
<kbd>Esc</kbd> to close · <kbd>Ctrl</kbd>+<kbd>/</kbd> to search · ❓ for help
|
||||
</div>
|
||||
@@ -1228,6 +1228,14 @@
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="fp-row">
|
||||
<div style="flex:1">
|
||||
<div class="fp-label">Branch</div>
|
||||
<select id="filterBranch" class="input-field" onchange="onFilterChange()">
|
||||
<option value="">All branches</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="fp-row">
|
||||
<div style="flex:1">
|
||||
<div class="fp-label">Data Health</div>
|
||||
@@ -3323,7 +3331,7 @@
|
||||
|
||||
// ── Filter state ──────────────────────────────────────────────────────
|
||||
let assetFilters = { category: null, status: null, make: null, disney_park: null, disney_filter: null, q: '',
|
||||
customer_id: null, location_id: null, assigned_to: null };
|
||||
customer_id: null, location_id: null, assigned_to: null, branch: null };
|
||||
let noDexFilterActive = false;
|
||||
let assetOffset = 0;
|
||||
const ASSET_PAGE_SIZE = 2000;
|
||||
@@ -3372,6 +3380,11 @@
|
||||
assnSel.innerHTML = '<option value="">Anyone</option>' +
|
||||
users.filter(function(u) { return u.role === 'technician' || u.role === 'admin'; })
|
||||
.map(function(u) { return '<option value="' + esc(u.username) + '">' + esc(u.username) + '</option>'; }).join('');
|
||||
// Load branches
|
||||
var branches = await api('/api/branches');
|
||||
var branchSel = document.getElementById('filterBranch');
|
||||
branchSel.innerHTML = '<option value="">All branches</option>' +
|
||||
branches.branches.map(function(b) { return '<option value="' + esc(b.name) + '">' + esc(b.name) + ' (' + b.machine_count + ')</option>'; }).join('');
|
||||
} catch (e) {
|
||||
showToast('Failed to load filter options', true);
|
||||
} finally {
|
||||
@@ -3406,6 +3419,7 @@
|
||||
var locEl = document.getElementById('filterLocation');
|
||||
|
||||
var assnEl = document.getElementById('filterAssignedTo');
|
||||
var branchEl = document.getElementById('filterBranch');
|
||||
|
||||
assetFilters.category = catEl.value || null;
|
||||
assetFilters.make = makeEl.value || null;
|
||||
@@ -3422,6 +3436,7 @@
|
||||
assetFilters.customer_id = custEl.value || null;
|
||||
|
||||
assetFilters.assigned_to = assnEl.value || null;
|
||||
assetFilters.branch = branchEl.value || null;
|
||||
|
||||
var prevCust = locEl.dataset.customerId;
|
||||
if (custEl.value !== prevCust) {
|
||||
@@ -3461,6 +3476,7 @@
|
||||
if (assetFilters.customer_id) count++;
|
||||
if (assetFilters.location_id) count++;
|
||||
if (assetFilters.assigned_to) count++;
|
||||
if (assetFilters.branch) count++;
|
||||
if (noDexFilterActive) count++;
|
||||
if (assetFilters.q) count++;
|
||||
return count;
|
||||
@@ -3492,10 +3508,11 @@
|
||||
if (assetFilters.location_id) {
|
||||
var locSel = document.getElementById('filterLocation');
|
||||
var txt = locSel.options[locSel.selectedIndex] ? locSel.options[locSel.selectedIndex].text : 'Location';
|
||||
tags.push({key:'location_id', label:'\uD83D\uDCCD ' + txt});
|
||||
tags.push({key:'location_id', label:'📍 ' + txt});
|
||||
}
|
||||
if (assetFilters.assigned_to) tags.push({key:'assigned_to', label:'\uD83D\uDC64 ' + assetFilters.assigned_to});
|
||||
if (noDexFilterActive) tags.push({key:'no_dex_days', label:'\u26a0\ufe0f No DEX (5d)'});
|
||||
if (assetFilters.assigned_to) tags.push({key:'assigned_to', label:'👤 ' + assetFilters.assigned_to});
|
||||
if (assetFilters.branch) tags.push({key:'branch', label:'🏢 ' + assetFilters.branch});
|
||||
if (noDexFilterActive) tags.push({key:'no_dex_days', label:'⚠️ No DEX (5d)'});
|
||||
|
||||
el.innerHTML = tags.map(function(t) {
|
||||
return '<span class="filter-tag">' + esc(t.label) + ' <span class="ft-remove" onclick="removeFilter(\'' + t.key + '\')">\u2715</span></span>';
|
||||
@@ -3538,6 +3555,9 @@
|
||||
} else if (key === 'assigned_to') {
|
||||
document.getElementById('filterAssignedTo').value = '';
|
||||
assetFilters.assigned_to = null;
|
||||
} else if (key === 'branch') {
|
||||
document.getElementById('filterBranch').value = '';
|
||||
assetFilters.branch = null;
|
||||
} else if (key === 'no_dex_days') {
|
||||
noDexFilterActive = false;
|
||||
var btn = document.getElementById('filterNoDex');
|
||||
@@ -3550,7 +3570,7 @@
|
||||
|
||||
function clearAllFilters() {
|
||||
assetFilters = { category: null, status: null, make: null, disney_park: null, disney_filter: null, q: '',
|
||||
customer_id: null, location_id: null, assigned_to: null };
|
||||
customer_id: null, location_id: null, assigned_to: null, branch: null };
|
||||
document.getElementById('assetSearch').value = '';
|
||||
document.getElementById('clearSearch').style.display = 'none';
|
||||
document.getElementById('filterCategory').value = '';
|
||||
@@ -3562,6 +3582,7 @@
|
||||
document.getElementById('filterLocation').disabled = true;
|
||||
|
||||
document.getElementById('filterAssignedTo').value = '';
|
||||
document.getElementById('filterBranch').value = '';
|
||||
noDexFilterActive = false;
|
||||
var noDexBtn = document.getElementById('filterNoDex');
|
||||
if (noDexBtn) noDexBtn.classList.remove('active');
|
||||
@@ -3597,6 +3618,7 @@
|
||||
if (assetFilters.customer_id) params.set('customer_id', assetFilters.customer_id);
|
||||
if (assetFilters.location_id) params.set('location_id', assetFilters.location_id);
|
||||
if (assetFilters.assigned_to) params.set('assigned_to', assetFilters.assigned_to);
|
||||
if (assetFilters.branch) params.set('branch', assetFilters.branch);
|
||||
if (noDexFilterActive) params.set('no_dex_days', '5');
|
||||
params.set('limit', String(ASSET_PAGE_SIZE));
|
||||
params.set('offset', String(assetOffset));
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@
|
||||
// Caches app shell + CDN deps. Network-first for API, cache fallback for static.
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
|
||||
const CACHE_NAME = 'canteen-v3';
|
||||
const CACHE_NAME = 'canteen-v5';
|
||||
|
||||
// App shell — core resources needed to boot the PWA
|
||||
const APP_SHELL = [
|
||||
|
||||
Reference in New Issue
Block a user