check and fix call mobile
This commit is contained in:
1 parent
b66fe69660
commit
e353ed6fc2
5 files changed
+473
-20
No files matched your search
@@ -52,10 +52,10 @@
|
|||||||
<input disabled value="{{ phone_obj.title }}" name="phone_titles" class="input" type="text" placeholder="عنوان">
|
<input disabled value="{{ phone_obj.title }}" name="phone_titles" class="input" type="text" placeholder="عنوان">
|
||||||
</div>
|
</div>
|
||||||
<div class="control is-expanded">
|
<div class="control is-expanded">
|
||||||
<input disabled value="{{ phone_obj.phone_number }}" name="phones" class="input an_phone" type="text" placeholder="تلفن">
|
<input disabled value="0{{ phone_obj.phone_number }}" name="phones" class="input an_phone" type="text" placeholder="تلفن">
|
||||||
</div>
|
</div>
|
||||||
<div class="control">
|
<div class="control">
|
||||||
<a href="tel:{{ phone_obj.phone_number }}" class="button is-success" title="تماس با {{ phone_obj.phone_number }}">
|
<a href="tel:0{{ phone_obj.phone_number }}" class="button is-success" title="تماس با {{ phone_obj.phone_number }}">
|
||||||
📞
|
📞
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
@@ -74,10 +74,10 @@
|
|||||||
<input disabled value="تلفن 1" name="phone_titles" class="input" type="text" placeholder="عنوان">
|
<input disabled value="تلفن 1" name="phone_titles" class="input" type="text" placeholder="عنوان">
|
||||||
</div>
|
</div>
|
||||||
<div class="control is-expanded">
|
<div class="control is-expanded">
|
||||||
<input disabled value="{{ customer.phone1 }}" name="phones" class="input an_phone" type="text" placeholder="تلفن">
|
<input disabled value="0{{ customer.phone1 }}" name="phones" class="input an_phone" type="text" placeholder="تلفن">
|
||||||
</div>
|
</div>
|
||||||
<div class="control">
|
<div class="control">
|
||||||
<a href="tel:{{ customer.phone1 }}" class="button is-success" title="تماس">
|
<a href="tel:0{{ customer.phone1 }}" class="button is-success" title="تماس">
|
||||||
📞
|
📞
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
@@ -93,10 +93,10 @@
|
|||||||
<input disabled value="تلفن 2" name="phone_titles" class="input" type="text">
|
<input disabled value="تلفن 2" name="phone_titles" class="input" type="text">
|
||||||
</div>
|
</div>
|
||||||
<div class="control is-expanded">
|
<div class="control is-expanded">
|
||||||
<input disabled value="{{ customer.phone2 }}" name="phones" class="input an_phone" type="text">
|
<input disabled value="0{{ customer.phone2 }}" name="phones" class="input an_phone" type="text">
|
||||||
</div>
|
</div>
|
||||||
<div class="control">
|
<div class="control">
|
||||||
<a href="tel:{{ customer.phone2 }}" class="button is-success" title="تماس">
|
<a href="tel:0{{ customer.phone2 }}" class="button is-success" title="تماس">
|
||||||
📞
|
📞
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
@@ -112,10 +112,10 @@
|
|||||||
<input disabled value="تلفن 3" name="phone_titles" class="input" type="text">
|
<input disabled value="تلفن 3" name="phone_titles" class="input" type="text">
|
||||||
</div>
|
</div>
|
||||||
<div class="control is-expanded">
|
<div class="control is-expanded">
|
||||||
<input disabled value="{{ customer.phone3 }}" name="phones" class="input an_phone" type="text">
|
<input disabled value="0{{ customer.phone3 }}" name="phones" class="input an_phone" type="text">
|
||||||
</div>
|
</div>
|
||||||
<div class="control">
|
<div class="control">
|
||||||
<a href="tel:{{ customer.phone3 }}" class="button is-success" title="تماس">
|
<a href="tel:0{{ customer.phone3 }}" class="button is-success" title="تماس">
|
||||||
📞
|
📞
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -75,6 +75,15 @@
|
|||||||
-webkit-overflow-scrolling: touch;
|
-webkit-overflow-scrolling: touch;
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* استایلهای نتایج استعلام */
|
||||||
|
.inquiry-box {
|
||||||
|
background-color: #f8fafc;
|
||||||
|
padding: 15px;
|
||||||
|
border-radius: 8px;
|
||||||
|
margin-top: 15px;
|
||||||
|
border: 1px solid #cbd5e1;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<div id="hesabdari" class="column is-12-mobile is-12-desktop">
|
<div id="hesabdari" class="column is-12-mobile is-12-desktop">
|
||||||
@@ -181,7 +190,7 @@
|
|||||||
data-owner="{{ sarresid.check_owner_name|default:'' }}"
|
data-owner="{{ sarresid.check_owner_name|default:'' }}"
|
||||||
data-phone="{{ sarresid.check_owner_phone|default:'' }}">
|
data-phone="{{ sarresid.check_owner_phone|default:'' }}">
|
||||||
<span class="icon is-small"><i class="fas fa-edit"></i></span>
|
<span class="icon is-small"><i class="fas fa-edit"></i></span>
|
||||||
<span>افزودن اطلاعات</span>
|
<span>ویرایش و استعلام</span>
|
||||||
</button>
|
</button>
|
||||||
{% else %}
|
{% else %}
|
||||||
<span class="has-text-grey-light">-</span>
|
<span class="has-text-grey-light">-</span>
|
||||||
@@ -276,7 +285,7 @@
|
|||||||
|
|
||||||
<input type="hidden" name="n_sarresidDateTime" id="NewSarResidVal" class="reset_input">
|
<input type="hidden" name="n_sarresidDateTime" id="NewSarResidVal" class="reset_input">
|
||||||
|
|
||||||
<div id="check_extra_fields" style="display: none; background-color: #f8fafc; padding: 15px; border-radius: 8px; margin-top: 15px; border: 1px solid #cbd5e1;">
|
<div id="check_extra_fields" class="inquiry-box" style="display: none;">
|
||||||
<p class="has-text-weight-bold mb-3 has-text-info">اطلاعات چک صیادی</p>
|
<p class="has-text-weight-bold mb-3 has-text-info">اطلاعات چک صیادی</p>
|
||||||
|
|
||||||
<div class="field">
|
<div class="field">
|
||||||
@@ -289,14 +298,15 @@
|
|||||||
<div class="field">
|
<div class="field">
|
||||||
<label class="label">شناسه صیادی (۱۶ رقم) <span class="has-text-danger">*</span></label>
|
<label class="label">شناسه صیادی (۱۶ رقم) <span class="has-text-danger">*</span></label>
|
||||||
<div class="control">
|
<div class="control">
|
||||||
<input name="check_sayyad_id" class="input reset_input is-required-check" type="text" inputmode="numeric" pattern="[0-9]{16}" minlength="16" maxlength="16" placeholder="فقط اعداد ۱۶ رقمی" title="لطفاً دقیقاً ۱۶ رقم وارد کنید">
|
<input id="new_check_sayyad" name="check_sayyad_id" class="input reset_input is-required-check sayyad-input-dynamic" data-target="new" type="text" inputmode="numeric" pattern="[0-9]{16}" minlength="16" maxlength="16" placeholder="فقط اعداد ۱۶ رقمی را وارد کنید تا استعلام خودکار انجام شود...">
|
||||||
</div>
|
</div>
|
||||||
|
<div id="new_inquiry_results" class="mt-3" style="display:none;"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<label class="label">نام و نامخانوادگی صاحب حساب <span class="has-text-danger">*</span></label>
|
<label class="label">نام و نامخانوادگی صاحب حساب <span class="has-text-danger">*</span></label>
|
||||||
<div class="control">
|
<div class="control">
|
||||||
<input name="check_owner_name" class="input reset_input is-required-check" type="text" placeholder="نام مالک چک">
|
<input name="check_owner_name" id="new_check_owner" class="input reset_input is-required-check" type="text" placeholder="نام مالک چک">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -310,7 +320,7 @@
|
|||||||
|
|
||||||
</section>
|
</section>
|
||||||
<footer class="modal-card-foot">
|
<footer class="modal-card-foot">
|
||||||
<button type="submit" class="button is-success">
|
<button type="submit" class="button is-success" id="new_submit_btn" style="display: none;">
|
||||||
<i class="fas fa-save ml-1"></i> ثبت اطلاعات
|
<i class="fas fa-save ml-1"></i> ثبت اطلاعات
|
||||||
</button>
|
</button>
|
||||||
<span class="button cancellBTNSR">لغو</span>
|
<span class="button cancellBTNSR">لغو</span>
|
||||||
@@ -323,7 +333,7 @@
|
|||||||
<div class="modal-background"></div>
|
<div class="modal-background"></div>
|
||||||
<div class="modal-card">
|
<div class="modal-card">
|
||||||
<header class="modal-card-head">
|
<header class="modal-card-head">
|
||||||
<p class="modal-card-title">تکمیل اطلاعات چک صیادی</p>
|
<p class="modal-card-title">ویرایش و استعلام چک صیادی</p>
|
||||||
<button type="button" class="delete" aria-label="close"></button>
|
<button type="button" class="delete" aria-label="close"></button>
|
||||||
</header>
|
</header>
|
||||||
<section class="modal-card-body">
|
<section class="modal-card-body">
|
||||||
@@ -338,8 +348,9 @@
|
|||||||
<div class="field">
|
<div class="field">
|
||||||
<label class="label">شناسه صیادی (۱۶ رقم) <span class="has-text-danger">*</span></label>
|
<label class="label">شناسه صیادی (۱۶ رقم) <span class="has-text-danger">*</span></label>
|
||||||
<div class="control">
|
<div class="control">
|
||||||
<input id="up_check_sayyad" name="check_sayyad_id" class="input reset_input" type="text" required inputmode="numeric" pattern="[0-9]{16}" minlength="16" maxlength="16" placeholder="فقط اعداد ۱۶ رقمی" title="لطفاً دقیقاً ۱۶ رقم وارد کنید">
|
<input id="up_check_sayyad" name="check_sayyad_id" class="input reset_input sayyad-input-dynamic" data-target="up" type="text" required inputmode="numeric" pattern="[0-9]{16}" minlength="16" maxlength="16" placeholder="فقط اعداد ۱۶ رقمی را وارد کنید تا استعلام خودکار انجام شود...">
|
||||||
</div>
|
</div>
|
||||||
|
<div id="up_inquiry_results" class="mt-3" style="display:none;"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="field">
|
<div class="field">
|
||||||
@@ -358,7 +369,7 @@
|
|||||||
|
|
||||||
</section>
|
</section>
|
||||||
<footer class="modal-card-foot">
|
<footer class="modal-card-foot">
|
||||||
<button type="submit" class="button is-info">
|
<button type="submit" class="button is-info" id="up_submit_btn" style="display: none;">
|
||||||
<i class="fas fa-check ml-1"></i> ذخیره اطلاعات
|
<i class="fas fa-check ml-1"></i> ذخیره اطلاعات
|
||||||
</button>
|
</button>
|
||||||
<span class="button cancellBTNSR">لغو</span>
|
<span class="button cancellBTNSR">لغو</span>
|
||||||
@@ -451,6 +462,7 @@
|
|||||||
// فرمتبندی اعداد (۳ رقم ۳ رقم)
|
// فرمتبندی اعداد (۳ رقم ۳ رقم)
|
||||||
// ----------------------------------------------------
|
// ----------------------------------------------------
|
||||||
function formatNumber(num) {
|
function formatNumber(num) {
|
||||||
|
if (!num) return "0";
|
||||||
return num.toString().replace(/\D/g, "").replace(/\B(?=(\d{3})+(?!\d))/g, ",");
|
return num.toString().replace(/\D/g, "").replace(/\B(?=(\d{3})+(?!\d))/g, ",");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -475,7 +487,163 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
// ----------------------------------------------------
|
// ----------------------------------------------------
|
||||||
// مدیریت فرم و نمایش دینامیک چک
|
// تبدیل JSON ثبت پاسارگاد به حالت خوانا و فارسی
|
||||||
|
// ----------------------------------------------------
|
||||||
|
function parseSabtData(dataObj) {
|
||||||
|
if (typeof dataObj === 'string') {
|
||||||
|
try { dataObj = JSON.parse(dataObj); } catch(e) { return dataObj; }
|
||||||
|
}
|
||||||
|
|
||||||
|
let onGoing = dataObj.onGoingAmount || 0;
|
||||||
|
let blocked = dataObj.blocked || 0;
|
||||||
|
let bounced = 0;
|
||||||
|
let cleared = 0;
|
||||||
|
|
||||||
|
if (dataObj.ownersInfo && dataObj.ownersInfo.length > 0) {
|
||||||
|
bounced = dataObj.ownersInfo[0].bouncedAmount || 0;
|
||||||
|
cleared = dataObj.ownersInfo[0].clearedAmount || 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// تبدیل از ریال به تومان (تقسیم بر ۱۰) و جدا کردن با کاما
|
||||||
|
let onGoingToman = formatNumber(onGoing / 10);
|
||||||
|
let blockedToman = formatNumber(blocked / 10);
|
||||||
|
let bouncedToman = formatNumber(bounced / 10);
|
||||||
|
let clearedToman = formatNumber(cleared / 10);
|
||||||
|
|
||||||
|
let colorClassSabt = bounced > 0 ? "is-danger" : "is-success";
|
||||||
|
|
||||||
|
return `
|
||||||
|
<div class="notification is-light ${colorClassSabt} py-3 px-4 mb-2 is-size-7">
|
||||||
|
<p class="mb-2"><strong><i class="fas fa-file-signature"></i> وضعیت ثبت در صیاد:</strong></p>
|
||||||
|
<ul style="list-style: none; margin: 0; padding: 0; line-height: 2;">
|
||||||
|
<li><i class="fas fa-money-bill-wave has-text-info"></i> <b>مبلغ در جریان:</b> <span dir="ltr">${onGoingToman}</span> تومان</li>
|
||||||
|
<li><i class="fas fa-ban has-text-danger"></i> <b>مبلغ مسدودی:</b> <span dir="ltr">${blockedToman}</span> تومان</li>
|
||||||
|
<li><i class="fas fa-exclamation-triangle has-text-warning"></i> <b>مبلغ برگشتی:</b> <span dir="ltr">${bouncedToman}</span> تومان</li>
|
||||||
|
<li><i class="fas fa-check-circle has-text-success"></i> <b>مبلغ پاس شده:</b> <span dir="ltr">${clearedToman}</span> تومان</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------
|
||||||
|
// استعلام خودکار با تایپ شدن کامل ۱۶ رقم صیادی
|
||||||
|
// ----------------------------------------------------
|
||||||
|
$(".sayyad-input-dynamic").on("input", function() {
|
||||||
|
let val = $(this).val();
|
||||||
|
let target = $(this).data('target'); // 'new' یا 'up'
|
||||||
|
let resultsContainer = $(`#${target}_inquiry_results`);
|
||||||
|
let submitBtn = $(`#${target}_submit_btn`);
|
||||||
|
|
||||||
|
// اگر طول رشته کمتر یا بیشتر از ۱۶ بود دکمه ثبت و نتایج را مخفی کن
|
||||||
|
if (val.length !== 16) {
|
||||||
|
resultsContainer.hide().html('');
|
||||||
|
submitBtn.hide();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// به محض رسیدن به ۱۶ رقم، استعلامها شروع میشود
|
||||||
|
let sayad_id = val;
|
||||||
|
|
||||||
|
resultsContainer.show().html(`
|
||||||
|
<div class="notification is-light is-info py-2 px-3 mb-2 is-size-7">
|
||||||
|
<i class="fas fa-spinner fa-spin"></i> در حال برقراری ارتباط با سامانههای بانک مرکزی و صیاد...
|
||||||
|
</div>
|
||||||
|
`);
|
||||||
|
submitBtn.hide();
|
||||||
|
|
||||||
|
let req1_CBI = $.ajax({
|
||||||
|
url: "{% url 'admin_panel:api_check_status_inquiry' %}",
|
||||||
|
data: { sayad_id: sayad_id },
|
||||||
|
method: "GET"
|
||||||
|
});
|
||||||
|
|
||||||
|
let req2_SABT = $.ajax({
|
||||||
|
url: "{% url 'admin_panel:api_check_sabt_inquiry' %}",
|
||||||
|
data: { sayad_id: sayad_id, national_id: '', id_type: 1 },
|
||||||
|
method: "GET"
|
||||||
|
});
|
||||||
|
|
||||||
|
// مدیریت همزمان هر دو درخواست
|
||||||
|
$.when(req1_CBI, req2_SABT).done(function(cbiResArray, sabtResArray) {
|
||||||
|
let resCBI = cbiResArray[0];
|
||||||
|
let resSABT = sabtResArray[0];
|
||||||
|
|
||||||
|
let htmlOutput = "";
|
||||||
|
|
||||||
|
// --- تحلیل نتیجه بانک مرکزی (رنگ چک) ---
|
||||||
|
if (resCBI.success && resCBI.data) {
|
||||||
|
let colorClassCBI = "is-info";
|
||||||
|
let statusText = resCBI.data.status || "";
|
||||||
|
|
||||||
|
// برطرف کردن باگ فاصلههای زیاد و مخفی در نام صاحب حساب
|
||||||
|
let ownerNameClean = (resCBI.data.owner_name || "").replace(/ /ig, ' ').replace(/\s+/g, ' ').trim();
|
||||||
|
|
||||||
|
if (statusText.includes("سفید")) colorClassCBI = "is-success";
|
||||||
|
else if (statusText.includes("زرد") || statusText.includes("نارنجی")) colorClassCBI = "is-warning";
|
||||||
|
else if (statusText.includes("قرمز") || statusText.includes("قهوه ای") || statusText.includes("برگشت")) colorClassCBI = "is-danger";
|
||||||
|
|
||||||
|
htmlOutput += `
|
||||||
|
<div class="notification is-light ${colorClassCBI} py-2 px-3 mb-2 is-size-7">
|
||||||
|
<strong><i class="fas fa-university"></i> وضعیت حساب صادرکننده در بانک مرکزی:</strong>
|
||||||
|
<br>وضعیت چک: <b>${statusText}</b>
|
||||||
|
<br>مالک حساب: <b>${ownerNameClean}</b>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
// بهروزرسانی فیلد نام صاحب حساب با متن اصلاحشده و بدون فاصله اضافی
|
||||||
|
$(`#${target}_check_owner`).val(ownerNameClean);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
htmlOutput += `
|
||||||
|
<div class="notification is-light is-danger py-2 px-3 mb-2 is-size-7">
|
||||||
|
<strong><i class="fas fa-exclamation-triangle"></i> خطا (بانک مرکزی):</strong> ${resCBI.error || 'ارتباط با بانک مرکزی برقرار نشد'}
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
// در صورت وجود خطا در استعلام، فیلد نام صاحب حساب خالی شود
|
||||||
|
$(`#${target}_check_owner`).val('');
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- تحلیل نتیجه سامانه ثبت صیاد ---
|
||||||
|
if (resSABT.success && resSABT.data) {
|
||||||
|
htmlOutput += parseSabtData(resSABT.data);
|
||||||
|
} else {
|
||||||
|
let sabtErrorMsg = resSABT.error || 'سیستم استعلام ثبت در دسترس نیست';
|
||||||
|
|
||||||
|
// بررسی خطای کد 524 و عدم ثبت چک توسط صادرکننده
|
||||||
|
if (sabtErrorMsg.includes("کارتابل مشتري وجود ندارد") || sabtErrorMsg.includes("524")) {
|
||||||
|
sabtErrorMsg = "مشتری هنوز چک را برای شما (کد ملی 1219968218) ثبت نکرده، از مشتری بخواهید چک را ثبت کند.";
|
||||||
|
}
|
||||||
|
|
||||||
|
htmlOutput += `
|
||||||
|
<div class="notification is-light is-warning py-2 px-3 mb-2 is-size-7">
|
||||||
|
<strong><i class="fas fa-exclamation-triangle"></i> خطا (استعلام ثبت):</strong> ${sabtErrorMsg}
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// نمایش نتایج و نمایش دادن دکمه ثبت
|
||||||
|
resultsContainer.html(htmlOutput);
|
||||||
|
submitBtn.fadeIn();
|
||||||
|
|
||||||
|
}).fail(function() {
|
||||||
|
// در صورتی که کلا اینترنت سرور یا ریکوئست ایجکس فیِل شود
|
||||||
|
resultsContainer.html(`
|
||||||
|
<div class="notification is-light is-danger py-2 px-3 mb-2 is-size-7">
|
||||||
|
<strong><i class="fas fa-wifi"></i> خطای سرور در برقراری ارتباط با سامانههای استعلام (احتمالا کپچا یا قطعی شبکه)</strong>
|
||||||
|
</div>
|
||||||
|
`);
|
||||||
|
|
||||||
|
// در صورت خطای کلی شبکه یا سرور، نام صاحب حساب پاک شود
|
||||||
|
$(`#${target}_check_owner`).val('');
|
||||||
|
|
||||||
|
// با وجود خطا، دکمه ذخیره را نمایش میدهیم تا کاربر بتواند اطلاعات را دستی ذخیره کند
|
||||||
|
submitBtn.fadeIn();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// ----------------------------------------------------
|
||||||
|
// مدیریت فرم و نمایش دینامیک باکس چک
|
||||||
// ----------------------------------------------------
|
// ----------------------------------------------------
|
||||||
$("#sarresidTypeSelect").change(function(){
|
$("#sarresidTypeSelect").change(function(){
|
||||||
let selectedType = $(this).val();
|
let selectedType = $(this).val();
|
||||||
@@ -486,6 +654,9 @@
|
|||||||
$("#check_extra_fields").slideUp(250);
|
$("#check_extra_fields").slideUp(250);
|
||||||
$(".is-required-check").prop('required', false);
|
$(".is-required-check").prop('required', false);
|
||||||
$("#check_extra_fields input").val('');
|
$("#check_extra_fields input").val('');
|
||||||
|
$("#new_inquiry_results").hide().html('');
|
||||||
|
// برای نسیه دکمه ثبت مستقیما باز میشود
|
||||||
|
$("#new_submit_btn").show();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -501,9 +672,10 @@
|
|||||||
// ----------------------------------------------------
|
// ----------------------------------------------------
|
||||||
$("#newSarresid").click(function(){
|
$("#newSarresid").click(function(){
|
||||||
$(".modalAddNew").addClass("is-active");
|
$(".modalAddNew").addClass("is-active");
|
||||||
|
$("#new_submit_btn").hide(); // پیشفرض مخفی تا نوع سند انتخاب شود
|
||||||
});
|
});
|
||||||
|
|
||||||
// رویداد کلیک برای باز کردن مُدال تکمیل اطلاعات چک
|
// رویداد کلیک برای باز کردن مُدال ویرایش
|
||||||
$(".btn-update-check").click(function(){
|
$(".btn-update-check").click(function(){
|
||||||
let sarresidId = $(this).data('id');
|
let sarresidId = $(this).data('id');
|
||||||
let rowNum = $(this).data('row');
|
let rowNum = $(this).data('row');
|
||||||
@@ -522,6 +694,13 @@
|
|||||||
|
|
||||||
// باز کردن مُدال
|
// باز کردن مُدال
|
||||||
$(".modalUpdateCheck").addClass("is-active");
|
$(".modalUpdateCheck").addClass("is-active");
|
||||||
|
|
||||||
|
// اگر شناسه صیادی از قبل ۱۶ رقم است، استعلام خودکار فراخوانی شود
|
||||||
|
if ($("#up_check_sayyad").val().length === 16) {
|
||||||
|
$("#up_check_sayyad").trigger("input");
|
||||||
|
} else {
|
||||||
|
$("#up_submit_btn").show(); // اگر ناقص بود دکمه را نشان بده تا ویرایش کند
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// بستن تمامی مدالها
|
// بستن تمامی مدالها
|
||||||
@@ -531,6 +710,8 @@
|
|||||||
$('#priceInWords, #nextPriceInWords').text('');
|
$('#priceInWords, #nextPriceInWords').text('');
|
||||||
$("#check_extra_fields").hide();
|
$("#check_extra_fields").hide();
|
||||||
$(".is-required-check").prop('required', false);
|
$(".is-required-check").prop('required', false);
|
||||||
|
$("#new_inquiry_results, #up_inquiry_results").hide().html('');
|
||||||
|
$("#new_submit_btn, #up_submit_btn").hide();
|
||||||
});
|
});
|
||||||
|
|
||||||
// ----------------------------------------------------
|
// ----------------------------------------------------
|
||||||
|
|||||||
@@ -25,6 +25,9 @@ urlpatterns = [
|
|||||||
path('customers/add', views.customersAdd, name='customersAdd'),
|
path('customers/add', views.customersAdd, name='customersAdd'),
|
||||||
path('customer/<int:c_id>', views.customerNew2, name='customer'),
|
path('customer/<int:c_id>', views.customerNew2, name='customer'),
|
||||||
|
|
||||||
|
# === مسیرهای مربوط به استعلام چک صیادی ===
|
||||||
|
path('hesabdari/check/api/status-inquiry/', views.api_check_status_inquiry, name='api_check_status_inquiry'),
|
||||||
|
path('hesabdari/check/api/sabt-inquiry/', views.api_check_sabt_inquiry, name='api_check_sabt_inquiry'),
|
||||||
|
|
||||||
path('customer2/<int:c_id>', views.customerNew2, name='customer2'),
|
path('customer2/<int:c_id>', views.customerNew2, name='customer2'),
|
||||||
|
|
||||||
|
|||||||
+265
-1
@@ -4,6 +4,15 @@ from django.urls import reverse # تکراری در کد اصلی شما بود
|
|||||||
from persiantools.jdatetime import JalaliDate
|
from persiantools.jdatetime import JalaliDate
|
||||||
from persiantools import characters, digits
|
from persiantools import characters, digits
|
||||||
import datetime
|
import datetime
|
||||||
|
from django.http import JsonResponse
|
||||||
|
from asgiref.sync import async_to_sync
|
||||||
|
import asyncio
|
||||||
|
import cv2
|
||||||
|
import numpy as np
|
||||||
|
import ddddocr
|
||||||
|
import re
|
||||||
|
import base64
|
||||||
|
from playwright.async_api import async_playwright
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
from django.views.decorators.http import require_POST
|
from django.views.decorators.http import require_POST
|
||||||
from django.utils import timezone # جدید
|
from django.utils import timezone # جدید
|
||||||
@@ -2472,4 +2481,259 @@ def pardakhtNesiye(request, cs_id):
|
|||||||
except:
|
except:
|
||||||
return HttpResponse("خطا در ثبت ")
|
return HttpResponse("خطا در ثبت ")
|
||||||
|
|
||||||
#return HttpResponse(customerObj.id)
|
# =====================================================================
|
||||||
|
# توابع کمکی و منطق استعلام چک (برگرفته از اسکریپتهای شما)
|
||||||
|
# =====================================================================
|
||||||
|
|
||||||
|
def solve_base64_captcha(base64_string: str) -> str:
|
||||||
|
try:
|
||||||
|
if ',' in base64_string:
|
||||||
|
base64_string = base64_string.split(',')[1]
|
||||||
|
|
||||||
|
image_bytes = base64.b64decode(base64_string)
|
||||||
|
np_arr = np.frombuffer(image_bytes, np.uint8)
|
||||||
|
img = cv2.imdecode(np_arr, cv2.IMREAD_COLOR)
|
||||||
|
|
||||||
|
if img is None:
|
||||||
|
return ""
|
||||||
|
|
||||||
|
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
|
||||||
|
blur = cv2.medianBlur(gray, 3)
|
||||||
|
_, thresh = cv2.threshold(blur, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)
|
||||||
|
|
||||||
|
kernel = np.ones((2, 2), np.uint8)
|
||||||
|
cleaned_img = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel, iterations=1)
|
||||||
|
final_img = cv2.bitwise_not(cleaned_img)
|
||||||
|
|
||||||
|
success, encoded_image = cv2.imencode('.png', final_img)
|
||||||
|
if not success:
|
||||||
|
return ""
|
||||||
|
|
||||||
|
processed_bytes = encoded_image.tobytes()
|
||||||
|
ocr = ddddocr.DdddOcr(show_ad=False)
|
||||||
|
extracted_text = ocr.classification(processed_bytes)
|
||||||
|
|
||||||
|
numbers = re.findall(r'\d+', extracted_text)
|
||||||
|
if len(numbers) >= 2:
|
||||||
|
num1, num2 = int(numbers[0]), int(numbers[1])
|
||||||
|
if '+' in extracted_text:
|
||||||
|
ans = num1 + num2
|
||||||
|
elif '-' in extracted_text:
|
||||||
|
ans = num1 - num2
|
||||||
|
elif '*' in extracted_text or 'x' in extracted_text.lower():
|
||||||
|
ans = num1 * num2
|
||||||
|
else:
|
||||||
|
ans = num1 + num2
|
||||||
|
|
||||||
|
return str(ans)
|
||||||
|
return ""
|
||||||
|
except Exception:
|
||||||
|
return ""
|
||||||
|
|
||||||
|
def parse_cheque_result_html(html_content: str) -> dict:
|
||||||
|
sayad_id = re.search(r'شناسه\s*<b>?\s*(\d+)\s*</b>?', html_content)
|
||||||
|
owner_name = re.search(r'متعلق به\s*<b>?\s*(.*?)\s*</b>?\s*می باشد', html_content)
|
||||||
|
validity_date = re.search(r'تا تاریخ\s*<b>?\s*([\d/]+)\s*</b>?', html_content)
|
||||||
|
status = re.search(r'در وضعیت\s*<b>?\s*(.*?)\s*</b>?\s*در پایگاه', html_content)
|
||||||
|
|
||||||
|
clean_full_text = re.sub(r'<[^>]+>', '', html_content).strip()
|
||||||
|
|
||||||
|
return {
|
||||||
|
"success": True,
|
||||||
|
"data": {
|
||||||
|
"sayad_id": sayad_id.group(1) if sayad_id else "",
|
||||||
|
"owner_name": owner_name.group(1).strip() if owner_name else "",
|
||||||
|
"validity_date": validity_date.group(1) if validity_date else "",
|
||||||
|
"status": status.group(1).strip() if status else "",
|
||||||
|
"raw_message": clean_full_text
|
||||||
|
},
|
||||||
|
"error": None
|
||||||
|
}
|
||||||
|
|
||||||
|
async def run_cbi_sayad_inquiry_logic(sayad_id: str, max_retries: int = 7) -> str:
|
||||||
|
lightpanda_url = os.environ.get("LIGHTPANDA_URL") or "ws://127.0.0.1:9222"
|
||||||
|
async with async_playwright() as p:
|
||||||
|
try:
|
||||||
|
browser = await p.chromium.connect_over_cdp(lightpanda_url)
|
||||||
|
except Exception as e:
|
||||||
|
return json.dumps({"success": False, "data": None, "error": f"عدم اتصال به Lightpanda: {str(e)}"})
|
||||||
|
|
||||||
|
for attempt in range(1, max_retries + 1):
|
||||||
|
context = await browser.new_context()
|
||||||
|
page = await context.new_page()
|
||||||
|
|
||||||
|
try:
|
||||||
|
url = "https://cbi.ir/EstelamSayad/19689.aspx"
|
||||||
|
await page.goto(url, wait_until="domcontentloaded", timeout=20000)
|
||||||
|
|
||||||
|
captcha_selector = "#ctl00_ucBody_ucContent_ctl00_imgcpatcha"
|
||||||
|
await page.wait_for_selector(captcha_selector, state="visible", timeout=15000)
|
||||||
|
await asyncio.sleep(1)
|
||||||
|
|
||||||
|
base64_src = await page.locator(captcha_selector).get_attribute("src")
|
||||||
|
viewstate = await page.locator("#__VIEWSTATE").get_attribute("value") or ""
|
||||||
|
viewstate_gen = await page.locator("#__VIEWSTATEGENERATOR").get_attribute("value") or "453B48D8"
|
||||||
|
|
||||||
|
if not base64_src or not viewstate:
|
||||||
|
await context.close()
|
||||||
|
continue
|
||||||
|
|
||||||
|
captcha_answer = solve_base64_captcha(base64_src)
|
||||||
|
if not captcha_answer:
|
||||||
|
await context.close()
|
||||||
|
continue
|
||||||
|
|
||||||
|
ajax_raw_response = await page.evaluate("""
|
||||||
|
async ({ sayadId, captchaAns, viewState, viewStateGen }) => {
|
||||||
|
try {
|
||||||
|
const formData = new URLSearchParams();
|
||||||
|
formData.append('ctl00$ucBody$ucContent$ctl00$ScriptManager1', 'ctl00$ucBody$ucContent$ctl00$UpdatePanel1|ctl00$ucBody$ucContent$ctl00$btnSearch');
|
||||||
|
formData.append('__EVENTTARGET', '');
|
||||||
|
formData.append('__EVENTARGUMENT', '');
|
||||||
|
formData.append('__VIEWSTATE', viewState);
|
||||||
|
formData.append('__VIEWSTATEGENERATOR', viewStateGen);
|
||||||
|
formData.append('__VIEWSTATEENCRYPTED', '');
|
||||||
|
formData.append('ctl00$ucHeader$txtQuickSearch', '');
|
||||||
|
formData.append('ctl00$ucBody$ucContent$ctl00$txtChequeNumber', sayadId);
|
||||||
|
formData.append('ctl00$ucBody$ucContent$ctl00$txtCaptchaInput', captchaAns);
|
||||||
|
formData.append('__ASYNCPOST', 'true');
|
||||||
|
formData.append('ctl00$ucBody$ucContent$ctl00$btnSearch', 'استعلام چک صیادی');
|
||||||
|
|
||||||
|
const response = await fetch(window.location.href, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
|
||||||
|
'X-Requested-With': 'XMLHttpRequest',
|
||||||
|
'Cache-Control': 'no-cache'
|
||||||
|
},
|
||||||
|
body: formData.toString()
|
||||||
|
});
|
||||||
|
return await response.text();
|
||||||
|
} catch (err) {
|
||||||
|
return 'FETCH_ERROR: ' + err.message;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
""", {'sayadId': sayad_id, 'captchaAns': captcha_answer, 'viewState': viewstate, 'viewStateGen': viewstate_gen})
|
||||||
|
|
||||||
|
if "FETCH_ERROR:" in ajax_raw_response:
|
||||||
|
await context.close()
|
||||||
|
await asyncio.sleep(1)
|
||||||
|
continue
|
||||||
|
|
||||||
|
error_match = re.search(r'id="ctl00_ucBody_ucContent_ctl00_lblError"[^>]*>(.*?)</span>', ajax_raw_response, re.DOTALL)
|
||||||
|
if error_match and error_match.group(1).strip():
|
||||||
|
clean_error = re.sub(r'<[^>]+>', '', error_match.group(1)).strip()
|
||||||
|
if "عبارت محاسباتی" in clean_error:
|
||||||
|
await context.close()
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
await context.close()
|
||||||
|
await browser.close()
|
||||||
|
return json.dumps({"success": False, "data": None, "error": clean_error})
|
||||||
|
|
||||||
|
result_match = re.search(r'id="ctl00_ucBody_ucContent_ctl00_lblChequeNumberResult"[^>]*>(.*?)</span>', ajax_raw_response, re.DOTALL)
|
||||||
|
if result_match:
|
||||||
|
raw_html = result_match.group(1)
|
||||||
|
parsed_result = parse_cheque_result_html(raw_html)
|
||||||
|
await context.close()
|
||||||
|
await browser.close()
|
||||||
|
return json.dumps(parsed_result)
|
||||||
|
|
||||||
|
await context.close()
|
||||||
|
except Exception:
|
||||||
|
await context.close()
|
||||||
|
await asyncio.sleep(1)
|
||||||
|
|
||||||
|
await browser.close()
|
||||||
|
return json.dumps({"success": False, "data": None, "error": "استعلام ناموفق بود (خطای شبکه یا تشخیص کپچا)"})
|
||||||
|
|
||||||
|
|
||||||
|
async def run_bpi_cheque_inquiry_logic(national_id: str, sayad_id: str, id_type: int = 1):
|
||||||
|
lightpanda_url = os.environ.get("LIGHTPANDA_URL") or "ws://127.0.0.1:9222"
|
||||||
|
async with async_playwright() as p:
|
||||||
|
try:
|
||||||
|
browser = await p.chromium.connect_over_cdp(lightpanda_url)
|
||||||
|
except Exception as e:
|
||||||
|
return {"success": False, "error": f"عدم اتصال به مرورگر Lightpanda: {str(e)}"}
|
||||||
|
|
||||||
|
context = await browser.new_context()
|
||||||
|
page = await context.new_page()
|
||||||
|
|
||||||
|
try:
|
||||||
|
await page.goto("https://vbank.bpi.ir/public/inquiries/inquiry-bounced-cheques", wait_until="domcontentloaded", timeout=20000)
|
||||||
|
api_url = f"https://sec.bpi.ir/prls/api/v1/inquiry/chequeStatus?IdCode={national_id}&IdType={id_type}&SayadId={sayad_id}"
|
||||||
|
|
||||||
|
response = await context.request.get(
|
||||||
|
api_url,
|
||||||
|
headers={
|
||||||
|
"Referer": "https://vbank.bpi.ir/public/inquiries/inquiry-bounced-cheques",
|
||||||
|
"Accept": "application/json, text/plain, */*"
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
if response.ok:
|
||||||
|
data = await response.json()
|
||||||
|
return {"success": True, "data": data}
|
||||||
|
else:
|
||||||
|
error_text = await response.text()
|
||||||
|
return {"success": False, "error": f"سرور خطا برگرداند (کد {response.status}): {error_text}"}
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
return {"success": False, "error": f"خطایی در فرآیند استعلام رخ داد: {str(e)}"}
|
||||||
|
finally:
|
||||||
|
await context.close()
|
||||||
|
await browser.close()
|
||||||
|
|
||||||
|
|
||||||
|
# =====================================================================
|
||||||
|
# View های مربوط به استعلام (API)
|
||||||
|
# =====================================================================
|
||||||
|
|
||||||
|
@login_required(login_url='/panel/p_login/')
|
||||||
|
@permission_required('admin_panel.ViewStore', raise_exception=True)
|
||||||
|
def api_check_status_inquiry(request):
|
||||||
|
"""
|
||||||
|
استعلام وضعیت چک از بانک مرکزی
|
||||||
|
پارامتر ورودی: sayad_id (GET)
|
||||||
|
"""
|
||||||
|
sayad_id = request.GET.get('sayad_id', '').strip()
|
||||||
|
|
||||||
|
if not sayad_id or not sayad_id.isdigit() or len(sayad_id) != 16:
|
||||||
|
return JsonResponse({"success": False, "error": "شناسه صیادی باید دقیقاً ۱۶ رقم باشد."})
|
||||||
|
|
||||||
|
try:
|
||||||
|
# اجرای تابع Async به صورت Sync
|
||||||
|
result_json_str = async_to_sync(run_cbi_sayad_inquiry_logic)(sayad_id)
|
||||||
|
# تبدیل رشته جیسون به دیکشنری برای ارسال مجدد با JsonResponse
|
||||||
|
result_data = json.loads(result_json_str)
|
||||||
|
return JsonResponse(result_data)
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error in CBI Check Status Inquiry: {str(e)}", exc_info=True)
|
||||||
|
return JsonResponse({"success": False, "error": "خطای سرور در برقراری ارتباط با بانک مرکزی."})
|
||||||
|
|
||||||
|
|
||||||
|
@login_required(login_url='/panel/p_login/')
|
||||||
|
@permission_required('admin_panel.ViewStore', raise_exception=True)
|
||||||
|
def api_check_sabt_inquiry(request):
|
||||||
|
"""
|
||||||
|
استعلام ثبت چک از بانک پاسارگاد
|
||||||
|
پارامترهای ورودی: sayad_id, national_id, id_type (GET)
|
||||||
|
"""
|
||||||
|
sayad_id = request.GET.get('sayad_id', '').strip()
|
||||||
|
national_id = request.GET.get('national_id', '').strip()
|
||||||
|
id_type = _safe_int(request.GET.get('id_type', 1)) # عدد 1 حقیقی، 2 حقوقی
|
||||||
|
|
||||||
|
# اگر کدملی از فرانتاند خالی ارسال شد، از یک کدملی پیشفرض استفاده میکنیم
|
||||||
|
if not national_id:
|
||||||
|
national_id = "1219968218"
|
||||||
|
|
||||||
|
if not sayad_id:
|
||||||
|
return JsonResponse({"success": False, "error": "شناسه صیادی برای استعلام ثبت الزامی است."})
|
||||||
|
|
||||||
|
try:
|
||||||
|
# اجرای تابع Async به صورت Sync
|
||||||
|
result_data = async_to_sync(run_bpi_cheque_inquiry_logic)(national_id, sayad_id, id_type)
|
||||||
|
return JsonResponse(result_data)
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error in BPI Check Registration Inquiry: {str(e)}", exc_info=True)
|
||||||
|
return JsonResponse({"success": False, "error": "خطای سرور در برقراری ارتباط با سامانه بانک."})
|
||||||
+6
-1
@@ -4,4 +4,9 @@ persian==0.5.0
|
|||||||
persiantools==2.2.0
|
persiantools==2.2.0
|
||||||
django-extensions==3.2.1
|
django-extensions==3.2.1
|
||||||
boto3==1.34.131
|
boto3==1.34.131
|
||||||
botocore==1.34.131
|
botocore==1.34.131
|
||||||
|
playwright==1.44.0
|
||||||
|
opencv-python-headless==4.9.0.80
|
||||||
|
numpy==1.26.4
|
||||||
|
ddddocr==1.4.11
|
||||||
|
asgiref==3.4.1
|
||||||
Reference in new issue
Block a user