yhart
February 6, 2025, 3:49am
1
Hi all i am from Indonesia.
There has been a recent change in the tax system here where they change the VAT to be 12% from the old 11%.
However essentially is the same 11%, just that value of taxable supply changes.
Old regulation:
Sub-total of Invoice = 100
value of taxable supply (DPP) = 100
VAT 11% = 11
total = 111
New regulation:
Sub-total of Invoice = 100
value of taxable supply (DPP) = 11/12 x Sub-total = 91.666
VAT 12% = 11
total = 111
Now many of my customers (soon perhaps all) require us to have this value of taxable supply (DPP) amount printed on the invoice.
can we have that in manager??
yhart
February 6, 2025, 4:04am
3
oh wow sorry for not looking thorougly, seems like someone brought it up already!
will check on it thanks!
yhart
February 6, 2025, 4:51am
4
can you help revise the code and it’s shown like this.
remove Rp. and remove decimal
location of the DPP should be before the tax?
Can you share a screenshot of your Edit Form, I’m a little confused by the invoice you showed.
Script Updated to support :
Business data without Base Currency,
Sales with Withholding Tax,
Indonesian and English Language,
en_US and id_ID Number Format,
<script id="script-wrapper">
document.addEventListener("DOMContentLoaded", processSubtotal);
function processSubtotal() {
// Cari semua sel di kolom pertama yang mungkin mengandung "Subtotal" atau "Sub-total"
const subtotalCell = [...document.querySelectorAll("tbody td:first-child")]
.find(td => ["subtotal", "sub-total"].includes(td.textContent.trim().toLowerCase()));
if (!subtotalCell) return;
// Ambil baris tempat Subtotal ditemukan
const firstSubtotalRow = subtotalCell.closest("tr");
const subtotalValueCell = firstSubtotalRow.querySelector("td[data-value]") || firstSubtotalRow.querySelector("td:last-child");
if (!subtotalValueCell) return;
// Ambil nilai Subtotal
const rawValue = subtotalValueCell.dataset.value || subtotalValueCell.textContent.trim();
const isIdLocale = /,\d{2}$/.test(rawValue);
const isEnLocale = /\.\d{2}$/.test(rawValue);
let subtotalValue = isIdLocale
? parseFloat(rawValue.replace(/\./g, "").replace(",", "."))
: parseFloat(rawValue.replace(/,/g, ""));
if (isNaN(subtotalValue) || subtotalValue === 0) return;
// Hitung DPP Nilai Lain
let dppNilaiLain = Math.round((11 / 12) * subtotalValue * 100) / 100;
// Kloning baris Subtotal dan ubah menjadi DPP Nilai Lain
const dppRow = firstSubtotalRow.cloneNode(true);
const dppLabelCell = dppRow.querySelector("td[colspan]") || dppRow.querySelector("td:first-child");
const dppValueCell = dppRow.querySelector("td[data-value]") || dppRow.querySelector("td:last-child");
if (!dppLabelCell || !dppValueCell) return;
dppLabelCell.textContent = "DPP Nilai Lain";
const includeCurrencySymbol = subtotalValueCell.textContent.trim().includes("Rp");
// Format angka sesuai locale
const numberFormatter = new Intl.NumberFormat(isIdLocale ? "id-ID" : "en-US", {
minimumFractionDigits: 2,
maximumFractionDigits: 2
});
dppValueCell.textContent = includeCurrencySymbol
? `Rp ${numberFormatter.format(dppNilaiLain)}`
: numberFormatter.format(dppNilaiLain);
dppValueCell.setAttribute("data-value", dppNilaiLain.toFixed(2).replace(".", isIdLocale ? "," : "."));
// Sisipkan baris setelah Subtotal pertama
firstSubtotalRow.insertAdjacentElement("afterend", dppRow);
// Hapus script setelah eksekusi
document.getElementById("script-wrapper")?.remove();
}
</script>
Script optimization as suggested by @eko . Thanks for the advice!
yhart
February 6, 2025, 11:24am
9
Perfect!! i tried it and still got the decimals going, then i set the min and max fraction from 2 to 0. and its perfect.
thanks so much!
eko
February 6, 2025, 12:02pm
11
@Mabaega maybe consider changing that part of the code to:
// Format angka sesuai locale
const numberFormatter = new Intl.NumberFormat(isIdLocale ? "id-ID" : "en-US", {
minimumFractionDigits: isIdLocale ? 0 : 2,
maximumFractionDigits: isIdLocale ? 0 : 2
});
Thus for Indonesia (id-ID), we set both minimumFractionDigits and maximumFractionDigits to 0, thus no decimal places will be shown, and for the US (en-US), we keep minimumFractionDigits and maximumFractionDigits set to 2, ensuring that 2 decimal places are always shown.
yhart
February 7, 2025, 4:07am
12
Thanks for the update both of you! @eko @Mabaega you guys are awesome!
yhart
February 7, 2025, 4:36am
13
By the way i tried changing this code. but then it’s still reverting back to 2 digits.
i may not understand completely what is this id-ID and en-US means? is this related to the language of manager it’s shown as?
Mabaega
February 7, 2025, 5:20am
14
@yhart
I don’t understand why you choose to use numbers without decimals on your invoices. As far as I know, in the CoreTax system all numbers use two Decimal Places.
For your needs, you can change this section, replacing 2 with 0.
// Format angka sesuai locale
const numberFormatter = new Intl.NumberFormat(isIdLocale ? "id-ID" : "en-US", {
minimumFractionDigits: 2,
maximumFractionDigits: 2
});
to
// Format angka sesuai locale
const numberFormatter = new Intl.NumberFormat(isIdLocale ? "id-ID" : "en-US", {
minimumFractionDigits: 0,
maximumFractionDigits: 0
});
eko
February 7, 2025, 6:15am
15
id-ID stand for Indonesian language and country code, respectively. en-US for English and United States. These codes are used in internationalization (i18n) functions like Intl.NumberFormat() to apply locale-specific formatting rules, such as number formatting, currency symbols, and date/time formats.
yhart
February 7, 2025, 8:04am
16
my customer’s requirements invoices to be without decimals.
yhart
August 14, 2025, 3:49am
17
@mabaega I have sinced updated to 25.8 version and my footers doesnt work anymore (both retention field and DPPBL). is there some changes in the code to be done?
You can try using a custom theme and moving the footer script to that theme.
This should work.
yhart
August 14, 2025, 7:08am
19
Below is the code, i copied all directly but doesnt work. The invoice just goes blank. where does it need modification?
%%Retention (%)%%
yhart
August 14, 2025, 7:11am
22
%%Retention (%)%%
document.addEventListener("DOMContentLoaded", processSubtotal);
function processSubtotal() {
// Locate "Sub-Total" row to fetch Retention percentage base
const subTotalCell = [...document.querySelectorAll("tbody td:first-child")]
.find(td => td.textContent.trim().toLowerCase() === "sub-total");
if (!subTotalCell) return;
const subTotalRow = subTotalCell.closest("tr");
const subTotalValueCell = subTotalRow.querySelector("td[data-value]") || subTotalRow.querySelector("td:last-child");
if (!subTotalValueCell) return;
const rawValue = subTotalValueCell.dataset.value || subTotalValueCell.textContent.trim();
const isIdLocale = /,\d{2}$/.test(rawValue);
let subTotalValue = isIdLocale
? parseFloat(rawValue.replace(/\./g, "").replace(",", "."))
: parseFloat(rawValue.replace(/,/g, ""));
if (isNaN(subTotalValue) || subTotalValue === 0) return;
// Fetch Retention Percentage from UI Element
let retentionText = document.getElementById("retention-value")?.textContent.trim();
let retentionPercentage = parseFloat(retentionText.replace(/[^\d.]/g, "")); // Extract only numbers
if (isNaN(retentionPercentage) || retentionPercentage < 0 || retentionPercentage > 100) {
retentionPercentage = 10; // Default to 10% if invalid
}
// Compute Retention
let Retention = Math.round(subTotalValue * (retentionPercentage / 100));
// Locate "Total" row for Balance Due calculation
const totalCell = [...document.querySelectorAll("tbody td:first-child")]
.find(td => td.textContent.trim().toLowerCase() === "total");
if (!totalCell) return;
const totalRow = totalCell.closest("tr");
const totalValueCell = totalRow.querySelector("td[data-value]") || totalRow.querySelector("td:last-child");
if (!totalValueCell) return;
// Extract and parse Total value
const rawTotalValue = totalValueCell.dataset.value || totalValueCell.textContent.trim();
let totalValue = isIdLocale
? parseFloat(rawTotalValue.replace(/\./g, "").replace(",", "."))
: parseFloat(rawTotalValue.replace(/,/g, ""));
if (isNaN(totalValue)) return;
// Compute Balance Due using Total
let BalanceDue = totalValue - Retention; // ✅ Correct formula
// Format numbers
const numberFormatter = new Intl.NumberFormat(isIdLocale ? "id-ID" : "en-US", {
minimumFractionDigits: 0,
maximumFractionDigits: 0
});
const includeCurrencySymbol = totalValueCell.textContent.trim().includes("Rp");
function createRow(label, value) {
const newRow = totalRow.cloneNode(true);
const labelCell = newRow.querySelector("td[colspan]") || newRow.querySelector("td:first-child");
const valueCell = newRow.querySelector("td[data-value]") || newRow.querySelector("td:last-child");
if (!labelCell || !valueCell) return null;
labelCell.textContent = label;
valueCell.textContent = includeCurrencySymbol
? `Rp ${numberFormatter.format(value)}`
: numberFormatter.format(value);
valueCell.setAttribute("data-value", value.toFixed(2).replace(".", isIdLocale ? "," : "."));
return newRow;
}
const retRow = createRow(`Retention (${retentionPercentage}%)`, Retention);
const baldueRow = createRow("Balance Due", BalanceDue); // ✅ Now correctly using Total - Retention
if (retRow && baldueRow) {
totalRow.insertAdjacentElement("afterend", retRow);
retRow.insertAdjacentElement("afterend", baldueRow);
}
document.getElementById("script-wrapper")?.remove();
}