/* InvoiceScreen #/master/invoice/:measurementId Rooms: chip grid (3 cols, mono-add) + list with rename/remove */ const InvoiceScreen = (function () { 'use strict'; const ROOM_GROUPS = [ ['Гостиная','Спальня','Детская'], ['Кабинет','Кухня','Кухня-гостиная'], ['Ванная','Санузел','Прихожая'], ['Коридор','Кладовая','Балкон'], ['Лоджия','Столовая','Доп. помещение'], ]; const ALL_CHIPS = ROOM_GROUPS.flat(); function escHtml(s){return String(s==null?'':s).replace(/&/g,'&').replace(//g,'>').replace(/"/g,'"');} function el(html){const t=document.createElement('template');t.innerHTML=html.trim();return t.content.firstChild;} function fmtMoney(n){return Math.round(n||0).toLocaleString('ru-RU')+' ₽';} const FEE_BASE=2500,FEE_EXTRA=1000; function calcTotal(rooms){if(!rooms.length)return 0;return FEE_BASE+Math.max(0,rooms.length-1)*FEE_EXTRA;} async function _api(path,body){ const ctrl=new AbortController();const t=setTimeout(()=>ctrl.abort(),30000); try{ const res=await fetch(BACKEND_URL+'/api/'+path,{method:'POST',signal:ctrl.signal, headers:{'Content-Type':'application/json'}, body:JSON.stringify(Object.assign({ initData:(typeof Platform!=='undefined'?Platform.initData:''), initDataUnsafe:(typeof Platform!=='undefined'?Platform.initDataUnsafe:null), },body))}); if(!res.ok)throw new Error('HTTP '+res.status);return await res.json(); }catch(e){if(e.name==='AbortError')throw new Error('Timeout');throw e;} finally{clearTimeout(t);} } async function mount(container,measurementId){ container.innerHTML=''; document.body.classList.remove('has-bottom-nav'); const nav=document.getElementById('bottom-nav');if(nav)nav.remove(); const icons=window.ICONS||{}; const header=el('
Счёт на оплату
'); header.querySelector('.podbor-back').addEventListener('click',()=>{if(typeof haptic!=='undefined')haptic('impact');history.back();}); const screen=el('
'); container.appendChild(header);container.appendChild(screen); screen.innerHTML='
'; try{ const data=await _api('measurement_detail',{measurement_id:measurementId}); if(data.error)throw new Error(data.error); _renderForm(screen,data,measurementId); }catch(e){screen.innerHTML='
Ошибка: '+escHtml(e.message)+'
';} } function _renderForm(screen,meas,measurementId){ screen.innerHTML=''; const existingFee=parseFloat(meas.measurement_fee)||0; const rooms=[];let nextId=0; // Client card screen.appendChild(el( '
'+ '
Клиент
'+ '
'+escHtml(meas.client_name||'—')+'
'+ (meas.client_phone?'
'+escHtml(meas.client_phone)+'
':'')+ (meas.address?'
📍 '+escHtml(meas.address)+'
':'')+ '
' )); // Already invoiced warning if(existingFee>0){ const eb=el('
'+ '
⚠ Счёт уже выставлен
'+ '
'+fmtMoney(existingFee)+'
'+ '
'); eb.querySelector('#reviseBtn').addEventListener('click',()=>{eb.remove();if(typeof haptic!=='undefined')haptic('impact');}); screen.appendChild(eb); } // Rooms list const listWrap=el('
'); screen.appendChild(listWrap); // Total bar const totalWrap=el('
Итого
0 ₽
'); screen.appendChild(totalWrap); const totalEl=totalWrap.querySelector('#totalAmt'); // Issue button const bw=el('
'); const rb=el('
'); const issueBtn=bw.querySelector('#issueBtn'); // ── CHIP GRID ────────────────────────────────────────────────────────── const chipLabel=el('
Выберите помещения
'); const chipGrid=el('
'); function updateTotal(){ totalEl.textContent=rooms.length?fmtMoney(calcTotal(rooms)):'0 ₽'; issueBtn.disabled=!rooms.length; issueBtn.style.opacity=rooms.length?'1':'0.45'; } function removeRoom(id){ const idx=rooms.findIndex(r=>r.id===id);if(idx===-1)return; rooms.splice(idx,1); const card=listWrap.querySelector('[data-room-id="'+id+'"]'); if(card)card.remove(); updateTotal(); } function addRoomCard(room){ const isBase=rooms.length===1&&rooms[0].id===room.id; const price=isBase?FEE_BASE:FEE_EXTRA; const card=el( '
'+ ''+ ''+fmtMoney(price)+''+ ''+ '
' ); card.querySelector('input').addEventListener('input',e=>{room.name=e.target.value;}); card.querySelector('button').addEventListener('click',()=>{ if(typeof haptic!=='undefined')haptic('selection'); removeRoom(room.id); // re-render first card price if needed _refreshPriceLabels(); }); listWrap.appendChild(card); } function _refreshPriceLabels(){ const cards=listWrap.querySelectorAll('[data-room-id]'); cards.forEach((card,i)=>{ const span=card.querySelector('span'); if(span)span.textContent=fmtMoney(i===0?FEE_BASE:FEE_EXTRA); }); } // Chip click: count how many rooms have this base name, auto-number function addFromChip(chipName){ const existing=rooms.filter(r=>r.name===chipName||r.name.startsWith(chipName+' ')).length; let name=chipName; if(existing===1)name=chipName+' 2'; else if(existing>1)name=chipName+' '+(existing+1); const room={id:nextId++,name}; rooms.push(room); addRoomCard(room); updateTotal(); } ALL_CHIPS.forEach(chipName=>{ const chip=el( '' ); chip.addEventListener('click',()=>{ if(typeof haptic!=='undefined')haptic('selection'); addFromChip(chipName); // flash chip chip.style.background='var(--accent)';chip.style.color='#fff';chip.style.borderColor='var(--accent)'; setTimeout(()=>{chip.style.background='';chip.style.color='';chip.style.borderColor='';},180); }); chipGrid.appendChild(chip); }); // Pre-fill if rooms_count set const existingCount=parseInt(meas.rooms_count)||0; for(let i=0;i{ if(typeof haptic!=='undefined')haptic('impact'); issueBtn.disabled=true;issueBtn.textContent='Создаём счёт…'; const names=rooms.map((r,i)=>r.name||(i===0?'Основное помещение':'Помещение '+(i+1))); _api('invoice_create',{measurement_id:measurementId,rooms_count:rooms.length,rooms_names:names}) .then(data=>{ if(data.error)throw new Error(data.error); _renderResult(rb,data);issueBtn.style.display='none'; chipLabel.style.display='none';chipGrid.style.display='none'; }) .catch(e=>{ rb.innerHTML='
Ошибка: '+escHtml(e.message)+'
'; issueBtn.disabled=false;issueBtn.textContent='Выставить счёт'; }); }); updateTotal(); } function _renderResult(container,data){ container.innerHTML=''; const qr=data.qr_b64 ?'
QR для оплаты (СБП)
QR
' :''; container.appendChild(el( '
'+ '
✅ Счёт выставлен
'+ '
'+fmtMoney(data.amount)+'
'+ '
'+ '
Получатель: '+escHtml(data.ip_name||'—')+'
'+ '
ИНН: '+escHtml(data.ip_inn||'—')+'
'+ '
Банк: '+escHtml(data.bank_name||'—')+'
'+ '
БИК: '+escHtml(data.bic||'—')+'
'+ '
Р/С: '+escHtml(data.rs||'—')+'
'+ (data.ks?'
К/С: '+escHtml(data.ks)+'
':'')+ '
Назначение: '+escHtml(data.purpose||'—')+'
'+ '
'+qr+'
' )); } return{mount}; })();