Subversion Repositories SmartDukaan

Rev

Rev 37039 | Rev 37060 | Go to most recent revision | Show entire file | Ignore whitespace | Details | Blame | Last modification | View Log | RSS feed

Rev 37039 Rev 37044
Line 225... Line 225...
225
  .rhexec{font-size:10px;color:var(--soft);font-weight:600;margin:3px 0 4px}
225
  .rhexec{font-size:10px;color:var(--soft);font-weight:600;margin:3px 0 4px}
226
  .rhtext{font-size:12px;color:#4a5468;line-height:1.45;word-break:break-word;white-space:pre-wrap}
226
  .rhtext{font-size:12px;color:#4a5468;line-height:1.45;word-break:break-word;white-space:pre-wrap}
227
  .rhloading,.rhempty{color:var(--soft);font-size:12px;text-align:center;padding:24px 8px}
227
  .rhloading,.rhempty{color:var(--soft);font-size:12px;text-align:center;padding:24px 8px}
228
  .rh-more{margin-top:10px;background:var(--brand);color:#fff;border:0;border-radius:8px;padding:9px;font-weight:650;font-size:12.5px;cursor:pointer;display:none}
228
  .rh-more{margin-top:10px;background:var(--brand);color:#fff;border:0;border-radius:8px;padding:9px;font-weight:650;font-size:12.5px;cursor:pointer;display:none}
229
  .rh-more:disabled{opacity:.6;cursor:default}
229
  .rh-more:disabled{opacity:.6;cursor:default}
230
  /* order-details drawer (stacks above the remark drawer) */
230
  /* orders drawer (stacks above the remark drawer) */
231
  .od-back{position:fixed;inset:0;background:rgba(15,22,40,.42);z-index:1300;display:none}
231
  .od-back{position:fixed;inset:0;background:rgba(15,22,40,.42);z-index:1300;display:none}
232
  .od-back.show{display:block}
232
  .od-back.show{display:block}
233
  .od-panel{position:absolute;top:0;right:0;height:100%;width:min(440px,94vw);background:#fff;box-shadow:-8px 0 30px rgba(20,30,60,.18);display:flex;flex-direction:column;padding:18px 18px 14px;transform:translateX(100%);transition:transform .22s ease}
233
  .od-panel{position:absolute;top:0;right:0;height:100%;width:min(440px,94vw);background:#fff;box-shadow:-8px 0 30px rgba(20,30,60,.18);display:flex;flex-direction:column;padding:18px 18px 14px;transform:translateX(100%);transition:transform .22s ease}
234
  .od-back.show .od-panel{transform:translateX(0)}
234
  .od-back.show .od-panel{transform:translateX(0)}
235
  .od-panel h3{margin:0 0 2px;font-size:15px;color:var(--ink)}
235
  .od-panel h3{margin:0 0 2px;font-size:15px;color:var(--ink)}
236
  .od-panel .desc{margin:0;font-size:12px;color:var(--soft)}
236
  .od-panel .desc{margin:0;font-size:12px;color:var(--soft)}
237
  .od-body{flex:1;overflow-y:auto;margin-top:12px}
237
  .od-body{flex:1;overflow-y:auto;margin-top:12px}
-
 
238
  .odorder{border:1px solid var(--line);border-radius:11px;padding:11px;margin-bottom:12px;box-shadow:0 1px 2px rgba(20,30,60,.04)}
-
 
239
  .odorder .odsum{margin-bottom:8px}
238
  .odsum{display:flex;flex-wrap:wrap;gap:8px 18px;padding:10px 12px;background:#fbfcfe;border:1px solid var(--line2);border-radius:9px;margin-bottom:12px}
240
  .odsum{display:flex;flex-wrap:wrap;gap:8px 18px;padding:10px 12px;background:#fbfcfe;border:1px solid var(--line2);border-radius:9px;margin-bottom:12px}
239
  .odsum .k{font-size:9px;text-transform:uppercase;letter-spacing:.05em;color:var(--soft);font-weight:700}
241
  .odsum .k{font-size:9px;text-transform:uppercase;letter-spacing:.05em;color:var(--soft);font-weight:700}
240
  .odsum .v{font-size:13px;font-weight:700;color:var(--ink);font-variant-numeric:tabular-nums}
242
  .odsum .v{font-size:13px;font-weight:700;color:var(--ink);font-variant-numeric:tabular-nums}
241
  .odtable{width:100%;border-collapse:collapse;font-size:11.5px}
243
  .odtable{width:100%;border-collapse:collapse;font-size:11.5px}
242
  .odtable th{text-align:left;color:var(--soft);font-weight:700;font-size:9px;text-transform:uppercase;letter-spacing:.03em;padding:5px 6px;border-bottom:1px solid var(--line2)}
244
  .odtable th{text-align:left;color:var(--soft);font-weight:700;font-size:9px;text-transform:uppercase;letter-spacing:.03em;padding:5px 6px;border-bottom:1px solid var(--line2)}
Line 248... Line 250...
248
  .tlcard .bd{padding:8px 11px;flex:1;min-width:0}
250
  .tlcard .bd{padding:8px 11px;flex:1;min-width:0}
249
  .tlcard .bd .ti{font-size:12.5px;font-weight:700;color:var(--ink);line-height:1.25;word-break:break-word}
251
  .tlcard .bd .ti{font-size:12.5px;font-weight:700;color:var(--ink);line-height:1.25;word-break:break-word}
250
  .tlcard .bd .stt{display:inline-block;font-size:9.5px;font-weight:700;text-transform:uppercase;letter-spacing:.03em;padding:1px 6px;border-radius:5px;margin-top:5px;color:#fff}
252
  .tlcard .bd .stt{display:inline-block;font-size:9.5px;font-weight:700;text-transform:uppercase;letter-spacing:.03em;padding:1px 6px;border-radius:5px;margin-top:5px;color:#fff}
251
  .tlcard .bd .stt.unplanned{background:#fff4e6;color:#e8590c;border:1px solid #ffd8a8;margin-left:5px}
253
  .tlcard .bd .stt.unplanned{background:#fff4e6;color:#e8590c;border:1px solid #ffd8a8;margin-left:5px}
252
  .tlcard .bd .mt{font-size:11px;color:var(--muted);margin-top:6px}
254
  .tlcard .bd .mt{font-size:11px;color:var(--muted);margin-top:6px}
-
 
255
  .tlcard .bd .tlags{margin-top:6px}
-
 
256
  .tlag{display:inline-block;font-size:9.5px;font-weight:700;color:#5646c9;background:#eeeafc;border:1px solid #ddd5f7;border-radius:5px;padding:1px 6px;margin:2px 4px 0 0}
-
 
257
  .tlrow.left .tlcard .bd .tlag{margin:2px 0 0 4px}
253
  .tlextra{display:flex;align-items:center;gap:7px;margin-top:8px;flex-wrap:wrap}
258
  .tlextra{display:flex;align-items:center;gap:7px;margin-top:8px;flex-wrap:wrap}
254
  .tlrow.left .tlextra{justify-content:flex-end}
259
  .tlrow.left .tlextra{justify-content:flex-end}
255
  .tlrow.right .tlextra{justify-content:flex-start}
260
  .tlrow.right .tlextra{justify-content:flex-start}
256
  .tlthumb{width:46px;height:46px;border-radius:8px;object-fit:cover;border:1px solid var(--line);cursor:pointer;background:#f1f3f9}
261
  .tlthumb{width:46px;height:46px;border-radius:8px;object-fit:cover;border:1px solid var(--line);cursor:pointer;background:#f1f3f9}
257
  .tlph{width:46px;height:46px;border-radius:8px;border:1px dashed #cfd6e6;background:#f4f6fb;display:flex;align-items:center;justify-content:center;color:#b3bcce;font-size:18px}
262
  .tlph{width:46px;height:46px;border-radius:8px;border:1px dashed #cfd6e6;background:#f4f6fb;display:flex;align-items:center;justify-content:center;color:#b3bcce;font-size:18px}
Line 332... Line 337...
332
    <div class="rh-list" id="bj-rh-list"></div>
337
    <div class="rh-list" id="bj-rh-list"></div>
333
    <button class="rh-more" id="bj-rh-more" onclick="bjLoadRemarkHistory()">Load more</button>
338
    <button class="rh-more" id="bj-rh-more" onclick="bjLoadRemarkHistory()">Load more</button>
334
  </div>
339
  </div>
335
</div>
340
</div>
336
 
341
 
337
<!-- order-details drawer — slides in from the right, above the remark drawer -->
342
<!-- orders drawer — slides in from the right, above the remark drawer -->
338
<div class="od-back" id="bj-od-back" onclick="if(event.target===this)closeOrderDetail()">
343
<div class="od-back" id="bj-od-back" onclick="if(event.target===this)closeOrderDetail()">
339
  <div class="od-panel" id="bj-od-panel">
344
  <div class="od-panel" id="bj-od-panel">
340
    <button class="x" onclick="closeOrderDetail()">&times;</button>
345
    <button class="x" onclick="closeOrderDetail()">&times;</button>
341
    <h3 id="bj-od-title"></h3>
346
    <h3 id="bj-od-title"></h3>
342
    <p class="desc" id="bj-od-sub"></p>
347
    <p class="desc" id="bj-od-sub"></p>
Line 1547... Line 1552...
1547
  }
1552
  }
1548
  return '';
1553
  return '';
1549
}
1554
}
1550
function tlThumb(att){
1555
function tlThumb(att){
1551
  if(att && window._mediaUrl) return '<img class="tlthumb" src="'+esc(window._mediaUrl+att)+'" onclick="bjOpenImage(this.src)" onerror="tlImgErr(this)">';
1556
  if(att && window._mediaUrl) return '<img class="tlthumb" src="'+esc(window._mediaUrl+att)+'" onclick="bjOpenImage(this.src)" onerror="tlImgErr(this)">';
1552
  return '<div class="tlph" title="No photo">&#128247;</div>';
1557
  return '';   // no image -> render nothing (no placeholder box)
1553
}
1558
}
1554
function tlImgErr(im){ var d=document.createElement('div'); d.className='tlph'; d.title='Photo unavailable'; d.innerHTML='&#128247;'; if(im.parentNode) im.parentNode.replaceChild(d, im); }
1559
function tlImgErr(im){ var d=document.createElement('div'); d.className='tlph'; d.title='Photo unavailable'; d.innerHTML='&#128247;'; if(im.parentNode) im.parentNode.replaceChild(d, im); }
1555
function punchEntry(kind, time){ return {_punch:kind, checkInTime:time, markType:(kind==='in'?'PUNCHIN':'PUNCHOUT'), taskType:'attendance'}; }
1560
function punchEntry(kind, time){ return {_punch:kind, checkInTime:time, markType:(kind==='in'?'PUNCHIN':'PUNCHOUT'), taskType:'attendance'}; }
1556
function tlStyle(x){
1561
function tlStyle(x){
1557
  var mt=(x.markType||'').toUpperCase(), tt=(x.taskType||'').toLowerCase();
1562
  var mt=(x.markType||'').toUpperCase(), tt=(x.taskType||'').toLowerCase();
1558
  if(x._punch || tt==='attendance' || mt.indexOf('PUNCH')>=0) return {c:'#7048e8', l:(x._punch==='out'?'Punch out':'Punch in'), ic:'&#9873;'};
1563
  if(x._punch || tt==='attendance' || mt.indexOf('PUNCH')>=0) return {c:'#7048e8', l:(x._punch==='out'?'Punch out':'Punch in'), ic:'&#9873;'};
1559
  if(mt.indexOf('CHECKOUT')>=0) return {c:'#2f9e44', l:'Visited', ic:'&#10003;'};
1564
  if(mt.indexOf('CHECKOUT')>=0) return {c:'#2f9e44', l:'Visited', ic:'&#10003;'};
1560
  if(mt==='CHECKIN') return {c:'#1098ad', l:'In', ic:'&#128205;'};
1565
  if(mt==='CHECKIN') return {c:'#1098ad', l:'In', ic:'&#128205;'};
1561
  if(mt==='DEFERRED') return {c:'#e8590c', l:'Deferred', ic:'&#8635;'};
1566
  if(mt==='DEFERRED') return {c:'#e8590c', l:'Deferred', ic:'&#9888;'};
1562
  if(mt==='PENDING' || !x.checkInTime) return {c:'#97a1b5', l:'Pending', ic:'&#9675;'};
1567
  if(mt==='PENDING' || !x.checkInTime) return {c:'#97a1b5', l:'Pending', ic:'&#9675;'};
1563
  return {c:'#3b5bdb', l:(mt||'-'), ic:'&#9679;'};
1568
  return {c:'#3b5bdb', l:(mt||'-'), ic:'&#9679;'};
1564
}
1569
}
1565
function tlTitle(x){
1570
function tlTitle(x){
1566
  if(x._punch) return x._punch==='in' ? 'Journey start (Punch in)' : 'Journey end (Punch out)';
1571
  if(x._punch) return x._punch==='in' ? 'Journey start (Punch in)' : 'Journey end (Punch out)';
Line 1571... Line 1576...
1571
    if (pipe >= 0) n = n.slice(pipe + 1);      // drop leading agenda tags
1576
    if (pipe >= 0) n = n.slice(pipe + 1);      // drop leading agenda tags
1572
    n = n.replace(/\s*VISIT\b.*$/i, '').replace(/\s*visit\s*$/i, '').replace(/\bnull\b/gi, '').replace(/\s{2,}/g, ' ').trim();
1577
    n = n.replace(/\s*VISIT\b.*$/i, '').replace(/\s*visit\s*$/i, '').replace(/\bnull\b/gi, '').replace(/\s{2,}/g, ' ').trim();
1573
  }
1578
  }
1574
  return n || '(stop)';
1579
  return n || '(stop)';
1575
}
1580
}
-
 
1581
// Agenda chips: task_name is "agenda1, agenda2 | Outlet" — show the prefix (partner visits only).
-
 
1582
function tlAgendas(x){
-
 
1583
  if(x._punch || (x.taskType||'').toLowerCase()!=='franchisee-visit') return '';
-
 
1584
  var n=x.taskName||'', pipe=n.lastIndexOf(' | ');
-
 
1585
  var part = pipe>0 ? n.slice(0,pipe) : '';
-
 
1586
  if(!part) return '';
-
 
1587
  var out='';
-
 
1588
  part.split(',').forEach(function(a){ var t=a.trim(); if(t) out += '<span class="tlag">'+esc(t)+'</span>'; });
-
 
1589
  return out ? '<div class="tlags">'+out+'</div>' : '';
-
 
1590
}
1576
function tlRow(x, side){
1591
function tlRow(x, side){
1577
  var s=tlStyle(x);
1592
  var s=tlStyle(x);
1578
  var tt=(x.taskType||'').toLowerCase();
1593
  var tt=(x.taskType||'').toLowerCase();
1579
  var timeTop = x.checkInTime ? hhmm(x.checkInTime) : '-';
1594
  var timeTop = x.checkInTime ? hhmm(x.checkInTime) : '-';
1580
  var tlabel = x._punch ? (x._punch==='in'?'punch in':'punch out') : (x.checkInTime?'check-in':'pending');
1595
  var tlabel = x._punch ? (x._punch==='in'?'punch in':'punch out') : (x.checkInTime?'check-in':'pending');
Line 1591... Line 1606...
1591
  var extra='';
1606
  var extra='';
1592
  if(!x._punch){
1607
  if(!x._punch){
1593
    var chips='';
1608
    var chips='';
1594
    if(x.battery!=null) chips += '<span class="tlchip">&#128267; '+x.battery+'%</span>';
1609
    if(x.battery!=null) chips += '<span class="tlchip">&#128267; '+x.battery+'%</span>';
1595
    if(x.locOff!=null) chips += '<span class="tlchip">&#128244; '+x.locOff+'</span>';
1610
    if(x.locOff!=null) chips += '<span class="tlchip">&#128244; '+x.locOff+'</span>';
1596
    // billing-agenda partner stop: order badge(s) win; else a parsed "scheduled" chip
1611
    // orders placed that day (any agenda): one badge -> drawer lists them all.
1597
    if(x.billingAgenda){
1612
    // No order + billing agenda -> a parsed "scheduled" chip instead.
1598
      if(x.orders && x.orders.length){
1613
    if(x.orders && x.orders.length){
1599
        x.orders.slice(0,2).forEach(function(o){
1614
      var n=x.orders.length;
1600
          var label = o.invoice ? ('#'+o.invoice) : ('#'+o.orderId);
-
 
1601
          chips += '<button type="button" class="tlbadge ord" title="View order details"'
1615
      chips += '<button type="button" class="tlbadge ord" title="View orders placed this day"'
1602
                 + ' onclick="bjOpenOrderDetail('+o.orderId+','+x.taskId+')">&#129534; '+esc(label)+'</button>';
1616
             + ' onclick="bjOpenOrderList('+x.taskId+')">&#129534; '+n+' order'+(n>1?'s':'')+'</button>';
1603
        });
-
 
1604
        if(x.orders.length>2) chips += '<span class="tlchip">+'+(x.orders.length-2)+'</span>';
-
 
1605
      } else {
1617
    } else if(x.billingAgenda){
1606
        var sch = bjScheduledFromRemark(x.remarks);
1618
      var sch = bjScheduledFromRemark(x.remarks);
1607
        if(sch) chips += '<span class="tlbadge sch" title="Scheduled (from remark)">&#128197; '+esc(sch)+'</span>';
1619
      if(sch) chips += '<span class="tlbadge sch" title="Scheduled (from remark)">&#128197; '+esc(sch)+'</span>';
1608
      }
-
 
1609
    }
1620
    }
-
 
1621
    var thumb=tlThumb(x.attachment);
1610
    extra = '<div class="tlextra">'+tlThumb(x.attachment)+chips+'</div>';
1622
    if(thumb || chips) extra = '<div class="tlextra">'+thumb+chips+'</div>';
1611
  }
1623
  }
1612
  var card='<div class="tlcard">'+
1624
  var card='<div class="tlcard">'+
1613
        '<div class="tm" style="background:'+s.c+'">'+timeTop+'<small>'+tlabel+'</small></div>'+
1625
        '<div class="tm" style="background:'+s.c+'">'+timeTop+'<small>'+tlabel+'</small></div>'+
1614
        '<div class="bd"><div class="ti">'+esc(tlTitle(x))+'</div>'+
1626
        '<div class="bd"><div class="ti">'+esc(tlTitle(x))+'</div>'+
1615
          '<span class="stt" style="background:'+s.c+'">'+s.l+'</span>'+
1627
          '<span class="stt" style="background:'+s.c+'">'+s.l+'</span>'+
1616
          (tt==='lead' ? '<span class="stt unplanned">Unplanned</span>' : '')+
1628
          (tt==='lead' ? '<span class="stt unplanned">Unplanned</span>' : '')+
-
 
1629
          tlAgendas(x)+
1617
          '<div class="mt">'+meta+'</div>'+extra+'</div>'+
1630
          '<div class="mt">'+meta+'</div>'+extra+'</div>'+
1618
      '</div>';
1631
      '</div>';
1619
  // remark sits on the opposite half of the timeline (empty spacer when none)
1632
  // remark sits on the opposite half of the timeline (empty spacer when none)
1620
  var rk='';
1633
  var rk='';
1621
  if(!x._punch && x.remarks){
1634
  if(!x._punch && x.remarks){
Line 1696... Line 1709...
1696
  var more=el('bj-rh-more');
1709
  var more=el('bj-rh-more');
1697
  more.disabled=false; more.textContent='Load more';
1710
  more.disabled=false; more.textContent='Load more';
1698
  more.style.display = hasMore ? 'block' : 'none';
1711
  more.style.display = hasMore ? 'block' : 'none';
1699
}
1712
}
1700
 
1713
 
1701
/* ===================== ORDER DETAILS DRAWER ===================== */
1714
/* ===================== ORDERS DRAWER ===================== */
1702
// Header + line items for one retail order a partner placed on the visit day.
1715
// All retail orders a partner placed on the visit day, each with its line items.
1703
function bjOpenOrderDetail(orderId, fofoId){
1716
function bjOpenOrderList(fofoId){
1704
  if(!orderId || !fofoId) return;
1717
  if(!fofoId) return;
-
 
1718
  var date=(window._exec && window._exec.date) || '';
1705
  window._od = { orderId:orderId, fofoId:fofoId };
1719
  window._od = { fofoId:fofoId, date:date };
1706
  el('bj-od-title').innerHTML='&#129534; Order #'+esc(orderId);
1720
  el('bj-od-title').innerHTML='&#129534; Orders';
1707
  el('bj-od-sub').textContent='Retail order placed on the visit day';
1721
  el('bj-od-sub').textContent='Orders placed on '+date;
1708
  el('bj-od-body').innerHTML='<div class="odloading"><div class="spinner"></div>Loading&hellip;</div>';
1722
  el('bj-od-body').innerHTML='<div class="odloading"><div class="spinner"></div>Loading&hellip;</div>';
1709
  el('bj-od-back').classList.add('show');
1723
  el('bj-od-back').classList.add('show');
1710
  fetch(CTX+'/beat-journey/order-detail?orderId='+encodeURIComponent(orderId)+'&fofoId='+encodeURIComponent(fofoId),
1724
  fetch(CTX+'/beat-journey/order-list?fofoId='+encodeURIComponent(fofoId)+'&date='+encodeURIComponent(date),
1711
        {headers:{'Accept':'application/json'}})
1725
        {headers:{'Accept':'application/json'}})
1712
    .then(function(r){ return r.json(); })
1726
    .then(function(r){ return r.json(); })
1713
    .then(function(d){
1727
    .then(function(d){
1714
      var st=window._od; if(!st || st.orderId!==orderId) return;   // drawer changed mid-flight
1728
      var st=window._od; if(!st || st.fofoId!==fofoId) return;   // drawer changed mid-flight
1715
      bjRenderOrderDetail(d);
1729
      bjRenderOrderList(d);
1716
    })
1730
    })
1717
    .catch(function(){ el('bj-od-body').innerHTML='<div class="odempty">Could not load order details.</div>'; });
1731
    .catch(function(){ el('bj-od-body').innerHTML='<div class="odempty">Could not load orders.</div>'; });
1718
}
1732
}
1719
function closeOrderDetail(){ el('bj-od-back').classList.remove('show'); window._od=null; }
1733
function closeOrderDetail(){ el('bj-od-back').classList.remove('show'); window._od=null; }
1720
function bjRenderOrderDetail(d){
1734
function bjRenderOrderList(d){
1721
  var hd=d.header;
1735
  var orders=d.orders||[];
1722
  if(!hd){ el('bj-od-body').innerHTML='<div class="odempty">Order not found.</div>'; return; }
1736
  if(!orders.length){ el('bj-od-body').innerHTML='<div class="odempty">No orders placed on this day.</div>'; return; }
1723
  var money=function(n){ return '₹'+(Math.round(Number(n)||0)).toLocaleString(); };
1737
  var money=function(n){ return '₹'+(Math.round(Number(n)||0)).toLocaleString(); };
-
 
1738
  el('bj-od-sub').textContent=orders.length+' order'+(orders.length>1?'s':'')+' placed on '+(window._od?window._od.date:'');
-
 
1739
  var h='';
-
 
1740
  orders.forEach(function(o){
1724
  var h='<div class="odsum">'
1741
    h+='<div class="odorder"><div class="odsum">'
1725
    +'<div><div class="k">Invoice</div><div class="v">'+esc(hd.invoice||('#'+hd.orderId))+'</div></div>'
1742
      +'<div><div class="k">Invoice</div><div class="v">'+esc(o.invoice||('#'+o.orderId))+'</div></div>'
1726
    +'<div><div class="k">Amount</div><div class="v">'+money(hd.amount)+'</div></div>'
1743
      +'<div><div class="k">Amount</div><div class="v">'+money(o.amount)+'</div></div>'
1727
    +'<div><div class="k">Date</div><div class="v">'+esc((hd.date||'')+(hd.time?' '+hhmm(hd.time):''))+'</div></div>'
1744
      +'<div><div class="k">Time</div><div class="v">'+esc(o.time?hhmm(o.time):'—')+'</div></div>'
1728
    +'</div>';
1745
      +'</div>';
1729
  var items=d.items||[];
1746
    var items=o.items||[];
1730
  if(!items.length){ h+='<div class="odempty">No line items on this order.</div>'; el('bj-od-body').innerHTML=h; return; }
1747
    if(items.length){
1731
  h+='<table class="odtable"><thead><tr><th>Item</th><th class="num">Qty</th><th class="num">MOP</th></tr></thead><tbody>';
1748
      h+='<table class="odtable"><thead><tr><th>Item</th><th class="num">Qty</th><th class="num">MOP</th></tr></thead><tbody>';
1732
  items.forEach(function(it){
1749
      items.forEach(function(it){
1733
    var name=[it.brand,it.model,it.color].filter(function(s){return s;}).map(esc).join(' &middot; ');
1750
        var name=[it.brand,it.model,it.color].filter(function(s){return s;}).map(esc).join(' &middot; ');
1734
    h+='<tr><td>'+(name||'&mdash;')+'</td><td class="num">'+(it.qty||0)+'</td><td class="num">'+money(it.mop)+'</td></tr>';
1751
        h+='<tr><td>'+(name||'&mdash;')+'</td><td class="num">'+(it.qty||0)+'</td><td class="num">'+money(it.mop)+'</td></tr>';
-
 
1752
      });
-
 
1753
      h+='</tbody></table>';
-
 
1754
    } else { h+='<div class="odempty">No line items.</div>'; }
-
 
1755
    h+='</div>';
1735
  });
1756
  });
1736
  h+='</tbody></table>';
-
 
1737
  el('bj-od-body').innerHTML=h;
1757
  el('bj-od-body').innerHTML=h;
1738
}
1758
}
1739
 
1759
 
1740
/* ============================ APPROVAL ============================ */
1760
/* ============================ APPROVAL ============================ */
1741
function loadApproval(){
1761
function loadApproval(){