Google App Script : Cetak Kwitansi Online dengan Google Apps Script dan Google Sheets
Tutorial Google App Script : Cetak Kwitansi Online dengan Google Apps Script dan Google Sheets
Code.gs
function doGet() {
return HtmlService.createHtmlOutputFromFile('index');
}
function include(index) {
return HtmlService.createHtmlOutputFromFile(index)
.getContent();
}
function addDataToSpreadsheet(noKwitansi, namaPenerima, alamat, items) {
var ss = SpreadsheetApp.openById("Ganti dengan ID Googel Spreadsheet anda");
var ws = ss.getSheetByName("data");
items.forEach(function(item) {
var rowData = [noKwitansi, namaPenerima, alamat].concat(item);
ws.appendRow(rowData);
});
}
<!DOCTYPE html>
<html lang="id">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Kwitansi</title>
<style>
body {
font-family: Arial, sans-serif;
margin: 20px;
}
.header {
text-align: center;
margin-bottom: 20px;
}
input[type="text"],
input[type="number"],
button {
padding: 8px;
border: 1px solid #ccc;
border-radius: 8px;
margin-bottom: 10px;
width: 100%;
box-sizing: border-box;
}
.button-print {
background: #808280;
background-image: -webkit-linear-gradient(top, #808280, #262626);
background-image: -moz-linear-gradient(top, #808280, #262626);
background-image: -ms-linear-gradient(top, #808280, #262626);
background-image: -o-linear-gradient(top, #808280, #262626);
background-image: linear-gradient(to bottom, #808280, #262626);
-webkit-border-radius: 28px;
-moz-border-radius: 28px;
border-radius: 28px;
font-family: Arial;
color: #ffffff;
font-size: 20px;
padding: 10px 20px 10px 20px;
cursor: pointer;
}
.button-print:hover {
background: #9e9e9e;
background-image: -webkit-linear-gradient(top, #9e9e9e, #6b6b6b);
background-image: -moz-linear-gradient(top, #9e9e9e, #6b6b6b);
background-image: -ms-linear-gradient(top, #9e9e9e, #6b6b6b);
background-image: -o-linear-gradient(top, #9e9e9e, #6b6b6b);
background-image: linear-gradient(to bottom, #9e9e9e, #6b6b6b);
text-decoration: none;
}
.button-submit {
background-color: #4285F4;
background-image: -webkit-linear-gradient(top, #3498db, #2980b9);
background-image: -moz-linear-gradient(top, #3498db, #2980b9);
background-image: -ms-linear-gradient(top, #3498db, #2980b9);
background-image: -o-linear-gradient(top, #3498db, #2980b9);
background-image: linear-gradient(to bottom, #3498db, #2980b9);
-webkit-border-radius: 28px;
-moz-border-radius: 28px;
border-radius: 28px;
font-family: Arial;
color: #ffffff;
font-size: 20px;
padding: 10px 20px 10px 20px;
text-decoration: none;
cursor: pointer;
}
.button-submit:hover {
background: #3cb0fd;
background-image: -webkit-linear-gradient(top, #3cb0fd, #3498db);
background-image: -moz-linear-gradient(top, #3cb0fd, #3498db);
background-image: -ms-linear-gradient(top, #3cb0fd, #3498db);
background-image: -o-linear-gradient(top, #3cb0fd, #3498db);
background-image: linear-gradient(to bottom, #3cb0fd, #3498db);
text-decoration: none;
}
.button-add {
background: #00c400;
background-image: -webkit-linear-gradient(top, #00c400, #009118);
background-image: -moz-linear-gradient(top, #00c400, #009118);
background-image: -ms-linear-gradient(top, #00c400, #009118);
background-image: -o-linear-gradient(top, #00c400, #009118);
background-image: linear-gradient(to bottom, #00c400, #009118);
-webkit-border-radius: 28px;
-moz-border-radius: 28px;
border-radius: 28px;
font-family: Arial;
color: #ffffff;
font-size: 20px;
padding: 10px 20px 10px 20px;
text-decoration: none;
cursor: pointer;
}
.button-add:hover {
background: #4CAF50;
background-image: -webkit-linear-gradient(top, #4CAF50, #31a34d);
background-image: -moz-linear-gradient(top, #4CAF50, #31a34d);
background-image: -ms-linear-gradient(top, #4CAF50, #31a34d);
background-image: -o-linear-gradient(top, #4CAF50, #31a34d);
background-image: linear-gradient(to bottom, #4CAF50, #31a34d);
}
.button-new {
background: #F4B400;
background-image: -webkit-linear-gradient(top, #F4B400, #c9a814);
background-image: -moz-linear-gradient(top, #F4B400, #c9a814);
background-image: -ms-linear-gradient(top, #F4B400, #c9a814);
background-image: -o-linear-gradient(top, #F4B400, #c9a814);
background-image: linear-gradient(to bottom, #F4B400, #c9a814);
-webkit-border-radius: 28px;
-moz-border-radius: 28px;
border-radius: 28px;
font-family: Arial;
color: #ffffff;
font-size: 20px;
padding: 10px 20px 10px 20px;
text-decoration: none;
cursor: pointer;
}
.button-new:hover {
background: #afdea5;
background-image: -webkit-linear-gradient(top, #afdea5, #31a34d);
background-image: -moz-linear-gradient(top, #afdea5, #31a34d);
background-image: -ms-linear-gradient(top, #afdea5, #31a34d);
background-image: -o-linear-gradient(top, #afdea5, #31a34d);
background-image: linear-gradient(to bottom, #afdea5, #31a34d);
text-decoration: none;
}
.disabled-button {
background-color: #ccc;
color: #999;
cursor: not-allowed;
}
.form label {
display: block;
margin-bottom: 8px;
}
.info, .form {
margin-bottom: 15px;
}
.info {
display: flex;
flex-direction: column;
}
.info p {
display: flex;
align-items: center;
margin-bottom: 10px;
}
.info label {
width: 150px;
flex-shrink: 0;
}
table {
width: 100%;
border-collapse: collapse;
margin-bottom: 20px;
}
table, th, td {
border: 1px solid black;
}
th, td {
padding: 8px;
text-align: left;
}
th {
background-color: #f2f2f2;
}
.footer {
display: flex;
justify-content: space-between;
margin-top: 20px;
}
.footer .left, .footer .right {
width: 45%;
}
.footer .right {
text-align: right;
}
.button-container {
display: flex;
justify-content: space-between;
margin-bottom: 20px;
}
@media print {
.button-submit {
display: none;
}
.button-print {
display: none;
}
.form {
display: none;
}
.print-button {
display: none;
}
.aksi-column {
display: none;
}
}
.loading {
display: none;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
color: #06940B;
background-color: rgba(255, 255, 255, 0.9);
z-index: 9999;
}
.loading-text {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
</style>
</head>
<body>
<div class="header">
<h1>Kwitansi</h1>
</div>
<div id="loading" class="loading">
<div class="loading-text"><h2>Loading...</h2></div>
</div>
<form id="myForm" onsubmit="event.preventDefault(); simpanData()">
<div class="form">
<label>No Kwitansi <input type="text" id="inputNoKwitansi" placeholder="" readonly ></label>
<label>Nama Penerima<input type="text" id="inputNamaPenerima" placeholder="Penerima" required></label>
<label>Alamat<input type="text" id="inputAlamat" placeholder="Masukan Alamat" required></label>
<label>Deskripsi<input type="text" id="inputDeskripsi" placeholder="Isi Deskripsi" required></label>
<label>Kuantitas<input type="number" id="inputKuantitas" placeholder="Isi Kuantitas dengan angka" required></label>
<label>Harga Satuan<input type="text" id="inputHargaSatuan" placeholder="Isi Harga dengan angka" required></label>
<div class="button-container">
<button class="button-add" type="button" onclick="addItem()">Add</button>
<button class="button-new" type="button" onclick="reloadPage()">New</button>
<button id="printButton" class="button-print disabled-button" onclick="checkAndPrint()">Print</button>
</div>
</div>
</form>
<div class="info">
<p><label>No Kwitansi</label>: <span id="noKwitansi"></span></p>
<p><label>Tanggal:</label>: <span id="tanggal"></span></p>
<p><label>Nama Penerima</label>: <span id="namaPenerima"></span></p>
<p><label>Alamat:</label>: <span id="alamat"></span></p>
</div>
<table>
<thead>
<tr>
<th>No</th>
<th>Deskripsi</th>
<th>Kuantitas</th>
<th>Harga Satuan</th>
<th>Total</th>
<th class="aksi-column">Aksi</th>
</tr>
</thead>
<tbody id="itemTableBody">
</tbody>
</table>
<div class="footer">
<div class="left">
<p><strong>Terbilang:</strong> <span id="terbilang"></span></p>
</div>
<div class="right">
<p><strong>Grand Total:</strong> <span id="total"></span></p><br>
</div>
</div>
<button class="button-submit" type="submit" onclick="simpanData()">Save</button>
<script>
let isDataSaved = false; // Flag to check if data is saved
function reloadPage() {
document.getElementById('loading').style.display = 'block';
window.open("https://script.google.com/macros/s/ganti-dengan-id-deploy/exec",'_top');
setTimeout(function(){
document.getElementById('loading').style.display = 'none';
}, 3000);
}
function generateNoKwitansi() {
const today = new Date();
const year = today.getFullYear();
const month = String(today.getMonth() + 1).padStart(2, '0');
const day = String(today.getDate()).padStart(2, '0');
const noKwitansi = `KW${year}${month}${day}${Math.floor(Math.random() * 1000)}`;
document.getElementById('inputNoKwitansi').value = noKwitansi;
}
window.onload = generateNoKwitansi;
function updateKwitansi() {
document.getElementById('noKwitansi').textContent = document.getElementById('inputNoKwitansi').value;
document.getElementById('namaPenerima').textContent = document.getElementById('inputNamaPenerima').value;
document.getElementById('alamat').textContent = document.getElementById('inputAlamat').value;
}
function lockRecipientAddress() {
document.getElementById('inputNamaPenerima').readOnly = true;
document.getElementById('inputAlamat').readOnly = true;
}
function clearFormInput() {
document.getElementById('inputDeskripsi').value = '';
document.getElementById('inputKuantitas').value = '';
document.getElementById('inputHargaSatuan').value = '';
}
function addItem() {
const deskripsi = document.getElementById('inputDeskripsi').value;
const kuantitas = parseInt(document.getElementById('inputKuantitas').value);
const hargaSatuan = parseInt(document.getElementById('inputHargaSatuan').value.replace(/\D/g, ''));
const total = kuantitas * hargaSatuan;
if (deskripsi === "" || isNaN(kuantitas) || isNaN(hargaSatuan) || kuantitas <= 0 || hargaSatuan <= 0) {
alert("Harap lengkapi semua field sebelum menambahkan item.");
return;
}
const tbody = document.getElementById('itemTableBody');
const tr = document.createElement('tr');
tr.innerHTML = `
<td>${tbody.rows.length + 1}</td>
<td>${deskripsi}</td>
<td>${kuantitas}</td>
<td>Rp ${hargaSatuan.toLocaleString('id-ID')}</td>
<td>Rp ${total.toLocaleString('id-ID')}</td>
<td class="aksi-column">
<button onclick="editItem(this)">Edit</button>
<button onclick="deleteItem(this)">Delete</button>
</td>
`;
tbody.appendChild(tr);
updateTotal();
updateKwitansi();
lockRecipientAddress();
clearFormInput();
}
function editItem(button) {
const tr = button.parentElement.parentElement;
const cells = tr.getElementsByTagName('td');
document.getElementById('inputDeskripsi').value = cells[1].textContent;
document.getElementById('inputKuantitas').value = cells[2].textContent;
document.getElementById('inputHargaSatuan').value = cells[3].textContent.replace(/[^0-9]/g, '');
deleteItem(button);
}
function deleteItem(button) {
const tr = button.parentElement.parentElement;
tr.parentElement.removeChild(tr);
updateTotal();
updateKwitansi();
const tbody = document.getElementById('itemTableBody');
for (let i = 0; i < tbody.rows.length; i++) {
tbody.rows[i].cells[0].textContent = i + 1;
}
}
function updateTotal() {
const tbody = document.getElementById('itemTableBody');
let total = 0;
for (let i = 0; i < tbody.rows.length; i++) {
const row = tbody.rows[i];
const hargaSatuanText = row.cells[3].textContent.trim();
const hargaSatuan = parseInt(hargaSatuanText.split(' ')[1].replace(/\./g, ''));
const kuantitas = parseInt(row.cells[2].textContent);
const rowTotal = hargaSatuan * kuantitas;
total += rowTotal;
}
document.getElementById('total').textContent = `Rp ${total.toLocaleString('id-ID')}`;
document.getElementById('terbilang').textContent = terbilang(total) + ' rupiah';
}
function terbilang(angka) {
const bilangan = ["", "satu", "dua", "tiga", "empat", "lima", "enam", "tujuh", "delapan", "sembilan", "sepuluh", "sebelas"];
if (angka < 12) {
return bilangan[angka];
} else if (angka < 20) {
return bilangan[angka - 10] + " belas";
} else if (angka < 100) {
return bilangan[Math.floor(angka / 10)] + " puluh " + bilangan[angka % 10];
} else if (angka < 200) {
return "seratus " + terbilang(angka - 100);
} else if (angka < 1000) {
return bilangan[Math.floor(angka / 100)] + " ratus " + terbilang(angka % 100);
} else if (angka < 2000) {
return "seribu " + terbilang(angka - 1000);
} else if (angka < 1000000) {
return terbilang(Math.floor(angka / 1000)) + " ribu " + terbilang(angka % 1000);
} else if (angka < 1000000000) {
return terbilang(Math.floor(angka / 1000000)) + " juta " + terbilang(angka % 1000000);
} else if (angka < 1000000000000) {
return terbilang(Math.floor(angka / 1000000000)) + " milyar " + terbilang(angka % 1000000000);
} else if (angka < 1000000000000000) {
return terbilang(Math.floor(angka / 1000000000000)) + " triliun " + terbilang(angka % 1000000000000);
} else {
return "Angka terlalu besar";
}
}
function formatCurrency(input) {
let cleanedInput = input.replace(/\D/g, '');
let number = parseInt(cleanedInput);
let formattedInput = number.toLocaleString('id-ID');
return formattedInput;
}
document.getElementById('inputHargaSatuan').addEventListener('input', function() {
let formattedValue = formatCurrency(this.value);
this.value = formattedValue;
});
function simpanData() {
var noKwitansi = document.getElementById('inputNoKwitansi').value;
var namaPenerima = document.getElementById('inputNamaPenerima').value;
var alamat = document.getElementById('inputAlamat').value;
var items = [];
var tbody = document.getElementById('itemTableBody');
for (var i = 0; i < tbody.rows.length; i++) {
var cells = tbody.rows[i].cells;
var deskripsi = cells[1].textContent;
var kuantitas = parseInt(cells[2].textContent);
var hargaSatuan = parseInt(cells[3].textContent.replace(/\D/g, ''));
items.push([deskripsi, kuantitas, hargaSatuan]);
}
console.log("No Kwitansi:", noKwitansi);
console.log("Nama Penerima:", namaPenerima);
console.log("Alamat:", alamat);
console.log("Items:", items);
google.script.run
.withSuccessHandler(function() {
alert("Data berhasil disimpan!");
isDataSaved = true; // Set the flag to true when data is saved
document.getElementById('printButton').disabled = false; // Enable the print button
document.getElementById('printButton').classList.remove('disabled-button');
})
.addDataToSpreadsheet(noKwitansi, namaPenerima, alamat, items);
}
function checkAndPrint() {
if (!isDataSaved) {
alert("Data Belum siap di cetak, Anda Harus simpan terlebih dahulu");
return;
}
updateKwitansi();
window.print();
}
const options = { timeZone: 'Asia/Jakarta', year: 'numeric', month: 'long', day: 'numeric' };
const tanggal = new Date().toLocaleDateString('id-ID', options);
document.getElementById('tanggal').textContent = tanggal;
</script>
</body>
</html>