(function ($) {
"use strict";
var cfg = window.SALES_PAGE,
cart = [],
selectedItem = null,
selectedBars = [],
fileBag = new DataTransfer();
function esc(v) {
return $("
")
.text(v == null ? "" : v)
.html();
}
function number(v) {
if (typeof v === "number") return v;
v = String(v || "").replace(/Rp|\s/g, "");
if (v.indexOf(",") >= 0) v = v.replace(/\./g, "").replace(",", ".");
else if (/^\d{1,3}(\.\d{3})+$/.test(v)) v = v.replace(/\./g, "");
return Number(v) || 0;
}
function money(v) {
return (
"Rp " +
new Intl.NumberFormat("id-ID", {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
}).format(Number(v) || 0)
);
}
function toast(icon, title) {
if (window.AppAlert && AppAlert.toast) return AppAlert.toast(title, icon);
return Swal.fire({
toast: true,
position: "top-end",
icon: icon,
title: title,
showConfirmButton: false,
timer: 2800,
timerProgressBar: true,
});
}
function request(url, data, files) {
data = data || {};
if (data instanceof FormData) {
if (!data.has(cfg.csrfName)) data.append(cfg.csrfName, cfg.csrfHash);
} else data[cfg.csrfName] = cfg.csrfHash;
return $.ajax({
url: url,
type: "POST",
data: data,
processData: !files,
contentType: files
? false
: "application/x-www-form-urlencoded; charset=UTF-8",
dataType: "json",
});
}
function modal(id) {
return bootstrap.Modal.getOrCreateInstance(document.getElementById(id));
}
function loadCustomers() {
return $.getJSON(cfg.base + "sales/lookup_customers", { q: "" }).done(
function (r) {
$("#customerLookup").html(
'
' +
r.rows
.map(function (x) {
return (
'
"
);
})
.join(""),
);
},
);
}
function loadItems() {
return $.getJSON(cfg.base + "sales/lookup_items", { q: "" }).done(
function (r) {
$("#itemLookup").html(
'
' +
r.rows
.map(function (x) {
return (
'
"
);
})
.join(""),
);
},
);
}
function loadWarehouses() {
return $.getJSON(cfg.base + "sales/warehouses").done(function (r) {
$("#warehouseLookup").html(
'
' +
r.rows
.map(function (x) {
return (
'
"
);
})
.join(""),
);
});
}
$("#newInvoice").on("click", function () {
cart = [];
$("#appendId").val("");
$("#invoiceForm")[0].reset();
$(
"#invoiceForm [name=customer_id],#invoiceForm [name=invoice_type],#invoiceForm [name=recognition_policy],#invoiceForm [name=period_start],#invoiceForm [name=period_end],#invoiceForm [name=due_date]",
).prop("disabled", false);
$(".invoiceModalTitle").text("Buat Invoice");
Promise.all([loadCustomers(), loadItems(), loadWarehouses()])
.then(function () {
renderCart();
modal("invoiceModal").show();
})
.catch(fail);
});
$(document).on("click", ".append-invoice", function () {
cart = [];
var id = $(this).data("id");
$("#appendId").val(id);
Promise.all([loadItems(), loadWarehouses()])
.then(function () {
$(
"#invoiceForm [name=customer_id],#invoiceForm [name=invoice_type],#invoiceForm [name=recognition_policy],#invoiceForm [name=period_start],#invoiceForm [name=period_end],#invoiceForm [name=due_date]",
).prop("disabled", true);
renderCart();
modal("invoiceModal").show();
})
.catch(fail);
});
$("#itemLookup").on("change", function () {
var o = this.options[this.selectedIndex];
selectedItem =
o && o.dataset.row ? JSON.parse(decodeURIComponent(o.dataset.row)) : null;
if (selectedItem) $("#linePrice").val(formatInput(selectedItem.harga_jual));
});
$("#chooseBarcode").on("click", function () {
if (!selectedItem || !$("#warehouseLookup").val())
return toast("warning", "Pilih barang dan gudang terlebih dahulu.");
$.getJSON(cfg.base + "sales/lookup_barcodes", {
item_id: selectedItem.id,
warehouse_id: $("#warehouseLookup").val(),
}).done(function (r) {
selectedBars = [];
$("#barcodeChoices").html(
r.rows.length
? r.rows
.map(function (b) {
var available =
number(b.qty_sisa) -
number(b.reserved_qty) -
number(b.return_reserved_qty);
return (
'
"
);
})
.join("")
: '
Tidak ada barcode tersedia.',
);
});
});
$(document).on("change", ".bar-check", function () {
var b = JSON.parse(decodeURIComponent(this.dataset.row));
if (this.checked) {
var qty =
selectedItem && selectedItem.tracking_type === "QTY"
? Math.min(
number(b.qty_sisa) -
number(b.reserved_qty) -
number(b.return_reserved_qty),
Math.max(0, number($("#lineQty").val())),
)
: 1;
selectedBars.push({ id: b.id, barcode: b.barcode, qty: qty });
} else
selectedBars = selectedBars.filter(function (x) {
return Number(x.id) !== Number(b.id);
});
});
$("#addLine").on("click", function () {
var qty = number($("#lineQty").val()),
price = number($("#linePrice").val());
if (!selectedItem || !$("#warehouseLookup").val() || qty <= 0 || price < 0)
return toast("warning", "Lengkapi barang, gudang, qty, dan harga.");
var bqty = selectedBars.reduce(function (s, b) {
return s + number(b.qty);
}, 0);
if (Math.abs(bqty - qty) > 0.0001)
return toast(
"warning",
"Total qty barcode (" +
bqty +
") harus sama dengan qty barang (" +
qty +
").",
);
cart.push({
item_id: selectedItem.id,
item_code: selectedItem.kode_barang,
item_name: selectedItem.nama_barang,
warehouse_id: $("#warehouseLookup").val(),
service_date: $("#invoiceForm [name=date]").val(),
qty: qty,
unit_price: price,
discount_amount: 0,
tax_rate: 0,
specification: "",
barcodes: selectedBars.slice(),
});
selectedBars = [];
$("#barcodeChoices").empty();
renderCart();
});
function renderCart() {
var total = 0;
$("#invoiceCart tbody").html(
cart
.map(function (x, i) {
var sub =
Math.max(0, x.qty * x.unit_price - x.discount_amount) *
(1 + x.tax_rate / 100);
total += sub;
return (
'
| ' +
esc(x.item_code + " - " + x.item_name) +
'' +
x.barcodes.length +
' barcode | | ' +
x.qty +
" | " +
money(x.unit_price) +
' | | | ' +
money(sub) +
' | |
'
);
})
.join(""),
);
$("#cartTotal").text(money(total));
}
$(document).on(
"input change",
".cart-discount,.cart-tax,.cart-date,.cart-spec",
function () {
var tr = $(this).closest("tr"),
x = cart[tr.data("index")];
x.discount_amount = number(tr.find(".cart-discount").val());
x.tax_rate = number(tr.find(".cart-tax").val());
x.service_date = tr.find(".cart-date").val();
x.specification = tr.find(".cart-spec").val();
renderCart();
},
);
$(document).on("click", ".cart-remove", function () {
cart.splice($(this).closest("tr").data("index"), 1);
renderCart();
});
$("#invoiceForm").on("submit", function (e) {
e.preventDefault();
if (!cart.length) return toast("warning", "Tambahkan minimal satu barang.");
var f = new FormData(this),
id = $("#appendId").val();
f.append("rows", JSON.stringify(cart));
var url = id
? cfg.base + "sales/append_invoice/" + id
: cfg.base + "sales/create_invoice";
request(url, f, true)
.done(function (r) {
if (!r.status) return toast("error", r.message);
modal("invoiceModal").hide();
toast("success", r.message);
setTimeout(function () {
location.reload();
}, 600);
})
.fail(fail);
});
function fail(xhr) {
var m = "Transaksi tidak dapat diproses.";
try {
m = JSON.parse(xhr.responseText).message || m;
} catch (e) {}
toast("error", m);
}
$(document).on("click", ".sale-action", function () {
var btn = $(this),
url = btn.data("url");
Swal.fire({
icon: "question",
title: btn.data("confirm"),
showCancelButton: true,
confirmButtonText: "Ya, lanjutkan",
cancelButtonText: "Batal",
}).then(function (r) {
if (!r.isConfirmed) return;
btn.prop("disabled", true);
request(url, {})
.done(function (x) {
if (!x.status) {
btn.prop("disabled", false);
return toast("error", x.message);
}
toast("success", x.message);
setTimeout(function () {
location.reload();
}, 600);
})
.fail(function (x) {
btn.prop("disabled", false);
fail(x);
});
});
});
$(document).on("click", ".reject-invoice", function () {
var id = $(this).data("id");
Swal.fire({
icon: "warning",
title: "Tolak invoice?",
input: "textarea",
inputLabel: "Alasan penolakan",
inputValidator: function (v) {
return !String(v || "").trim() ? "Alasan wajib diisi." : undefined;
},
showCancelButton: true,
confirmButtonText: "Tolak",
}).then(function (r) {
if (r.isConfirmed)
request(cfg.base + "sales/reject_invoice/" + id, { reason: r.value })
.done(function (x) {
if (x.status) location.reload();
else toast("error", x.message);
})
.fail(fail);
});
});
$(document).on("click", ".pdf-open", function () {
$("#pdfModal iframe").attr("src", $(this).data("url"));
modal("pdfModal").show();
});
document
.getElementById("pdfModal")
.addEventListener("hidden.bs.modal", function () {
$("#pdfModal iframe").attr("src", "about:blank");
});
function qty(v) {
v = number(v);
return Math.round(v) === v
? new Intl.NumberFormat("id-ID").format(v)
: new Intl.NumberFormat("id-ID", { maximumFractionDigits: 4 }).format(v);
}
function table(rows, cols) {
return (
'
' +
cols
.map(function (c) {
return "| " + c[0] + " | ";
})
.join("") +
"
" +
rows
.map(function (r) {
return (
"" +
cols
.map(function (c) {
return "| " + c[1](r) + " | ";
})
.join("") +
"
"
);
})
.join("") +
"
"
);
}
$(document).on("click", ".detail-invoice", function () {
$.getJSON(cfg.base + "sales/invoice_detail/" + $(this).data("id"))
.done(function (r) {
if (!r.status) return toast("error", r.message);
var i = r.invoice,
body =
'
Customer' +
esc(i.customer_name) +
"
" +
esc(i.customer_address || "-") +
'
Invoice' +
esc(i.no_invoice) +
"
" +
esc(i.period_start + " — " + i.period_end) +
" · " +
esc(
i.recognition_policy === "accrual"
? "Akrual"
: "Setelah pembayaran",
) +
'
Ringkasan Barang
' +
table(r.lines, [
[
"Tanggal",
function (x) {
return esc(x.service_date);
},
],
[
"Barang",
function (x) {
return (
"" +
esc(x.item_code + " - " + x.nama_item) +
"" +
esc(x.specification || "-") +
""
);
},
],
[
"Qty",
function (x) {
return qty(x.qty);
},
],
[
"Harga",
function (x) {
return money(x.harga);
},
],
[
"Subtotal",
function (x) {
return money(x.subtotal);
},
],
]) +
'Detail Barcode
' +
table(r.barcodes, [
[
"Surat Jalan",
function (x) {
return esc(x.delivery_no || "-");
},
],
[
"Barang",
function (x) {
return esc(x.item_code + " - " + x.nama_barang);
},
],
[
"Barcode / SN",
function (x) {
return (
"" +
esc(x.barcode) +
"" +
esc(x.serial_number || "-") +
""
);
},
],
[
"Qty",
function (x) {
return qty(x.qty);
},
],
[
"Gudang",
function (x) {
return esc(x.warehouse_name || "-");
},
],
]) +
'Pembayaran
' +
table(r.payments, [
[
"Tanggal",
function (x) {
return esc(x.tanggal);
},
],
[
"Nomor",
function (x) {
return esc(x.payment_no);
},
],
[
"Alokasi",
function (x) {
return money(x.allocated);
},
],
]) +
'Surat Jalan
' +
r.deliveries
.map(function (x) {
return (
"
" +
esc(x.delivery_no) +
" · " +
esc(x.delivery_date) +
" · " +
esc(x.status) +
"
"
);
})
.join("") +
"
";
$("#detailBody").html(body);
modal("detailModal").show();
})
.fail(fail);
});
$(document).on("click", ".detail-delivery", function () {
$.getJSON(cfg.base + "sales/delivery_detail/" + $(this).data("id"))
.done(function (r) {
if (!r.status) return toast("error", r.message);
var d = r.delivery;
$("#detailBody").html(
'
' +
esc(d.delivery_no) +
"
" +
esc(d.customer_name) +
"
" +
esc(d.customer_address || "-") +
'
' +
esc(d.no_invoice || "-") +
"" +
esc(d.delivery_date) +
" · " +
esc(d.warehouse_name || "-") +
" · " +
esc(d.status) +
'
' +
table(r.lines, [
[
"Barang",
function (x) {
return (
"" +
esc(x.description) +
"" +
esc(x.specification || "-") +
""
);
},
],
[
"Qty",
function (x) {
return qty(x.qty) + " " + esc(x.unit || "");
},
],
[
"Dikembalikan",
function (x) {
return qty(x.returned_qty);
},
],
]) +
'
Barcode / Serial
' +
table(r.barcodes, [
[
"Barcode",
function (x) {
return esc(x.barcode);
},
],
[
"Serial",
function (x) {
return esc(x.serial_number || "-");
},
],
[
"Qty",
function (x) {
return qty(x.qty);
},
],
[
"Status",
function (x) {
return esc(x.status);
},
],
]) +
"",
);
modal("detailModal").show();
})
.fail(fail);
});
$(document).on("click", ".detail-return", function () {
$.getJSON(cfg.base + "sales/return_detail/" + $(this).data("id"))
.done(function (r) {
if (!r.status) return toast("error", r.message);
var x = r.return;
$("#detailBody").html(
'
' +
esc(x.return_no) +
"
" +
esc(x.customer_name) +
" · " +
esc(x.no_invoice) +
'
' +
esc(x.status) +
"" +
esc(x.reason) +
'
' +
table(r.lines, [
[
"Barang",
function (l) {
return esc(l.kode_barang + " - " + l.nama_barang);
},
],
[
"Qty",
function (l) {
return qty(l.qty);
},
],
[
"Kondisi",
function (l) {
return esc(l.physical_condition);
},
],
[
"Nilai",
function (l) {
return money(number(l.net_amount) + number(l.tax_amount));
},
],
]) +
'
Riwayat
' +
r.history
.map(function (h) {
return (
"
" +
esc(h.to_status) +
" · " +
esc(h.created_at) +
" · " +
esc(h.user_name || "-") +
"" +
esc(h.notes || "") +
"
"
);
})
.join("") +
"
",
);
modal("detailModal").show();
})
.fail(fail);
});
function accountSelect() {
return (
'
"
);
}
function addSource(amount) {
$("#paymentSources").append(
'
',
);
}
$(document).on("click", ".pay-invoice", function () {
$.getJSON(cfg.base + "sales/invoice_detail/" + $(this).data("id"))
.done(function (r) {
if (!r.status) return toast("error", r.message);
var i = r.invoice;
$("#paymentForm")[0].reset();
fileBag = new DataTransfer();
renderFiles();
$("#paymentForm [name=customer_id]").val(i.customer_id);
$("#paymentOrder").html(
'
Customer' +
esc(i.customer_name) +
"
" +
esc(i.no_invoice) +
"" +
table(r.lines, [
[
"Barang",
function (x) {
return esc(x.item_code + " - " + x.nama_item);
},
],
[
"Qty",
function (x) {
return qty(x.qty);
},
],
[
"Subtotal",
function (x) {
return money(x.subtotal);
},
],
]) +
'
Total invoice' +
money(i.total) +
"
Sudah dibayar" +
money(i.total_bayar) +
"
Sisa piutang" +
money(i.sisa_piutang) +
'
',
);
$("#paymentSources").empty();
addSource(i.sisa_piutang);
calcPayment();
modal("paymentModal").show();
})
.fail(fail);
});
$("#addPaymentSource").on("click", function () {
addSource(0);
});
$(document).on("click", ".remove-source", function () {
if ($(".pay-source").length > 1) $(this).closest(".pay-source").remove();
else $(this).closest(".pay-source").find("input").val("");
calcPayment();
});
$(document).on("input", ".pay-source-amount,.allocation", calcPayment);
function calcPayment() {
var a = 0,
s = 0;
$(".allocation").each(function () {
a += number(this.value);
});
$(".pay-source-amount").each(function () {
s += number(this.value);
});
$("#payAllocated").text(money(a));
$("#paySources").text(money(s));
$("#payDiff")
.text(money(s - a))
.toggleClass("text-danger", Math.abs(s - a) > 0.001);
}
$("#paymentForm").on("submit", function (e) {
e.preventDefault();
var f = new FormData(this),
sources = [],
alloc = {};
$(".pay-source").each(function () {
sources.push({
account_id: $(this).find(".pay-account").val(),
amount: $(this).find(".pay-source-amount").val(),
});
});
$(".allocation").each(function () {
alloc[$(this).data("id")] = this.value;
});
f.append("sources", JSON.stringify(sources));
f.append("allocations", JSON.stringify(alloc));
request(cfg.base + "sales/payment", f, true)
.done(function (r) {
if (!r.status) return toast("error", r.message);
modal("paymentModal").hide();
Swal.fire({
icon: "success",
title: r.message,
showCancelButton: true,
confirmButtonText: "Lihat Bukti Penerimaan",
cancelButtonText: "Tutup",
}).then(function (x) {
if (x.isConfirmed) {
$("#pdfModal iframe").attr(
"src",
cfg.base + "sales/receipt_pdf/" + r.result.payment_id,
);
modal("pdfModal").show();
} else location.reload();
});
})
.fail(fail);
});
$("#payFiles input").on("change", function () {
Array.from(this.files).forEach(function (f) {
fileBag.items.add(f);
});
this.files = fileBag.files;
renderFiles();
});
function renderFiles() {
$("#payFiles .file-thumb").remove();
Array.from(fileBag.files).forEach(function (f, i) {
var url = f.type.indexOf("image/") === 0 ? URL.createObjectURL(f) : "";
$("#payFiles").append(
'
' +
(url
? '

'
: '
') +
'
' +
esc(f.name.slice(0, 12)) +
"",
);
});
$("#payFiles input")[0].files = fileBag.files;
}
$(document).on("click", "#payFiles .file-thumb button", function () {
var next = new DataTransfer(),
remove = Number(this.dataset.file);
Array.from(fileBag.files).forEach(function (f, i) {
if (i !== remove) next.items.add(f);
});
fileBag = next;
renderFiles();
});
$(document).on("click", ".new-return", function () {
$.getJSON(cfg.base + "sales/delivery_detail/" + $(this).data("id"))
.done(function (r) {
if (!r.status) return toast("error", r.message);
$("#returnForm")[0].reset();
$("#returnForm [name=sales_delivery_id]").val(r.delivery.id);
$("#returnLines").html(
"
Barcode yang dapat diretur
" +
r.barcodes
.filter(function (b) {
return number(b.qty) - number(b.returned_qty) > 0;
})
.map(function (b) {
var left = number(b.qty) - number(b.returned_qty);
return (
'
'
);
})
.join(""),
);
modal("returnModal").show();
})
.fail(fail);
});
$("#returnForm").on("submit", function (e) {
e.preventDefault();
var rows = [];
$(".return-check:checked").each(function () {
var x = $(this).closest(".return-row");
rows.push({
sales_delivery_barcode_id: $(this).data("id"),
qty: x.find(".return-qty").val(),
physical_condition: x.find(".return-condition").val(),
notes: x.find(".return-note").val(),
});
});
if (!rows.length) return toast("warning", "Pilih minimal satu barcode.");
var f = new FormData(this);
f.append("rows", JSON.stringify(rows));
request(cfg.base + "sales/create_return", f, true)
.done(function (r) {
if (r.status) {
modal("returnModal").hide();
toast("success", r.message);
setTimeout(function () {
location.reload();
}, 600);
} else toast("error", r.message);
})
.fail(fail);
});
$(document).on("click", ".new-return", function () {
if (!$('#returnForm [name="attachments[]"]').length)
$("#returnLines").before(
'
Maksimal 10 file, masing-masing 5 MB.
',
);
});
$(document).on("click", ".detail-delivery", function () {
var id = $(this).data("id");
$.getJSON(cfg.base + "sales/delivery_detail/" + id).done(function (r) {
if (!r.status || r.delivery.status !== "draft") return;
setTimeout(function () {
if ($("#deliveryEditForm").length) return;
var d = r.delivery;
$("#detailBody").append(
'
',
);
$("#deliveryEditForm").on("submit", function (e) {
e.preventDefault();
request(
cfg.base + "sales/update_delivery/" + id,
new FormData(this),
true,
)
.done(function (x) {
if (x.status) {
toast("success", x.message);
modal("detailModal").hide();
setTimeout(function () {
location.reload();
}, 500);
} else toast("error", x.message);
})
.fail(fail);
});
}, 120);
});
});
function formatInput(v) {
return new Intl.NumberFormat("id-ID", {
minimumFractionDigits: 0,
maximumFractionDigits: 2,
}).format(Number(v) || 0);
}
$(document).on(
"blur",
".money-field,.pay-source-amount,.allocation",
function () {
this.value = formatInput(number(this.value));
},
);
if (cfg.sourceOrder) {
Promise.all([
loadCustomers(),
loadItems(),
loadWarehouses(),
$.getJSON(cfg.base + "salesdocuments/detail/" + cfg.sourceOrder),
])
.then(function (result) {
var source = result[3];
if (!source.status) return toast("error", source.message);
$("#invoiceForm").append(
'
',
);
$("#customerLookup").val(source.document.customer_id);
$("#invoiceForm [name=customer_id]").prop("disabled", false);
$(".line-builder").prepend(
'
Sumber ' +
esc(source.document.document_no) +
"
" +
source.lines
.map(function (x) {
return esc(
(x.kode_barang || "") +
" - " +
(x.nama_barang || x.description) +
" · " +
qty(x.qty),
);
})
.join("
") +
'Pilih kembali setiap barang, gudang, dan barcode agar stok yang dikirim tervalidasi.
',
);
modal("invoiceModal").show();
})
.catch(fail);
}
if (cfg.focus) {
setTimeout(function () {
$('.detail-invoice[data-id="' + Number(cfg.focus) + '"]').trigger(
"click",
);
}, 100);
}
})(jQuery);