fix payment

This commit is contained in:
javad committed 2026-10-07 13:39:29 +03:30
1 parent 4f7486152c
commit bb3ddd1610
47 files changed
+2137 -51

No files matched your search

+1 -1
View File
@@ -48,7 +48,7 @@
const data = await response.json();
if (request !== controller || customer.value !== selectedCustomer) return;
if (!response.ok) {failureMessage = data.error || 'شناسه تولید نشد؛ دوباره تلاش کنید.'; throw new Error('generation');}
if (data.customer !== selectedCustomer || !/^[0-9]{13}$/.test(data.identifier)) {failureMessage = 'پاسخ سیستم معتبر نیست؛ دوباره تلاش کنید.'; throw new Error('generation');}
if (data.customer !== selectedCustomer || !/^9[0-9]{11}$/.test(data.identifier)) {failureMessage = 'پاسخ سیستم معتبر نیست؛ دوباره تلاش کنید.'; throw new Error('generation');}
if (input.value.trim() && input.value !== data.identifier) {
pending = data.identifier; candidate.textContent = pending; replacement.hidden = false;
status.textContent = 'شناسه آماده است؛ برای جایگزینی مقدار فعلی، دکمه جایگزینی را بزنید.';
+16
View File
@@ -13,6 +13,22 @@ async function copy(text){
}
}
document.querySelectorAll('[data-copy]').forEach(button=>button.addEventListener('click',()=>copy(button.dataset.copy)));
document.querySelectorAll('[data-notice-sms]').forEach(form=>form.addEventListener('submit',event=>{
if(form.dataset.sending==='true'){event.preventDefault();return;}
form.dataset.sending='true';const button=form.querySelector('button');button.disabled=true;button.textContent='در حال ثبت درخواست ارسال…';
}));
document.querySelectorAll('[data-share-link]').forEach(form=>form.addEventListener('submit',async event=>{
event.preventDefault();
const button=form.querySelector('button'),feedback=form.querySelector('[role="status"]');
button.disabled=true;button.setAttribute('aria-busy','true');feedback.textContent='در حال آماده‌سازی لینک…';
try{
const response=await fetch(form.action,{method:'POST',body:new FormData(form),credentials:'same-origin',cache:'no-store',headers:{'Accept':'application/json'}});
if(!response.ok || !response.headers.get('Content-Type')?.includes('application/json'))throw new Error('share unavailable');
const data=await response.json();
await copy(data.url);feedback.textContent='لینک آماده است؛ می‌توانید آن را برای دیگران ارسال کنید.';
}catch{feedback.textContent='دریافت لینک ممکن نشد. دوباره تلاش کنید یا لینک پرداخت دریافتی را باز کنید.';}
finally{button.disabled=false;button.removeAttribute('aria-busy');}
}));
const identifiers=document.querySelector('#identifier-accounts'),account=document.querySelector('#id_account'),deposit=document.querySelector('#id_deposit_id');
if(identifiers && account && deposit){const ids=JSON.parse(identifiers.textContent).map(String);function update(){deposit.required=ids.includes(account.value);document.querySelector('#identifier-help').textContent=deposit.required?'این حساب شناسه‌دار است؛ شناسه واریز مشتری الزامی است.':'شناسه واریز در صورت اعلام بانک وارد شود.';}account.addEventListener('change',update);update();}
const badge=document.querySelector('[data-payment-count]');
+8 -5
View File
@@ -42,8 +42,8 @@ fields.forEach(field=>{const controls=field.querySelectorAll('input,select,texta
let busy=false;
function showErrors(errors){
fields.forEach(field=>{const name=field.dataset.field,message=errors.get(name)||'',error=field.querySelector('.field-error');error.textContent=message;error.hidden=!message;field.querySelectorAll('input,select,textarea').forEach(control=>control.setAttribute('aria-invalid',String(Boolean(message))));});
const summary=document.querySelector('#receipt-errors'),list=summary.querySelector('ul');list.replaceChildren();errors.forEach((message,name)=>{const item=document.createElement('li'),link=document.createElement('a');link.href='#id_'+name;link.textContent=message;link.onclick=event=>{event.preventDefault();q(name).focus();q(name).scrollIntoView({block:'center'});};item.append(link);list.append(item);});summary.hidden=!errors.size;
if(errors.size){const first=q(errors.keys().next().value);first.focus();first.scrollIntoView({block:'center'});}
const summary=document.querySelector('#receipt-errors'),list=summary.querySelector('ul');list.replaceChildren();errors.forEach((message,name)=>{const item=document.createElement('li'),control=q(name);if(control && typeof control.focus==='function'){const link=document.createElement('a');link.href='#'+control.id;link.textContent=message;link.onclick=event=>{event.preventDefault();control.focus();control.scrollIntoView({block:'center'});};item.append(link);}else item.textContent=message;list.append(item);});summary.hidden=!errors.size;
if(errors.size){const first=q(errors.keys().next().value);if(first && typeof first.focus==='function'){first.focus();first.scrollIntoView({block:'center'});}else{summary.tabIndex=-1;summary.focus();summary.scrollIntoView({block:'center'});}}
}
async function validateImage(file){
if(!file)return 'تصویر فیش واریز را انتخاب کنید.';
@@ -53,7 +53,7 @@ async function validateImage(file){
}
let revision=0;form.addEventListener('input',()=>revision++);form.addEventListener('change',()=>revision++);
form.addEventListener('submit',async event=>{
event.preventDefault();if(busy)return;busy=true;button.disabled=true;const version=revision,errors=new Map();
event.preventDefault();if(busy)return;busy=true;button.disabled=true;button.textContent='در حال بررسی تصویر…';document.querySelector('#form-status').textContent='';const version=revision,errors=new Map();let navigating=false;
try{
const value=digits(q('paid_amount').value).trim();if(!/^\d+$/.test(value) && !/^\d{1,3}(?:[,٬ ]\d{3})+$/.test(value))errors.set('paid_amount','مبلغ را به ریال و با عدد صحیح وارد کنید.');else{const amount=BigInt(value.replace(/[,٬ ]/g,''));if(amount<=0n || amount>9007199254740991n)errors.set('paid_amount','مبلغ باید مثبت و در محدوده مجاز باشد.');}
if(q('receipt_mode').value==='linked' && !q('notice_id').value)errors.set('notice_id','اعلامیه را انتخاب کنید یا واریز مستقل را بزنید.');
@@ -63,8 +63,11 @@ form.addEventListener('submit',async event=>{
const imageError=await validateImage(q('receipt_image').files[0]);if(imageError)errors.set('receipt_image',imageError);
if(version!==revision){document.querySelector('#form-status').textContent='اطلاعات تغییر کرده است؛ دوباره ارسال را بزنید.';return;}
showErrors(errors);if(errors.size)return;
button.textContent='در حال ثبت…';HTMLFormElement.prototype.submit.call(form);return;
}finally{busy=false;if(button.textContent!=='در حال ثبت…')button.disabled=false;}
const result=await window.ReceiptUpload.send(form);
if(result.redirect_url){const destination=new URL(result.redirect_url,location.href);if(destination.origin!==location.origin)throw new Error('پاسخ ثبت فیش معتبر نیست.');navigating=true;location.assign(destination.href);return;}
showErrors(new Map(Object.entries(result.errors).map(([name,items])=>[name,items.map(item=>item.message).join(' ')])));
}catch(error){showErrors(new Map([['__all__',error.message || 'ارسال فیش انجام نشد؛ دوباره تلاش کنید.']]));}
finally{if(!navigating){busy=false;button.disabled=false;button.textContent='ثبت و ارسال فیش';}}
});
if(document.querySelector('#receipt-errors').hidden===false){const field=form.querySelector('[aria-invalid=true]');if(field){field.focus();field.scrollIntoView({block:'center'});}}
})();
+65
View File
@@ -0,0 +1,65 @@
/* Real multipart upload progress; registration remains authoritative on the server. */
(() => {
'use strict';
const number=new Intl.NumberFormat('fa-IR',{maximumFractionDigits:1});
function bytes(value){return value>=1048576?number.format(value/1048576)+' مگابایت':number.format(value/1024)+' کیلوبایت';}
function duration(seconds){return seconds>=60?number.format(Math.ceil(seconds/60))+' دقیقه':number.format(Math.max(1,Math.ceil(seconds)))+' ثانیه';}
function send(form){
const payload=new FormData(form),panel=document.querySelector('#receipt-upload');
const title=document.querySelector('#receipt-upload-title'),meter=document.querySelector('#receipt-upload-progress');
const percent=document.querySelector('#receipt-upload-percent'),amount=document.querySelector('#receipt-upload-bytes'),time=document.querySelector('#receipt-upload-time');
const button=document.querySelector('#submit-receipt');
const controls=[...form.querySelectorAll('input,select,textarea,button')].map(control=>[control,control.disabled]);
controls.forEach(([control])=>{control.disabled=true;});form.classList.add('is-uploading');button.setAttribute('aria-busy','true');
panel.hidden=false;title.textContent='در حال ارسال فیش…';button.textContent='در حال ارسال…';
meter.value=0;percent.textContent='۰٪';amount.textContent='در انتظار شروع ارسال';
const started=performance.now();let loaded=0,total=0,lastProgress=started,processing=false;
function updateTime(){
if(processing)return;
const elapsed=(performance.now()-started)/1000;
const estimate=loaded>0 && elapsed>=1 && total>loaded;
time.textContent='زمان سپری‌شده: '+number.format(Math.floor(elapsed))+' ثانیه'+(performance.now()-lastProgress>5000?' · منتظر ادامه ارسال؛ سرعت اتصال تغییر کرده است.':estimate?' · حدود '+duration((total-loaded)*elapsed/loaded)+' دیگر برای ارسال':' · در حال محاسبه زمان باقی‌مانده…');
}
function process(){
processing=true;meter.value=100;percent.textContent='۱۰۰٪';
title.textContent='ارسال کامل شد؛ در حال بررسی و ثبت فیش…';button.textContent='در حال ثبت…';
time.textContent='منتظر تأیید ثبت از سرور؛ زمان این مرحله به سرعت پردازش بستگی دارد.';
}
updateTime();panel.scrollIntoView({block:'nearest'});
return new Promise((resolve,reject)=>{
const xhr=new XMLHttpRequest(),timer=setInterval(updateTime,1000);let settled=false;
function finish(error,result){
if(settled)return;settled=true;clearInterval(timer);
if(!result?.redirect_url){
controls.forEach(([control,disabled])=>{control.disabled=disabled;});form.classList.remove('is-uploading');button.removeAttribute('aria-busy');
title.textContent='ثبت فیش انجام نشد';time.textContent='اطلاعات و تصویر حفظ شده‌اند؛ پس از رفع مشکل دوباره ارسال کنید.';
}
if(error)reject(error);else resolve(result);
}
xhr.upload.addEventListener('progress',event=>{
loaded=event.loaded;total=event.lengthComputable?event.total:0;lastProgress=performance.now();
amount.textContent='ارسال‌شده: '+bytes(loaded)+(total?' از '+bytes(total):'');
if(total){const value=Math.min(100,Math.floor(loaded/total*100));meter.value=value;percent.textContent=number.format(value)+'٪';}
else{meter.removeAttribute('value');percent.textContent='در حال ارسال';}
updateTime();
});
xhr.upload.addEventListener('load',process);
xhr.addEventListener('load',()=>{
const result=xhr.response;
if(xhr.status>=200 && xhr.status<300 && result?.redirect_url){finish(null,result);return;}
if(xhr.status===422 && result?.errors){finish(null,result);return;}
const message=[401,403,410].includes(xhr.status)?'دسترسی یا نشست شما معتبر نیست؛ صفحه را دوباره باز کنید.':xhr.status===413?'حجم تصویر بیش از حد مجاز سرور است؛ تصویر کوچک‌تری انتخاب کنید.':'ثبت فیش تأیید نشد؛ دوباره تلاش کنید. در صورت تکرار، وضعیت فیش‌های خود را بررسی کنید.';
finish(new Error(message));
});
xhr.addEventListener('error',()=>finish(new Error('ارتباط با سرور قطع شد؛ اتصال اینترنت را بررسی و دوباره ارسال کنید.')));
xhr.addEventListener('abort',()=>finish(new Error('ارسال متوقف شد؛ دوباره تلاش کنید.')));
try{
xhr.open('POST',form.action || location.href);xhr.responseType='json';
xhr.setRequestHeader('Accept','application/json');
xhr.setRequestHeader('X-CSRFToken',payload.get('csrfmiddlewaretoken'));
xhr.send(payload);
}catch(error){finish(error);}
});
}
window.ReceiptUpload=Object.freeze({send});
})();