Files
customerTicket/static/js/chaharsu/receipt-amount.js
T
2026-10-06 10:03:22 +03:30

79 lines
4.5 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* Local amount presentation: never convert rials to tomans for the server.
Exact BigInt arithmetic; preserve fractional toman (one rial = one tenth toman).
Shared with notice amounts; visible grouping does not replace server validation. */
(() => {
'use strict';
const input=document.querySelector('#id_paid_amount, #id_amount');if(!input)return;
input.inputMode='numeric';
const words=document.querySelector('#amount-words');
const small=['صفر','یک','دو','سه','چهار','پنج','شش','هفت','هشت','نه','ده','یازده','دوازده','سیزده','چهارده','پانزده','شانزده','هفده','هجده','نوزده'];
const tens=['','','بیست','سی','چهل','پنجاه','شصت','هفتاد','هشتاد','نود'];
const hundreds=['','صد','دویست','سیصد','چهارصد','پانصد','ششصد','هفتصد','هشتصد','نهصد'];
const scales=['','هزار','میلیون','میلیارد','تریلیون','کوادریلیون'];
const ascii=value=>value.replace(/[۰-۹]/g,digit=>String(digit.charCodeAt(0)-1776)).replace(/[٠-٩]/g,digit=>String(digit.charCodeAt(0)-1632));
const digitCount=value=>(ascii(value).match(/\d/g) || []).length;
const separators=/[,٬ \u00a0]/g;
const grouped=value=>value.replace(/\B(?=(\d{3})+(?!\d))/g,'٬').replace(/\d/g,digit=>'۰۱۲۳۴۵۶۷۸۹'[digit]);
function threeDigits(value){
const parts=[];
if(value>=100)parts.push(hundreds[Math.floor(value/100)]);
const rest=value%100;
if(rest && rest<20)parts.push(small[rest]);
if(rest>=20){parts.push(tens[Math.floor(rest/10)]);if(rest%10)parts.push(small[rest%10]);}
return parts.join(' و ');
}
function numberWords(value){
if(value===0n)return 'صفر';
const parts=[];let index=0;
while(value>0n){
const group=Number(value%1000n);
if(group){const text=group===1 && index===1?'هزار':threeDigits(group)+(scales[index]?' '+scales[index]:'');parts.unshift(text);}
value/=1000n;index++;
}
return parts.join(' و ');
}
function tomanWords(rials){
const whole=rials/10n,fraction=rials%10n;
if(!fraction)return numberWords(whole)+' تومان';
return (whole?numberWords(whole)+' و ':'')+numberWords(fraction)+' دهم تومان';
}
function caretAfterDigits(value,count){
if(!count)return 0;
for(let index=0;index<value.length;index++)if(/\d/.test(ascii(value[index])) && --count===0)return index+1;
return value.length;
}
function formatAmount(event){
if(event?.isComposing)return;
const raw=input.value,start=input.selectionStart ?? raw.length,end=input.selectionEnd ?? start;
const normalized=ascii(raw).trim();
const valid=/^\d+$/.test(normalized) || /^\d{1,3}(?:[,٬ \u00a0]\d{3})+$/.test(normalized);
// Re-group after genuine digit edits/deletion, but never sanitize letters, signs or decimal input.
const editing=event?.inputType?.startsWith('delete') || (event?.inputType==='insertText' && /^\d+$/.test(ascii(event.data || '')));
if(!valid && !(editing && /^[\d,٬ \u00a0]+$/.test(normalized))){if(words)words.textContent='';return;}
const digits=normalized.replace(separators,'').replace(/^0+(?=\d)/,'');
if(!digits || digits.length>16 || BigInt(digits)>BigInt(Number.MAX_SAFE_INTEGER)){if(words)words.textContent='';return;}
const leading=normalized.replace(separators,'').length-digits.length;
const left=Math.max(0,digitCount(raw.slice(0,start))-leading),right=Math.max(0,digitCount(raw.slice(0,end))-leading);
input.value=grouped(digits);
input.setSelectionRange(caretAfterDigits(input.value,left),caretAfterDigits(input.value,right));
if(words)words.textContent='معادل: '+tomanWords(BigInt(digits));
}
input.addEventListener('input',formatAmount);
input.addEventListener('compositionend',()=>formatAmount());
input.addEventListener('beforeinput',event=>{
const start=input.selectionStart,end=input.selectionEnd;
if(start!==end || event.isComposing)return;
const backward=event.inputType==='deleteContentBackward',forward=event.inputType==='deleteContentForward';
const adjacent=backward?start-1:start;
if((!backward && !forward) || !/[٬,]/.test(input.value[adjacent] || ''))return;
// Deleting a separator removes the adjacent digit, so grouping cannot trap the caret.
const target=backward?adjacent-1:adjacent+1;
const count=digitCount(input.value.slice(0,target));
const raw=input.value.slice(0,target)+input.value.slice(target+1);
event.preventDefault();input.value=ascii(raw).replace(separators,'');
input.setSelectionRange(count,count);input.dispatchEvent(new InputEvent('input',{bubbles:true,inputType:event.inputType}));
});
input.addEventListener('blur',()=>formatAmount());
formatAmount();
})();