745 lines
46 KiB
Markdown
745 lines
46 KiB
Markdown
# «قرار بعدی» بازدید و صفحهٔ انتظار — توضیح کامل عملکرد
|
||
|
||
این سند رفتار نهایی صفحهٔ `/panel/entezar/`، فیلد «تا تاریخ پیگیری نشود» در فرم
|
||
اقدام، و قواعد محاسبه را توضیح میدهد. هدف: بعد از این تغییر، **هماهنگیِ انجامشده
|
||
هم** مشتری را تا تاریخ توافق از فهرست خارج کند و کاربر بتواند بازدیدهای
|
||
«هماهنگشده» را از «هماهنگنشده» جدا ببیند.
|
||
|
||
---
|
||
|
||
## ۱. صورت مسئله
|
||
|
||
پیش از این تغییر، فقط دو حالت مشتری را از فهرست خارج میکرد:
|
||
|
||
1. اقدام `bazdid` با وضعیت `waite` و تاریخ آینده (بازدید برنامهریزیشده)
|
||
2. اقدام `hamahangi` با تاریخ **آینده** در فیلد تاریخ اقدام
|
||
|
||
اما سناریوی واقعی کارشناس این بود:
|
||
|
||
> «همین حالا با مشتری تماس گرفتم، توافق کردیم هفتهٔ بعد بیاییم، و تیک *انجام شد* را
|
||
> زدم.»
|
||
|
||
در این حالت، فیلد «تاریخ اقدام» روی **همین حالا** مینشیند، پس هیچ قرار آیندهای
|
||
وجود نداشت و مشتری در فهرست میماند و کارشناس دیگری دوباره تماس میگرفت.
|
||
|
||
**ریشه:** «تاریخ تماس» و «تاریخ قرار بعدی» دو مفهوم متفاوتاند ولی در یک فیلد
|
||
(`actionDateTime`) جمع شده بودند.
|
||
|
||
---
|
||
|
||
## ۲. راهحل: ستون «قرار بعدی»
|
||
|
||
دو ستون به مدل `Customer` اضافه شد:
|
||
|
||
| ستون | نوع | معنا |
|
||
|---|---|---|
|
||
| `visit_hold_until` | `DateTimeField(null=True)` | تا **پایان** این روز پیگیری لازم نیست |
|
||
| `visit_hold_set_at` | `DateTimeField(null=True)` | زمان ثبت، برای حسابرسی |
|
||
|
||
### قاعدهٔ طلایی: ستون، «مرجع» نیست
|
||
|
||
> `visit_hold_until` هیچ اعتباری بهتنهایی ندارد. یک قرار دستی فقط وقتی اثر دارد
|
||
> که **هر سه** شرط برقرار باشد:
|
||
>
|
||
> ۱. تاریخش از حالا جلوتر باشد،
|
||
> ۲. برای همان مشتری یک اقدام **هماهنگیِ فعال** (`waite` یا `finish`) وجود داشته باشد،
|
||
> ۳. **آخرین اقدامِ جایگزینکنندهٔ قرار، همان هماهنگی باشد** — اگر بعد از آن
|
||
> «بازدید» (`bazdid`) یا «نصب» (`nasb`) ثبت شده باشد، قرار منتفی است.
|
||
> اقدامهای دیگر (وصول، تماس ورودی/خروجی، …) قرار را باطل **نمیکنند**.
|
||
|
||
شرط ۳ برای این است که «ثبت بازدید پس از هماهنگی» قرار را خودکار باطل کند.
|
||
بدون آن، قرار قدیمی تا تاریخش زنده میماند و مشتری در فهرست «با قرار» میماند،
|
||
هرچند بازدید جای هماهنگی را گرفته است.
|
||
|
||
> **اصلاح ۲۰۲۶-۱۰-۲۲:** قبلاً «هر اقدام غیرهماهنگی» قرار را باطل میکرد؛ نتیجه
|
||
> این بود که یک سند وصول یا یک تماس ثبتشده، قرارِ توافقشده را بیصدا از بین
|
||
> میبرد و مشتری دوباره در فهرست تماس ظاهر میشد. حالا فقط `bazdid`/`nasb`
|
||
> (یعنی اقدامهایی که واقعاً جای قرار مینشینند) قرار را باطل میکنند
|
||
> (`visit_policy._SUPERSEDING_TYPES`).
|
||
|
||
نتیجهٔ این قاعده:
|
||
|
||
| رویداد | اثر روی قرار |
|
||
|---|---|
|
||
| حذف اقدام هماهنگی | قرار **خودکار بیاثر** میشود |
|
||
| لغو هماهنگی (`cancell`) | بیاثر میشود |
|
||
| ویرایش تاریخ هماهنگی | قرار با تاریخ جدید همراستا میشود |
|
||
| **ثبت بازدید یا نصب پس از هماهنگی** | قرار **خودکار بیاثر** میشود |
|
||
| ثبت سایر اقدامها (وصول، تماس…) | قرار دستنخورده میماند |
|
||
| کهنه ماندن ستون | **هیچ اثر مخربی ندارد** |
|
||
|
||
> **تکمیل ۲۰۲۶-۱۰-۲۷:** «نصب» در فهرستهای کار هم مثل «بازدید» عمل میکند:
|
||
> `nasb + waite` با تاریخ از امروز به بعد و `nasb + finish` از موعد به بعد، ردیف را
|
||
> از `?view=today` و `?view=today&missed=1` خارج میکند
|
||
> (`visit_policy._pending_appointment_ids` و `cycle_anchor_map`). این دو نوع همان
|
||
> `_CYCLE_RESET_TYPES` هستند که چرخهٔ ۹ ماهه و قرار دستی را هم بازنشانی میکنند.
|
||
|
||
به همین دلیل دیگر خطر «کش کهنه» وجود ندارد: منبع حقیقت همیشه جدول `Action` است.
|
||
|
||
---
|
||
|
||
## ۳. سه منبع «قرار بعدی»
|
||
|
||
تصمیم «مشتری در فهرست هست یا نه» از اجتماع سه منبع گرفته میشود:
|
||
|
||
| منبع | تعریف | تاریخ قرار | مرز زمانی |
|
||
|---|---|---|---|
|
||
| `manual` | ستون `visit_hold_until` (با اعتبارسنجی بالا) | همان ستون (پایان روز) | پایان امروز |
|
||
| `scheduled` | `bazdid + waite` | `actionDateTime` | **پایان روز** |
|
||
| `coordination` | `hamahangi` با `waite` یا `finish` | `actionDateTime` (یا `finishDateTime`) | **لحظهای** |
|
||
|
||
تابع واحد: `visit_policy.next_appointment_map(now)` — **همان تابعی که اسکنر پیامک
|
||
هم استفاده میکند**، پس صفحه و پیامک هرگز واگرا نمیشوند.
|
||
|
||
**چرا مرز زمانیِ دو منبع فرق دارد؟**
|
||
|
||
* در `scheduled`، `actionDateTime` یک **قرارِ آینده** است که کارشناس با تقویم
|
||
روزانه انتخاب میکند؛ پس «قرار امروز» باید تا پایان امروز معتبر بماند. با
|
||
مقایسهٔ لحظهای، بازدیدِ امروز بلافاصله پس از گذشتن ساعتش از فهرست ناپدید
|
||
میشد (باگی که گزارش شد).
|
||
* در `coordination`، `actionDateTime` **زمانِ تماس** است، نه قرار آینده. اگر آن
|
||
را هم تا پایان روز معتبر بگیریم، هر تماسِ امروز مشتری را تا آخر امروز از
|
||
گروه «قرار دستی» بیرون میبرد و سناریوی اصلی (تماس امروز + قرار دو هفته بعد)
|
||
بیاثر میشد.
|
||
|
||
اگر مشتری چند قرار داشته باشد، **زودترین** تاریخ ملاک نمایش است؛ اما برای
|
||
«بازگشت به فهرست» کافی است **آخرین** قرار منقضی شود.
|
||
|
||
---
|
||
|
||
## ۴. فرم ثبت اقدام (UI)
|
||
|
||
در مودال «ایجاد وظیفه» (`customerActionInc.html`):
|
||
|
||
| مورد | رفتار |
|
||
|---|---|
|
||
| نمایش فیلد | فقط وقتی نوع اقدام = **هماهنگی** انتخاب شود |
|
||
| برچسب | «تا تاریخ پیگیری نشود» |
|
||
| کنترل | تقویم شمسی (همان `pDatepicker` پروژه) |
|
||
| پیشفرض | اگر خالی بماند و تیک «انجام شد» زده شود ⇒ **+۱۴ روز** |
|
||
| سقف | **۶ ماه** (اعتبارسنجی سروری) |
|
||
| حالت ویرایش | فیلد با نوع اقدام همراستا میشود (نمایش/پنهان) **و مقدار ذخیرهشدهٔ مشتری در آن بارگذاری میشود** |
|
||
|
||
نکتهٔ مهم: فیلد «تاریخ و ساعتی که کار باید انجام شود» (`dateTimeAnjam`) همچنان
|
||
«زمان تماس» است و دست نخورده باقی میماند. دو مفهوم، دو فیلد.
|
||
|
||
### ۴.۱ چیدمان مودال (mobile first)
|
||
|
||
مودال به سه گام شمارهدار تقسیم شده تا کاربر بداند چه چیزی را کجا وارد کند:
|
||
|
||
| گام | محتوا |
|
||
|---|---|
|
||
| ۱ | نوع اقدام + مسئول (توسط) — کنار هم در دسکتاپ، زیر هم در موبایل |
|
||
| ۲ | زمانبندی: تاریخ انجام، «تا تاریخ پیگیری نشود»، و راهنمای بازشونده |
|
||
| ۳ | توضیحات، تیک «انجام شده»، تاریخ اتمام و نتیجه |
|
||
|
||
نکات پیادهسازی:
|
||
|
||
* پیشوند همهٔ کلاسها `ax-` و همهٔ انتخابگرها زیر `#ActionBox` است ⇒ هیچ استایلی
|
||
به سایر صفحات نشت نمیکند.
|
||
* روی موبایل: کارت تمامعرض (`calc(100% - 1rem)`)، بدنهٔ اسکرولشونده، هدف لمسی
|
||
حداقل ۲٫۶rem، و دکمههای پاصفحه تمامعرض.
|
||
* روی دسکتاپ (≥۷۶۸px): شبکهٔ دوستونه، عرض حداکثر ۴۴rem.
|
||
* سرصفحه نام مشتری را نشان میدهد تا کاربر بداند برای چه کسی وظیفه میسازد.
|
||
* **همان تقویم شمسی قبلی** استفاده میشود: `dateTimePickerAnjam`،
|
||
`dateTimePickerFinish` و `dateTimePickerHoldUntil` با `pDatepicker` و
|
||
`calendar.persian.locale = 'fa'`. هیچ کتابخانهٔ جدیدی اضافه نشده است.
|
||
* **نام کلاس فیلد باید با سلکتور JS یکی باشد.** هر فیلد تاریخ دو المان دارد:
|
||
ورودی نمایشی (با کلاس `dateTimePickerXxx`) و ورودی مخفیِ ارسال (با کلاس
|
||
`dateTimePickerXxx-alt`). اگر این نامها همخوانی نداشته باشند،
|
||
`pDatepicker` روی مجموعهٔ خالی اجرا میشود، `undefined` برمیگرداند و
|
||
`setDate()` با `TypeError` کل هندلر را میشکند — نتیجه: تقویم باز نمیشود و
|
||
بقیهٔ منطق (پنهانکردن فیلدها، برداشتن تیک خودکار) هم اجرا نمیشود.
|
||
دو تست اختصاصی
|
||
(`test_every_picker_selector_matches_markup` و
|
||
`test_every_alt_field_class_exists_in_markup`) همین را قفل کردهاند.
|
||
* `altField` با فرمت پیشفرض `unix` مقدار میدهد؛ بنابراین `visitHoldUntil`
|
||
یک timestamp میلیثانیهای است که `_parse_form_datetime` آن را میخواند.
|
||
* تقویم به `body` اضافه میشود (نه داخل مودال) ⇒ اسکرول بدنهٔ مودال آن را نمیبرد.
|
||
|
||
### ۴.۲ هشدار زمانبندی و پیشفرض «انجام شده» (فقط هماهنگی)
|
||
|
||
چون در عمل «ثبت هماهنگی» یعنی «همین حالا با مشتری تماس گرفتم»، انتخاب نوع
|
||
**هماهنگی** بهطور خودکار تیک «این کار انجام شده است» را میزند (و فیلدهای
|
||
تاریخ اتمام و نتیجه را نمایش میدهد). برای جلوگیری از ثبتِ ناخواسته، دو لایه
|
||
محافظ اضافه شده است:
|
||
|
||
**۱) هشدار همیشگی** — بهمحض تیکخوردن و تا وقتی نوع هماهنگی است، یک کادر
|
||
کهربایی زیر تیک نمایش داده میشود:
|
||
|
||
> آیا مطمئن هستی که هماهنگی را انجام دادی؟
|
||
> اگر هنوز هماهنگی نکردی، این تیک را بردار.
|
||
|
||
**۲) تأیید در لحظهٔ ثبت** — اگر کاربر روی «ثبت» بزند و نوع هماهنگی و تیک
|
||
«انجام شده» فعال باشد، ارسال متوقف میشود و همان کادر برجسته شده و دو دکمه
|
||
نشان میدهد:
|
||
|
||
| دکمه | نتیجه |
|
||
|---|---|
|
||
| بله، هماهنگی انجام شد | فرم ارسال میشود (هماهنگی با وضعیت `finish` ثبت میشود) |
|
||
| نه، تیک را بردار | تیک برداشته میشود، فیلدهای اتمام پنهان میشوند و ارسال نمیشود |
|
||
|
||
قواعد ایمنی:
|
||
|
||
* **فقط هماهنگی**: سایر انواع اقدام (بازدید، نصب، وصول، تماس…) هیچ تیک
|
||
خودکار و هیچ تأییدی ندارند و رفتارشان دقیقاً مثل قبل است.
|
||
* **تغییر نوع = برداشتن تیک خودکار**: اگر سیستم تیک زده باشد و کاربر نوع را
|
||
به غیر هماهنگی تغییر دهد، تیک برداشته میشود (تا ناخواسته `last_bazdid_dt`
|
||
یا وضعیت «انجام شده» ثبت نشود). اگر کاربر خودش تیک زده باشد، دستنخورده
|
||
میماند.
|
||
* **حالت ویرایش**: تأیید گرفته نمیشود (کاربر فقط رکورد موجود را اصلاح میکند)؛
|
||
اما کادر هشدار برای هماهنگیِ انجامشده بهصورت اطلاعاتی نمایش داده میشود.
|
||
* **باز شدن مجدد مودال**: تیک، هشدار و وضعیت تأیید همیشه ریست میشوند.
|
||
|
||
#### بارگذاری مقدار ذخیرهشده در حالت ویرایش
|
||
|
||
هر ردیف اقدام، مقدار فعلی `Customer.visit_hold_until` را بهصورت ISO در
|
||
صفت `customer_hold` نگه میدارد. هنگام کلیک روی ردیف (باز کردن فرم ویرایش)،
|
||
تابع `seedHoldPicker()` آن را به تقویم شمسی و فیلد مخفی `visitHoldUntil`
|
||
مینویسد.
|
||
|
||
> **باگی که این بخش رفع میکند:** قبلاً فرم ویرایش مقدار را از دیتابیس
|
||
> نمیخواند. مقدار در سرور **درست ذخیره میشد** ولی چون فیلد هنگام باز شدن
|
||
> مجدد خالی نمایش داده میشد، کاربر فکر میکرد «ثبت نشده است».
|
||
|
||
ترتیب کار مهم است: `seedHoldPicker()` **بعد از** `toggleCoordinationField()`
|
||
صدا زده میشود، چون برای انواع غیرهماهنگی، `toggle` فیلد را پاک میکند.
|
||
|
||
#### ایمنی ویرایش رکوردهای قدیمی
|
||
|
||
در حالت **ویرایش**، تغییر نوع اقدام به غیرهماهنگی، قرار دستی را پاک
|
||
**نمیکند**؛ زیرا رکورد ویرایششده ممکن است قدیمیتر از یک هماهنگیِ جدیدتر
|
||
باشد و پاککردن، قرار معتبر را از بین میبرد. اعتبارسنجی `manual_holds_map`
|
||
(شرط «آخرین فعالیت = هماهنگی») خودش قرار را بیاثر میکند.
|
||
|
||
در حالت **ایجاد** برعکس است: اقدام تازه آخرین فعالیت مشتری است، پس ثبت
|
||
بازدید جدید قرار قبلی را پاک میکند.
|
||
|
||
### ۴.۳ راهنمای بازشونده (فقط «هماهنگی»)
|
||
|
||
زیر فیلد تاریخ پیگیری، یک دکمهٔ راهنما با آیکون `!` قرار دارد که پنل توضیحات را
|
||
باز/بسته میکند. محتوای پنل:
|
||
|
||
* «این تاریخ چه کاری میکند؟» — تعریف ساده + اینکه تا پایان همان روز معتبر است.
|
||
* «مثال» — سناریوی واقعی با تاریخ نمونه.
|
||
* «نکات مهم» — خالی + تیک ⇒ ۱۴ روز، خالی + بدون تیک ⇒ بدون تعویق،
|
||
فقط تمدید (نه کوتاهکردن)، حذف/لغو هماهنگی ⇒ بازگشت فوری، سقف ۶ ماه،
|
||
تفاوت با «تاریخ بازدید پرسنل»، و توقف پیامک یادآوری در مدت تعویق.
|
||
|
||
رفتار:
|
||
|
||
| رویداد | نتیجه |
|
||
|---|---|
|
||
| انتخاب نوع = هماهنگی | دکمهٔ راهنما ظاهر میشود (پنل بسته) |
|
||
| کلیک روی دکمه | پنل باز/بسته میشود و `aria-expanded` بهروز میشود |
|
||
| تغییر نوع به غیر هماهنگی | فیلد و راهنما پنهان و پنل بسته میشود |
|
||
| باز شدن مجدد مودال (ایجاد یا ویرایش) | پنل همیشه بسته شروع میشود |
|
||
|
||
دسترسیپذیری: دکمه `aria-expanded` و `aria-controls="holdHelpPanel"` دارد و
|
||
پنل با `is-hidden` کنترل میشود.
|
||
|
||
### قواعد اعتبارسنجی (سرور)
|
||
|
||
* تاریخ قرار نباید **پیش از امروز** باشد ⇒ پیام: «تاریخ قرار نمیتواند پیش از امروز باشد.»
|
||
* حداکثر **۱۸۳ روز** (≈۶ ماه) جلوتر ⇒ پیام: «قرار بعدی حداکثر میتواند ۱۸۳ روز (۶ ماه) جلوتر باشد.»
|
||
* تاریخ «امروز» پذیرفته است و به **۲۳:۵۹:۵۹ همان روز** تبدیل میشود (تا پایان روز معتبر).
|
||
* اگر اعتبارسنجی رد شود، **هیچ اقدامی ذخیره نمیشود**.
|
||
|
||
### قاعدهٔ «تمدید مجاز، کوتاهکردن ممنوع» (max)
|
||
|
||
قرار ذخیرهشده همیشه **بزرگترین** تاریخِ ثبتشده است:
|
||
|
||
```
|
||
visit_hold_until = max(مقدار فعلی، تاریخ جدید)
|
||
```
|
||
|
||
| وضعیت | نتیجه |
|
||
|---|---|
|
||
| قرار فعلی ۱ آذر، کارشناس جدید ۸ آذر | **تمدید میشود به ۸ آذر** ✅ |
|
||
| قرار فعلی ۸ آذر، کارشناس جدید ۱ آذر | **تغییری نمیکند** (۸ آذر میماند) |
|
||
|
||
منطق: اگر مشتری گفته «۸ آذر تماس بگیرید»، سیستم نباید ۱ آذر زنگ بزند. در مقابل،
|
||
یک ثبت اشتباه نباید قرارِ طولانیِ قبلی را نابود کند.
|
||
|
||
**برای عقببردن قرار** (مثلاً مشتری گفت زودتر بیایید): هماهنگی را **حذف** و دوباره
|
||
با تاریخ جدید ثبت کنید.
|
||
|
||
|
||
---
|
||
|
||
## ۵. صفحهٔ انتظار: چهار گروه و فیلترها
|
||
|
||
تفکیک بر اساس **نوعِ منبع** است، نه «چند گروه ثابت». هر مشتری میتواند صفر تا
|
||
سه منبع داشته باشد:
|
||
|
||
| منبع | تعریف | فیلتر |
|
||
|---|---|---|
|
||
| `manual` | ستون `visit_hold_until` (تعویق از فرم هماهنگی) | `?view=manual` |
|
||
| `scheduled` | اقدام `bazdid` + `waite` با تاریخ آینده | `?view=scheduled` |
|
||
| `coordination` | اقدام `hamahangi` (waite/finish) با تاریخ آینده | `?view=coordination` |
|
||
|
||
> **باگ قبلی:** `coordination` با `scheduled` در یک گروه جمع میشد؛ نتیجه اینکه
|
||
> مشتریانی که فقط «هماهنگی» داشتند در نمای «بازدید برنامهریزیشده» دیده
|
||
> میشدند و برچسب «بازدید برنامهریزیشده» میگرفتند. حالا هر منبع فیلتر و
|
||
> ستون خودش را دارد.
|
||
|
||
### فیلترها (قابل بوکمارک و ترکیب با جستوجو)
|
||
|
||
| URL | نمایش | شامل |
|
||
|---|---|---|
|
||
| `/panel/entezar/` (بدون پارامتر) | **پیشفرض = نیازمند پیگیری امروز** | موعد تماسهای امروز |
|
||
| `/panel/entezar/?view=today` | نیازمند پیگیری امروز (یا تاریخ `fdate`) | موعد تماسهای آن روز |
|
||
| `?view=uncoordinated` | بدون قرار | بدون هیچ منبع |
|
||
| `?view=manual` | قرار دستی | هر مشتری با `manual` |
|
||
| `?view=scheduled` | بازدید برنامهریزیشده | هر مشتری با `scheduled` |
|
||
| `?view=coordination` | هماهنگی | هر مشتری با `coordination` |
|
||
| `?view=coordinated` | همهٔ دارای قرار | اجتماع هر سه منبع |
|
||
| `?view=all` | همهٔ معوقها | همه |
|
||
| مقدار نامعتبر | برگشت به پیشفرض (`today`)، **بدون خطا** | موعد تماسهای امروز |
|
||
|
||
چون یک مشتری میتواند چند منبع داشته باشد، در چند فیلتر دیده میشود؛ اما در
|
||
`?view=coordinated` فقط **یکبار** شمرده میشود. به همین دلیل جمع سه کارت منبع
|
||
میتواند از «با قرار» بیشتر باشد (صفحه این را در راهنمای بالای خود توضیح میدهد).
|
||
|
||
### ستونهای جدول
|
||
|
||
| ستون | توضیح |
|
||
|---|---|
|
||
| وضعیت قرار | «بدون قرار» / «قرار دستی» / «بازدید برنامه» / «هماهنگی» / «N قرار» |
|
||
| قرار دستی | تاریخ شمسی قرار دستی (یا —) |
|
||
| بازدید برنامهریزیشده | تاریخ شمسی اقدام بازدید با تاریخ آینده (یا —) |
|
||
| هماهنگی | تاریخ شمسی اقدام هماهنگی با تاریخ آینده (یا —) |
|
||
| آخرین بازدید | بدون تغییر نسبت به قبل |
|
||
|
||
### شمارندهٔ داشبورد
|
||
|
||
`CacheTable('9mahbazdid')` = **بدون قرار** (کارِ فوری). هر بار باز شدن صفحه
|
||
بهروز میشود. با تغییر تفکیک، این عدد **تغییر نمیکند**.
|
||
|
||
---
|
||
|
||
## ۶. تعامل با سایر بخشها
|
||
|
||
| بخش | اثر |
|
||
|---|---|
|
||
| **آخرین بازدید** (`last_visit_map`) | دستنخورده — فقط `bazdid + finish` |
|
||
| **چرخهٔ ۹ ماهه** (`cycle_anchor_map`) | دستنخورده — `bazdid` و `nasb` |
|
||
| **اسکنر پیامک** (`scan_visit`) | فقط یک منبع اضافه در `next_appointment_map` |
|
||
| **`recalc_last_bazdid`** | دستنخورده |
|
||
| **`last_bazdid_dt`** | بدون تغییر (نمایش/ترتیب) |
|
||
| **ایندکس `act_visit_idx`** | بدون تغییر |
|
||
|
||
قاعدهٔ طلایی حفظ شده است: **هماهنگی هرگز بازدید حساب نمیشود.**
|
||
|
||
---
|
||
|
||
## ۶.۱ حذف یک اقدام (soft-delete)
|
||
|
||
دکمهٔ «حذف» فقط در صفحهٔ اقدامهای مشتری (`/panel/customer/<id>/action`) نمایش
|
||
داده میشود و **هیچ رکوردی فیزیکی حذف نمیشود**؛ بهجای آن:
|
||
|
||
| ستون | مقدار |
|
||
|---|---|
|
||
| `status` | `cancell` |
|
||
| `deleted_at` | زمان حذف |
|
||
| `deleted_by` | کاربر حذفکننده |
|
||
|
||
### چرا `cancell` و نه ستون `is_deleted`؟
|
||
|
||
همهٔ محاسبات (`last_visit_map`، `cycle_anchor_map`، `appointment_sources_map`،
|
||
`followups_on_date`، `overdue_followups`) از قبل فقط `waite`/`finish` را
|
||
میبینند؛ و `ActionList*` و `ActionCount` هم با `status='waite'` فیلتر میشوند.
|
||
پس همین یک مقدار، اقدام را از **همهٔ** خروجیها خارج میکند — بدون Manager
|
||
سفارشی، بدون تغییر ایندکس پوشا و بدون دستزدن به ۳۱ کوئری.
|
||
|
||
### قاعدهٔ محصولی: اقدام دارای نظرسنجی حذف نمیشود
|
||
|
||
اگر `nazarDT` پر باشد (نظرسنجی ثبتشده)، حذف انجام **نمیشود**؛ چون هیچ
|
||
آرشیو/کپیای وجود ندارد و گزارش رضایتسنجی برای همیشه تغییر میکرد. در UI
|
||
دکمه به حالت قفلشده با برچسب «نظرسنجی» درمیآید.
|
||
|
||
### پیامدهای جانبی (در همان تراکنش)
|
||
|
||
۱. `last_bazdid_dt` بازمحاسبه میشود (کش کهنه نمیماند).
|
||
۲. قرار دستی اگر دیگر مرجعی نداشته باشد، خودکار بیاثر میشود.
|
||
۳. پیامکهای **ارسالنشدهٔ** «بازدید ثبت شد» همان مشتری به `cancelled` میروند
|
||
(پیامکهای ارسالشده دست نمیخورند).
|
||
|
||
### اثر روی مصرفکنندهها
|
||
|
||
| بخش | اثر |
|
||
|---|---|
|
||
| `/panel/entezar/` (همهٔ نماها) | اقدام کاملاً نادیده گرفته میشود |
|
||
| `/panel/ActionList/<u>/today` | از صف وظایف خارج میشود |
|
||
| `ActionCount` داشبورد | بلافاصله کم میشود |
|
||
| فهرست اقدامهای مشتری | بهطور پیشفرض پنهان (با `?status=cancell` قابل مشاهده برای حسابرسی) |
|
||
| `/panel/customer/<id>/nazar/` | پنهان |
|
||
| `/panel/ActionList/all` | پنهان |
|
||
| گزارش وصول (`finance.py`) | آخرین اقدام وصول دیگر آن را نمیبیند |
|
||
| داشبورد رضایتسنجی و گزارش روزانه | `deleted_at__isnull=True` |
|
||
| صف پیامک | `CANCELLED` در `DONE_STATUSES` ⇒ از صف بیرون میرود |
|
||
|
||
### امنیت
|
||
|
||
- فقط **POST** مجاز است؛ درخواست GET ریدایرکت میشود و چیزی را عوض نمیکند.
|
||
- `get_object_or_404(Action, id=..., customer=customer)` ⇒ حذف اقدام مشتری دیگر
|
||
ممکن نیست (۴۰۴).
|
||
- توکن CSRF در فرم اجباری است.
|
||
- تأیید دومرحلهای پیش از حذف (نوع اقدام در متن تأیید نمایش داده میشود).
|
||
|
||
---
|
||
|
||
## ۷. سناریوهای تستشده
|
||
|
||
| # | سناریو | نتیجهٔ انتظار | وضعیت |
|
||
|---|---|---|---|
|
||
| ۱ | هماهنگی با قرار «امروز» | تا پایان روز خارج از فهرست | ✅ |
|
||
| ۲ | هماهنگی finish بدون تاریخ قرار | پیشفرض ۱۴ روز | ✅ |
|
||
| ۳ | حذف اقدام هماهنگی | قرار بیاثر، مشتری برمیگردد | ✅ |
|
||
| ۴ | لغو هماهنگی (`cancell`) | قرار بیاثر | ✅ |
|
||
| ۵ | هماهنگی دوم با تاریخ کوچکتر | قرار قبلی حفظ میشود | ✅ |
|
||
| ۶ | هماهنگی دوم با تاریخ بزرگتر | قرار تمدید میشود | ✅ |
|
||
| ۷ | قرار بیش از ۶ ماه | رد با پیام، هیچ ذخیرهای | ✅ |
|
||
| ۸ | قرار در گذشته | رد با پیام | ✅ |
|
||
| ۹ | بازدید واقعی بعد از قرار | مشتری از فهرست خارج میماند | ✅ |
|
||
| ۱۰ | هماهنگی finish با تاریخ آینده (بدون ستون) | خارج از فهرست | ✅ |
|
||
| ۱۱ | تفکیک A/B/C/D و هر پنج فیلتر | اعداد و فهرست درست | ✅ |
|
||
| ۱۲ | `?view=nonsense` | پیشفرض، بدون خطا | ✅ |
|
||
| ۱۳ | اسکنر پیامک با قرار دستی | پیامک ارسال نمیشود | ✅ |
|
||
| ۱۴ | اجتماع منابع در `next_appointment_map` | زودترین تاریخ | ✅ |
|
||
|
||
جمع: **۳۷۶ تست موفق** در کل پروژه (۱۵ تست جدید مربوط به این قابلیت).
|
||
|
||
---
|
||
|
||
## ۸. استقرار
|
||
|
||
```bash
|
||
# ۱) بکاپ
|
||
python manage.py sqlite_db_backup # یا کپی فایل دیتابیس
|
||
|
||
# ۲) مهاجرت (دو ستون nullable — امن و سریع)
|
||
python manage.py migrate
|
||
|
||
# ۳) بررسی
|
||
python manage.py check
|
||
python manage.py test tests
|
||
```
|
||
|
||
### رفتار دادهٔ موجود
|
||
|
||
* همهٔ مشتریان با `visit_hold_until = NULL` شروع میکنند ⇒ **رفتار امروز دقیقاً حفظ میشود**.
|
||
* هماهنگیهای قدیمی (۵ رکورد سال ۲۰۲۲) هیچ ستونی ندارند و فقط از مسیر `Action`
|
||
ارزیابی میشوند.
|
||
|
||
---
|
||
|
||
## ۹. نکات عملیاتی
|
||
|
||
1. **حذف هماهنگی** بهترین راه برای «لغو قرار» است.
|
||
2. اگر قرار اشتباه ثبت شد، هماهنگی را **حذف** و دوباره ثبت کنید (قرار جدید فقط
|
||
میتواند تمدید کند، نه آن را کوتاهتر کند).
|
||
|
||
---
|
||
|
||
# بخش دوم — امکانات جدید رابط کاربری صفحهٔ انتظار
|
||
|
||
این بخش قابلیتهایی را توضیح میدهد که برای «راحت دیدن اطلاعات و تصمیم درست
|
||
گرفتن» اضافه شدهاند. طراحی **mobile first** است: در موبایل هر مشتری یک کارت
|
||
میشود و در دسکتاپ همان جدول قبلی؛ هیچ صفحه/منوی جدیدی اضافه نشده و شکل کلی
|
||
صفحه حفظ شده است.
|
||
|
||
## ۱. نوار آمار (بالای صفحه)
|
||
|
||
چهار کارت. عنوانها با **زبان کسبوکار** انتخاب شدهاند، نه زبان دیتابیس. هر کارت
|
||
هم عدد را نشان میدهد و هم **لینک فیلتر** است:
|
||
|
||
| کارت | معنا | توضیح زیر عدد |
|
||
|---|---|---|
|
||
| **نیازمند پیگیری امروز** | موعد تماسشان **امروز** است و بازدید ندارند — فهرست کارِ امروز | موعد تماسشان امروز است — بازدید ندارند |
|
||
| **همهٔ مشتریان عقبافتاده** | همه، با هر وضعیت پیگیری | با هر وضعیت پیگیری |
|
||
| **تعیین تکلیف نشده** | هنوز هیچ وقت بازدید/پیگیری ندارند | هنوز وقت بازدید تعیین نشده — نیاز به تماس |
|
||
| **تعیین تکلیف شده** | وقت بعدیشان مشخص است | وقت بعدیشان مشخص است |
|
||
|
||
> **چرا «مشتری» و نه «بازدید»؟** عددِ کارت دوم تعداد **مشتری** است، نه تعداد
|
||
> بازدید. یک مشتری میتواند دهها بازدید گذشته داشته باشد ولی فقط **یک بار**
|
||
> در این فهرست شمرده شود.
|
||
|
||
### تفکیک نوع قرار — در دراپداون، نه روی کارت
|
||
|
||
کارتها **عمداً ساده** نگه داشته شدهاند: فقط عنوان، عدد و یک توضیح. جزئیات سه
|
||
نوع قرار (تعویق / بازدید / پیگیری) روی کارت چاپ **نمیشود**؛ چون:
|
||
|
||
- شش عدد روی کارتها باعث سردرگمی میشد (بازخورد کاربران).
|
||
- ارتفاع کارتها نامساوی و چیدمان موبایل بههم میریخت.
|
||
- همان اطلاعات از دراپداون **«نوع قرار»** در نوار فیلتر در دسترس است.
|
||
|
||
> جمع سه منبع میتواند از عدد «تعیین تکلیف شده» بیشتر باشد، چون یک مشتری
|
||
> میتواند چند منبع قرار داشته باشد؛ در `?view=coordinated` هر مشتری فقط
|
||
> **یکبار** شمرده میشود.
|
||
|
||
## ۱.۱ نمای «نیازمند پیگیری امروز» (`?view=today`) — **پیشفرض صفحه**
|
||
|
||
> **پیشفرض:** با باز کردن `/panel/entezar/` (بدون پارامتر)، همین نما نمایش داده
|
||
> میشود؛ کاربر بلافاصله فهرست کارِ امروز را میبیند.
|
||
|
||
فهرست کاریِ روزانهٔ کارشناس: **با چه کسانی امروز باید تماس بگیریم؟**
|
||
|
||
### این فهرست از کجا میآید؟
|
||
|
||
دو منبع، هر دو با شرط «موعد = همان روز»:
|
||
|
||
| منبع | تعریف |
|
||
|---|---|
|
||
| **هماهنگی** | اقدام `hamahangi` با `status` در (`waite`/`finish`) و `actionDateTime` (وگرنه `finishDateTime`) در همان روز |
|
||
| **قرار دستی** | `Customer.visit_hold_until` که تاریخش همان روز است (با همان اعتبارسنجی «ستون، مرجع نیست») |
|
||
|
||
### قاعدهٔ قطعی: «بازدید» اینجا نمیآید
|
||
|
||
> اگر برای مشتری **قرار بازدید** (`bazdid + waite`) ثبت شده باشد، **هرگز** در این
|
||
> فهرست نمایش داده نمیشود — حتی اگر برای همان روز هم موعد پیگیری داشته باشد.
|
||
|
||
دلیلش روشن است: وقتی پرسنل برای بازدید میآید، تماس تلفنی لازم نیست.
|
||
|
||
**استثنای مهم:** بازدیدهای بسیار قدیمی که هرگز بسته نشدهاند (backlog) قرار فعال
|
||
محسوب نمیشوند؛ وگرنه یک رکورد کهنه میتوانست مشتری را برای همیشه از فهرست
|
||
پنهان کند. شرط دقیق: بازدیدِ در انتظار با تاریخ **≥ آغاز روز مورد نظر**.
|
||
|
||
### تفاوت با فهرست «عقبافتاده»
|
||
|
||
| | «نیازمند پیگیری» | «عقبافتاده» |
|
||
|---|---|---|
|
||
| شرط ورود | موعد تماس = آن روز | از آخرین بازدید بیش از دوره گذشته |
|
||
| مشتری سالم | ✅ اگر قرار تماس داشته باشد میآید | ❌ نمیآید |
|
||
| شرط `installDate` | ندارد | دارد |
|
||
| ترتیب پیشفرض | زودترین ساعت موعد | قدیمیترین بازدید |
|
||
|
||
### تقویم شمسی (گذشته و آینده)
|
||
|
||
نوار تاریخ فقط در این نما ظاهر میشود و شامل اینهاست:
|
||
|
||
- **تقویم شمسی** (`pDatepicker`) برای انتخاب هر روزی
|
||
- دکمههای **‹ روز قبل** و **روز بعد ›**
|
||
- دکمهٔ **بازگشت به امروز** (اگر تاریخ انتخابی امروز نباشد)
|
||
|
||
```
|
||
/panel/entezar/?view=today امروز
|
||
/panel/entezar/?view=today&fdate=1405/07/28 روز دیگر (شمسی)
|
||
```
|
||
|
||
- مقدار نامعتبر/خالی ⇒ **امروز** (بدون خطا).
|
||
- عدد کارت «نیازمند پیگیری امروز» **همیشه عدد امروز** را نشان میدهد، حتی وقتی
|
||
در تقویم روز دیگری را میبینید.
|
||
- فیلتر «میزان تأخیر» و تفکیک آن در این نما **پنهان** میشود (مشتری این فهرست
|
||
ممکن است اصلاً تأخیری نداشته باشد)؛ اما جستوجو و مرتبسازی فعال است.
|
||
|
||
### ستون «تأخیر» در این نما نمایش داده نمیشود
|
||
|
||
این فهرست دربارهٔ «موعد تماس امروز» است، نه عقبافتادگی بازدید؛ پس ستون «تأخیر»
|
||
فقط در سایر نماها دیده میشود. عرض آن (۸٪) به ستون «نام» اضافه میشود تا مجموع
|
||
عرض ستونها همچنان دقیقاً ۱۰۰٪ بماند (۸ ستون بهجای ۹).
|
||
|
||
### هشدار «پیگیریهای عقبافتاده»
|
||
|
||
اگر موعد پیگیری مشتریانی گذشته باشد و هنوز تعیین تکلیف نشده باشند، زیر نوار تاریخ
|
||
یک هشدار مینیمال قرمز با نقطهٔ کوچک نمایش داده میشود:
|
||
|
||
```
|
||
● 72 مورد نیازمند پیگیری از روزهای قبل باقی مانده
|
||
```
|
||
|
||
قواعد محاسبه (`visit_policy.overdue_followups`):
|
||
|
||
- مبنای هر مشتری، **آخرین** اقدام هماهنگی (`waite`/`finish`) است.
|
||
- اگر آن تاریخ **پیش از امروز** باشد ⇒ عقبافتاده.
|
||
- اگر مشتری هماهنگیِ امروز یا آینده داشته باشد ⇒ عقبافتاده **نیست**
|
||
(یعنی تمدید شده و از فهرست بیرون میرود).
|
||
- اگر برایش **قرار بازدید** ثبت شده باشد ⇒ حذف میشود (همان قاعدهٔ «بازدید گذاشته
|
||
شده ⇒ تماس لازم نیست»).
|
||
- هماهنگیِ لغوشده (`cancell`) اصلاً دیده نمیشود.
|
||
|
||
کلیک روی هشدار، همان فهرست را باز میکند:
|
||
|
||
```
|
||
/panel/entezar/?view=today&missed=1
|
||
```
|
||
|
||
در این حالت یک چیپ قرمز «نمایش N پیگیریِ عقبافتاده ✕ بازگشت به امروز» نشان داده
|
||
میشود و هشدار پنهان میگردد. عدد هشدار **دقیقاً** برابر تعداد ردیفهای همین
|
||
فهرست است (هر دو از یک مسیر محاسبه میشوند).
|
||
|
||
## ۲. فیلترها (نوار زیر کارتها)
|
||
|
||
یک ردیف فشرده با چهار کنترل؛ همه با هم ترکیبپذیرند و در URL حفظ میشوند.
|
||
|
||
| کنترل | پارامتر | مقادیر |
|
||
|---|---|---|
|
||
| نوع قرار | `view` | `all` · `uncoordinated` · `coordinated` · `manual` · `scheduled` · `coordination` |
|
||
| میزان تأخیر | `delay` | `lt30` · `30to90` · `90to180` · `gt180` (خالی = همه) |
|
||
| مرتبسازی | `sort` + `dir` | `last_visit` · `overdue` · `name` · `status` · `coordination` (و هر ستون دیگر) |
|
||
| جستوجو | `searchVal` + `searchOn` | `name` · `phone` · `code` · `address` |
|
||
|
||
- تغییر هر دراپداون ⇒ فرم **خودکار** ارسال میشود.
|
||
- دکمهٔ **✕ حذف فیلترها** فقط وقتی فیلتری فعال است ظاهر میشود و همه را به
|
||
حالت پیشفرض برمیگرداند.
|
||
- **کارتها هیچوقت با فیلتر تغییر نمیکنند** — همیشه کل جامعه را نشان میدهند.
|
||
فقط فهرست زیرشان فیلتر میشود.
|
||
|
||
## ۳. تفکیک مدت تأخیر (کلیکپذیر)
|
||
|
||
زیر نوار فیلتر، تعداد مشتریان بر اساس **مدت عقبافتادگی** در **نمای جاری**
|
||
نمایش داده میشود و هر عدد **لینک فیلتر** است:
|
||
|
||
- تا ۳۰ روز
|
||
- ۳۰ تا ۹۰ روز
|
||
- ۹۰ تا ۱۸۰ روز
|
||
- بیش از ۶ ماه
|
||
|
||
**مبنای محاسبه:** «موعد بازدید» = آخرین بازدید معتبر + دورهٔ قالب. برای
|
||
مشتریِ بدون بازدید، مبنای مقایسه مقطع دوره است تا عدد بیمعنا نشود.
|
||
یعنی «۳ ماه تأخیر» = «۳ ماه از موعد بازدید گذشته»، نه «۳ ماه از خود بازدید».
|
||
|
||
> جمع این چهار عدد همیشه برابر تعداد ردیفهای نمای جاری است (تستشده).
|
||
|
||
## ۴. مرتبسازی (ستونها و دراپداون)
|
||
|
||
**هر ۹ ستون** با کلیک روی سرستون مرتب میشوند؛ کلیک دوباره جهت را برمیگرداند:
|
||
|
||
| ستون | مبنا |
|
||
|---|---|
|
||
| نام و نام خانوادگی | الفبایی |
|
||
| وضعیت قرار | بدون قرار ← تعویق ← بازدید ← پیگیری ← چندقراره |
|
||
| قرار دستی | تاریخ قرار (خالیها آخر) |
|
||
| بازدید برنامهریزیشده | تاریخ قرار (خالیها آخر) |
|
||
| هماهنگی | تاریخ پیگیری (خالیها آخر) |
|
||
| آخرین بازدید | تاریخ مؤثر بازدید از `Action` |
|
||
| تأخیر | تعداد روز تأخیر |
|
||
| تلفن | مقدار `phone1` |
|
||
| آدرس | الفبایی |
|
||
|
||
- سرستون فعال با فلش **▲/▼** و ستونهای دیگر با **⇅** مشخص میشوند.
|
||
- **مقادیر خالی همیشه آخر میآیند**، مستقل از جهت — وگرنه مرتبسازی ستونهای
|
||
قرار، فهرست را بیمعنا میکرد.
|
||
- دراپداون «مرتبسازی» چند گزینهٔ پرکاربرد را کوتاه میکند.
|
||
- مقادیر قدیمی `sort=oldest|newest|overdue` همچنان کار میکنند (سازگاری با
|
||
بوکمارکهای قبلی).
|
||
|
||
## ۵. جستوجو
|
||
|
||
| فیلد | جستوجو در |
|
||
|---|---|
|
||
| نام و نام خانوادگی | `name_family` |
|
||
| تلفن | `phone1`، `phone2`، `phone3` |
|
||
| آدرس | `address` |
|
||
| کد مشتری | `idCode` |
|
||
|
||
نما، فیلتر تأخیر و مرتبسازی هنگام جستوجو **حفظ میشوند**.
|
||
|
||
## ۵. چیدمان ستونها (بازطراحی)
|
||
|
||
پیشتر ستون «ردیف» یک ستون کامل جدول را برای یک عدد دو رقمی میگرفت و دو ستون
|
||
با `min-width` ثابت (۱۹۰px و ۳۲۰px) تعیین عرض میکردند؛ نتیجه: بیتناسبی
|
||
فاصلهها و اسکرول افقی. تغییرات:
|
||
|
||
1. **ستون «ردیف» حذف شد.** شمارهٔ ردیف حالا یک **چیپ کوچک کنار نام** است
|
||
(`.ez-idx`) که هم فضا نمیخورد و هم برای پیدا کردن ردیف کافی است.
|
||
2. **عرض ستونها با `<colgroup>` درصدی** تعیین میشود و جدول
|
||
`table-layout: fixed` است. بنابراین:
|
||
- مجموع عرضها همیشه ۱۰۰٪ است،
|
||
- هیچ ستونی بیدلیل پهن نمیشود،
|
||
- اسکرول افقی در حالت جدول رخ نمیدهد.
|
||
3. **مقیاس فاصله یکسان:** همهٔ حاشیهها مضرب ۰٫۲۵rem (۴px) هستند.
|
||
4. **سرستون چسبان (sticky)** هنگام اسکرول عمودی.
|
||
5. **اعداد همعرض** (`tabular-nums`) تا ستون تاریخها لق نزند.
|
||
6. **تلفن با `direction: ltr`** تا ارقام در متن راستبهچپ جابهجا نشوند.
|
||
7. **آدرس حداکثر دو خط** (`line-clamp`) با متن کامل در `title`.
|
||
8. **تأخیر بهشکل چیپ رنگی** (خاکستری/کهربایی/قرمز/تیره) برای اسکن سریع.
|
||
|
||
| ستون | عرض | محتوا |
|
||
|---|---|---|
|
||
| نام و نام خانوادگی | ۲۳٪ | چیپ شماره + نام + کد مشتری |
|
||
| وضعیت قرار | ۱۳٪ | برچسب رنگی وضعیت |
|
||
| قرار هماهنگی | ۱۰٪ | تاریخ شمسی قرار دستی |
|
||
| بازدید برنامهریزیشده | ۱۱٪ | تاریخ قرار از `Action` |
|
||
| آخرین بازدید | ۱۰٪ | تاریخ مؤثر واقعی |
|
||
| تأخیر | ۹٪ | چیپ تعداد روز |
|
||
| تلفن | ۱۱٪ | شمارهٔ اول (LTR) |
|
||
| آدرس | ۱۳٪ | دو خط + `title` |
|
||
|
||
**نقطهٔ شکست (breakpoint):** تا **۸۹۹px** حالت «کارت» و از **۹۰۰px** حالت
|
||
«جدول». این دو مرز باید همیشه یکی باشند؛ اگر نباشند، در بازهای از عرض صفحه هر
|
||
دو حالت همزمان اعمال میشوند و چیدمان میشکند. یک تست اختصاصی
|
||
(`test_card_and_table_breakpoints_agree`) همین را قفل کرده است.
|
||
|
||
## ۶. قابلیتهای موبایل
|
||
|
||
- هر ردیف یک **کارت** میشود. نام مشتری سرِ کارت است (بدون برچسب) و بقیهٔ
|
||
مقادیر با برچسب ثابت ۶٫۵rem همتراز میشوند.
|
||
- برچسب «وضعیت» حذف میشود چون خودِ برچسب رنگی گویاست.
|
||
- دکمهٔ **«تماس»** (`tel:`) و **«ثبت اقدام»** بهصورت دو دکمهٔ تمامعرض.
|
||
- شمارههای دوم و سوم در موبایل مخفیاند (برای تماس، شمارهٔ اول کافی است).
|
||
|
||
## ۷. نمای خالی
|
||
|
||
اگر در نما/جستوجوی جاری موردی نباشد، پیام «موردی در این نما پیدا نشد»
|
||
نمایش داده میشود (بهجای جدولِ خالی و گمراهکننده).
|
||
|
||
## ۸. کارایی
|
||
|
||
| سنجه | مقدار |
|
||
|---|---|
|
||
| تعداد کوئری در بارگذاری صفحه | **۱۹** |
|
||
| رندر کامل صفحه روی دادهٔ واقعی (۳٬۲۴۳ معوق) | ~۱ ثانیه |
|
||
|
||
نکتهٔ پیادهسازی: دورهٔ قالب **یک بار** خوانده میشود و به حلقهٔ محاسبهٔ تأخیر
|
||
پاس داده میشود (`months=period_months`). اگر این پارامتر پاس نشود، برای هر
|
||
مشتری یک کوئری اضافه زده میشود (باگ N+1 که در تستها کشف و رفع شد).
|
||
|
||
## ۹. آنچه تغییر نکرد
|
||
|
||
- منطق «چه کسی عقبافتاده است» (همان `last_visit_map` + `due_cut`).
|
||
- قاعدهٔ اعتبار قرار دستی (وجود هماهنگی فعال + آخرین فعالیت بودن).
|
||
- `visit_policy.py` بهطور کامل: `next_appointment_map`، `scheduled_visits_map`،
|
||
`coordination_holds_map` — پس **اسکنر پیامک**، `recalc_last_bazdid` و چرخهٔ
|
||
۹ ماهه دقیقاً مثل قبل کار میکنند.
|
||
- شمارندهٔ داشبورد (`CacheTable('9mahbazdid')` = بدون قرار).
|
||
- لینکهای قدیمی `?view=...` و رفتار پیشفرض (نمای «بدون قرار»).
|
||
|
||
## ۱۰. پارامترهای URL
|
||
|
||
```
|
||
/panel/entezar/ پیشفرض: نیازمند پیگیری امروز
|
||
?view=all همهٔ عقبافتادهها
|
||
?view=coordinated تعیین تکلیف شده (اجتماع سه منبع)
|
||
?view=today نیازمند پیگیری امروز
|
||
?view=today&fdate=1405/07/28 نیازمند پیگیری در یک روز دیگر (شمسی)
|
||
?view=manual فقط تعویق دستی
|
||
?view=scheduled فقط بازدید برنامهریزیشده
|
||
?view=coordination فقط پیگیری تلفنی
|
||
?delay=gt180 فقط بیش از ۶ ماه تأخیر
|
||
?sort=overdue&dir=desc بیشترین تأخیر اول
|
||
?sort=name&dir=asc الفبایی (صعودی)
|
||
?sort=manual&dir=desc تاریخ قرار دستی (نزولی)
|
||
?searchVal=رضایی&searchOn=name جستوجوی نام
|
||
?searchVal=0912&searchOn=phone جستوجوی تلفن
|
||
?view=all&delay=gt180&sort=name&dir=asc&page=3 همه با هم (ترکیبپذیر)
|
||
?view=today&missed=1 فقط پیگیریهای عقبافتاده
|
||
```
|
||
|
||
- **اندازهٔ صفحه = ۱۰۰ ردیف** (`ENTEZAR_PAGE_SIZE`)؛ صفحهبندی فقط از ردیف ۱۰۱ به
|
||
بعد فعال میشود.
|
||
|
||
- مقدار نامعتبر `view`/`sort`/`delay` بیصدا به پیشفرض برمیگردد (بدون ۴۰۰/۵۰۰).
|
||
- مقادیر قدیمی `sort=oldest|newest|overdue` همچنان معتبرند.
|
||
- همهٔ لینکهای داخلی (کارتها، بازههای تأخیر، سرستونها، صفحهبندی) **بقیهٔ
|
||
فیلترها را حفظ میکنند**.
|
||
|
||
---
|
||
|
||
3. پس از تاریخ قرار، مشتری **خودکار** در گروه «تعیین تکلیف نشده» ظاهر میشود؛
|
||
نیازی به کار دستی نیست.
|
||
4. عدد «تعیین تکلیف نشده» در داشبورد همان لیست کاریِ فوری است.
|
||
|
||
|
||
|