1433 lines
42 KiB
JavaScript
1433 lines
42 KiB
JavaScript
(function ($) {
|
||
"use strict";
|
||
var C = window.FIXED_ASSET_PAGE || {},
|
||
B = C.base || "/",
|
||
P = C.permissions || {},
|
||
files = [],
|
||
table,
|
||
currentAsset = null,
|
||
warehouseUnitCost = 0;
|
||
function esc(v) {
|
||
return $("<div>")
|
||
.text(v == null ? "" : v)
|
||
.html();
|
||
}
|
||
function rp(v) {
|
||
return (
|
||
"Rp " +
|
||
Number(v || 0).toLocaleString("id-ID", {
|
||
minimumFractionDigits: 2,
|
||
maximumFractionDigits: 2,
|
||
})
|
||
);
|
||
}
|
||
function q(v) {
|
||
return Number(v || 0).toLocaleString("id-ID", { maximumFractionDigits: 4 });
|
||
}
|
||
function fd(obj) {
|
||
var f = new FormData();
|
||
Object.keys(obj || {}).forEach(function (k) {
|
||
f.append(k, obj[k] == null ? "" : obj[k]);
|
||
});
|
||
if (window.APP_CSRF) f.append(APP_CSRF.name, APP_CSRF.value);
|
||
return f;
|
||
}
|
||
function request(url, opt) {
|
||
opt = opt || {};
|
||
if (
|
||
opt.body instanceof FormData &&
|
||
window.APP_CSRF &&
|
||
!opt.body.has(APP_CSRF.name)
|
||
)
|
||
opt.body.append(APP_CSRF.name, APP_CSRF.value);
|
||
opt.headers = Object.assign(
|
||
{ Accept: "application/json", "X-Requested-With": "XMLHttpRequest" },
|
||
opt.headers || {},
|
||
);
|
||
return fetch(url, opt).then(function (r) {
|
||
return r.text().then(function (t) {
|
||
var x;
|
||
try {
|
||
x = JSON.parse(t);
|
||
} catch (e) {
|
||
throw Error("Server tidak mengirim JSON yang valid.");
|
||
}
|
||
if (!r.ok || x.status === false)
|
||
throw Error(x.message || "Proses gagal.");
|
||
return x;
|
||
});
|
||
});
|
||
}
|
||
function toast(m, i) {
|
||
return AppAlert.toast(m, i || "success");
|
||
}
|
||
function fail(e) {
|
||
toast(e.message || String(e), "error");
|
||
}
|
||
function reload() {
|
||
if (table) table.ajax.reload(null, false);
|
||
}
|
||
function status(v) {
|
||
var label = {
|
||
draft: "DRAFT",
|
||
submitted: "MENUNGGU APPROVAL",
|
||
approved: "SIAP KAPITALISASI",
|
||
capitalized: "TERKAPITALISASI",
|
||
rejected: "DITOLAK",
|
||
active: "AKTIF",
|
||
fully_depreciated: "TERSUSUT PENUH",
|
||
disposed: "DILEPAS",
|
||
lost: "HILANG",
|
||
damaged: "RUSAK",
|
||
};
|
||
return (
|
||
'<span class="status-pill status-' +
|
||
esc(v) +
|
||
'">' +
|
||
esc(label[v] || String(v || "-").toUpperCase()) +
|
||
"</span>"
|
||
);
|
||
}
|
||
function initTable() {
|
||
table = $("#assetTable").DataTable({
|
||
processing: true,
|
||
serverSide: true,
|
||
responsive: true,
|
||
searching: false,
|
||
pageLength: 10,
|
||
order: [],
|
||
ajax: {
|
||
url: B + "fixedassets/data",
|
||
data: function (d) {
|
||
d.search = { value: $("#assetSearch").val() };
|
||
d.category_id = $("#filterCategory").val();
|
||
d.workflow_status = $("#filterWorkflow").val();
|
||
d.lifecycle_status = $("#filterLifecycle").val();
|
||
},
|
||
},
|
||
columns: [
|
||
{
|
||
data: null,
|
||
render: function (_, t, r) {
|
||
return (
|
||
"<strong>" +
|
||
esc(r.kode_asset) +
|
||
"</strong><br><small>" +
|
||
esc(r.nama_asset) +
|
||
"</small>"
|
||
);
|
||
},
|
||
},
|
||
{
|
||
data: null,
|
||
render: function (_, t, r) {
|
||
return (
|
||
esc(r.category_name || "-") +
|
||
"<br><small>" +
|
||
esc(r.location_name || "-") +
|
||
"</small>"
|
||
);
|
||
},
|
||
},
|
||
{
|
||
data: "source_type",
|
||
render: function (v, t, r) {
|
||
return (
|
||
esc(v === "warehouse" ? "Gudang" : "Input langsung") +
|
||
(r.barcode ? "<br><small>" + esc(r.barcode) + "</small>" : "")
|
||
);
|
||
},
|
||
},
|
||
{
|
||
data: null,
|
||
render: function (_, t, r) {
|
||
return (
|
||
status(r.workflow_status) + "<br>" + status(r.lifecycle_status)
|
||
);
|
||
},
|
||
},
|
||
{
|
||
data: "nilai_perolehan",
|
||
className: "text-end",
|
||
render: function (v) {
|
||
return rp(v);
|
||
},
|
||
},
|
||
{
|
||
data: "nilai_buku",
|
||
className: "text-end",
|
||
render: function (v) {
|
||
return rp(v);
|
||
},
|
||
},
|
||
{
|
||
data: "id",
|
||
orderable: false,
|
||
render: function (id) {
|
||
return (
|
||
'<button class="btn btn-sm btn-outline-primary btn-detail" data-id="' +
|
||
id +
|
||
'"><i class="bi bi-eye"></i> Detail</button>'
|
||
);
|
||
},
|
||
},
|
||
],
|
||
language: {
|
||
processing: "Memuat data…",
|
||
emptyTable: "Belum ada aset.",
|
||
info: "Menampilkan _START_–_END_ dari _TOTAL_",
|
||
infoEmpty: "Tidak ada data",
|
||
lengthMenu: "Tampilkan _MENU_",
|
||
paginate: { previous: "‹", next: "›" },
|
||
},
|
||
});
|
||
var timer;
|
||
$("#assetSearch").on("input", function () {
|
||
clearTimeout(timer);
|
||
timer = setTimeout(function () {
|
||
table.ajax.reload();
|
||
}, 350);
|
||
});
|
||
$("#filterCategory,#filterWorkflow,#filterLifecycle").on(
|
||
"change",
|
||
function () {
|
||
table.ajax.reload();
|
||
},
|
||
);
|
||
}
|
||
function initSelects() {
|
||
if (!$.fn.select2) return;
|
||
$("#inventoryLookup")
|
||
.select2({
|
||
width: "100%",
|
||
dropdownParent: $("#assetFormModal"),
|
||
placeholder: "Cari barang/barcode gudang",
|
||
ajax: {
|
||
url: B + "fixedassets/lookup_inventory",
|
||
delay: 300,
|
||
data: function (p) {
|
||
return { q: p.term || "", page: p.page || 1 };
|
||
},
|
||
processResults: function (r) {
|
||
return {
|
||
results: r.results || [],
|
||
pagination: r.pagination || { more: false },
|
||
};
|
||
},
|
||
},
|
||
})
|
||
.on("select2:select", function (e) {
|
||
var r = e.params.data.data || {};
|
||
$("[name=item_id]").val(r.item_id);
|
||
$("[name=barcode_id]").val(r.barcode_id);
|
||
$("[name=warehouse_id]").val(r.warehouse_id);
|
||
$("[name=bin_id]").val(r.bin_id || "");
|
||
$("[name=nama_asset]").val(r.nama_barang);
|
||
warehouseUnitCost = Number(r.ledger_unit_cost || 0);
|
||
$("[name=qty]").val(
|
||
q(r.tracking_type === "UNIT" ? 1 : r.available_qty),
|
||
);
|
||
updateWarehouseValue();
|
||
$("#inventoryInfo").html(
|
||
"<b>" +
|
||
esc(r.kode_detail) +
|
||
" - " +
|
||
esc(r.nama_barang) +
|
||
"</b><br>Barcode " +
|
||
esc(r.barcode) +
|
||
(r.serial_number ? " · SN " + esc(r.serial_number) : "") +
|
||
" · " +
|
||
esc(r.warehouse_name) +
|
||
" · tersedia " +
|
||
q(r.available_qty) +
|
||
" · biaya ledger per unit " +
|
||
rp(warehouseUnitCost),
|
||
);
|
||
});
|
||
$("#employeeLookup").select2({
|
||
width: "100%",
|
||
dropdownParent: $("#assetFormModal"),
|
||
allowClear: true,
|
||
placeholder: "Cari karyawan",
|
||
ajax: {
|
||
url: B + "fixedassets/lookup_employees",
|
||
delay: 300,
|
||
data: function (p) {
|
||
return { q: p.term || "", page: p.page || 1 };
|
||
},
|
||
processResults: function (r) {
|
||
return {
|
||
results: r.results || [],
|
||
pagination: r.pagination || { more: false },
|
||
};
|
||
},
|
||
},
|
||
});
|
||
$("#journalLookup").select2({
|
||
width: "100%",
|
||
dropdownParent: $("#assetFormModal"),
|
||
placeholder: "Cari nomor jurnal",
|
||
ajax: {
|
||
url: B + "fixedassets/lookup_journals",
|
||
delay: 300,
|
||
data: function (p) {
|
||
return { q: p.term || "" };
|
||
},
|
||
processResults: function (r) {
|
||
return { results: r.results || [] };
|
||
},
|
||
},
|
||
});
|
||
}
|
||
function resetForm() {
|
||
var f = document.getElementById("assetForm");
|
||
f.reset();
|
||
warehouseUnitCost = 0;
|
||
files = [];
|
||
renderFiles();
|
||
f.elements.idempotency_key.value =
|
||
"AST-" + Date.now() + "-" + Math.random().toString(36).slice(2);
|
||
f.elements.tanggal_perolehan.value = new Date().toISOString().slice(0, 10);
|
||
f.elements.capitalization_date.value = f.elements.tanggal_perolehan.value;
|
||
$("#inventoryLookup,#employeeLookup,#journalLookup")
|
||
.val(null)
|
||
.trigger("change");
|
||
toggleSource();
|
||
toggleJournal();
|
||
}
|
||
function updateWarehouseValue() {
|
||
if ($("[name=source_type]:checked").val() !== "warehouse") return;
|
||
var qty =
|
||
Number(
|
||
window.cleanNumber
|
||
? window.cleanNumber($("[name=qty]").val())
|
||
: $("[name=qty]").val(),
|
||
) || 0,
|
||
value = warehouseUnitCost * qty;
|
||
$("[name=nilai_perolehan]").val(
|
||
window.formatNumber
|
||
? window.formatNumber(value.toFixed(2))
|
||
: value.toFixed(2),
|
||
);
|
||
}
|
||
function toggleSource() {
|
||
var w = $("[name=source_type]:checked").val() === "warehouse",
|
||
value = $("[name=nilai_perolehan]");
|
||
$(".warehouse-source").toggleClass("d-none", !w);
|
||
$(".manual-source").toggleClass("d-none", w);
|
||
value.prop("readonly", w);
|
||
$("#acquisitionValueLabel").text(
|
||
w ? "Nilai perolehan dari ledger" : "Nilai perolehan",
|
||
);
|
||
if (w && warehouseUnitCost <= 0 && $("[name=barcode_id]").val()) {
|
||
var qty =
|
||
Number(
|
||
window.cleanNumber
|
||
? window.cleanNumber($("[name=qty]").val())
|
||
: $("[name=qty]").val(),
|
||
) || 0,
|
||
total =
|
||
Number(
|
||
window.cleanNumber ? window.cleanNumber(value.val()) : value.val(),
|
||
) || 0;
|
||
warehouseUnitCost = qty > 0 ? total / qty : 0;
|
||
}
|
||
if (w) updateWarehouseValue();
|
||
$("[name=source_journal_mode]").val(
|
||
w
|
||
? "post_journal"
|
||
: $("[name=source_journal_mode]").val() || "post_journal",
|
||
);
|
||
toggleJournal();
|
||
}
|
||
function toggleJournal() {
|
||
var mode = $("[name=source_journal_mode]").val();
|
||
$(".linked-journal").toggleClass("d-none", mode !== "linked_journal");
|
||
$(".credit-account").toggleClass("d-none", mode !== "post_journal");
|
||
}
|
||
$(document).on("input", "[name=qty]", updateWarehouseValue);
|
||
function renderFiles() {
|
||
var add = $("#assetFiles .file-add").detach();
|
||
$("#assetFiles").empty();
|
||
files.forEach(function (f, i) {
|
||
var tile = $('<div class="file-tile"></div>');
|
||
if (f.type.indexOf("image/") === 0) {
|
||
var img = $('<img alt="Preview">');
|
||
img.attr("src", URL.createObjectURL(f));
|
||
tile.append(img);
|
||
} else
|
||
tile.append(
|
||
'<i class="bi bi-file-earmark-pdf pdf-icon"></i><small>PDF</small>',
|
||
);
|
||
tile.append(
|
||
$('<button type="button" aria-label="Hapus">×</button>').on(
|
||
"click",
|
||
function () {
|
||
files.splice(i, 1);
|
||
renderFiles();
|
||
},
|
||
),
|
||
);
|
||
$("#assetFiles").append(tile);
|
||
});
|
||
$("#assetFiles").append(add);
|
||
}
|
||
function uploadFiles(id) {
|
||
return files.reduce(function (chain, file) {
|
||
return chain.then(function () {
|
||
var f = new FormData();
|
||
f.append("entity_type", "asset");
|
||
f.append("entity_id", id);
|
||
f.append("attachment", file);
|
||
if (window.APP_CSRF) f.append(APP_CSRF.name, APP_CSRF.value);
|
||
return request(B + "accountingattachments/upload", {
|
||
method: "POST",
|
||
body: f,
|
||
});
|
||
});
|
||
}, Promise.resolve());
|
||
}
|
||
function uploadEntity(type, id, file) {
|
||
var f = new FormData();
|
||
f.append("entity_type", type);
|
||
f.append("entity_id", id);
|
||
f.append("attachment", file);
|
||
return request(B + "accountingattachments/upload", {
|
||
method: "POST",
|
||
body: f,
|
||
});
|
||
}
|
||
function saveAsset(e) {
|
||
e.preventDefault();
|
||
var form = e.currentTarget,
|
||
button = $(form).find("[type=submit]").prop("disabled", true);
|
||
request(B + "fixedassets/save", {
|
||
method: "POST",
|
||
body: new FormData(form),
|
||
})
|
||
.then(function (r) {
|
||
var id = r.result;
|
||
return uploadFiles(id).then(function () {
|
||
return r;
|
||
});
|
||
})
|
||
.then(function (r) {
|
||
bootstrap.Modal.getInstance(
|
||
document.getElementById("assetFormModal"),
|
||
).hide();
|
||
toast(r.message);
|
||
reload();
|
||
})
|
||
.catch(fail)
|
||
.finally(function () {
|
||
button.prop("disabled", false);
|
||
});
|
||
}
|
||
function attachments(list) {
|
||
if (!list || !list.length)
|
||
return '<div class="text-muted">Belum ada lampiran.</div>';
|
||
return (
|
||
'<div class="file-grid">' +
|
||
list
|
||
.map(function (f) {
|
||
var isPdf = (f.mime_type || "").indexOf("pdf") >= 0;
|
||
return (
|
||
'<button class="file-tile preview-file" data-url="' +
|
||
esc(f.preview_url) +
|
||
'">' +
|
||
(isPdf
|
||
? '<i class="bi bi-file-earmark-pdf pdf-icon"></i>'
|
||
: '<img src="' + esc(f.preview_url) + '" alt="Lampiran">') +
|
||
"<small>" +
|
||
esc(f.original_name) +
|
||
"</small></button>"
|
||
);
|
||
})
|
||
.join("") +
|
||
"</div>"
|
||
);
|
||
}
|
||
function detailHtml(a) {
|
||
var ev =
|
||
(a.events || [])
|
||
.map(function (x) {
|
||
return (
|
||
"<article><b>" +
|
||
esc(x.event_no || x.event_type) +
|
||
"</b><div>" +
|
||
esc(x.event_type) +
|
||
" · " +
|
||
esc(x.event_date) +
|
||
" · " +
|
||
rp(x.amount) +
|
||
"</div><small>" +
|
||
esc(x.notes || "-") +
|
||
(x.journal_no ? " · Jurnal " + esc(x.journal_no) : "") +
|
||
"</small></article>"
|
||
);
|
||
})
|
||
.join("") || '<span class="text-muted">Belum ada event.</span>';
|
||
return (
|
||
'<div class="detail-grid"><section class="detail-panel"><h6>' +
|
||
esc(a.kode_asset) +
|
||
" · " +
|
||
esc(a.nama_asset) +
|
||
'</h6><div class="detail-kv"><span>Dokumen</span><b>' +
|
||
esc(a.document_no) +
|
||
"</b><span>Status</span><div>" +
|
||
status(a.workflow_status) +
|
||
" " +
|
||
status(a.lifecycle_status) +
|
||
"</div><span>Kategori</span><b>" +
|
||
esc(a.category_name || "-") +
|
||
"</b><span>Lokasi / PIC</span><b>" +
|
||
esc(a.location_name || "-") +
|
||
" / " +
|
||
esc(a.responsible_name || "-") +
|
||
"</b><span>Sumber</span><b>" +
|
||
esc(a.source_type) +
|
||
(a.kode_detail ? " · " + esc(a.kode_detail) : "") +
|
||
"</b><span>Barcode / SN</span><b>" +
|
||
esc(a.barcode || "-") +
|
||
" / " +
|
||
esc(a.serial_number || "-") +
|
||
"</b><span>Perolehan</span><b>" +
|
||
rp(a.nilai_perolehan) +
|
||
"</b><span>Residu</span><b>" +
|
||
rp(a.nilai_residu) +
|
||
"</b><span>Akumulasi</span><b>" +
|
||
rp(a.akumulasi_penyusutan) +
|
||
"</b><span>Nilai buku</span><b>" +
|
||
rp(a.nilai_buku) +
|
||
"</b><span>Pembuat</span><b>" +
|
||
esc(a.creator_name || "-") +
|
||
"</b><span>Penyetuju</span><b>" +
|
||
esc(a.approver_name || "-") +
|
||
'</b></div></section><section class="detail-panel"><h6>Dokumen Pendukung</h6>' +
|
||
attachments(a.attachments) +
|
||
'</section><section class="detail-panel"><h6>Riwayat Mutasi & Jurnal</h6><div class="timeline">' +
|
||
ev +
|
||
'</div></section><section class="detail-panel"><h6>Catatan</h6><p>' +
|
||
esc(a.keterangan || "-") +
|
||
"</p><h6>Jadwal Penyusutan</h6><p>" +
|
||
(a.schedule || []).length +
|
||
" periode tercatat · " +
|
||
(a.maintenance || []).length +
|
||
" maintenance</p></section></div>"
|
||
);
|
||
}
|
||
function detailActions(a) {
|
||
var out =
|
||
'<button class="btn btn-light" data-bs-dismiss="modal">Tutup</button>';
|
||
if (P.export)
|
||
out +=
|
||
'<button class="btn btn-outline-danger btn-pdf" data-url="' +
|
||
B +
|
||
"fixedassets/asset_pdf/" +
|
||
a.id +
|
||
'">PDF Kartu Aset</button><button class="btn btn-outline-dark" onclick="window.open(\'' +
|
||
B +
|
||
"fixedassets/qr/" +
|
||
a.id +
|
||
"','_blank')\">QR</button>";
|
||
if (P.update && a.workflow_status === "draft")
|
||
out +=
|
||
'<button class="btn btn-outline-primary edit-asset">Edit Draft</button>';
|
||
if (P.create && a.workflow_status === "draft")
|
||
out +=
|
||
'<button class="btn btn-primary action-post" data-action="submit" data-id="' +
|
||
a.id +
|
||
'">Ajukan</button>';
|
||
if (P.approve && a.workflow_status === "submitted")
|
||
out +=
|
||
'<button class="btn btn-outline-danger action-post" data-action="reject" data-id="' +
|
||
a.id +
|
||
'">Tolak</button><button class="btn btn-success action-post" data-action="approve" data-id="' +
|
||
a.id +
|
||
'">Setujui</button>';
|
||
if (P.post && a.workflow_status === "approved")
|
||
out +=
|
||
'<button class="btn btn-success action-post" data-action="capitalize" data-id="' +
|
||
a.id +
|
||
'">Kapitalisasi</button>';
|
||
if (
|
||
P.post &&
|
||
a.workflow_status === "capitalized" &&
|
||
["active", "fully_depreciated"].indexOf(a.lifecycle_status) >= 0
|
||
)
|
||
out +=
|
||
'<button class="btn btn-primary asset-process" data-kind="depreciate" data-id="' +
|
||
a.id +
|
||
'">Penyusutan</button><button class="btn btn-warning asset-process" data-kind="mutate" data-id="' +
|
||
a.id +
|
||
'">Mutasi Nilai</button>';
|
||
if (P.update && a.workflow_status === "capitalized")
|
||
out +=
|
||
'<button class="btn btn-outline-primary asset-process" data-kind="transfer" data-id="' +
|
||
a.id +
|
||
'">Transfer</button><button class="btn btn-outline-secondary asset-process" data-kind="maintenance" data-id="' +
|
||
a.id +
|
||
'">Maintenance</button>';
|
||
return out;
|
||
}
|
||
function openDetail(id) {
|
||
request(B + "fixedassets/detail/" + id)
|
||
.then(function (r) {
|
||
currentAsset = r.asset;
|
||
$("#assetDetailBody").html(detailHtml(r.asset));
|
||
$("#assetDetailActions").html(detailActions(r.asset));
|
||
bootstrap.Modal.getOrCreateInstance(
|
||
document.getElementById("assetDetailModal"),
|
||
).show();
|
||
})
|
||
.catch(fail);
|
||
}
|
||
function editAsset() {
|
||
var a = currentAsset;
|
||
if (!a) return;
|
||
resetForm();
|
||
var f = document.getElementById("assetForm"),
|
||
names = [
|
||
"id",
|
||
"idempotency_key",
|
||
"nama_asset",
|
||
"tanggal_perolehan",
|
||
"capitalization_date",
|
||
"category_id",
|
||
"lokasi_asset_id",
|
||
"source_reference_no",
|
||
"qty",
|
||
"nilai_perolehan",
|
||
"nilai_residu",
|
||
"masa_manfaat",
|
||
"depreciation_method",
|
||
"asset_account_id",
|
||
"accumulated_depreciation_account_id",
|
||
"depreciation_expense_account_id",
|
||
"source_journal_mode",
|
||
"account_kredit_id",
|
||
"source_journal_id",
|
||
"item_id",
|
||
"barcode_id",
|
||
"warehouse_id",
|
||
"bin_id",
|
||
"keterangan",
|
||
];
|
||
names.forEach(function (n) {
|
||
if (f.elements[n] && a[n] != null) $(f.elements[n]).val(a[n]);
|
||
});
|
||
$('[name=source_type][value="' + a.source_type + '"]').prop(
|
||
"checked",
|
||
true,
|
||
);
|
||
if (a.responsible_employee_id)
|
||
$("#employeeLookup")
|
||
.append(
|
||
new Option(
|
||
a.responsible_name || "Karyawan",
|
||
a.responsible_employee_id,
|
||
true,
|
||
true,
|
||
),
|
||
)
|
||
.trigger("change");
|
||
if (a.source_journal_id)
|
||
$("#journalLookup")
|
||
.append(
|
||
new Option(
|
||
a.source_journal_no || "Jurnal sumber",
|
||
a.source_journal_id,
|
||
true,
|
||
true,
|
||
),
|
||
)
|
||
.trigger("change");
|
||
if (a.barcode_id)
|
||
$("#inventoryLookup")
|
||
.append(
|
||
new Option(
|
||
(a.kode_detail || "Barang") + " - " + (a.barcode || ""),
|
||
a.barcode_id,
|
||
true,
|
||
true,
|
||
),
|
||
)
|
||
.trigger("change");
|
||
toggleSource();
|
||
toggleJournal();
|
||
bootstrap.Modal.getInstance(
|
||
document.getElementById("assetDetailModal"),
|
||
).hide();
|
||
bootstrap.Modal.getOrCreateInstance(
|
||
document.getElementById("assetFormModal"),
|
||
).show();
|
||
}
|
||
function postAction(action, id, extra) {
|
||
var labels = {
|
||
submit: "Ajukan dokumen aset?",
|
||
approve: "Setujui dokumen aset?",
|
||
capitalize: "Kapitalisasi aset dan posting jurnal?",
|
||
};
|
||
if (action === "reject")
|
||
return AppAlert.prompt({
|
||
title: "Tolak dokumen aset",
|
||
input: "textarea",
|
||
inputLabel: "Alasan penolakan wajib diisi",
|
||
inputValidator: function (v) {
|
||
if (!v) return "Alasan wajib diisi.";
|
||
},
|
||
}).then(function (x) {
|
||
if (x.isConfirmed) doPost(action, id, { reason: x.value });
|
||
});
|
||
AppAlert.confirm({
|
||
title: labels[action] || "Lanjutkan proses?",
|
||
text: "Tindakan akan dicatat pada audit trail.",
|
||
}).then(function (x) {
|
||
if (x.isConfirmed) doPost(action, id, extra || {});
|
||
});
|
||
}
|
||
function doPost(action, id, obj) {
|
||
obj = obj || {};
|
||
var uploads = obj._files || [];
|
||
delete obj._files;
|
||
request(B + "fixedassets/" + action + "/" + id, {
|
||
method: "POST",
|
||
body: fd(obj),
|
||
})
|
||
.then(function (r) {
|
||
if (!uploads.length) return r;
|
||
return Promise.all(
|
||
uploads.map(function (file) {
|
||
return uploadEntity("asset_maintenance", r.result, file);
|
||
}),
|
||
).then(function () {
|
||
return r;
|
||
});
|
||
})
|
||
.then(function (r) {
|
||
toast(r.message);
|
||
var modal = bootstrap.Modal.getInstance(
|
||
document.getElementById("assetDetailModal"),
|
||
);
|
||
if (modal) modal.hide();
|
||
reload();
|
||
})
|
||
.catch(fail);
|
||
}
|
||
function process(kind, id) {
|
||
var today = new Date().toISOString().slice(0, 10),
|
||
html = "",
|
||
title = "";
|
||
if (kind === "depreciate") {
|
||
title = "Proses Penyusutan";
|
||
html =
|
||
'<input id="sw-date" type="date" class="swal2-input" value="' +
|
||
today +
|
||
'">';
|
||
}
|
||
if (kind === "transfer") {
|
||
title = "Transfer Lokasi";
|
||
html =
|
||
'<input id="sw-date" type="date" class="swal2-input" value="' +
|
||
today +
|
||
'"><select id="sw-location" class="swal2-select"><option value="">Pilih lokasi</option>' +
|
||
C.locations
|
||
.filter(function (x) {
|
||
return Number(x.is_active);
|
||
})
|
||
.map(function (x) {
|
||
return (
|
||
'<option value="' +
|
||
x.id +
|
||
'">' +
|
||
esc(x.code + " - " + x.nama) +
|
||
"</option>"
|
||
);
|
||
})
|
||
.join("") +
|
||
'</select><textarea id="sw-notes" class="swal2-textarea" placeholder="Alasan transfer"></textarea>';
|
||
}
|
||
if (kind === "maintenance") {
|
||
title = "Catat Maintenance";
|
||
html =
|
||
'<input id="sw-date" type="date" class="swal2-input" value="' +
|
||
today +
|
||
'"><input id="sw-vendor" class="swal2-input" placeholder="Vendor"><textarea id="sw-description" class="swal2-textarea" placeholder="Pekerjaan maintenance"></textarea><input id="sw-cost" class="swal2-input" inputmode="decimal" placeholder="Biaya"><input id="sw-next" type="date" class="swal2-input"><label class="d-block text-start mx-4 mt-2">Bukti maintenance (gambar/PDF)</label><input id="sw-file" type="file" class="swal2-file" accept="image/*,.pdf" multiple>';
|
||
}
|
||
if (kind === "mutate") {
|
||
title = "Mutasi Bernilai";
|
||
html =
|
||
'<select id="sw-type" class="swal2-select"><option value="addition">Penambahan Nilai</option><option value="impairment">Penurunan Nilai</option><option value="revaluation">Revaluasi</option><option value="disposal">Penjualan/Pelepasan</option><option value="lost">Hilang</option><option value="damaged">Rusak</option></select><input id="sw-date" type="date" class="swal2-input" value="' +
|
||
today +
|
||
'"><input id="sw-amount" class="swal2-input" inputmode="decimal" placeholder="Nilai/proceeds"><select id="sw-account" class="swal2-select"><option value="">Pilih akun lawan</option>' +
|
||
C.accounts
|
||
.map(function (x) {
|
||
return (
|
||
'<option value="' +
|
||
x.id +
|
||
'">' +
|
||
esc(x.kode_akun + " - " + x.nama_akun) +
|
||
"</option>"
|
||
);
|
||
})
|
||
.join("") +
|
||
'</select><textarea id="sw-notes" class="swal2-textarea" placeholder="Alasan/rincian wajib"></textarea>';
|
||
}
|
||
AppAlert.prompt({
|
||
title: title,
|
||
html: html,
|
||
preConfirm: function () {
|
||
var o = { date: $("#sw-date").val() };
|
||
if (kind === "depreciate") o.through_date = o.date;
|
||
if (kind === "transfer") {
|
||
o.location_id = $("#sw-location").val();
|
||
o.notes = $("#sw-notes").val();
|
||
}
|
||
if (kind === "maintenance") {
|
||
o.vendor = $("#sw-vendor").val();
|
||
o.description = $("#sw-description").val();
|
||
o.cost = $("#sw-cost").val();
|
||
o.next_due_date = $("#sw-next").val();
|
||
o.idempotency_key = "MNT-" + Date.now();
|
||
o._files = Array.from(($("#sw-file")[0] || {}).files || []);
|
||
}
|
||
if (kind === "mutate") {
|
||
o.type = $("#sw-type").val();
|
||
o.amount = $("#sw-amount").val();
|
||
o.account_id = $("#sw-account").val();
|
||
o.notes = $("#sw-notes").val();
|
||
}
|
||
return o;
|
||
},
|
||
}).then(function (x) {
|
||
if (x.isConfirmed) doPost(kind, id, x.value);
|
||
});
|
||
}
|
||
function pdf(url) {
|
||
$("#pdfModal iframe").attr("src", url);
|
||
bootstrap.Modal.getOrCreateInstance(
|
||
document.getElementById("pdfModal"),
|
||
).show();
|
||
}
|
||
function master(type, row) {
|
||
row = row || {};
|
||
var account =
|
||
'<option value="">Pilih akun</option>' +
|
||
C.accounts
|
||
.map(function (a) {
|
||
return (
|
||
'<option value="' +
|
||
a.id +
|
||
'">' +
|
||
esc(a.kode_akun + " - " + a.nama_akun) +
|
||
"</option>"
|
||
);
|
||
})
|
||
.join("");
|
||
var h =
|
||
'<input type="hidden" name="id" value="' +
|
||
esc(row.id || "") +
|
||
'"><input type="hidden" name="master_type" value="' +
|
||
type +
|
||
'"><div class="row g-3"><div class="col-md-4"><label class="form-label">Kode</label><input class="form-control" name="code" value="' +
|
||
esc(row.code || "") +
|
||
'" required></div><div class="col-md-8"><label class="form-label">Nama</label><input class="form-control" name="name" value="' +
|
||
esc(row.name || row.nama || "") +
|
||
'" required></div>';
|
||
if (type === "category")
|
||
h +=
|
||
'<div class="col-md-4"><label class="form-label">Masa manfaat (bulan)</label><input type="number" class="form-control" name="life" value="' +
|
||
esc(row.default_useful_life_months || 48) +
|
||
'"></div><div class="col-md-4"><label class="form-label">Nilai residu (%)</label><input class="form-control" name="residual_percent" value="' +
|
||
esc(row.default_residual_percent || 0) +
|
||
'"></div><div class="col-md-4"><label class="form-label">Metode</label><select class="form-select" name="method"><option value="straight_line">Garis lurus</option><option value="declining_balance">Saldo menurun</option></select></div><div class="col-md-4"><label class="form-label">Akun aset</label><select class="form-select" name="asset_account_id">' +
|
||
account +
|
||
'</select></div><div class="col-md-4"><label class="form-label">Akumulasi</label><select class="form-select" name="accumulated_account_id">' +
|
||
account +
|
||
'</select></div><div class="col-md-4"><label class="form-label">Beban penyusutan</label><select class="form-select" name="expense_account_id">' +
|
||
account +
|
||
"</select></div>";
|
||
else
|
||
h +=
|
||
'<div class="col-12"><label class="form-label">Alamat/keterangan lokasi</label><textarea class="form-control" name="address">' +
|
||
esc(row.address || "") +
|
||
"</textarea></div>";
|
||
h +=
|
||
'<div class="col-12"><div class="form-check"><input type="checkbox" class="form-check-input" name="is_active" value="1" ' +
|
||
(row.id && !Number(row.is_active) ? "" : "checked") +
|
||
'><label class="form-check-label">Aktif</label></div></div></div>';
|
||
$("#masterFields").html(h);
|
||
if (type === "category") {
|
||
$("[name=method]").val(row.depreciation_method || "straight_line");
|
||
$("[name=asset_account_id]").val(row.asset_account_id || "");
|
||
$("[name=accumulated_account_id]").val(
|
||
row.accumulated_depreciation_account_id || "",
|
||
);
|
||
$("[name=expense_account_id]").val(
|
||
row.depreciation_expense_account_id || "",
|
||
);
|
||
}
|
||
bootstrap.Modal.getOrCreateInstance(
|
||
document.getElementById("masterModal"),
|
||
).show();
|
||
}
|
||
function saveMaster(e) {
|
||
e.preventDefault();
|
||
var form = e.currentTarget,
|
||
type = form.elements.master_type.value;
|
||
request(B + "fixedassets/save_" + type, {
|
||
method: "POST",
|
||
body: new FormData(form),
|
||
})
|
||
.then(function (r) {
|
||
toast(r.message);
|
||
location.reload();
|
||
})
|
||
.catch(fail);
|
||
}
|
||
function newOpname() {
|
||
var opts = C.locations
|
||
.filter(function (x) {
|
||
return Number(x.is_active);
|
||
})
|
||
.map(function (x) {
|
||
return (
|
||
'<option value="' +
|
||
x.id +
|
||
'">' +
|
||
esc(x.code + " - " + x.nama) +
|
||
"</option>"
|
||
);
|
||
})
|
||
.join("");
|
||
AppAlert.prompt({
|
||
title: "Buat Asset Opname",
|
||
html:
|
||
'<select id="op-location" class="swal2-select"><option value="">Pilih lokasi</option>' +
|
||
opts +
|
||
'</select><input id="op-date" type="date" class="swal2-input" value="' +
|
||
new Date().toISOString().slice(0, 10) +
|
||
'"><textarea id="op-notes" class="swal2-textarea" placeholder="Ruang lingkup/catatan"></textarea>',
|
||
preConfirm: function () {
|
||
return {
|
||
location_id: $("#op-location").val(),
|
||
date: $("#op-date").val(),
|
||
notes: $("#op-notes").val(),
|
||
idempotency_key: "AOP-" + Date.now(),
|
||
};
|
||
},
|
||
}).then(function (x) {
|
||
if (x.isConfirmed)
|
||
request(B + "fixedassets/opname_create", {
|
||
method: "POST",
|
||
body: fd(x.value),
|
||
})
|
||
.then(function (r) {
|
||
toast(r.message);
|
||
location.reload();
|
||
})
|
||
.catch(fail);
|
||
});
|
||
}
|
||
function openOpname(id) {
|
||
request(B + "fixedassets/opname_detail/" + id)
|
||
.then(function (r) {
|
||
var o = r.opname,
|
||
lines = (o.lines || [])
|
||
.map(function (l) {
|
||
return (
|
||
'<div class="opname-line-grid" data-line="' +
|
||
l.id +
|
||
'"><b>' +
|
||
esc(l.kode_asset) +
|
||
"<br><small>" +
|
||
esc(l.nama_asset) +
|
||
'</small></b><label><input type="checkbox" class="found" ' +
|
||
(Number(l.is_found) ? "checked" : "") +
|
||
'> Ditemukan</label><select class="form-select actual-location">' +
|
||
C.locations
|
||
.map(function (x) {
|
||
return (
|
||
'<option value="' +
|
||
x.id +
|
||
'" ' +
|
||
(x.id == l.actual_location_id ? "selected" : "") +
|
||
">" +
|
||
esc(x.code + " - " + x.nama) +
|
||
"</option>"
|
||
);
|
||
})
|
||
.join("") +
|
||
'</select><select class="form-select actual-status"><option value="active">Baik/Aktif</option><option value="fully_depreciated">Tersusut penuh</option><option value="lost">Hilang</option><option value="damaged">Rusak</option></select><input class="form-control notes" value="' +
|
||
esc(l.notes || "") +
|
||
'" placeholder="Catatan hasil cek"></div>'
|
||
);
|
||
})
|
||
.join("");
|
||
$("#opnameBody").html(
|
||
'<div class="detail-kv mb-3"><span>Dokumen</span><b>' +
|
||
esc(o.opname_no) +
|
||
"</b><span>Lokasi/Tanggal</span><b>" +
|
||
esc(o.location_name) +
|
||
" / " +
|
||
esc(o.opname_date) +
|
||
"</b><span>Status</span><div>" +
|
||
status(o.status) +
|
||
'</div></div><div class="alert alert-info">Hasil hilang/rusak tidak langsung membuat jurnal. Setelah opname diposting, proses mutasi Hilang/Rusak pada aset terkait agar nilai buku dan jurnal tetap terkontrol.</div>' +
|
||
lines,
|
||
);
|
||
$(".opname-line-grid").each(function (i) {
|
||
$(this).find(".actual-status").val(o.lines[i].actual_status);
|
||
});
|
||
var a =
|
||
'<button class="btn btn-light" data-bs-dismiss="modal">Tutup</button>';
|
||
if (P.update && o.status === "draft")
|
||
a +=
|
||
'<button class="btn btn-primary save-opname" data-id="' +
|
||
o.id +
|
||
'">Simpan Hasil</button>';
|
||
if (P.create && o.status === "draft")
|
||
a +=
|
||
'<button class="btn btn-success state-opname" data-action="submit" data-id="' +
|
||
o.id +
|
||
'">Ajukan</button>';
|
||
if (P.approve && o.status === "submitted")
|
||
a +=
|
||
'<button class="btn btn-danger state-opname" data-action="reject" data-id="' +
|
||
o.id +
|
||
'">Tolak</button><button class="btn btn-success state-opname" data-action="approve" data-id="' +
|
||
o.id +
|
||
'">Setujui</button>';
|
||
if (P.post && o.status === "approved")
|
||
a +=
|
||
'<button class="btn btn-success state-opname" data-action="post" data-id="' +
|
||
o.id +
|
||
'">Posting</button>';
|
||
$("#opnameActions").html(a);
|
||
bootstrap.Modal.getOrCreateInstance(
|
||
document.getElementById("opnameModal"),
|
||
).show();
|
||
})
|
||
.catch(fail);
|
||
}
|
||
function reportUrl(kind) {
|
||
var p = new URLSearchParams({
|
||
type: $("#reportType").val(),
|
||
date_from: $("#reportFrom").val(),
|
||
date_to: $("#reportTo").val(),
|
||
});
|
||
return B + "fixedassets/" + kind + "?" + p.toString();
|
||
}
|
||
function runReport() {
|
||
request(reportUrl("report_data"))
|
||
.then(function (r) {
|
||
var data = r.data || [];
|
||
if (!data.length) {
|
||
$("#reportResult").html(
|
||
'<div class="empty-state">Tidak ada data.</div>',
|
||
);
|
||
return;
|
||
}
|
||
var keys = Object.keys(data[0]).filter(function (k) {
|
||
return typeof data[0][k] !== "object";
|
||
}),
|
||
h =
|
||
'<table class="table table-sm"><thead><tr>' +
|
||
keys
|
||
.map(function (k) {
|
||
return "<th>" + esc(k.replaceAll("_", " ")) + "</th>";
|
||
})
|
||
.join("") +
|
||
"</tr></thead><tbody>";
|
||
data.forEach(function (x) {
|
||
h +=
|
||
"<tr>" +
|
||
keys
|
||
.map(function (k) {
|
||
return "<td>" + esc(x[k]) + "</td>";
|
||
})
|
||
.join("") +
|
||
"</tr>";
|
||
});
|
||
$("#reportResult").html(h + "</tbody></table>");
|
||
})
|
||
.catch(fail);
|
||
}
|
||
$(function () {
|
||
initTable();
|
||
initSelects();
|
||
$("#btnNewAsset").on("click", function () {
|
||
resetForm();
|
||
bootstrap.Modal.getOrCreateInstance(
|
||
document.getElementById("assetFormModal"),
|
||
).show();
|
||
});
|
||
$("[name=source_type]").on("change", toggleSource);
|
||
$("[name=source_journal_mode]").on("change", toggleJournal);
|
||
$("[name=category_id]").on("change", function () {
|
||
var o = this.options[this.selectedIndex];
|
||
$("[name=masa_manfaat]").val(o.dataset.life || "");
|
||
$("[name=depreciation_method]").val(o.dataset.method || "straight_line");
|
||
$("[name=asset_account_id]").val(o.dataset.asset || "");
|
||
$("[name=accumulated_depreciation_account_id]").val(
|
||
o.dataset.accum || "",
|
||
);
|
||
$("[name=depreciation_expense_account_id]").val(o.dataset.expense || "");
|
||
});
|
||
$("#assetFileInput").on("change", function () {
|
||
Array.from(this.files).forEach(function (f) {
|
||
if (files.length < 10) files.push(f);
|
||
});
|
||
this.value = "";
|
||
renderFiles();
|
||
});
|
||
$("#assetForm").on("submit", saveAsset);
|
||
$(document)
|
||
.on("click", ".btn-detail", function () {
|
||
openDetail(this.dataset.id);
|
||
})
|
||
.on("click", ".btn-pdf", function () {
|
||
pdf(this.dataset.url);
|
||
})
|
||
.on("click", ".preview-file", function () {
|
||
pdf(this.dataset.url);
|
||
})
|
||
.on("click", ".action-post", function () {
|
||
postAction(this.dataset.action, this.dataset.id);
|
||
})
|
||
.on("click", ".asset-process", function () {
|
||
process(this.dataset.kind, this.dataset.id);
|
||
})
|
||
.on("click", "[data-master]", function () {
|
||
master(this.dataset.master);
|
||
})
|
||
.on("click", ".edit-category", function () {
|
||
master("category", JSON.parse(this.dataset.row));
|
||
})
|
||
.on("click", ".edit-location", function () {
|
||
master("location", JSON.parse(this.dataset.row));
|
||
})
|
||
.on("click", ".btn-opname", function () {
|
||
openOpname(this.dataset.id);
|
||
});
|
||
$("#masterForm").on("submit", saveMaster);
|
||
$("#btnNewOpname").on("click", newOpname);
|
||
$("#btnRunReport").on("click", runReport);
|
||
$("#btnReportPdf").on("click", function () {
|
||
pdf(reportUrl("report_pdf"));
|
||
});
|
||
$("#btnReportExcel").on("click", function () {
|
||
location.href = reportUrl("report_excel");
|
||
});
|
||
$(document)
|
||
.on("click", ".save-opname", function () {
|
||
var id = this.dataset.id,
|
||
lines = [];
|
||
$(".opname-line-grid").each(function () {
|
||
lines.push({
|
||
id: this.dataset.line,
|
||
is_found: $(this).find(".found").prop("checked") ? 1 : 0,
|
||
actual_location_id: $(this).find(".actual-location").val(),
|
||
actual_status: $(this).find(".actual-status").val(),
|
||
notes: $(this).find(".notes").val(),
|
||
});
|
||
});
|
||
request(B + "fixedassets/opname_save/" + id, {
|
||
method: "POST",
|
||
body: fd({ lines: JSON.stringify(lines) }),
|
||
})
|
||
.then(function (r) {
|
||
toast(r.message);
|
||
openOpname(id);
|
||
})
|
||
.catch(fail);
|
||
})
|
||
.on("click", ".state-opname", function () {
|
||
var id = this.dataset.id,
|
||
action = this.dataset.action;
|
||
AppAlert.confirm({
|
||
title: "Proses " + action + " opname?",
|
||
text: "Status dan audit trail akan diperbarui.",
|
||
}).then(function (x) {
|
||
if (x.isConfirmed)
|
||
request(B + "fixedassets/opname_state/" + id, {
|
||
method: "POST",
|
||
body: fd({ action: action }),
|
||
})
|
||
.then(function (r) {
|
||
toast(r.message);
|
||
location.reload();
|
||
})
|
||
.catch(fail);
|
||
});
|
||
});
|
||
});
|
||
var currentOpnameId = 0,
|
||
assetScanner = null;
|
||
var assetSwalLabels = {
|
||
"sw-date": "Tanggal proses",
|
||
"sw-location": "Lokasi tujuan",
|
||
"sw-notes": "Alasan / keterangan",
|
||
"sw-vendor": "Vendor maintenance",
|
||
"sw-description": "Pekerjaan maintenance",
|
||
"sw-cost": "Biaya maintenance",
|
||
"sw-next": "Jadwal maintenance berikutnya",
|
||
"sw-file": "Bukti maintenance",
|
||
"sw-type": "Jenis mutasi",
|
||
"sw-amount": "Nilai transaksi / hasil pelepasan",
|
||
"sw-account": "Akun lawan",
|
||
"op-location": "Lokasi yang akan diperiksa",
|
||
"op-date": "Tanggal asset opname",
|
||
"op-notes": "Ruang lingkup / catatan",
|
||
"pic-select": "Penanggung jawab baru",
|
||
"pic-date": "Tanggal efektif",
|
||
"pic-notes": "Alasan pergantian",
|
||
"rev-date": "Tanggal reversal",
|
||
"rev-reason": "Alasan reversal",
|
||
};
|
||
function styleAssetSwal(root) {
|
||
Object.keys(assetSwalLabels).forEach(function (id) {
|
||
var input = (root || document).querySelector("#" + id);
|
||
if (
|
||
!input ||
|
||
(input.previousElementSibling &&
|
||
input.previousElementSibling.classList.contains("asset-swal-label"))
|
||
)
|
||
return;
|
||
var label = document.createElement("label");
|
||
label.className = "asset-swal-label";
|
||
label.htmlFor = id;
|
||
label.textContent = assetSwalLabels[id];
|
||
input.parentNode.insertBefore(label, input);
|
||
});
|
||
}
|
||
new MutationObserver(function (records) {
|
||
records.forEach(function (record) {
|
||
Array.from(record.addedNodes || []).forEach(function (node) {
|
||
if (node.nodeType === 1) styleAssetSwal(node);
|
||
});
|
||
});
|
||
}).observe(document.body, { childList: true, subtree: true });
|
||
var baseDetailActions = detailActions;
|
||
detailActions = function (a) {
|
||
var out = baseDetailActions(a);
|
||
if (
|
||
P.update &&
|
||
a.workflow_status === "capitalized" &&
|
||
["active", "fully_depreciated"].indexOf(a.lifecycle_status) >= 0
|
||
)
|
||
out +=
|
||
'<button class="btn btn-outline-secondary change-pic" data-id="' +
|
||
a.id +
|
||
'">Ganti PIC</button>';
|
||
var latest = (a.events || [])[0];
|
||
if (
|
||
P.post &&
|
||
latest &&
|
||
latest.event_type !== "acquisition" &&
|
||
latest.event_type !== "reversal"
|
||
)
|
||
out +=
|
||
'<button class="btn btn-outline-danger reverse-event" data-id="' +
|
||
latest.id +
|
||
'">Reversal Event Terakhir</button>';
|
||
return out;
|
||
};
|
||
$(document).on("click", ".change-pic", function (e) {
|
||
e.preventDefault();
|
||
e.stopImmediatePropagation();
|
||
var id = this.dataset.id,
|
||
today = new Date().toISOString().slice(0, 10);
|
||
AppAlert.prompt({
|
||
title: "Ganti Penanggung Jawab",
|
||
html:
|
||
'<select id="pic-select" style="width:100%"></select><input id="pic-date" type="date" class="swal2-input" value="' +
|
||
today +
|
||
'"><textarea id="pic-notes" class="swal2-textarea" placeholder="Alasan pergantian PIC"></textarea>',
|
||
didOpen: function () {
|
||
$("#pic-select").select2({
|
||
dropdownParent: $(".swal2-container"),
|
||
placeholder: "Cari karyawan",
|
||
ajax: {
|
||
url: B + "fixedassets/lookup_employees",
|
||
delay: 250,
|
||
data: function (p) {
|
||
return { q: p.term || "", page: p.page || 1 };
|
||
},
|
||
processResults: function (r) {
|
||
return {
|
||
results: r.results || [],
|
||
pagination: r.pagination || { more: false },
|
||
};
|
||
},
|
||
},
|
||
});
|
||
},
|
||
preConfirm: function () {
|
||
return {
|
||
employee_id: $("#pic-select").val(),
|
||
date: $("#pic-date").val(),
|
||
notes: $("#pic-notes").val(),
|
||
};
|
||
},
|
||
}).then(function (x) {
|
||
if (x.isConfirmed) doPost("responsibility", id, x.value);
|
||
});
|
||
});
|
||
$(document).on("click", ".reverse-event", function (e) {
|
||
e.preventDefault();
|
||
e.stopImmediatePropagation();
|
||
var id = this.dataset.id,
|
||
today = new Date().toISOString().slice(0, 10);
|
||
AppAlert.prompt({
|
||
title: "Reversal Event Aset",
|
||
html:
|
||
'<input id="rev-date" type="date" class="swal2-input" value="' +
|
||
today +
|
||
'"><textarea id="rev-reason" class="swal2-textarea" placeholder="Alasan reversal wajib dan harus jelas"></textarea>',
|
||
preConfirm: function () {
|
||
var reason = $("#rev-reason").val();
|
||
if (!reason) {
|
||
Swal.showValidationMessage("Alasan reversal wajib diisi.");
|
||
return false;
|
||
}
|
||
return { date: $("#rev-date").val(), reason: reason };
|
||
},
|
||
}).then(function (x) {
|
||
if (x.isConfirmed) doPost("reverse", id, x.value);
|
||
});
|
||
});
|
||
$(document).on("click", ".btn-opname", function () {
|
||
currentOpnameId = this.dataset.id || 0;
|
||
});
|
||
$("#opnameModal")
|
||
.on("shown.bs.modal", function () {
|
||
if (!currentOpnameId) return;
|
||
request(B + "fixedassets/opname_detail/" + currentOpnameId)
|
||
.then(function (r) {
|
||
var o = r.opname,
|
||
box = $(
|
||
'<div class="form-section mt-3" id="opnameProof"><h6>Pindai & Bukti Asset Opname</h6></div>',
|
||
);
|
||
if (o.status === "draft")
|
||
box.append(
|
||
'<div class="input-group mb-2"><input id="assetScanValue" class="form-control" placeholder="Pindai/ketik kode aset atau QR"><button class="btn btn-outline-primary apply-asset-scan">Tandai Ditemukan</button><button class="btn btn-outline-dark start-asset-camera"><i class="bi bi-camera"></i> Kamera</button></div><div id="assetQrReader" class="d-none mb-2"></div>',
|
||
);
|
||
box.append(attachments(o.attachments));
|
||
if (o.status === "draft" && P.create)
|
||
box.append(
|
||
'<label class="btn btn-outline-primary mt-2">Tambah gambar/PDF<input type="file" class="opname-upload" accept="image/*,.pdf" multiple hidden></label>',
|
||
);
|
||
$("#opnameProof").remove();
|
||
$("#opnameBody").append(box);
|
||
})
|
||
.catch(fail);
|
||
})
|
||
.on("hidden.bs.modal", function () {
|
||
if (assetScanner && assetScanner.clear)
|
||
assetScanner.clear().catch(function () {});
|
||
assetScanner = null;
|
||
});
|
||
function applyAssetScan(raw) {
|
||
var code = decodeURIComponent(
|
||
String(raw || "")
|
||
.trim()
|
||
.split("/")
|
||
.filter(Boolean)
|
||
.pop() || "",
|
||
).replace(/^ASSET:/i, ""),
|
||
found = false;
|
||
$(".opname-line-grid").each(function () {
|
||
var label = $(this).find("b").text().trim();
|
||
if (label.indexOf(code) === 0) {
|
||
$(this).find(".found").prop("checked", true);
|
||
this.scrollIntoView({ behavior: "smooth", block: "center" });
|
||
$(this).addClass("approval-focus");
|
||
found = true;
|
||
return false;
|
||
}
|
||
});
|
||
toast(
|
||
found
|
||
? "Aset " + code + " ditandai ditemukan."
|
||
: "Kode aset tidak ada pada dokumen opname.",
|
||
found ? "success" : "warning",
|
||
);
|
||
}
|
||
$(document)
|
||
.on("click", ".apply-asset-scan", function () {
|
||
applyAssetScan($("#assetScanValue").val());
|
||
})
|
||
.on("keydown", "#assetScanValue", function (e) {
|
||
if (e.key === "Enter") {
|
||
e.preventDefault();
|
||
applyAssetScan(this.value);
|
||
}
|
||
})
|
||
.on("click", ".start-asset-camera", function () {
|
||
if (typeof Html5QrcodeScanner === "undefined") {
|
||
toast(
|
||
"Library kamera QR belum tersedia. Gunakan scanner atau input kode.",
|
||
"warning",
|
||
);
|
||
return;
|
||
}
|
||
$("#assetQrReader").removeClass("d-none");
|
||
if (assetScanner) return;
|
||
assetScanner = new Html5QrcodeScanner(
|
||
"assetQrReader",
|
||
{ fps: 10, qrbox: { width: 220, height: 220 } },
|
||
false,
|
||
);
|
||
assetScanner.render(
|
||
function (text) {
|
||
$("#assetScanValue").val(text);
|
||
applyAssetScan(text);
|
||
},
|
||
function () {},
|
||
);
|
||
});
|
||
$(document).on("change", ".opname-upload", function () {
|
||
var input = this,
|
||
id = currentOpnameId,
|
||
list = Array.from(input.files || []);
|
||
list
|
||
.reduce(function (chain, file) {
|
||
return chain.then(function () {
|
||
return uploadEntity("asset_opname", id, file);
|
||
});
|
||
}, Promise.resolve())
|
||
.then(function () {
|
||
toast("Bukti opname berhasil diunggah.");
|
||
input.value = "";
|
||
$("#opnameModal").trigger("shown.bs.modal");
|
||
})
|
||
.catch(fail);
|
||
});
|
||
$(document).on("click", '.state-opname[data-action="reject"]', function (e) {
|
||
e.preventDefault();
|
||
e.stopImmediatePropagation();
|
||
var id = this.dataset.id;
|
||
AppAlert.prompt({
|
||
title: "Tolak Asset Opname",
|
||
input: "textarea",
|
||
inputLabel: "Alasan penolakan wajib diisi",
|
||
inputValidator: function (v) {
|
||
if (!v) return "Alasan wajib diisi.";
|
||
},
|
||
}).then(function (x) {
|
||
if (x.isConfirmed)
|
||
request(B + "fixedassets/opname_state/" + id, {
|
||
method: "POST",
|
||
body: fd({ action: "reject", reason: x.value }),
|
||
})
|
||
.then(function (r) {
|
||
toast(r.message);
|
||
location.reload();
|
||
})
|
||
.catch(fail);
|
||
});
|
||
});
|
||
$(document).on("click", ".edit-asset", editAsset);
|
||
})(jQuery);
|