đĻ Koperasi Terpadu V16 Full Integrated âĸ Offline/PWA ready
Dashboard
Anggota0
SimpananRp 0
PenjualanRp 0
PinjamanRp 0
AngsuranRp 0
SHU/LabaRp 0
Semua nilai dibulatkan ke rupiah penuh. Semua data disimpan di perangkat melalui localStorage. Backup data tersedia di menu Laporan.
Gunakan No. Kontrak yang sudah ada untuk membuka dan merevisi kredit lama; nomor tidak dibuat ulang.
`}
function fillKMember(){let m=member(k_id.value);k_nama.value=m.nama||'';k_alamat.value=m.alamat||''}
function calcPay(P,n,r,met){if(!P||!n)return 0;if(met==='Flat')return P/n+P*r/12;let rm=r/12;return P*rm/(1-Math.pow(1+rm,-n))}
function saveKredit(){let m=member(k_id.value);if(!m.id)return alert('Anggota tidak ditemukan');let P=+k_pokok.value||0,n=+k_tenor.value||0,rate=(+k_bunga.value||0)/100;if(P<=0||n<=0)return alert('Pokok dan tenor wajib diisi');let a=arr('kredit'),no=k_no.value.trim();if(!no)return alert('No. Kontrak wajib diisi');let ix=a.findIndex(x=>String(x.no).toLowerCase()===no.toLowerCase());let old=ix>=0?a[ix]:null;let tgl=k_tgl.value||old?.tanggal||new Date().toISOString().slice(0,10);let akhir=k_akhir.value||old?.tanggalAkhir||'';if(!akhir){let d=new Date(tgl);d.setMonth(d.getMonth()+n);akhir=d.toISOString().slice(0,10)}let d0=new Date(tgl),d1=new Date(akhir);if(d1<=d0)return alert('Tanggal akhir kredit harus setelah tanggal mulai.');let rec={no,id:m.id,nama:m.nama,alamat:m.alamat,tanggal:tgl,tanggalAkhir:akhir,jatuhTempo:akhir,pokok:P,tenor:n,bungaRate:rate,metode:k_metode.value,angsuran:calcPay(P,n,rate,k_metode.value)};if(ix>=0)a[ix]=rec;else a.unshift(rec);save('kredit',a);alert(ix>=0?'Kredit berhasil direvisi.':'Kredit baru berhasil disimpan.');render('kredit');dash()}
function editKredit(no){let k=kredit(no);k_no.value=k.no||'';k_id.value=k.id||'';k_nama.value=k.nama||'';k_alamat.value=k.alamat||'';k_tgl.value=k.tanggal||'';k_akhir.value=k.tanggalAkhir||k.jatuhTempo||'';k_pokok.value=k.pokok||0;k_tenor.value=k.tenor||0;k_bunga.value=(+k.bungaRate||0)*100;k_metode.value=k.metode||'Efektif';window.scrollTo(0,0)}
function clearKredit(){['k_no','k_id','k_nama','k_alamat','k_tgl','k_akhir','k_pokok','k_tenor'].forEach(id=>{let e=document.getElementById(id);if(e)e.value=''});k_bunga.value=6;k_metode.value='Efektif'}
function renderKreditTable(){let a=arr('kredit');klist.innerHTML=a.length?'
No Kontrak
Anggota
Nama
Pokok
Tenor
Jatuh Tempo
Metode
Angsuran
Aksi
'+a.slice(0,100).map(x=>`
${esc(x.no)}
${esc(x.id)}
${esc(x.nama)}
${money(x.pokok)}
${esc(x.tenor)}
${esc(x.tanggalAkhir||x.jatuhTempo||'')}
${esc(x.metode)}
${money(x.angsuran)}
`).join('')+'
':'
Belum ada kredit.
'
function schedule(k){let rows=[],bal=+k.pokok||0,n=+k.tenor||0,r=+k.bungaRate||0,met=k.metode,pay=calcPay(bal,n,r,met);for(let i=1;i<=n;i++){let interest=met==='Flat'?k.pokok*r/12:bal*r/12;let principal=met==='Flat'?k.pokok/n:Math.min(bal,Math.max(0,pay-interest));if(i===n)principal=bal;let amount=principal+interest;let d=new Date(k.tanggal);d.setMonth(d.getMonth()+i);rows.push({ke:i,due:d.toISOString().slice(0,10),pokok:Math.round(principal),bunga:Math.round(interest),total:Math.round(amount)});bal=Math.max(0,bal-principal)}return rows}
function listPage(c){let a=arr('kredit');c.innerHTML=`
đ List Angsuran
Jadwal tagihan lengkap sampai selesai. Sisa pokok dan sisa bunga dihitung kumulatif dari seluruh pembayaran.
Note: Mohon melakukan pembayaran sebelum jatuh tempo untuk menghindari denda keterlambatan.
`}).join(''):'
Belum ada kredit.
'}`}
function nextDue(k){let n=arr('angsuran').filter(x=>x.kredit===k.no).length,sch=schedule(k)[n];return sch?sch.due:k.tanggalAkhir||k.jatuhTempo}
function nextDueISO(k){return nextDue(k)}
function angsuranPage(c){c.innerHTML=`
đŗ Pembayaran Angsuran
Pokok dan bunga otomatis dari jadwal; total pembayaran dapat disesuaikan bila ada pembayaran/penyesuaian khusus.
`}
function fillCredit(){let k=kredit(a_kredit.value);a_id.value=k.id||'';a_nama.value=k.nama||'';a_alamat.value=k.alamat||'';a_ke.value=arr('angsuran').filter(x=>x.kredit===k.no).length+1;a_tgl.value=new Date().toISOString().slice(0,10);fillInstallment()}
function fillInstallment(){let k=kredit(a_kredit.value);if(!k)return;let z=schedule(k)[(+a_ke.value||1)-1];if(!z)return;a_due.value=z.due;a_pokok.value=z.pokok;a_bunga.value=z.bunga;calcDenda()}
function calcDenda(){let k=kredit(a_kredit.value);if(!k)return;let due=new Date(a_due.value),pay=new Date(a_tgl.value||new Date());let days=Math.max(0,Math.floor((pay-due)/86400000));a_telat.value=days;a_denda.value=Math.round((+a_pokok.value||0)*0.001*days);if(!a_total.dataset.manual||a_total.value==='')a_total.value=(+a_pokok.value||0)+(+a_bunga.value||0)+(+a_denda.value||0)}
function syncPaymentTotal(){a_total.dataset.manual='1'}
function addAngsuran(){let k=kredit(a_kredit.value);if(!k)return alert('Pilih No. Kredit');let ke=+a_ke.value||1;if(arr('angsuran').some(x=>x.kredit===k.no&&+x.ke===ke))return alert('Angsuran ke-'+ke+' untuk kredit ini sudah dibayar.');calcDenda();let total=+a_total.value||0;if(total<=0)total=(+a_pokok.value||0)+(+a_bunga.value||0)+(+a_denda.value||0);let a=arr('angsuran');a.unshift({no:uid('A'),tanggal:a_tgl.value||new Date().toISOString().slice(0,10),kredit:k.no,id:k.id,nama:k.nama,alamat:k.alamat,ke,pokok:+a_pokok.value||0,bunga:+a_bunga.value||0,denda:+a_denda.value||0,total});save('angsuran',a);render('angsuran');dash()}
function laporan(c){let simp=arr('simpanan').reduce((a,x)=>a+(+x.nilai||0),0),modalPokok=arr('simpanan').filter(x=>x.jenis==='Pokok').reduce((a,x)=>a+(+x.nilai||0),0),wajib=arr('simpanan').filter(x=>x.jenis==='Wajib').reduce((a,x)=>a+(+x.nilai||0),0),sukarela=arr('simpanan').filter(x=>x.jenis==='Sukarela').reduce((a,x)=>a+(+x.nilai||0),0),jual=arr('penjualan').reduce((a,x)=>a+(+x.total||0),0),hpp=arr('penjualan').reduce((a,x)=>a+(+x.modal||0),0),keuntunganJual=jual-hpp,pok=arr('kredit').reduce((a,x)=>a+(+x.pokok||0),0),pokBayar=arr('angsuran').reduce((a,x)=>a+(+x.pokok||0),0),bung=arr('angsuran').reduce((a,x)=>a+(+x.bunga||0),0),dend=arr('angsuran').reduce((a,x)=>a+(+x.denda||0),0),piutang=Math.max(0,pok-pokBayar),persediaan=arr('barang').reduce((a,x)=>a+(+x.stok||0)*(+x.modal||0),0),kas=Math.max(0,simp+jual+arr('angsuran').reduce((a,x)=>a+(+x.total||0),0)-pok-hpp),shu=keuntunganJual+bung+dend,aset=kas+piutang+persediaan,kew=wajib+sukarela,modalBersih=aset-kew; c.innerHTML=`
đ Laporan Terpadu
Total Anggota${arr('anggota').length}
Total Simpanan${money(simp)}
Total Penjualan${money(jual)}
Total Pinjaman${money(pok)}
Total Angsuran${money(arr('angsuran').reduce((a,x)=>a+(+x.total||0),0))}
SHU/Laba${money(shu)}
Laba / Rugi & SHU
AKUN
NILAI
Penjualan
${money(jual)}
Harga Pokok Penjualan
${money(hpp)}
Keuntungan Penjualan
${money(keuntunganJual)}
Pendapatan Bunga
${money(bung)}
Pendapatan Denda
${money(dend)}
SHU / LABA
${money(shu)}
Neraca
ASET
NILAI
Kas / Bank
${money(kas)}
Piutang Pinjaman Anggota (sisa pokok)
${money(piutang)}
Persediaan Barang (stok à harga modal)
${money(persediaan)}
TOTAL ASET
${money(aset)}
KEWAJIBAN & EKUITAS
NILAI
Simpanan Wajib
${money(wajib)}
Simpanan Sukarela
${money(sukarela)}
Modal Pokok Anggota
${money(modalPokok)}
SHU / Laba Berjalan
${money(shu)}
Modal Bersih / Ekuitas (Aset â Kewajiban)
${money(modalBersih)}
TOTAL KEWAJIBAN + EKUITAS
${money(kew+modalBersih)}
Penjelasan Modal Bersih: pada versi ini Modal Bersih adalah nilai residual neraca, yaitu Total Aset dikurangi kewajiban. Modal Pokok Anggota dan SHU ditampilkan terpisah agar sumbernya dapat ditelusuri. Persediaan beras/barang masuk sebagai Aset sebesar stok tersisa à harga modal.
`}
function cetak(c){c.innerHTML=`
đ¨ī¸ Cetak
`}
function fillCetakMember(){let m=member(ct_id.value);ct_nama.value=m.nama||'';ct_hp.value=m.telp||''}
function wa(c){c.innerHTML=`
đą WhatsApp
`}
function fillWAMember(){let m=member(w_id.value);w_nama.value=m.nama||'';w_hp.value=m.telp||''}
function sendWA(){if(!w_hp.value)return alert('No. Anggota belum dipilih atau No. HP belum terdaftar');location.href='https://wa.me/'+w_hp.value.replace(/\D/g,'')+'?text='+encodeURIComponent(w_msg.value)}
function waSchedule(no){let k=kredit(no);if(!k)return;let m=member(k.id);let msg=`KOPERASI TERPADU%0A${k.nama}%0ANo Kontrak: ${k.no}%0ASisa pokok: ${money(Math.max(0,k.pokok-arr('angsuran').filter(x=>x.kredit===k.no).reduce((s,x)=>s+(+x.pokok||0),0)))}%0ASisa bunga: ${money(Math.max(0,schedule(k).reduce((s,x)=>s+x.bunga,0)-arr('angsuran').filter(x=>x.kredit===k.no).reduce((s,x)=>s+(+x.bunga||0),0)))}%0AJatuh tempo berikutnya: ${nextDue(k)}%0AMohon membayar sebelum jatuh tempo untuk menghindari denda keterlambatan.`;location.href='https://wa.me/'+String(m.telp||'').replace(/\D/g,'')+'?text='+msg}
function tableSimple(a,keys,heads){if(!a.length)return '