🧾 MeistarPro Rēķinu ģenerators · bezmaksas

Izveido rēķinu vienā minūtē

Aizpildi laukus pa kreisi — rēķins veidojas tiešraidē pa labi. Lejupielādē kā PDF un nosūti klientam. Bez reģistrācijas, bez maksas.

✨ 100% bez maksas

🎨 Šablona izvēle

🏢 Tava informācija (pārdevējs)

🏦 Bankas rekvizīti (apmaksai)

👤 Klients (pircējs)

📄 Rēķina dati

🧱 Darbi un pakalpojumi

Kods
Apraksts
Daudz.
Mērv.
Cena
Atl.%

💶 PVN

“Apgrieztā PVN” — būvniecības pakalpojumiem starp PVN maksātājiem (142. pants)

📝 Piezīmes

Priekšskatījums Tā izskatīsies PDF
RĒĶINS
Nr. —
Datums: —
Piegādes datums: —
Apmaksāt līdz: —
Tavs uzņēmums
Pārdevējs
Tavs uzņēmums
Pircējs
Klients
Kods Apraksts Daudz. Mērv. Cena Atl.% Summa
Summa bez PVN0,00 €
PVN 21%0,00 €
Kopā apmaksai0,00 €
Apmaksas rekvizīti
Banka: —
IBAN: —
SWIFT: —
Apmaksas veids: —
Summa: 0,00 €
Paldies par sadarbību! Izveidots ar MeistarPro

Vajag vairāk nekā rēķinus? 🚀

MeistarPro ir pilna būvniecības vadības sistēma: projekti, GPS brigāžu uzskaite, materiāli, tāmes un rēķini — vienuviet.

Apskatīt MeistarPro →

Vajag mājaslapu uzņēmumam? 🌐

SiteAI uzbūvē profesionālu mājaslapu dažās minūtēs ar mākslīgo intelektu. Domēns un e-pasts iekļauts.

Izmēģināt SiteAI →

Bezmaksas rēķinu ģenerators online

Бесплатный генератор счетов в Латвии

Что это за инструмент

Это бесплатный генератор счетов, сделанный для латвийского малого бизнеса: строителей, мастеров, самозанятых и небольших SIA. Заполняешь форму в браузере — счёт собирается рядом — и скачиваешь PDF. Регистрация не нужна, водяных знаков нет, платного тарифа нет.

Счёт оформляется по требованиям Закона о бухгалтерии к оправдательному документу: номер и дата, реквизиты обеих сторон, список позиций, расчёт НДС, итог и срок оплаты с банковским счётом.

Что генератор делает за тебя

  • Считает НДС — стандартная ставка 21%, вариант без НДС и режим реверсного НДС для строительства;
  • пишет сумму прописью по-латышски — именно так, как требует бухгалтерия;
  • нумерует счета по порядку, чтобы в нумерации не было дыр;
  • сохраняет реквизиты клиентов — второй счёт тому же клиенту выпишешь за 30 секунд;
  • считает скидки по каждой позиции отдельно;
  • формирует PDF одним кликом — без Word и шаблонов Excel.

Почему не шаблон в Word или Excel

В шаблоне каждый счёт приходится переписывать руками: менять номер, пересчитывать НДС, исправлять сумму прописью. Именно здесь возникают самые частые ошибки — задвоенный номер счёта, несходящаяся сумма НДС или отсутствующий номер плательщика НДС. Генератор заполняет и проверяет эти поля сам.

Кому подходит

Инструментом каждый день пользуются SIA и индивидуальные коммерсанты, самозанятые и плательщики налога микропредприятий, а также строители, у которых счёт идёт следом за сметой и перед актом приёмки-передачи. Зарубежные генераторы не учитывают латвийские ставки НДС, реверсный НДС и сумму прописью по-латышски — здесь это встроено.

Счёт в строительстве

В строительных услугах между двумя плательщиками НДС обычно применяется реверсный НДС (статья 142 закона о НДС) — налог считает и платит получатель услуги. Форма поддерживает этот режим и добавляет в счёт нужную пометку. Смету можно подготовить в генераторе смет, а сдачу работ оформить актом приёмки-передачи.

📑 Виды счетов и образцы

По каждому виду счёта — отдельная страница с обязательными реквизитами, примерами и частыми вопросами.

Калькулятор НДСКалькулятор НДС Латвия — расчёт НДС 21%, 12% и 5% онлайн Образец накладной (pavadzīme)Образец накладной (pavadzīme) — накладная и накладная-счёт онлайн Образец счёта в WordОбразец счёта в Word — скачать шаблон бесплатно Образец счёта в ExcelОбразец счёта в Excel — шаблон с формулами Образец счётаОбразец счёта — создай счёт в PDF бесплатно Счёт с НДССчёт с НДС — выставь счёт с НДС онлайн бесплатно Счёт без НДССчёт без НДС — образец, если ты не плательщик НДС Счёт на предоплату (авансовый счёт)Авансовый счёт — образец счёта на предоплату PDF Реверсный НДС в строительствеРеверсный НДС в строительстве — счёт по статье 142 закона об НДС E-rēķins — электронный счёт в ЛатвииE-rēķins (электронный счёт) в Латвии 2026 — кому обязателен и с какой даты Как отправить e-счётКак отправить e-счёт в Латвии — e-adrese, оператор или прямая договорённость Кредитный счётКредитный счёт (kredītrēķins) — образец и когда его выставлять Счёт самозанятогоСчёт самозанятого в Латвии — образец и как выставить без фирмы Счёт за строительные работыСчёт за строительные работы — образец с реверсным НДС и позициями сметы Счёт физическому лицуСчёт физическому лицу — образец и оформление Счёт на английском языкеСчёт на английском — invoice для зарубежных клиентов

❓ Частые вопросы

Генератор счетов бесплатный?

Да. Создание и скачивание счёта в PDF полностью бесплатно, без водяных знаков и без регистрации.

Счёт соответствует латвийскому законодательству?

В счёте есть все обязательные реквизиты, которые Закон о бухгалтерии предъявляет к оправдательному документу, и реквизиты НДС-счёта по закону об НДС.

Можно выставить счёт с реверсным НДС?

Да. Поддерживается режим реверсного НДС для строительных услуг между плательщиками НДС (статья 142 закона о НДС).

На каких языках можно создать счёт?

Счёт можно создать и скачать на латышском, русском и английском языке.

Мои данные сохраняются?

Реквизиты по умолчанию остаются в твоём браузере. Если создашь аккаунт, счета и клиенты сохраняются, чтобы их можно было повторить и просмотреть.

💡 Tavi dati saglabājas šajā pārlūkā līdz nākamajai reizei
'); idoc.close(); await new Promise(res=>{ const go=()=>res(); if(idoc.readyState==='complete') setTimeout(go,90); else iframe.onload=()=>setTimeout(go,90); setTimeout(go,800); }); try{ if(idoc.fonts && idoc.fonts.ready) await idoc.fonts.ready; }catch(e){} const ipaper=idoc.getElementById('paper'); if(ipaper){ ipaper.style.transform='none'; ipaper.style.transformOrigin=''; ipaper.style.margin='0'; } const W=794, H=Math.max(1123, Math.ceil((ipaper&&ipaper.offsetHeight)||1123)); iframe.style.height=H+'px'; try{ const canvas=await html2canvas(ipaper||idoc.body,{ scale:2, useCORS:true, backgroundColor:'#ffffff', width:W, height:H, windowWidth:W, windowHeight:H }); const imgData=canvas.toDataURL('image/jpeg',0.98); const JsPDF=(window.jspdf&&window.jspdf.jsPDF)||window.jsPDF; const pdf=new JsPDF({ unit:'px', format:[W,H], orientation:'portrait', compress:true }); pdf.addImage(imgData,'JPEG',0,0,W,H,undefined,'FAST'); if(mode==='blob') return { blob: pdf.output('blob'), filename }; pdf.save(filename); } finally { iframe.remove(); } } // Scale the fixed 794px invoice down to fit the phone screen in the mobile preview. function fitPreview(){ const scroll=document.querySelector('.paper-scroll'); const paper=document.getElementById('paper'); if(!scroll||!paper) return; // The invoice is a fixed 794px sheet. Scale it to fit the preview column on // BOTH desktop (right column ~600px) and mobile, so nothing is cut off and // there is no sideways scrolling. transform:scale is visual only, so we also // pin the scroll container's box to the available width and clip it. scroll.style.width=''; scroll.style.overflow=''; let avail=(scroll.parentElement&&scroll.parentElement.clientWidth)||scroll.clientWidth||0; if(window.innerWidth<=980){ const vw=document.documentElement.clientWidth||window.innerWidth||360; avail=Math.min(avail||vw, vw-28); // never exceed the phone viewport } if(!avail||avail<2) return; const scale=Math.min(1, avail/794); if(scale<1){ paper.style.transformOrigin='top left'; paper.style.transform='scale('+scale+')'; scroll.style.overflow='hidden'; scroll.style.width=avail+'px'; scroll.style.height=Math.ceil(paper.offsetHeight*scale)+'px'; }else{ paper.style.transform=''; paper.style.transformOrigin=''; scroll.style.height=''; scroll.style.width=''; scroll.style.overflow=''; } } $('#dl').onclick = async function(){ const btn=this; btn.disabled=true; const t=T[lang]; btn.textContent = lang==='ru'?'⏳ Готовим PDF…':lang==='en'?'⏳ Building PDF…':'⏳ Veido PDF…'; try{ await document.fonts.ready; }catch(e){} try{ await buildInvoicePdf('save'); } catch(err){ alert((lang==='ru'?'Ошибка при создании PDF: ':lang==='en'?'PDF error: ':'PDF kļūda: ')+err.message); } btn.disabled=false; btn.innerHTML = t.act_dl; }; /* ============ AUTH + CABINET (Phase 2b) ============ */ const API = '/api'; function authMsg(code){ const t=T[lang]; const map={invalid_email:'e_invalid_email',weak_password:'e_weak_password',email_taken:'e_email_taken',bad_credentials:'e_bad_credentials'}; return t[map[code]] || t.t_err; } async function api(path, opts={}){ const headers = Object.assign({'Content-Type':'application/json'}, opts.headers||{}); if(token) headers.Authorization = 'Bearer '+token; const r = await fetch(API+path, {method:opts.method||'GET', headers, body:opts.body?JSON.stringify(opts.body):undefined}); let data={}; try{ data=await r.json(); }catch(e){} if(r.status===401){ doLogout(true); throw new Error('unauth'); } if(!r.ok) throw new Error(data.error||'error'); return data; } function toast(msg){ const el=$('#toast'); el.textContent=msg; el.classList.add('show'); clearTimeout(el._t); el._t=setTimeout(()=>el.classList.remove('show'),2200); } /* ---- header authbox ---- */ function renderAuthbox(){ const t=T[lang], box=$('#authbox'); if(!box) return; if(token && me){ box.innerHTML = '' + ''+esc(me.user.email)+'' + ''; $('#cabBtn').onclick=openCabinet; $('#logoutBtn').onclick=()=>doLogout(); $('#save').classList.remove('hidden'); } else { box.innerHTML = ''; $('#loginBtn').onclick=()=>openAuth('login'); $('#save').classList.add('hidden'); } const cb=$('#clientbar'); if(cb) cb.classList.toggle('hidden', !(token&&me)); } /* ---- auth modal ---- */ let authMode='login'; function openAuth(mode){ authMode=mode||'login'; $('#authErr').classList.remove('show'); setAuthMode(); $('#authModal').classList.add('open'); $('#au_email').focus(); } function closeAuth(){ $('#authModal').classList.remove('open'); } function setAuthMode(){ const t=T[lang]; $('#authTitle').textContent = authMode==='login'?t.au_login:t.au_register; $('#authSub').textContent = authMode==='login'?t.au_sub_login:t.au_sub_register; $('#authSubmit').textContent= authMode==='login'?t.au_login:t.au_register; $('#authSwitch').innerHTML = authMode==='login' ? ''+t.au_to_register+'' : ''+t.au_to_login+''; const r=$('#auToReg'); if(r) r.onclick=()=>openAuth('register'); const l=$('#auToLog'); if(l) l.onclick=()=>openAuth('login'); } async function submitAuth(){ const email=$('#au_email').value.trim(); const password=$('#au_pass').value; const btn=$('#authSubmit'); btn.disabled=true; try{ const r=await api('/auth/'+authMode,{method:'POST',body:{email,password}}); token=r.token; localStorage.setItem('mp_inv_token',token); $('#au_pass').value=''; closeAuth(); await afterLogin(); toast(T[lang].t_saved); }catch(e){ const err=$('#authErr'); err.textContent=authMsg(e.message); err.classList.add('show'); }finally{ btn.disabled=false; } } async function afterLogin(){ try{ const d=await api('/auth/me'); me=d; if(d.profile) fillProfile(d.profile); } catch(e){ if(e.message==='unauth') return; } renderAuthbox(); if(token) loadClients(); } function doLogout(silent){ token=null; me=null; currentInvoiceId=null; localStorage.removeItem('mp_inv_token'); renderAuthbox(); if(!silent) toast(T[lang].au_logout); } /* ---- profile sync ---- */ function fillProfile(p){ if(!p) return; const set=(id,v)=>{ if(v!=null && v!=='' && $('#'+id)) $('#'+id).value=v; }; set('s_name',p.company_name); set('s_reg',p.reg_no); set('s_vat',p.vat_no); set('s_addr',p.address); set('s_email',p.email); set('s_phone',p.phone); set('b_name',p.bank_name); set('b_iban',p.bank_iban); set('b_swift',p.bank_swift); if(p.default_lang && T[p.default_lang]){ lang=p.default_lang; localStorage.setItem('mp_inv_lang',lang); } if(p.default_template){ tpl=p.default_template; localStorage.setItem('mp_inv_tpl',tpl); } if(p.default_vat_mode){ vatMode=p.default_vat_mode; } $$('#vatsw button').forEach(x=>x.classList.toggle('on',x.dataset.mode===vatMode)); applyTpl(); applyLang(); } function collectProfile(){ const g=id=>($('#'+id)?.value||'').trim(); return { company_name:g('s_name'),reg_no:g('s_reg'),vat_no:g('s_vat'),address:g('s_addr'), email:g('s_email'),phone:g('s_phone'),bank_name:g('b_name'),bank_iban:g('b_iban'),bank_swift:g('b_swift'), default_lang:lang,default_template:tpl,default_vat_mode:vatMode }; } let _profT; function scheduleProfileSave(){ if(!token) return; clearTimeout(_profT); _profT=setTimeout(()=>{ api('/profile',{method:'PUT',body:collectProfile()}).catch(()=>{}); },1200); } /* ---- invoice collect / save / load ---- */ function collectInvoice(){ const g=id=>($('#'+id)?.value||'').trim(); const items=[...$$('.item')].map(it=>{ const code=it.querySelector('.code-i').value.trim(); const qty=parseFloat(it.querySelector('.qty-i').value)||0; const price=parseFloat(it.querySelector('.price-i').value)||0; const discount=parseFloat(it.querySelector('.disc-i').value)||0; return { code, description:it.querySelector('.desc-i').value.trim(), qty, unit:it.querySelector('.unit-i').value.trim(), unit_price:price, discount, line_total:+(qty*price*(1-discount/100)).toFixed(2) }; }).filter(x=>x.description||x.unit_price||x.qty||x.code); const subtotal=items.reduce((a,x)=>a+x.line_total,0); const rate=vatMode==='std'?21:0; const vat_amount=+(subtotal*rate/100).toFixed(2); return { invoice_number:g('i_num'),lang,template:tpl,vat_mode:vatMode,doc_type:($('#i_doctype')?.value)||'invoice',payment_method:g('i_paymethod')||null, issue_date:g('i_date')||null,supply_date:g('i_supply')||null,due_date:g('i_due')||null,currency:'EUR', client_name:g('c_name')||null,client_reg:g('c_reg')||null,client_vat:g('c_vat')||null, client_address:g('c_addr')||null,client_email:g('c_email')||null, subtotal:+subtotal.toFixed(2),vat_rate:rate,vat_amount,total:+(subtotal+vat_amount).toFixed(2), notes:g('i_note')||null,status:'draft',items }; } async function saveInvoice(){ if(!token){ openAuth('login'); return; } const btn=$('#save'); btn.disabled=true; try{ const payload=collectInvoice(); if(currentInvoiceId){ await api('/invoices/'+currentInvoiceId,{method:'PUT',body:payload}); } else { const r=await api('/invoices',{method:'POST',body:payload}); currentInvoiceId=r.id; } api('/profile',{method:'PUT',body:collectProfile()}).catch(()=>{}); const g=id=>($('#'+id)?.value||'').trim(); if(g('c_name')) api('/clients',{method:'POST',body:{name:g('c_name'),reg_no:g('c_reg'),vat_no:g('c_vat'),address:g('c_addr'),email:g('c_email')}}).then(()=>loadClients()).catch(()=>{}); toast(T[lang].t_saved); }catch(e){ toast(T[lang].t_err); } finally{ btn.disabled=false; } } /* ---- cabinet ---- */ function openCabinet(){ $('#cabModal').classList.add('open'); cabTab('inv'); loadList(); loadClients(); } function closeCabinet(){ $('#cabModal').classList.remove('open'); } async function loadList(){ const wrap=$('#invlist'); wrap.innerHTML='
…
'; try{ const {invoices}=await api('/invoices'); let paid=0, totalAll=0; invoices.forEach(i=>{ totalAll+=+i.total||0; if(i.status==='paid') paid+=+i.total||0; }); const st=$('#invstats'); if(st){ st.innerHTML = invoices.length ? '
'+T[lang].cab_paid+'
' +'
'+T[lang].cab_unpaid+''+fmt(totalAll-paid)+'
' +'
'+T[lang].cab_total+''+fmt(totalAll)+'
' : ''; } if(!invoices.length){ wrap.innerHTML='
'+T[lang].cab_empty+'
'; return; } wrap.innerHTML=''; invoices.forEach(inv=>{ const isPaid=inv.status==='paid'; const row=document.createElement('div'); row.className='invrow'; row.innerHTML='
' +'
'+esc(inv.invoice_number||'—')+'
' +'
'+esc(inv.client_name||'')+(inv.issue_date?(' · '+inv.issue_date):'')+'
' +'
'+fmt(+inv.total)+'
' +'
' +'
' +'' +'' +'' +'' +'' +'
'; row.querySelector('.meta').onclick=()=>openInvoice(inv.id); row.querySelector('.badge').onclick=async(e)=>{ e.stopPropagation(); try{ await api('/invoices/'+inv.id+'/status',{method:'PUT',body:{status:isPaid?'unpaid':'paid'}}); loadList(); }catch(err){ toast(T[lang].t_err); } }; row.querySelector('.view').onclick=()=>viewInvoice(inv.id); row.querySelector('.dl').onclick=()=>downloadInvoice(inv.id); row.querySelector('.del').onclick=async()=>{ if(!confirm((inv.invoice_number||'')+' ✕?')) return; try{ await api('/invoices/'+inv.id,{method:'DELETE'}); if(currentInvoiceId===inv.id) currentInvoiceId=null; loadList(); }catch(e){ toast(T[lang].t_err); } }; wrap.appendChild(row); }); }catch(e){ wrap.innerHTML='
'+T[lang].t_err+'
'; } } async function openInvoice(id){ try{ const {invoice,items}=await api('/invoices/'+id); currentInvoiceId=id; const set=(k,v)=>{ if($('#'+k)) $('#'+k).value=v||''; }; set('i_num',invoice.invoice_number); set('i_date',invoice.issue_date); set('i_supply',invoice.supply_date); set('i_due',invoice.due_date); set('c_name',invoice.client_name); set('c_reg',invoice.client_reg); set('c_vat',invoice.client_vat); set('c_addr',invoice.client_address); set('c_email',invoice.client_email); set('i_note',invoice.notes); set('i_paymethod',invoice.payment_method); if($('#i_doctype')) $('#i_doctype').value=invoice.doc_type||'invoice'; vatMode=invoice.vat_mode||'std'; tpl=invoice.template||'classic'; if(invoice.lang&&T[invoice.lang]) lang=invoice.lang; itemsEl.innerHTML=''; (items||[]).forEach(it=>addItem(it.code||'',it.description||'',String(it.qty),it.unit||'',String(it.unit_price),it.discount?String(it.discount):'')); if(!itemsEl.children.length) addItem(); $$('#vatsw button').forEach(x=>x.classList.toggle('on',x.dataset.mode===vatMode)); applyTpl(); applyLang(); closeCabinet(); }catch(e){ toast(T[lang].t_err); } } // Open a saved invoice and show it in preview (a "window with it") async function viewInvoice(id){ await openInvoice(id); if(window.innerWidth<=980){ $$('.mtabs button').forEach(x=>x.classList.toggle('on', x.dataset.tab==='preview')); document.body.className='show-preview'; setTimeout(fitPreview,40); } const pv=document.querySelector('.col-preview'); if(pv) pv.scrollIntoView({behavior:'smooth',block:'start'}); } // Open a saved invoice and download its PDF directly async function downloadInvoice(id){ await openInvoice(id); await new Promise(r=>setTimeout(r,60)); await buildInvoicePdf('save'); } /* ============ CLIENTS (Phase 3) ============ */ let clientsCache=[]; function populateClientPick(){ const sel=$('#clientpick'); if(!sel) return; const cur=sel.value; sel.innerHTML='' + clientsCache.map(c=>'').join(''); sel.value=cur; } async function loadClients(){ if(!token) return; try{ const d=await api('/clients'); clientsCache=d.clients||[]; populateClientPick(); renderClientList(); }catch(e){} } function fillClient(c){ if(!c) return; const set=(id,v)=>{ if($('#'+id)) $('#'+id).value=v||''; }; set('c_name',c.name); set('c_reg',c.reg_no); set('c_vat',c.vat_no); set('c_addr',c.address); set('c_email',c.email); render(); } async function saveClient(){ const g=id=>($('#'+id)?.value||'').trim(); if(!g('c_name')){ toast(T[lang].cli_name_req); return; } try{ await api('/clients',{method:'POST',body:{name:g('c_name'),reg_no:g('c_reg'),vat_no:g('c_vat'),address:g('c_addr'),email:g('c_email')}}); await loadClients(); toast(T[lang].cli_saved); }catch(e){ toast(T[lang].t_err); } } async function deleteClient(id){ try{ await api('/clients/'+id,{method:'DELETE'}); await loadClients(); }catch(e){ toast(T[lang].t_err); } } function renderClientList(){ const wrap=$('#clientlist'); if(!wrap) return; if(!clientsCache.length){ wrap.innerHTML='
'+T[lang].cli_empty+'
'; return; } wrap.innerHTML=''; clientsCache.forEach(c=>{ const row=document.createElement('div'); row.className='invrow'; row.innerHTML='
'+esc(c.name)+'
' +'
'+esc([c.vat_no,c.email].filter(Boolean).join(' · '))+'
' +''; row.querySelector('.meta').onclick=()=>{ fillClient(c); closeCabinet(); }; row.querySelector('.del').onclick=()=>{ if(confirm(c.name+' ✕?')) deleteClient(c.id); }; wrap.appendChild(row); }); } function cabTab(which){ $$('.cabtab').forEach(b=>b.classList.toggle('on',b.dataset.cab===which)); $('#invlist').classList.toggle('hidden', which!=='inv'); $('#clientlist').classList.toggle('hidden', which!=='cli'); const st=$('#invstats'); if(st) st.classList.toggle('hidden', which!=='inv'); } /* ---------- init ---------- */ function initDates(){ const today=new Date(); const due=new Date(); due.setDate(due.getDate()+10); if(!$('#i_date').value) $('#i_date').value=today.toISOString().slice(0,10); if(!$('#i_due').value) $('#i_due').value=due.toISOString().slice(0,10); if(!$('#i_num').value) $('#i_num').value=today.getFullYear()+'-001'; if($('#i_paymethod') && !$('#i_paymethod').value) $('#i_paymethod').value=T[lang].pay_method_default; } $$('input,textarea').forEach(el=>el.addEventListener('input',render)); loadLocal(); addItem(); initDates(); applyLang(); applyTpl(); render(); applyHandoff(); setTimeout(fitPreview,300); /* ---- preview collapse toggle (like tame) ---- */ function updatePvText(){ const b=document.getElementById('pvToggle'); if(!b) return; const w=document.querySelector('.wrap'); const solo=w&&w.classList.contains('solo'); const span=b.querySelector('.pvtxt'); if(span) span.textContent = solo ? (T[lang].showPrev||'Show preview') : (T[lang].hidePrev||'Hide preview'); } (function(){ const b=document.getElementById('pvToggle'); const w=document.querySelector('.wrap'); if(w && localStorage.getItem('mp_inv_prev')==='1') w.classList.add('solo'); if(b) b.onclick=()=>{ w.classList.toggle('solo'); localStorage.setItem('mp_inv_prev', w.classList.contains('solo')?'1':'0'); updatePvText(); setTimeout(fitPreview,60); }; updatePvText(); })(); /* ---- Phase 2b wiring ---- */ $('#save').onclick = saveInvoice; $('#i_doctype').onchange = render; $('#dlxml').onclick = downloadEinvoice; $('#newinv').onclick = newInvoice; $('#saveclient').onclick = saveClient; $('#clientpick').onchange = e=>{ const c=clientsCache.find(x=>String(x.id)===String(e.target.value)); if(c) fillClient(c); }; $$('.cabtab').forEach(b=>b.onclick=()=>cabTab(b.dataset.cab)); $('#authSubmit').onclick = submitAuth; $('#authClose').onclick = closeAuth; $('#cabClose').onclick = closeCabinet; $('#authModal').onclick = e=>{ if(e.target.id==='authModal') closeAuth(); }; $('#cabModal').onclick = e=>{ if(e.target.id==='cabModal') closeCabinet(); }; $('#au_pass').addEventListener('keydown', e=>{ if(e.key==='Enter') submitAuth(); }); $$('[data-save]').forEach(el=>el.addEventListener('input', scheduleProfileSave)); renderAuthbox(); if(token) afterLogin();