1033 lines
30 KiB
JavaScript
1033 lines
30 KiB
JavaScript
(function ($) {
|
||
"use strict";
|
||
var cfg = window.SALES_PAGE,
|
||
cart = [],
|
||
selectedItem = null,
|
||
selectedBars = [],
|
||
fileBag = new DataTransfer();
|
||
function esc(v) {
|
||
return $("<div>")
|
||
.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(
|
||
'<option value="">Pilih customer</option>' +
|
||
r.rows
|
||
.map(function (x) {
|
||
return (
|
||
'<option value="' + x.id + '">' + esc(x.nama) + "</option>"
|
||
);
|
||
})
|
||
.join(""),
|
||
);
|
||
},
|
||
);
|
||
}
|
||
function loadItems() {
|
||
return $.getJSON(cfg.base + "sales/lookup_items", { q: "" }).done(
|
||
function (r) {
|
||
$("#itemLookup").html(
|
||
'<option value="">Cari/pilih barang</option>' +
|
||
r.rows
|
||
.map(function (x) {
|
||
return (
|
||
'<option value="' +
|
||
x.id +
|
||
'" data-row="' +
|
||
encodeURIComponent(JSON.stringify(x)) +
|
||
'">' +
|
||
esc(x.kode_barang + " - " + x.nama_barang) +
|
||
"</option>"
|
||
);
|
||
})
|
||
.join(""),
|
||
);
|
||
},
|
||
);
|
||
}
|
||
function loadWarehouses() {
|
||
return $.getJSON(cfg.base + "sales/warehouses").done(function (r) {
|
||
$("#warehouseLookup").html(
|
||
'<option value="">Pilih gudang</option>' +
|
||
r.rows
|
||
.map(function (x) {
|
||
return (
|
||
'<option value="' + x.id + '">' + esc(x.nama) + "</option>"
|
||
);
|
||
})
|
||
.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 (
|
||
'<label><input type="checkbox" class="bar-check" data-row="' +
|
||
encodeURIComponent(JSON.stringify(b)) +
|
||
'"> ' +
|
||
esc(b.barcode) +
|
||
" <small>(" +
|
||
available +
|
||
")</small></label>"
|
||
);
|
||
})
|
||
.join("")
|
||
: '<span class="text-danger">Tidak ada barcode tersedia.</span>',
|
||
);
|
||
});
|
||
});
|
||
$(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 (
|
||
'<tr data-index="' +
|
||
i +
|
||
'"><td><b>' +
|
||
esc(x.item_code + " - " + x.item_name) +
|
||
'</b><input class="form-control form-control-sm cart-spec" placeholder="Spesifikasi" value="' +
|
||
esc(x.specification) +
|
||
'"><small>' +
|
||
x.barcodes.length +
|
||
' barcode</small></td><td><input type="date" class="form-control form-control-sm cart-date" value="' +
|
||
esc(x.service_date) +
|
||
'"></td><td>' +
|
||
x.qty +
|
||
"</td><td>" +
|
||
money(x.unit_price) +
|
||
'</td><td><input class="form-control form-control-sm cart-discount" inputmode="decimal" value="' +
|
||
formatInput(x.discount_amount) +
|
||
'"></td><td><input type="number" class="form-control form-control-sm cart-tax" min="0" value="' +
|
||
x.tax_rate +
|
||
'"></td><td>' +
|
||
money(sub) +
|
||
'</td><td><button type="button" class="btn btn-sm btn-outline-danger cart-remove">×</button></td></tr>'
|
||
);
|
||
})
|
||
.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 (
|
||
'<div class="table-responsive"><table class="table table-sm"><thead><tr>' +
|
||
cols
|
||
.map(function (c) {
|
||
return "<th>" + c[0] + "</th>";
|
||
})
|
||
.join("") +
|
||
"</tr></thead><tbody>" +
|
||
rows
|
||
.map(function (r) {
|
||
return (
|
||
"<tr>" +
|
||
cols
|
||
.map(function (c) {
|
||
return "<td>" + c[1](r) + "</td>";
|
||
})
|
||
.join("") +
|
||
"</tr>"
|
||
);
|
||
})
|
||
.join("") +
|
||
"</tbody></table></div>"
|
||
);
|
||
}
|
||
$(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 =
|
||
'<div class="detail-grid"><div class="detail-card"><small>Customer</small><h5>' +
|
||
esc(i.customer_name) +
|
||
"</h5><p>" +
|
||
esc(i.customer_address || "-") +
|
||
'</p></div><div class="detail-card"><small>Invoice</small><h5>' +
|
||
esc(i.no_invoice) +
|
||
"</h5><p>" +
|
||
esc(i.period_start + " — " + i.period_end) +
|
||
" · " +
|
||
esc(
|
||
i.recognition_policy === "accrual"
|
||
? "Akrual"
|
||
: "Setelah pembayaran",
|
||
) +
|
||
'</p></div><div class="detail-card wide"><h6>Ringkasan Barang</h6>' +
|
||
table(r.lines, [
|
||
[
|
||
"Tanggal",
|
||
function (x) {
|
||
return esc(x.service_date);
|
||
},
|
||
],
|
||
[
|
||
"Barang",
|
||
function (x) {
|
||
return (
|
||
"<b>" +
|
||
esc(x.item_code + " - " + x.nama_item) +
|
||
"</b><small>" +
|
||
esc(x.specification || "-") +
|
||
"</small>"
|
||
);
|
||
},
|
||
],
|
||
[
|
||
"Qty",
|
||
function (x) {
|
||
return qty(x.qty);
|
||
},
|
||
],
|
||
[
|
||
"Harga",
|
||
function (x) {
|
||
return money(x.harga);
|
||
},
|
||
],
|
||
[
|
||
"Subtotal",
|
||
function (x) {
|
||
return money(x.subtotal);
|
||
},
|
||
],
|
||
]) +
|
||
'</div><div class="detail-card wide"><h6>Detail Barcode</h6>' +
|
||
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 (
|
||
"<b>" +
|
||
esc(x.barcode) +
|
||
"</b><small>" +
|
||
esc(x.serial_number || "-") +
|
||
"</small>"
|
||
);
|
||
},
|
||
],
|
||
[
|
||
"Qty",
|
||
function (x) {
|
||
return qty(x.qty);
|
||
},
|
||
],
|
||
[
|
||
"Gudang",
|
||
function (x) {
|
||
return esc(x.warehouse_name || "-");
|
||
},
|
||
],
|
||
]) +
|
||
'</div><div class="detail-card"><h6>Pembayaran</h6>' +
|
||
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);
|
||
},
|
||
],
|
||
]) +
|
||
'</div><div class="detail-card"><h6>Surat Jalan</h6>' +
|
||
r.deliveries
|
||
.map(function (x) {
|
||
return (
|
||
"<p><b>" +
|
||
esc(x.delivery_no) +
|
||
"</b> · " +
|
||
esc(x.delivery_date) +
|
||
" · " +
|
||
esc(x.status) +
|
||
"</p>"
|
||
);
|
||
})
|
||
.join("") +
|
||
"</div></div>";
|
||
$("#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(
|
||
'<div class="detail-grid"><div class="detail-card"><h5>' +
|
||
esc(d.delivery_no) +
|
||
"</h5><p>" +
|
||
esc(d.customer_name) +
|
||
"<br>" +
|
||
esc(d.customer_address || "-") +
|
||
'</p></div><div class="detail-card"><b>' +
|
||
esc(d.no_invoice || "-") +
|
||
"</b><p>" +
|
||
esc(d.delivery_date) +
|
||
" · " +
|
||
esc(d.warehouse_name || "-") +
|
||
" · " +
|
||
esc(d.status) +
|
||
'</p></div><div class="detail-card wide">' +
|
||
table(r.lines, [
|
||
[
|
||
"Barang",
|
||
function (x) {
|
||
return (
|
||
"<b>" +
|
||
esc(x.description) +
|
||
"</b><small>" +
|
||
esc(x.specification || "-") +
|
||
"</small>"
|
||
);
|
||
},
|
||
],
|
||
[
|
||
"Qty",
|
||
function (x) {
|
||
return qty(x.qty) + " " + esc(x.unit || "");
|
||
},
|
||
],
|
||
[
|
||
"Dikembalikan",
|
||
function (x) {
|
||
return qty(x.returned_qty);
|
||
},
|
||
],
|
||
]) +
|
||
'</div><div class="detail-card wide"><h6>Barcode / Serial</h6>' +
|
||
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);
|
||
},
|
||
],
|
||
]) +
|
||
"</div></div>",
|
||
);
|
||
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(
|
||
'<div class="detail-grid"><div class="detail-card"><h5>' +
|
||
esc(x.return_no) +
|
||
"</h5><p>" +
|
||
esc(x.customer_name) +
|
||
" · " +
|
||
esc(x.no_invoice) +
|
||
'</p></div><div class="detail-card"><b>' +
|
||
esc(x.status) +
|
||
"</b><p>" +
|
||
esc(x.reason) +
|
||
'</p></div><div class="detail-card wide">' +
|
||
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));
|
||
},
|
||
],
|
||
]) +
|
||
'</div><div class="detail-card wide"><h6>Riwayat</h6>' +
|
||
r.history
|
||
.map(function (h) {
|
||
return (
|
||
"<p><b>" +
|
||
esc(h.to_status) +
|
||
"</b> · " +
|
||
esc(h.created_at) +
|
||
" · " +
|
||
esc(h.user_name || "-") +
|
||
"<small>" +
|
||
esc(h.notes || "") +
|
||
"</small></p>"
|
||
);
|
||
})
|
||
.join("") +
|
||
"</div></div>",
|
||
);
|
||
modal("detailModal").show();
|
||
})
|
||
.fail(fail);
|
||
});
|
||
function accountSelect() {
|
||
return (
|
||
'<select class="form-select pay-account">' +
|
||
cfg.accounts
|
||
.map(function (a) {
|
||
return '<option value="' + a.id + '">' + esc(a.text) + "</option>";
|
||
})
|
||
.join("") +
|
||
"</select>"
|
||
);
|
||
}
|
||
function addSource(amount) {
|
||
$("#paymentSources").append(
|
||
'<div class="row g-2 pay-source mt-1"><div class="col-7">' +
|
||
accountSelect() +
|
||
'</div><div class="col-4"><input class="form-control pay-source-amount" inputmode="decimal" value="' +
|
||
formatInput(amount || 0) +
|
||
'"></div><div class="col-1"><button type="button" class="btn btn-outline-danger remove-source">×</button></div></div>',
|
||
);
|
||
}
|
||
$(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(
|
||
'<div class="detail-card"><small>Customer</small><h5>' +
|
||
esc(i.customer_name) +
|
||
"</h5><b>" +
|
||
esc(i.no_invoice) +
|
||
"</b>" +
|
||
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);
|
||
},
|
||
],
|
||
]) +
|
||
'<div class="payment-balance"><div><span>Total invoice</span><b>' +
|
||
money(i.total) +
|
||
"</b></div><div><span>Sudah dibayar</span><b>" +
|
||
money(i.total_bayar) +
|
||
"</b></div><div><span>Sisa piutang</span><b>" +
|
||
money(i.sisa_piutang) +
|
||
'</b></div><label class="mt-2">Nominal dialokasikan</label><input class="form-control allocation" data-id="' +
|
||
i.id +
|
||
'" value="' +
|
||
formatInput(i.sisa_piutang) +
|
||
'" inputmode="decimal"></div></div>',
|
||
);
|
||
$("#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(
|
||
'<div class="file-thumb">' +
|
||
(url
|
||
? '<img src="' + url + '">'
|
||
: '<i class="bi bi-file-pdf fs-2 text-danger"></i>') +
|
||
'<button type="button" data-file="' +
|
||
i +
|
||
'">×</button><small>' +
|
||
esc(f.name.slice(0, 12)) +
|
||
"</small></div>",
|
||
);
|
||
});
|
||
$("#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(
|
||
"<h6>Barcode yang dapat diretur</h6>" +
|
||
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 (
|
||
'<div class="return-row row g-2 align-items-center"><div class="col-md-4"><label><input type="checkbox" class="return-check" data-id="' +
|
||
b.id +
|
||
'"> <b>' +
|
||
esc(b.barcode) +
|
||
"</b><small>" +
|
||
esc(b.serial_number || "-") +
|
||
'</small></label></div><div class="col-md-2"><input class="form-control return-qty" value="' +
|
||
left +
|
||
'" max="' +
|
||
left +
|
||
'" inputmode="decimal"></div><div class="col-md-3"><select class="form-select return-condition"><option value="saleable">Layak jual</option><option value="quarantine">Karantina</option><option value="damaged">Rusak</option><option value="lost">Hilang</option></select></div><div class="col-md-3"><input class="form-control return-note" placeholder="Catatan kondisi"></div></div>'
|
||
);
|
||
})
|
||
.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(
|
||
'<div class="mt-3 return-evidence"><label>Bukti foto/PDF <span class="text-danger">*</span></label><input class="form-control" type="file" name="attachments[]" accept="image/*,.pdf,application/pdf" multiple required><small class="text-muted">Maksimal 10 file, masing-masing 5 MB.</small></div>',
|
||
);
|
||
});
|
||
$(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(
|
||
'<form id="deliveryEditForm" class="detail-card mt-3" enctype="multipart/form-data"><input type="hidden" name="' +
|
||
cfg.csrfName +
|
||
'" value="' +
|
||
cfg.csrfHash +
|
||
'"><h6>Lengkapi Data & Bukti Pengiriman</h6><div class="row g-2"><div class="col-md-3"><label>Tanggal kirim</label><input type="date" name="delivery_date" class="form-control" value="' +
|
||
esc(d.delivery_date) +
|
||
'"></div><div class="col-md-5"><label>Alamat pengiriman</label><input name="address" class="form-control" value="' +
|
||
esc(d.address || d.customer_address || "") +
|
||
'"></div><div class="col-md-2"><label>Pengemudi</label><input name="driver_name" class="form-control" value="' +
|
||
esc(d.driver_name || "") +
|
||
'"></div><div class="col-md-2"><label>Kendaraan</label><input name="vehicle_no" class="form-control" value="' +
|
||
esc(d.vehicle_no || "") +
|
||
'"></div><div class="col-md-3"><label>Kurir</label><input name="courier" class="form-control" value="' +
|
||
esc(d.courier || "") +
|
||
'"></div><div class="col-md-3"><label>Nomor resi</label><input name="tracking_no" class="form-control" value="' +
|
||
esc(d.tracking_no || "") +
|
||
'"></div><div class="col-md-3"><label>Penerima</label><input name="recipient_name" class="form-control" value="' +
|
||
esc(d.recipient_name || "") +
|
||
'"></div><div class="col-md-3"><label>Bukti kirim</label><input type="file" name="attachments[]" class="form-control" accept="image/*,.pdf" multiple></div><div class="col-12"><label>Catatan</label><textarea name="notes" class="form-control">' +
|
||
esc(d.notes || "") +
|
||
'</textarea></div><div class="col-12 text-end"><button class="btn btn-primary">Simpan Data Pengiriman</button></div></div></form>',
|
||
);
|
||
$("#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(
|
||
'<input type="hidden" name="source_sales_order_id" value="' +
|
||
cfg.sourceOrder +
|
||
'">',
|
||
);
|
||
$("#customerLookup").val(source.document.customer_id);
|
||
$("#invoiceForm [name=customer_id]").prop("disabled", false);
|
||
$(".line-builder").prepend(
|
||
'<div class="alert alert-info source-order-info"><b>Sumber ' +
|
||
esc(source.document.document_no) +
|
||
"</b><br>" +
|
||
source.lines
|
||
.map(function (x) {
|
||
return esc(
|
||
(x.kode_barang || "") +
|
||
" - " +
|
||
(x.nama_barang || x.description) +
|
||
" · " +
|
||
qty(x.qty),
|
||
);
|
||
})
|
||
.join("<br>") +
|
||
'<small class="d-block mt-1">Pilih kembali setiap barang, gudang, dan barcode agar stok yang dikirim tervalidasi.</small></div>',
|
||
);
|
||
modal("invoiceModal").show();
|
||
})
|
||
.catch(fail);
|
||
}
|
||
if (cfg.focus) {
|
||
setTimeout(function () {
|
||
$('.detail-invoice[data-id="' + Number(cfg.focus) + '"]').trigger(
|
||
"click",
|
||
);
|
||
}, 100);
|
||
}
|
||
})(jQuery);
|