1287 lines
40 KiB
JavaScript
1287 lines
40 KiB
JavaScript
(function ($) {
|
||
"use strict";
|
||
var cfg = window.INVOICE_PAGE || {},
|
||
lineTable = null,
|
||
selectedItem = null,
|
||
selectedBars = [],
|
||
batchLines = [];
|
||
function esc(v) {
|
||
return $("<div>")
|
||
.text(v == null ? "" : v)
|
||
.html();
|
||
}
|
||
function parseMoney(v) {
|
||
v = String(v == null ? "" : 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 qty(v) {
|
||
v = Number(v) || 0;
|
||
return new Intl.NumberFormat("id-ID", { maximumFractionDigits: 4 }).format(
|
||
v,
|
||
);
|
||
}
|
||
function fmt(v) {
|
||
return new Intl.NumberFormat("id-ID", {
|
||
minimumFractionDigits: 0,
|
||
maximumFractionDigits: 2,
|
||
}).format(Number(v) || 0);
|
||
}
|
||
function token() {
|
||
return (
|
||
window.crypto && crypto.randomUUID
|
||
? crypto.randomUUID()
|
||
: Date.now() + "-" + Math.random().toString(16).slice(2)
|
||
).replace(/[^a-zA-Z0-9-]/g, "");
|
||
}
|
||
function notify(message, icon) {
|
||
if (window.AppAlert) return AppAlert.toast(message, icon || "success");
|
||
return Swal.fire({
|
||
toast: true,
|
||
position: "top-end",
|
||
icon: icon || "success",
|
||
title: message,
|
||
showConfirmButton: false,
|
||
timer: 3200,
|
||
});
|
||
}
|
||
function error(xhr) {
|
||
var message = "Permintaan tidak dapat diproses.";
|
||
try {
|
||
var r =
|
||
typeof xhr === "object" && xhr.responseJSON
|
||
? xhr.responseJSON
|
||
: JSON.parse(xhr.responseText);
|
||
message = r.message || message;
|
||
} catch (e) {}
|
||
notify(message, "error");
|
||
}
|
||
function post(url, data) {
|
||
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,
|
||
dataType: "json",
|
||
processData: !(data instanceof FormData),
|
||
contentType:
|
||
data instanceof FormData
|
||
? false
|
||
: "application/x-www-form-urlencoded; charset=UTF-8",
|
||
});
|
||
}
|
||
function confirmAction(options) {
|
||
return window.AppAlert
|
||
? AppAlert.confirm(options)
|
||
: Swal.fire(
|
||
Object.assign(
|
||
{ icon: "warning", position: "center", showCancelButton: true },
|
||
options,
|
||
),
|
||
);
|
||
}
|
||
function statusLabel(v) {
|
||
var labels = {
|
||
draft: "Draft",
|
||
submitted: "Menunggu Approval",
|
||
approved: "Disetujui",
|
||
posted: "Posted",
|
||
unpaid: "Belum Lunas",
|
||
partial: "Sebagian",
|
||
paid: "Lunas",
|
||
not_delivered: "Belum Dikirim",
|
||
delivered: "Sudah Dikirim",
|
||
one_time: "Sekali Transaksi",
|
||
running: "Berjalan / Akumulasi",
|
||
};
|
||
return labels[v] || String(v || "-").replace(/_/g, " ");
|
||
}
|
||
function invoiceButtons(row) {
|
||
var p = cfg.permissions || {},
|
||
html =
|
||
'<div class="invoice-table-actions"><a class="btn btn-sm btn-outline-primary" href="' +
|
||
cfg.base +
|
||
"invoices/detail/" +
|
||
row.id +
|
||
'"><i class="bi bi-eye"></i> Detail</a>';
|
||
if (row.workflow_status === "draft" && p.update)
|
||
html +=
|
||
'<a class="btn btn-sm btn-outline-success" href="' +
|
||
cfg.base +
|
||
"invoices/edit/" +
|
||
row.id +
|
||
'"><i class="bi bi-pencil"></i> Kelola</a>';
|
||
if (
|
||
row.workflow_status === "draft" &&
|
||
Number(row.pending_delivery_count || 0) > 0 &&
|
||
p.update
|
||
)
|
||
html +=
|
||
'<button class="btn btn-sm btn-outline-primary row-generate-delivery" data-id="' +
|
||
row.id +
|
||
'"><i class="bi bi-truck"></i> Buat Surat Jalan</button>';
|
||
if (p.export)
|
||
html +=
|
||
'<button class="btn btn-sm btn-outline-danger open-invoice-pdf" data-id="' +
|
||
row.id +
|
||
'" data-url="' +
|
||
cfg.base +
|
||
"invoices/pdf/" +
|
||
row.id +
|
||
'"><i class="bi bi-file-pdf"></i></button>';
|
||
if (row.workflow_status === "draft" && p.submit)
|
||
html +=
|
||
'<button class="btn btn-sm btn-primary row-submit" data-id="' +
|
||
row.id +
|
||
'">Ajukan</button>';
|
||
if (row.workflow_status === "submitted" && p.approve)
|
||
html +=
|
||
'<button class="btn btn-sm btn-success row-approve" data-id="' +
|
||
row.id +
|
||
'">Setujui</button><button class="btn btn-sm btn-outline-danger row-reject" data-id="' +
|
||
row.id +
|
||
'">Tolak</button>';
|
||
if (
|
||
row.workflow_status === "posted" &&
|
||
["unpaid", "partial"].indexOf(row.status) >= 0 &&
|
||
p.post
|
||
)
|
||
html +=
|
||
'<a class="btn btn-sm btn-warning" href="' +
|
||
cfg.base +
|
||
"receivables?invoice_id=" +
|
||
row.id +
|
||
'">Bayar</a>';
|
||
if (row.workflow_status === "draft" && p.delete)
|
||
html +=
|
||
'<button class="btn btn-sm btn-outline-danger row-delete" data-id="' +
|
||
row.id +
|
||
'"><i class="bi bi-trash"></i></button>';
|
||
return html + "</div>";
|
||
}
|
||
|
||
function initList() {
|
||
if (!$("#invoiceTable").length) return;
|
||
var params = new URLSearchParams(location.search);
|
||
$("#filterWorkflow").val(params.get("workflow_status") || "");
|
||
$("#filterPayment").val(params.get("payment_status") || "");
|
||
var table = $("#invoiceTable").DataTable({
|
||
processing: true,
|
||
serverSide: true,
|
||
searchDelay: 350,
|
||
pageLength: 25,
|
||
order: [],
|
||
responsive: false,
|
||
ajax: function (data, callback) {
|
||
data.workflow_status = $("#filterWorkflow").val();
|
||
data.payment_status = $("#filterPayment").val();
|
||
data.date_from = $("#filterFrom").val();
|
||
data.date_to = $("#filterTo").val();
|
||
$.ajax({
|
||
url: cfg.base + "invoices/data",
|
||
data: data,
|
||
dataType: "json",
|
||
})
|
||
.done(function (r) {
|
||
callback(r);
|
||
})
|
||
.fail(function (x) {
|
||
error(x);
|
||
callback({
|
||
draw: data.draw,
|
||
recordsTotal: 0,
|
||
recordsFiltered: 0,
|
||
data: [],
|
||
});
|
||
});
|
||
},
|
||
columns: [
|
||
{
|
||
data: null,
|
||
render: function (d, t, r) {
|
||
return (
|
||
"<b>" +
|
||
esc(r.no_invoice) +
|
||
"</b><small>" +
|
||
esc(r.customer_name) +
|
||
"</small>"
|
||
);
|
||
},
|
||
},
|
||
{
|
||
data: "jatuh_tempo",
|
||
render: function (v) {
|
||
return esc(v || "-");
|
||
},
|
||
},
|
||
{
|
||
data: null,
|
||
render: function (d,t,r) {
|
||
if(r.invoice_purpose==="loan")return "Tagihan Pinjaman";
|
||
if(r.invoice_purpose==="savings")return "Tagihan Tabungan";
|
||
if(r.invoice_purpose==="mixed")return "Penjualan + Tabungan";
|
||
return esc(
|
||
r.recognition_policy === "accrual"
|
||
? "Diakui saat Posted"
|
||
: "Proporsional Setelah Dibayar",
|
||
);
|
||
},
|
||
},
|
||
{
|
||
data: "total",
|
||
className: "text-end",
|
||
render: function (v) {
|
||
return money(v);
|
||
},
|
||
},
|
||
{
|
||
data: "total_bayar",
|
||
className: "text-end",
|
||
render: function (v) {
|
||
return money(v);
|
||
},
|
||
},
|
||
{
|
||
data: "sisa_piutang",
|
||
className: "text-end",
|
||
render: function (v) {
|
||
return "<b>" + money(v) + "</b>";
|
||
},
|
||
},
|
||
{
|
||
data: null,
|
||
render: function (d, t, r) {
|
||
return (
|
||
'<span class="invoice-status status-' +
|
||
esc(r.workflow_status) +
|
||
'">' +
|
||
statusLabel(r.workflow_status) +
|
||
"</span><small>" +
|
||
statusLabel(r.status) +
|
||
"</small>"
|
||
);
|
||
},
|
||
},
|
||
{
|
||
data: null,
|
||
render: function (d,t,r) {
|
||
return Number(r.inventory_line_count||0)>0?statusLabel(r.delivery_status):"Tidak Memerlukan";
|
||
},
|
||
},
|
||
{ data: "creator_name", defaultContent: "-" },
|
||
{
|
||
data: null,
|
||
orderable: false,
|
||
searchable: false,
|
||
render: function (d, t, r) {
|
||
return invoiceButtons(r);
|
||
},
|
||
},
|
||
],
|
||
language: {
|
||
processing: "Memuat invoice...",
|
||
search: "Cari:",
|
||
lengthMenu: "Tampilkan _MENU_",
|
||
info: "_START_–_END_ dari _TOTAL_ invoice",
|
||
infoEmpty: "Belum ada invoice",
|
||
zeroRecords: "Invoice tidak ditemukan",
|
||
paginate: { previous: "Sebelumnya", next: "Berikutnya" },
|
||
},
|
||
});
|
||
$("#applyInvoiceFilter").on("click", function () {
|
||
table.ajax.reload();
|
||
});
|
||
$("#resetInvoiceFilter").on("click", function () {
|
||
$("#invoiceFilters select,#invoiceFilters input").val("");
|
||
table.search("").ajax.reload();
|
||
});
|
||
$("#toggleInvoiceFilters").on("click", function () {
|
||
$("#invoiceFilters").toggleClass("open");
|
||
});
|
||
}
|
||
|
||
function select2Ajax(selector, url, placeholder, extra) {
|
||
var element = $(selector);
|
||
if (!element.length) return;
|
||
var options = {
|
||
width: "100%",
|
||
placeholder: placeholder,
|
||
allowClear: true,
|
||
minimumInputLength: 0,
|
||
ajax: {
|
||
url: cfg.base + url,
|
||
dataType: "json",
|
||
delay: 300,
|
||
data: function (p) {
|
||
return Object.assign(
|
||
{ q: p.term || "", page: p.page || 1 },
|
||
extra ? extra() : {},
|
||
);
|
||
},
|
||
processResults: function (r) {
|
||
return {
|
||
results: r.results || [],
|
||
pagination: r.pagination || { more: false },
|
||
};
|
||
},
|
||
cache: true,
|
||
},
|
||
};
|
||
if (element.closest(".modal").length)
|
||
options.dropdownParent = element.closest(".modal");
|
||
element.select2(options);
|
||
}
|
||
function initEditor() {
|
||
if (cfg.mode === "list") return;
|
||
select2Ajax(".ajax-customer", "invoices/lookup_customers", "Cari customer");
|
||
select2Ajax(
|
||
".ajax-account",
|
||
"invoices/lookup_accounts",
|
||
"Pilih akun pendapatan",
|
||
);
|
||
select2Ajax(
|
||
".ajax-liability-account",
|
||
"invoices/lookup_accounts",
|
||
"Pilih akun simpanan Passiva",
|
||
function () { return { scope: "liability" }; },
|
||
);
|
||
select2Ajax(
|
||
".ajax-item",
|
||
"invoices/lookup_items",
|
||
"Cari kode atau nama barang",
|
||
);
|
||
select2Ajax(
|
||
".ajax-warehouse",
|
||
"invoices/lookup_warehouses",
|
||
"Pilih gudang",
|
||
);
|
||
select2Ajax(
|
||
".ajax-barcode",
|
||
"invoices/lookup_barcodes",
|
||
"Cari barcode atau serial",
|
||
function () {
|
||
return {
|
||
item_id: $(".ajax-item").val() || 0,
|
||
warehouse_id: $(".ajax-warehouse").val() || 0,
|
||
};
|
||
},
|
||
);
|
||
$(".ajax-customer")
|
||
.on("select2:select", function (e) {
|
||
showCustomerPreview(e.params.data);
|
||
})
|
||
.on("select2:clear", function () {
|
||
$("#selectedCustomerPreview").addClass("d-none").empty();
|
||
});
|
||
if (cfg.sourceOrder) {
|
||
$.getJSON(cfg.base + "salesdocuments/detail/" + cfg.sourceOrder)
|
||
.done(function (r) {
|
||
if (!r.status) return notify(r.message, "error");
|
||
var c = r.document;
|
||
var option = new Option(c.customer_name, c.customer_id, true, true);
|
||
$(".ajax-customer").append(option).trigger("change");
|
||
showCustomerPreview({
|
||
text: c.customer_name,
|
||
alamat: c.customer_address || c.address || "",
|
||
});
|
||
notify("Customer diambil dari Sales Order " + c.document_no, "info");
|
||
})
|
||
.fail(error);
|
||
}
|
||
$("#invoiceHeaderForm").on("submit", function (e) {
|
||
e.preventDefault();
|
||
var form = this,
|
||
button = $(form).find(":submit");
|
||
button
|
||
.prop("disabled", true)
|
||
.html(
|
||
'<span class="spinner-border spinner-border-sm"></span> Menyimpan...',
|
||
);
|
||
post(cfg.base + "invoices/create_header", new FormData(form))
|
||
.done(function (r) {
|
||
if (!r.status) {
|
||
notify(r.message, "error");
|
||
return;
|
||
}
|
||
notify(r.message, "success");
|
||
location.href = cfg.base + "invoices/edit/" + r.result;
|
||
})
|
||
.fail(error)
|
||
.always(function () {
|
||
button
|
||
.prop("disabled", false)
|
||
.html('<i class="bi bi-save"></i> Simpan Invoice & Lanjut');
|
||
});
|
||
});
|
||
if (!cfg.invoiceId) return;
|
||
initLineTable();
|
||
bindLineForm();
|
||
bindEditorActions();
|
||
}
|
||
|
||
function showCustomerPreview(customer) {
|
||
if (!customer) return;
|
||
$("#selectedCustomerPreview")
|
||
.removeClass("d-none")
|
||
.html(
|
||
"<b>" +
|
||
esc(customer.text || customer.nama || "Customer") +
|
||
"</b><small>" +
|
||
esc(customer.alamat || "Alamat belum tersedia") +
|
||
"</small>",
|
||
);
|
||
}
|
||
|
||
function bindLineForm() {
|
||
var form = $("#invoiceLineForm");
|
||
if (!form.length) return;
|
||
$("input[name=line_type]").on("change", function () {
|
||
var type = this.value,
|
||
inventory = type === "inventory",
|
||
savings = type === "savings";
|
||
$(".inventory-only").toggleClass("d-none", !inventory);
|
||
$(".noninventory-only").toggleClass("d-none", inventory);
|
||
$(".savings-only").toggleClass("d-none", !savings);
|
||
$(".sales-account-field,.sales-tax-field,.service-unit-field,.line-qty-field").toggleClass("d-none", savings);
|
||
$(".invoice-batch-common").toggleClass("d-none", savings);
|
||
$("#nonInventoryDescriptionLabel").html(savings ? 'Deskripsi <span class="text-danger">*</span>' : 'Nama Jasa / Deskripsi <span class="text-danger">*</span>');
|
||
$("#lineValueLabel").text(savings ? "Nominal" : "Harga Satuan / Nominal");
|
||
if(savings){form.find("[name=tax_rate]").val("0");form.find("[name=qty]").val("1");form.find("[name=unit]").val("Tabungan");}
|
||
else if(!inventory)form.find("[name=unit]").val("Jasa");
|
||
selectedItem = null;
|
||
selectedBars = [];
|
||
$(".ajax-item,.ajax-warehouse,.ajax-barcode").val(null).trigger("change");
|
||
renderBars();
|
||
calcBatch();
|
||
});
|
||
$(".ajax-item").on("select2:select", function (e) {
|
||
selectedItem = e.params.data;
|
||
form.find("[name=unit_price]").val(fmt(selectedItem.harga_jual));
|
||
$("#trackingHint").text(
|
||
(selectedItem.tracking_type || "UNIT") +
|
||
" - " +
|
||
(selectedItem.unit || "-"),
|
||
);
|
||
selectedBars = [];
|
||
$(".ajax-barcode").val(null).trigger("change");
|
||
renderBars();
|
||
calcBatch();
|
||
});
|
||
$(".ajax-warehouse").on("change", function () {
|
||
selectedBars = [];
|
||
$(".ajax-barcode").val(null).trigger("change");
|
||
renderBars();
|
||
calcBatch();
|
||
});
|
||
$(".ajax-barcode")
|
||
.on("select2:select", function (e) {
|
||
addBar(e.params.data);
|
||
})
|
||
.on("select2:unselect", function (e) {
|
||
selectedBars = selectedBars.filter(function (x) {
|
||
return String(x.id) !== String(e.params.data.id);
|
||
});
|
||
renderBars();
|
||
syncQtyFromBars();
|
||
});
|
||
$("#scanBarcodeButton").on("click", scanExact);
|
||
$("#barcodeScan").on("keydown", function (e) {
|
||
if (e.key === "Enter") {
|
||
e.preventDefault();
|
||
scanExact();
|
||
}
|
||
});
|
||
$(document).on("input", ".selected-barcode-qty", function () {
|
||
var id = $(this).data("id"),
|
||
bar = selectedBars.find(function (x) {
|
||
return String(x.id) === String(id);
|
||
});
|
||
if (bar)
|
||
bar.qty = Math.min(parseMoney(this.value), Number(bar.available));
|
||
syncQtyFromBars();
|
||
});
|
||
$(document).on("click", ".remove-selected-barcode", function () {
|
||
var id = $(this).data("id");
|
||
selectedBars = selectedBars.filter(function (x) {
|
||
return String(x.id) !== String(id);
|
||
});
|
||
$('.ajax-barcode option[value="' + id + '"]').prop("selected", false);
|
||
$(".ajax-barcode").trigger("change.select2");
|
||
renderBars();
|
||
syncQtyFromBars();
|
||
});
|
||
$(document).on("click", ".remove-batch-line", function () {
|
||
batchLines.splice(Number($(this).data("index")), 1);
|
||
renderBatchLines();
|
||
});
|
||
$("#stageInvoiceLine").on("click", stageCurrentLine);
|
||
form.on(
|
||
"input",
|
||
"[name=qty],[name=unit_price],[name=tax_rate],[name=batch_discount]",
|
||
calcBatch,
|
||
);
|
||
form.on("submit", function (e) {
|
||
e.preventDefault();
|
||
if (!batchLines.length)
|
||
return notify("Masukkan minimal satu item ke daftar.", "warning");
|
||
var date = form.find("[name=service_date]").val(),
|
||
discount = parseMoney(form.find("[name=batch_discount]").val()),
|
||
gross = batchLines.reduce(function (s, x) {
|
||
return s + (x.line_type === "savings" ? 0 : x.qty * x.unit_price);
|
||
}, 0);
|
||
if (!date) return notify("Tanggal transaksi wajib diisi.", "warning");
|
||
if (discount < 0 || discount > gross)
|
||
return notify(
|
||
"Diskon tidak boleh melebihi nilai bruto seluruh item.",
|
||
"warning",
|
||
);
|
||
var rows = batchLines.map(function (line) {
|
||
return {
|
||
line_type: line.line_type,
|
||
account_id: line.account_id,
|
||
counter_account_id: line.counter_account_id || "",
|
||
item_id: line.item_id || "",
|
||
warehouse_id: line.warehouse_id || "",
|
||
description: line.description || "",
|
||
unit: line.unit || "",
|
||
qty: line.qty,
|
||
unit_price: line.unit_price,
|
||
tax_rate: line.tax_rate,
|
||
notes: line.notes || "",
|
||
barcodes: line.barcodes || [],
|
||
};
|
||
});
|
||
var data = new FormData();
|
||
data.append("service_date", date);
|
||
data.append("batch_discount", discount);
|
||
data.append("idempotency_key", $("#lineIdempotency").val());
|
||
data.append("rows", JSON.stringify(rows));
|
||
var button = form.find(".batch-submit-button");
|
||
button
|
||
.prop("disabled", true)
|
||
.html(
|
||
'<span class="spinner-border spinner-border-sm"></span> Menyimpan...',
|
||
);
|
||
post(cfg.base + "invoices/add_lines/" + cfg.invoiceId, data)
|
||
.done(function (r) {
|
||
if (!r.status) return notify(r.message, "error");
|
||
notify(r.message, "success");
|
||
batchLines = [];
|
||
form.find("[name=batch_discount]").val("0");
|
||
$("#lineIdempotency").val(token());
|
||
resetComposer();
|
||
renderBatchLines();
|
||
if (lineTable) lineTable.ajax.reload(null, false);
|
||
bootstrap.Modal.getOrCreateInstance(
|
||
document.getElementById("invoiceItemModal"),
|
||
).hide();
|
||
})
|
||
.fail(error)
|
||
.always(function () {
|
||
renderBatchLines();
|
||
});
|
||
});
|
||
$(".money-input").on("blur", function () {
|
||
this.value = fmt(parseMoney(this.value));
|
||
});
|
||
renderBatchLines();
|
||
}
|
||
function stageCurrentLine() {
|
||
var form = $("#invoiceLineForm"),
|
||
type = form.find("[name=line_type]:checked").val(),
|
||
account = type === "savings" ? form.find("[name=savings_account_id]") : form.find("[name=revenue_account_id]"),
|
||
line = {
|
||
line_type: type,
|
||
account_id: Number(account.val()),
|
||
account_label: account.find(":selected").text(),
|
||
counter_account_id: 0,
|
||
counter_account_label: "",
|
||
qty: type === "savings" ? 1 : parseMoney(form.find("[name=qty]").val()),
|
||
unit_price: parseMoney(form.find("[name=unit_price]").val()),
|
||
tax_rate: Number(form.find("[name=tax_rate]").val()) || 0,
|
||
notes: String(form.find("[name=notes]").val() || "").trim(),
|
||
};
|
||
if (!line.account_id)
|
||
return notify(type === "savings" ? "Pilih akun simpanan customer terlebih dahulu." : "Pilih akun pendapatan terlebih dahulu.", "warning");
|
||
line.account_display = line.line_type === "loan" ? line.account_label + " ← " + line.counter_account_label : line.account_label;
|
||
if (!form.find("[name=service_date]").val())
|
||
return notify("Tanggal transaksi wajib diisi.", "warning");
|
||
if (line.qty <= 0) return notify("Qty harus lebih dari nol.", "warning");
|
||
if (line.unit_price < 0)
|
||
return notify("Harga satuan tidak valid.", "warning");
|
||
if (line.tax_rate < 0) return notify("Pajak tidak valid.", "warning");
|
||
if (batchLines.length >= 100)
|
||
return notify("Maksimal 100 item dalam satu kali penambahan.", "warning");
|
||
if (type === "inventory") {
|
||
if (!selectedItem || !form.find("[name=warehouse_id]").val())
|
||
return notify("Pilih gudang dan barang.", "warning");
|
||
if (!selectedBars.length)
|
||
return notify("Pilih minimal satu barcode barang.", "warning");
|
||
var barcodeQty = selectedBars.reduce(function (s, x) {
|
||
return s + Number(x.qty || 0);
|
||
}, 0);
|
||
if (Math.abs(barcodeQty - line.qty) > 0.0001)
|
||
return notify(
|
||
"Total qty barcode harus sama dengan qty barang.",
|
||
"warning",
|
||
);
|
||
line.item_id = Number(selectedItem.id);
|
||
line.warehouse_id = Number(form.find("[name=warehouse_id]").val());
|
||
line.description =
|
||
selectedItem.nama_barang || selectedItem.text || "Barang";
|
||
line.unit = selectedItem.unit || "";
|
||
line.label =
|
||
(selectedItem.kode_detail ? selectedItem.kode_detail + " - " : "") +
|
||
(selectedItem.nama_barang || selectedItem.text || "Barang");
|
||
line.barcodes = selectedBars.map(function (x) {
|
||
return {
|
||
id: Number(x.id),
|
||
qty: Number(x.qty),
|
||
barcode: x.barcode,
|
||
serial_number: x.serial_number || "",
|
||
};
|
||
});
|
||
} else {
|
||
line.description = String(
|
||
form.find("[name=description]").val() || "",
|
||
).trim();
|
||
line.unit = String(form.find("[name=unit]").val() || "Jasa").trim();
|
||
line.label = line.description;
|
||
if (!line.description)
|
||
return notify("Nama jasa atau deskripsi wajib diisi.", "warning");
|
||
line.barcodes = [];
|
||
}
|
||
if(type === "savings")line.tax_rate=0;
|
||
if(Number(form.find("[name=editing_line_id]").val())>0)return updateExistingLine(line);
|
||
batchLines.push(line);
|
||
resetComposer();
|
||
renderBatchLines();
|
||
notify("Item dimasukkan ke daftar.", "success");
|
||
}
|
||
function updateExistingLine(line){
|
||
var form=$("#invoiceLineForm"),id=Number(form.find("[name=editing_line_id]").val()),button=$("#stageInvoiceLine"),data={service_date:form.find("[name=service_date]").val(),description:line.description,unit:line.unit,qty:line.qty,unit_price:line.unit_price,discount_amount:parseMoney(form.find("[name=batch_discount]").val()),tax_rate:line.tax_rate,notes:line.notes,account_id:line.account_id,counter_account_id:line.counter_account_id||""};
|
||
button.prop("disabled",true).html('<span class="spinner-border spinner-border-sm"></span> Menyimpan...');
|
||
post(cfg.base+"invoices/update_line/"+cfg.invoiceId+"/"+id,data).done(function(r){if(!r.status)return notify(r.message,"error");notify(r.message,"success");bootstrap.Modal.getOrCreateInstance(document.getElementById("invoiceItemModal")).hide();if(lineTable)lineTable.ajax.reload(null,false)}).fail(error).always(function(){button.prop("disabled",false).html('<i class="bi bi-save"></i> Simpan Perubahan')});
|
||
}
|
||
function editExistingLine(row){
|
||
var form=$("#invoiceLineForm"),savings=row.line_type==="savings";
|
||
batchLines=[];form.find("[name=editing_line_id]").val(row.id);$("#invoiceItemModal").addClass("editing-line");$("#invoiceItemModal .modal-title").text("Edit Item Nonbarang");$("#invoiceItemModal .modal-header small").text("Perubahan hanya berlaku pada item Draft yang dipilih.");
|
||
form.find('[name=line_type][value="'+(savings?'savings':'service')+'"]').prop("checked",true).trigger("change");
|
||
if(row.line_type==="savings")setSelect(".ajax-liability-account",row.account_id,(row.kode_akun||"")+" - "+(row.nama_akun||""));
|
||
else setSelect(".ajax-account",row.account_id,(row.kode_akun||"")+" - "+(row.nama_akun||""));
|
||
form.find("[name=service_date]").val(row.service_date);form.find("[name=description]").val(row.nama_item);form.find("[name=unit]").val(row.unit||"Jasa");form.find("[name=qty]").val(fmt(row.qty));form.find("[name=unit_price]").val(fmt(row.harga));form.find("[name=batch_discount]").val(fmt(row.discount_amount));form.find("[name=tax_rate]").val(row.tax_rate||0);form.find("[name=notes]").val(row.keterangan||"");$("#stageInvoiceLine").html('<i class="bi bi-save"></i> Simpan Perubahan');renderBatchLines();bootstrap.Modal.getOrCreateInstance(document.getElementById("invoiceItemModal")).show();
|
||
}
|
||
function scanExact() {
|
||
var code = $("#barcodeScan").val().trim();
|
||
if (!code) return notify("Masukkan atau scan barcode.", "warning");
|
||
$.getJSON(cfg.base + "invoices/lookup_barcodes", { q: code, exact: 1 })
|
||
.done(function (r) {
|
||
if (!r.results || r.results.length !== 1)
|
||
return notify(
|
||
r.results && r.results.length > 1
|
||
? "Barcode tidak unik."
|
||
: "Barcode tidak tersedia.",
|
||
"warning",
|
||
);
|
||
var b = r.results[0];
|
||
setSelect(".ajax-warehouse", b.warehouse_id, b.warehouse_name);
|
||
setSelect(
|
||
".ajax-item",
|
||
b.item_id,
|
||
b.kode_detail + " - " + b.nama_barang,
|
||
);
|
||
selectedItem = {
|
||
id: b.item_id,
|
||
kode_detail: b.kode_detail,
|
||
nama_barang: b.nama_barang,
|
||
harga_jual: b.harga_jual,
|
||
tracking_type: b.tracking_type,
|
||
unit: b.unit,
|
||
};
|
||
$("#invoiceLineForm [name=unit_price]").val(fmt(b.harga_jual));
|
||
$("#trackingHint").text(
|
||
(b.tracking_type || "UNIT") + " - " + (b.unit || "-"),
|
||
);
|
||
addBar(b);
|
||
$("#barcodeScan").val("").focus();
|
||
calcBatch();
|
||
})
|
||
.fail(error);
|
||
}
|
||
$("#invoiceItemModal").on("shown.bs.modal", function () {
|
||
var date = $("#invoiceLineForm [name=service_date]");
|
||
if (date.length && !date.val()) date.val(date[0].defaultValue);
|
||
setTimeout(function () {
|
||
$("#barcodeScan").trigger("focus");
|
||
}, 100);
|
||
});
|
||
$("#invoiceItemModal").on("hidden.bs.modal", function () {
|
||
var form=$("#invoiceLineForm");if(!form.length||!form.find("[name=editing_line_id]").val())return;
|
||
form.find("[name=editing_line_id]").val("");$(this).removeClass("editing-line");$(this).find(".modal-title").text("Tambah Beberapa Item Invoice");$(this).find(".modal-header small").text("Susun maksimal 100 barang, jasa, atau tabungan dalam satu kali penyimpanan.");$("#stageInvoiceLine").html('<i class="bi bi-plus-lg"></i> Masukkan ke Daftar');batchLines=[];form.find("[name=batch_discount]").val("0");form.find('[name=line_type][value="inventory"]').prop("checked",true).trigger("change");resetComposer();renderBatchLines();
|
||
});
|
||
function setSelect(selector, value, text) {
|
||
var select = $(selector),
|
||
option = select.find('option[value="' + value + '"]');
|
||
if (!option.length) select.append(new Option(text, value, true, true));
|
||
else option.prop("selected", true);
|
||
select.trigger("change.select2");
|
||
}
|
||
function addBar(b) {
|
||
if (
|
||
selectedBars.some(function (x) {
|
||
return String(x.id) === String(b.id);
|
||
})
|
||
)
|
||
return notify("Barcode sudah dipilih.", "warning");
|
||
var unit =
|
||
(b.tracking_type || (selectedItem && selectedItem.tracking_type)) ===
|
||
"UNIT";
|
||
selectedBars.push({
|
||
id: b.id,
|
||
barcode: b.barcode,
|
||
serial_number: b.serial_number || "",
|
||
available: Number(b.available) || 1,
|
||
qty: unit ? 1 : Number(b.available) || 1,
|
||
tracking_type:
|
||
b.tracking_type ||
|
||
(selectedItem && selectedItem.tracking_type) ||
|
||
"UNIT",
|
||
});
|
||
renderBars();
|
||
syncQtyFromBars();
|
||
}
|
||
function renderBars() {
|
||
$("#selectedBarcodes").html(
|
||
selectedBars
|
||
.map(function (b) {
|
||
var readonly = b.tracking_type === "UNIT" ? "readonly" : "";
|
||
return (
|
||
'<div class="selected-barcode"><div><b>' +
|
||
esc(b.barcode) +
|
||
"</b><small>" +
|
||
esc(b.serial_number || "Tanpa serial") +
|
||
" · tersedia " +
|
||
qty(b.available) +
|
||
'</small></div><input class="form-control form-control-sm selected-barcode-qty" data-id="' +
|
||
b.id +
|
||
'" inputmode="decimal" value="' +
|
||
fmt(b.qty) +
|
||
'" ' +
|
||
readonly +
|
||
'><button type="button" class="remove-selected-barcode" data-id="' +
|
||
b.id +
|
||
'"><i class="bi bi-x-lg"></i></button></div>'
|
||
);
|
||
})
|
||
.join(""),
|
||
);
|
||
}
|
||
function syncQtyFromBars() {
|
||
if (selectedBars.length)
|
||
$("#invoiceLineForm [name=qty]").val(
|
||
fmt(
|
||
selectedBars.reduce(function (s, x) {
|
||
return s + Number(x.qty || 0);
|
||
}, 0),
|
||
),
|
||
);
|
||
calcBatch();
|
||
}
|
||
function resetComposer() {
|
||
var form = $("#invoiceLineForm");
|
||
form.find("[name=qty]").val("1");
|
||
form.find("[name=unit_price]").val("");
|
||
form.find("[name=tax_rate]").val("0");
|
||
form.find("[name=notes],[name=description]").val("");
|
||
form.find("[name=unit]").val(form.find("[name=line_type]:checked").val()==="savings"?"Tabungan":"Jasa");
|
||
$(".ajax-item,.ajax-warehouse,.ajax-barcode").val(null).trigger("change");
|
||
selectedItem = null;
|
||
selectedBars = [];
|
||
$("#trackingHint").text("Pilih gudang dan barang terlebih dahulu.");
|
||
renderBars();
|
||
calcBatch();
|
||
}
|
||
function allocatedDiscount(
|
||
line,
|
||
index,
|
||
grossTotal,
|
||
discount,
|
||
lastPositive,
|
||
used,
|
||
) {
|
||
if (discount <= 0 || grossTotal <= 0) return 0;
|
||
if (index === lastPositive) return Math.max(0, discount - used);
|
||
return (
|
||
Math.round(
|
||
((discount * (line.qty * line.unit_price)) / grossTotal) * 100,
|
||
) / 100
|
||
);
|
||
}
|
||
function calcBatch() {
|
||
var form = $("#invoiceLineForm");
|
||
if (!form.length) return;
|
||
var displayGross = batchLines.reduce(function (s, x) {
|
||
return s + x.qty * x.unit_price;
|
||
}, 0),
|
||
gross = batchLines.reduce(function (s, x) {
|
||
return s + (x.line_type === "savings" ? 0 : x.qty * x.unit_price);
|
||
}, 0),
|
||
discount = parseMoney(form.find("[name=batch_discount]").val()),
|
||
last = -1,
|
||
used = 0,
|
||
total = 0;
|
||
batchLines.forEach(function (x, i) {
|
||
if (x.line_type !== "savings" && x.qty * x.unit_price > 0) last = i;
|
||
});
|
||
batchLines.forEach(function (x, i) {
|
||
var share = x.line_type === "savings" ? 0 : allocatedDiscount(x, i, gross, discount, last, used);
|
||
used += share;
|
||
total +=
|
||
Math.max(0, x.qty * x.unit_price - share) * (1 + x.tax_rate / 100);
|
||
});
|
||
$("#batchGrossTotal").text(money(displayGross));
|
||
$("#batchDiscountTotal").text(money(discount));
|
||
$("#lineLiveTotal").text(money(total));
|
||
var valid = batchLines.length > 0 && discount >= 0 && discount <= gross;
|
||
$(".batch-submit-button")
|
||
.prop("disabled", !valid)
|
||
.html('<i class="bi bi-save"></i> Simpan ' + batchLines.length + " Item");
|
||
}
|
||
function renderBatchLines() {
|
||
var body = $("#invoiceBatchLines");
|
||
if (!body.length) return;
|
||
$("#batchLineCount").text(batchLines.length + " item");
|
||
if (!batchLines.length)
|
||
body.html(
|
||
'<tr class="batch-empty-row"><td colspan="8">Belum ada item. Isi form di atas lalu klik “Masukkan ke Daftar”.</td></tr>',
|
||
);
|
||
else
|
||
body.html(
|
||
batchLines
|
||
.map(function (line, index) {
|
||
var detail = line.notes
|
||
? "<small>" + esc(line.notes) + "</small>"
|
||
: "";
|
||
return (
|
||
"<tr><td><b>" +
|
||
esc(line.label) +
|
||
"</b>" +
|
||
detail +
|
||
"</td><td>" +
|
||
esc(line.account_display || line.account_label || "-") +
|
||
"</td><td>" +
|
||
({inventory:"Barang Gudang",service:"Jasa / Nonbarang",savings:"Tabungan Customer"}[line.line_type] || line.line_type) +
|
||
'</td><td class="text-end">' +
|
||
qty(line.qty) +
|
||
'</td><td class="text-end">' +
|
||
money(line.unit_price) +
|
||
'</td><td class="text-end">' +
|
||
fmt(line.tax_rate) +
|
||
'%</td><td class="text-end"><b>' +
|
||
money(line.qty * line.unit_price) +
|
||
'</b></td><td class="text-center"><button type="button" class="btn btn-sm btn-outline-danger remove-batch-line" data-index="' +
|
||
index +
|
||
'"><i class="bi bi-x-lg"></i></button></td></tr>'
|
||
);
|
||
})
|
||
.join(""),
|
||
);
|
||
calcBatch();
|
||
}
|
||
|
||
function initLineTable() {
|
||
lineTable = $("#invoiceLinesTable").DataTable({
|
||
processing: true,
|
||
serverSide: true,
|
||
searchDelay: 300,
|
||
pageLength: 25,
|
||
order: [],
|
||
ajax: function (data, callback) {
|
||
$.ajax({
|
||
url: cfg.base + "invoices/lines/" + cfg.invoiceId,
|
||
data: data,
|
||
dataType: "json",
|
||
})
|
||
.done(function (r) {
|
||
updateSummary(r.summary || {});
|
||
callback(r);
|
||
})
|
||
.fail(function (x) {
|
||
error(x);
|
||
callback({
|
||
draw: data.draw,
|
||
recordsTotal: 0,
|
||
recordsFiltered: 0,
|
||
data: [],
|
||
});
|
||
});
|
||
},
|
||
columns: [
|
||
{ data: "service_date" },
|
||
{
|
||
data: null,
|
||
render: function (d, t, r) {
|
||
return (
|
||
"<b>" +
|
||
esc((r.item_code ? r.item_code + " - " : "") + r.nama_item) +
|
||
"</b><small>" +
|
||
esc(
|
||
r.keterangan ||
|
||
({service:"Jasa / Nonbarang",loan:"Pinjaman Uang",savings:"Tabungan Customer"}[r.line_type] || r.specification) ||
|
||
"-",
|
||
) +
|
||
"</small>"
|
||
);
|
||
},
|
||
},
|
||
{
|
||
data: null,
|
||
render: function (d, t, r) {
|
||
var primary=(r.kode_akun || "") + " - " + (r.nama_akun || "");
|
||
if(r.line_type==="loan")primary += " ← " + (r.counter_kode_akun || "") + " - " + (r.counter_nama_akun || "");
|
||
return esc(primary);
|
||
},
|
||
},
|
||
{
|
||
data: "barcode_labels",
|
||
className: "barcode-cell",
|
||
render: function (v) {
|
||
return v
|
||
? String(v)
|
||
.split("||")
|
||
.map(function (x) {
|
||
return "<span>" + esc(x) + "</span>";
|
||
})
|
||
.join("")
|
||
: '<span class="text-muted">Tidak memakai barcode</span>';
|
||
},
|
||
},
|
||
{
|
||
data: "qty",
|
||
className: "text-end",
|
||
render: function (v) {
|
||
return qty(v);
|
||
},
|
||
},
|
||
{
|
||
data: "harga",
|
||
className: "text-end",
|
||
render: function (v) {
|
||
return money(v);
|
||
},
|
||
},
|
||
{
|
||
data: "discount_amount",
|
||
className: "text-end",
|
||
render: function (v) {
|
||
return money(v);
|
||
},
|
||
},
|
||
{
|
||
data: "tax_amount",
|
||
className: "text-end",
|
||
render: function (v) {
|
||
return money(v);
|
||
},
|
||
},
|
||
{
|
||
data: "subtotal",
|
||
className: "text-end",
|
||
render: function (v) {
|
||
return "<b>" + money(v) + "</b>";
|
||
},
|
||
},
|
||
{
|
||
data: null,
|
||
orderable: false,
|
||
searchable: false,
|
||
render: function (d, t, r) {
|
||
if(cfg.mode !== "edit" || !cfg.permissions.update)return "-";
|
||
var buttons="";
|
||
if(["service","savings"].indexOf(r.line_type)>=0)buttons += '<button class="btn btn-sm btn-outline-primary edit-line" data-id="'+r.id+'"><i class="bi bi-pencil"></i></button> ';
|
||
buttons += '<button class="btn btn-sm btn-outline-danger remove-line" data-id="'+r.id+'"><i class="bi bi-trash"></i></button>';
|
||
return buttons;
|
||
},
|
||
},
|
||
],
|
||
language: {
|
||
processing: "Memuat item...",
|
||
search: "Cari item:",
|
||
lengthMenu: "Tampilkan _MENU_",
|
||
info: "_START_–_END_ dari _TOTAL_ item",
|
||
infoEmpty: "Belum ada item",
|
||
zeroRecords: "Item tidak ditemukan",
|
||
paginate: { previous: "Sebelumnya", next: "Berikutnya" },
|
||
},
|
||
});
|
||
$(document).on("click", ".remove-line", function () {
|
||
var id = $(this).data("id");
|
||
confirmAction({
|
||
title: "Hapus item dari Draft?",
|
||
text: "Reservasi barcode akan dilepas dan jurnal awal item akan dibalik agar histori tetap utuh.",
|
||
confirmButtonText: "Ya, hapus",
|
||
}).then(function (r) {
|
||
if (!r.isConfirmed) return;
|
||
post(cfg.base + "invoices/remove_line/" + cfg.invoiceId + "/" + id, {})
|
||
.done(function (x) {
|
||
if (!x.status) return notify(x.message, "error");
|
||
notify(x.message, "success");
|
||
lineTable.ajax.reload(null, false);
|
||
})
|
||
.fail(error);
|
||
});
|
||
});
|
||
$(document).on("click", ".edit-line", function () {var row=lineTable.row($(this).closest("tr")).data();if(row)editExistingLine(row);});
|
||
}
|
||
function updateSummary(s) {
|
||
$("#lineCountBadge").text((s.line_count || 0) + " item");
|
||
$("#summaryNet").text(money(s.subtotal));
|
||
$("#summaryDiscount").text(money(s.discount));
|
||
$("#summaryTax").text(money(s.tax));
|
||
$("#summaryTotal").text(money(s.total));
|
||
}
|
||
function bindEditorActions() {
|
||
$(".generate-delivery").on("click", function () {
|
||
workflow(
|
||
"generate_delivery",
|
||
"Buat Surat Jalan?",
|
||
"Barang yang belum mempunyai Surat Jalan akan dikelompokkan berdasarkan tanggal dan gudang.",
|
||
"Ya, Buat Draft",
|
||
);
|
||
});
|
||
$(".submit-invoice").on("click", function () {
|
||
workflow(
|
||
"submit",
|
||
"Ajukan invoice?",
|
||
"Pastikan seluruh Surat Jalan barang sudah diposting.",
|
||
"Ya, ajukan",
|
||
);
|
||
});
|
||
$(".approve-invoice").on("click", function () {
|
||
workflow(
|
||
"approve",
|
||
"Setujui dan posting invoice?",
|
||
"Jurnal piutang dan pendapatan akan dibuat otomatis.",
|
||
"Setujui & Posting",
|
||
);
|
||
});
|
||
$(".reject-invoice").on("click", function () {
|
||
rejectInvoice(cfg.invoiceId, true);
|
||
});
|
||
$(".delete-invoice").on("click", function () {
|
||
workflow(
|
||
"delete_draft",
|
||
"Batalkan Draft invoice?",
|
||
"Reservasi barcode akan dilepas. Tindakan ini tidak dapat dibatalkan.",
|
||
"Ya, batalkan",
|
||
);
|
||
});
|
||
}
|
||
function workflow(action, title, text, button) {
|
||
workflowFor(cfg.invoiceId, action, title, text, button, true);
|
||
}
|
||
function workflowFor(id, action, title, description, button, openDetail) {
|
||
confirmAction({
|
||
title: title,
|
||
text: description,
|
||
confirmButtonText: button,
|
||
}).then(function (r) {
|
||
if (!r.isConfirmed) return;
|
||
post(cfg.base + "invoices/" + action + "/" + id, {})
|
||
.done(function (x) {
|
||
if (!x.status) return notify(x.message, "error");
|
||
notify(x.message, "success");
|
||
if (openDetail) location.href = cfg.base + "invoices/detail/" + id;
|
||
else $("#invoiceTable").DataTable().ajax.reload(null, false);
|
||
})
|
||
.fail(error);
|
||
});
|
||
}
|
||
function rejectInvoice(id, openEditor) {
|
||
id = id || cfg.invoiceId;
|
||
(window.AppAlert
|
||
? AppAlert.prompt({
|
||
icon: "warning",
|
||
title: "Tolak invoice?",
|
||
input: "textarea",
|
||
inputLabel: "Alasan penolakan",
|
||
inputValidator: function (v) {
|
||
return !String(v || "").trim() ? "Alasan wajib diisi." : undefined;
|
||
},
|
||
confirmButtonText: "Tolak Invoice",
|
||
})
|
||
: Swal.fire({
|
||
icon: "warning",
|
||
title: "Tolak invoice?",
|
||
input: "textarea",
|
||
showCancelButton: true,
|
||
})
|
||
).then(function (r) {
|
||
if (!r.isConfirmed) return;
|
||
post(cfg.base + "invoices/reject/" + id, { reason: r.value })
|
||
.done(function (x) {
|
||
if (!x.status) return notify(x.message, "error");
|
||
notify(x.message, "success");
|
||
if (openEditor) location.href = cfg.base + "invoices/edit/" + id;
|
||
else $("#invoiceTable").DataTable().ajax.reload(null, false);
|
||
})
|
||
.fail(error);
|
||
});
|
||
}
|
||
|
||
$(document).on("click", ".row-submit", function () {
|
||
workflowFor(
|
||
$(this).data("id"),
|
||
"submit",
|
||
"Ajukan invoice?",
|
||
"Pastikan data item dan pengiriman sudah lengkap.",
|
||
"Ya, ajukan",
|
||
false,
|
||
);
|
||
});
|
||
$(document).on("click", ".row-generate-delivery", function () {
|
||
workflowFor(
|
||
$(this).data("id"),
|
||
"generate_delivery",
|
||
"Buat Surat Jalan?",
|
||
"Seluruh barang yang belum mempunyai Surat Jalan akan dikelompokkan berdasarkan tanggal dan gudang.",
|
||
"Ya, Buat Draft",
|
||
false,
|
||
);
|
||
});
|
||
$(document).on("click", ".row-approve", function () {
|
||
workflowFor(
|
||
$(this).data("id"),
|
||
"approve",
|
||
"Setujui dan posting invoice?",
|
||
"Jurnal piutang dan pendapatan akan dibuat otomatis.",
|
||
"Setujui & Posting",
|
||
false,
|
||
);
|
||
});
|
||
$(document).on("click", ".row-reject", function () {
|
||
rejectInvoice($(this).data("id"), false);
|
||
});
|
||
$(document).on("click", ".row-delete", function () {
|
||
workflowFor(
|
||
$(this).data("id"),
|
||
"delete_draft",
|
||
"Batalkan Draft invoice?",
|
||
"Reservasi barcode akan dilepas dan histori pembatalan tetap dicatat.",
|
||
"Ya, batalkan",
|
||
false,
|
||
);
|
||
});
|
||
$(document).on("preXhr.dt", "#invoiceTable", function (e, settings, data) {
|
||
data.due_status = $("#filterDue").val() || "";
|
||
});
|
||
$(document).on("draw.dt", "#invoiceTable,#invoiceLinesTable", function () {
|
||
var labels = $(this)
|
||
.find("thead th")
|
||
.map(function () {
|
||
return $(this).text().trim();
|
||
})
|
||
.get();
|
||
$(this)
|
||
.find("tbody tr")
|
||
.each(function () {
|
||
$(this)
|
||
.children("td")
|
||
.each(function (i) {
|
||
$(this).attr("data-label", labels[i] || "");
|
||
});
|
||
});
|
||
});
|
||
$(function () {
|
||
if (!$(".ajax-customer-edit").length) return;
|
||
select2Ajax(
|
||
".ajax-customer-edit",
|
||
"invoices/lookup_customers",
|
||
"Cari customer",
|
||
);
|
||
$("#invoiceHeaderEditForm").on("submit", function (e) {
|
||
e.preventDefault();
|
||
var form = this,
|
||
button = $(form).find(":submit");
|
||
button.prop("disabled", true);
|
||
post(
|
||
cfg.base + "invoices/update_header/" + cfg.invoiceId,
|
||
new FormData(form),
|
||
)
|
||
.done(function (r) {
|
||
if (!r.status) return notify(r.message, "error");
|
||
notify(r.message, "success");
|
||
location.reload();
|
||
})
|
||
.fail(error)
|
||
.always(function () {
|
||
button.prop("disabled", false);
|
||
});
|
||
});
|
||
});
|
||
function openInvoicePdf(url) {
|
||
$("#invoicePdfModal iframe").attr("src", url);
|
||
bootstrap.Modal.getOrCreateInstance(
|
||
document.getElementById("invoicePdfModal"),
|
||
).show();
|
||
}
|
||
$(document).on("click", ".open-invoice-pdf", function () {
|
||
var button = $(this),
|
||
url = String(button.data("url") || ""),
|
||
id = Number(button.data("id") || url.split("/").filter(Boolean).pop());
|
||
if (!id || !url) return notify("Invoice tidak valid.", "error");
|
||
button.prop("disabled", true);
|
||
$.getJSON(cfg.base + "invoices/pdf_options/" + id)
|
||
.done(function (response) {
|
||
if (!response.status) return notify(response.message, "error");
|
||
var option = response.data || {};
|
||
if (Number(option.count || 0) < 1)
|
||
return openInvoicePdf(url + "?include_previous=0");
|
||
Swal.fire({
|
||
title: "Pilihan PDF Invoice",
|
||
html:
|
||
"Customer memiliki <b>" +
|
||
esc(option.count) +
|
||
" invoice sebelumnya</b> yang belum lunas dengan total " +
|
||
esc(money(option.total)) +
|
||
".",
|
||
icon: "question",
|
||
input: "checkbox",
|
||
inputValue: 0,
|
||
inputPlaceholder:
|
||
"Sertakan daftar tagihan sebelumnya pada PDF",
|
||
showCancelButton: true,
|
||
confirmButtonText: "Tampilkan PDF",
|
||
cancelButtonText: "Batal",
|
||
}).then(function (result) {
|
||
if (result.isConfirmed)
|
||
openInvoicePdf(
|
||
url + "?include_previous=" + (result.value ? "1" : "0"),
|
||
);
|
||
});
|
||
})
|
||
.fail(error)
|
||
.always(function () {
|
||
button.prop("disabled", false);
|
||
});
|
||
});
|
||
$("#invoicePdfModal").on("hidden.bs.modal", function () {
|
||
$(this).find("iframe").attr("src", "about:blank");
|
||
});
|
||
$(initList);
|
||
$(initEditor);
|
||
})(jQuery);
|