improve bazdid 3
This commit is contained in:
1 parent
f8d768a724
commit
f86d060e4d
6 files changed
+337
-121
No files matched your search
@@ -91,21 +91,26 @@ def _action_payload_from_request(request):
|
||||
}
|
||||
|
||||
|
||||
def _apply_visit_hold(customer_obj, payload, *, status):
|
||||
def _apply_visit_hold(customer_obj, payload, *, status, is_new):
|
||||
"""همراستاکردن «قرار بعدی» با اقدام ثبت/ویرایششده.
|
||||
|
||||
قواعد (یکسان برای ایجاد و ویرایش — قبلاً ایجاد این را انجام نمیداد):
|
||||
قواعد:
|
||||
|
||||
* فقط برای اقدام نوع «هماهنگی» معنا دارد؛
|
||||
- اگر تاریخ قرار داده شده باشد ⇒ ``set_visit_hold`` (فقط رو به جلو)
|
||||
- اگر «انجام شد» تیک خورده و تاریخ خالی است ⇒ پیشفرض ۱۴ روز
|
||||
- اگر هماهنگی لغو شده باشد ⇒ قرار پاک میشود
|
||||
* **هر اقدام دیگری** (از جمله «بازدید») ⇒ قرار دستی پاک میشود؛ چون هدفِ
|
||||
قرار (پیگیری در آینده) با انجام/برنامهریزی بازدید منتفی است.
|
||||
* برای اقدام نوع «هماهنگی»:
|
||||
- تاریخ قرار داده شده ⇒ ``set_visit_hold`` (فقط رو به جلو)
|
||||
- «انجام شد» تیک خورده و تاریخ خالی ⇒ پیشفرض ۱۴ روز
|
||||
- لغو شده ⇒ (در ایجاد) پاک میشود
|
||||
* برای **هر اقدام دیگری** (از جمله «بازدید»):
|
||||
- در حالت **ایجاد**، این اقدام آخرین فعالیت مشتری است ⇒ قرار دستی پاک
|
||||
میشود (سناریوی گزارششده: ثبت بازدید پس از هماهنگی).
|
||||
- در حالت **ویرایش**، رکوردی تاریخی است و ممکن است قرارِ فعلی از یک
|
||||
هماهنگیِ *جدیدتر* آمده باشد؛ پس دست نمیزنیم. اعتبارسنجی
|
||||
:func:`manual_holds_map` (شرط «آخرین فعالیت = هماهنگی») خودش تصمیم
|
||||
میگیرد. بدون این تفکیک، ویرایش یک اقدام قدیمی قرار معتبر را پاک میکرد.
|
||||
"""
|
||||
is_coordination = payload['actionType'] == Action.ActionType.HAMAHANGI
|
||||
if not is_coordination or status == Action.Status.CANCELL:
|
||||
return clear_visit_hold(customer_obj)
|
||||
return clear_visit_hold(customer_obj) if is_new else True
|
||||
|
||||
raw = payload.get('holdRaw')
|
||||
if raw is not None:
|
||||
@@ -152,7 +157,7 @@ def customerNewAction(request ,c_id):
|
||||
# last_bazdid_dt فقط از تابع واحد بازمحاسبه میشود؛ نه از finishDateTime.
|
||||
refresh_last_bazdid(customer_obj, reason=REASON_CREATE, user=request.user)
|
||||
# «قرار بعدی» فقط برای هماهنگی.
|
||||
_apply_visit_hold(customer_obj, payload, status=newAction.status)
|
||||
_apply_visit_hold(customer_obj, payload, status=newAction.status, is_new=True)
|
||||
|
||||
# پیامک «بازدید شما برای تاریخ … ثبت شد» فقط برای اقدام از نوع بازدید.
|
||||
if newAction.actionType == Action.ActionType.BAZDID:
|
||||
@@ -210,9 +215,10 @@ def customerEditAction(request ,c_id, action_id):
|
||||
# (تا مقداری که این رکورد ساخته بود با منبع حقیقت همراستا شود).
|
||||
reason = REASON_CANCEL if (was_done and not payload['is_done']) else REASON_EDIT
|
||||
refresh_last_bazdid(customer_obj, reason=reason, user=request.user)
|
||||
# «قرار بعدی»: با ویرایش همراستا میشود؛ اگر اقدام دیگر هماهنگی نباشد یا
|
||||
# هماهنگی لغو شود، قرار دستی پاک میشود.
|
||||
_apply_visit_hold(customer_obj, payload, status=newAction.status)
|
||||
# «قرار بعدی»: با ویرایش همراستا میشود. برای اقدام غیرهماهنگی عمداً
|
||||
# پاک نمیکنیم؛ رکورد ویرایششده ممکن است قدیمیتر از یک هماهنگیِ جدیدتر
|
||||
# باشد و پاککردن، قرار معتبر را از بین ببرد.
|
||||
_apply_visit_hold(customer_obj, payload, status=newAction.status, is_new=False)
|
||||
|
||||
#return redirect('/panel/customer/'+str(c_id)+'/action')
|
||||
return redirect(request.META.get('HTTP_REFERER', 'redirect_if_referer_not_found'))
|
||||
|
||||
@@ -24,7 +24,8 @@ from ..visit_policy import (SOURCE_COORDINATION, SOURCE_MANUAL, SOURCE_SCHEDULED
|
||||
|
||||
# نماهای صفحهٔ «انتظار» (فیلتر ?view=). پیشفرض = فقط «بدون قرار» تا رفتار
|
||||
# قبلی صفحه برای کاربران حفظ شود.
|
||||
ENTEZAR_VIEWS = ('uncoordinated', 'manual', 'scheduled', 'coordinated', 'all')
|
||||
ENTEZAR_VIEWS = ('uncoordinated', 'manual', 'scheduled', 'coordination',
|
||||
'coordinated', 'all')
|
||||
ENTEZAR_DEFAULT_VIEW = 'uncoordinated'
|
||||
# حالتهای مرتبسازی (?sort=):
|
||||
# oldest ⇒ قدیمیترین بازدید اول (پیشفرض/اولویت کارشناس)
|
||||
@@ -341,18 +342,17 @@ def customerEdit(request, c_id):
|
||||
|
||||
@login_required(login_url='/panel/p_login/')
|
||||
def entezar(request):
|
||||
"""فهرست «منتظر بازدید» + تفکیک «با قرار» و «بدون قرار».
|
||||
"""فهرست «منتظر بازدید» + تفکیک بر اساس **نوع قرار**.
|
||||
|
||||
منبع حقیقت تاریخ بازدید ``visit_policy.last_visit_map`` است و منبع «قرار بعدی»
|
||||
``appointment_sources_map`` (سه منبع: قرار دستی، بازدید برنامهریزیشده،
|
||||
هماهنگیِ در انتظار). هر مشتری دقیقاً در یکی از چهار گروه زیر است:
|
||||
منبع حقیقت تاریخ بازدید ``visit_policy.last_visit_map`` است و منبع قرارها
|
||||
``appointment_sources_map`` که سه منبع مستقل دارد:
|
||||
|
||||
* ``A`` — معوق بدون هیچ قرار (پیشفرض نمایش؛ رفتار قبلی صفحه)
|
||||
* ``B`` — معوق با قرار دستی (تعویق از فرم هماهنگی)
|
||||
* ``C`` — معوق با بازدید برنامهریزیشده یا هماهنگی در انتظار
|
||||
* ``D`` — معوق با بیش از یک منبعِ قرار
|
||||
* ``manual`` — قرار دستی روی مشتری (تعویق از فرم هماهنگی)
|
||||
* ``scheduled`` — اقدام «بازدید» با تاریخ آینده
|
||||
* ``coordination`` — اقدام «هماهنگی» با تاریخ پیگیری در آینده
|
||||
|
||||
فیلتر با ``?view=`` و قابل ترکیب با جستوجوی آدرس است.
|
||||
هر فیلتر فقط منبع خودش را نشان میدهد (یک مشتری میتواند چند منبع داشته
|
||||
باشد و در چند فیلتر دیده شود). فیلتر با ``?view=`` و قابل ترکیب با جستوجو.
|
||||
"""
|
||||
searchVal = request.GET.get('searchVal', '')
|
||||
searchOn = request.GET.get('searchOn', '')
|
||||
@@ -403,29 +403,35 @@ def entezar(request):
|
||||
if visits.get(customer_id) is None or visits[customer_id] <= due_cut
|
||||
]
|
||||
|
||||
# تفکیک چهارگروهی (MECE): هر مشتری فقط در یک گروه.
|
||||
groups = {'A': [], 'B': [], 'C': [], 'D': []}
|
||||
# تفکیک بر اساس **منبعِ** قرار: هر مشتری میتواند صفر تا سه منبع داشته باشد
|
||||
# (قرار دستی، بازدید برنامهریزیشده، هماهنگی). قبلاً «هماهنگی» با «بازدید»
|
||||
# در یک گروه جمع میشد و در نمای «بازدید برنامهریزیشده» دیده میشد؛ حالا
|
||||
# هر منبع گروه نمایشیِ خودش را دارد.
|
||||
with_manual, with_scheduled, with_coordination = set(), set(), set()
|
||||
for customer_id in stale_ids:
|
||||
customer_sources = sources.get(customer_id) or {}
|
||||
manual = SOURCE_MANUAL in customer_sources
|
||||
scheduled = (SOURCE_SCHEDULED in customer_sources
|
||||
or SOURCE_COORDINATION in customer_sources)
|
||||
if manual and scheduled:
|
||||
groups['D'].append(customer_id)
|
||||
elif manual:
|
||||
groups['B'].append(customer_id)
|
||||
elif scheduled:
|
||||
groups['C'].append(customer_id)
|
||||
else:
|
||||
groups['A'].append(customer_id)
|
||||
if SOURCE_MANUAL in customer_sources:
|
||||
with_manual.add(customer_id)
|
||||
if SOURCE_SCHEDULED in customer_sources:
|
||||
with_scheduled.add(customer_id)
|
||||
if SOURCE_COORDINATION in customer_sources:
|
||||
with_coordination.add(customer_id)
|
||||
|
||||
selected = {
|
||||
'uncoordinated': groups['A'],
|
||||
'manual': groups['B'] + groups['D'],
|
||||
'scheduled': groups['C'] + groups['D'],
|
||||
'coordinated': groups['B'] + groups['C'] + groups['D'],
|
||||
coordinated_ids = [cid for cid in stale_ids if cid in (
|
||||
with_manual | with_scheduled | with_coordination)]
|
||||
selected_map = {
|
||||
# نداشتن هیچ قرار
|
||||
'uncoordinated': [cid for cid in stale_ids if cid not in (
|
||||
with_manual | with_scheduled | with_coordination)],
|
||||
# هر مشتری که این منبع را دارد (حتی اگر منبع دیگری هم داشته باشد)
|
||||
'manual': [cid for cid in stale_ids if cid in with_manual],
|
||||
'scheduled': [cid for cid in stale_ids if cid in with_scheduled],
|
||||
'coordination': [cid for cid in stale_ids if cid in with_coordination],
|
||||
# اجتماع هر سه منبع
|
||||
'coordinated': coordinated_ids,
|
||||
'all': stale_ids,
|
||||
}[view]
|
||||
}
|
||||
selected = selected_map[view]
|
||||
|
||||
# «مدت تأخیر» = از چه زمانی بازدید عقب افتاده است (به روز).
|
||||
# مبنای محاسبه «موعد بازدید» است، نه خود بازدید: موعد = بازدید + دوره.
|
||||
@@ -491,17 +497,14 @@ def entezar(request):
|
||||
|
||||
counts = {
|
||||
'total': len(stale_ids),
|
||||
'uncoordinated': len(groups['A']),
|
||||
# توجه: عددِ هر کارت باید **همان چیزی** باشد که با کلیک روی آن کارت
|
||||
# دیده میشود. فیلتر ``view=manual`` گروههای B و D را نشان میدهد و
|
||||
# ``view=scheduled`` گروههای C و D را؛ پس اینجا هم همان اجتماع
|
||||
# شمرده میشود (قبلاً فقط B/C شمرده میشد و عدد با فهرست نمیخواند).
|
||||
'manual': len(groups['B']) + len(groups['D']),
|
||||
'scheduled': len(groups['C']) + len(groups['D']),
|
||||
'manual_only': len(groups['B']),
|
||||
'scheduled_only': len(groups['C']),
|
||||
'both': len(groups['D']),
|
||||
'coordinated': len(groups['B']) + len(groups['C']) + len(groups['D']),
|
||||
'uncoordinated': len(selected_map['uncoordinated']),
|
||||
# عددِ هر کارت دقیقاً همان چیزی است که با کلیک روی آن دیده میشود؛
|
||||
# چون یک مشتری میتواند چند منبع داشته باشد، جمع کارتها ممکن است
|
||||
# از «با قرار» بیشتر شود. در راهنمای بالای صفحه توضیح داده میشود.
|
||||
'manual': len(selected_map['manual']),
|
||||
'scheduled': len(selected_map['scheduled']),
|
||||
'coordination': len(selected_map['coordination']),
|
||||
'coordinated': len(selected_map['coordinated']),
|
||||
'selected': len(selected),
|
||||
'buckets': buckets,
|
||||
# برای متن راهنمای بالای صفحه («بیش از N ماه از بازدید گذشته»).
|
||||
@@ -524,19 +527,16 @@ def entezar(request):
|
||||
appointment_rows = {}
|
||||
for customer in customerHa.object_list:
|
||||
entry = sources.get(customer.id) or {}
|
||||
flags = [key for key in (SOURCE_MANUAL, SOURCE_SCHEDULED,
|
||||
SOURCE_COORDINATION) if key in entry]
|
||||
appointment_rows[customer.id] = {
|
||||
'manual': entry.get(SOURCE_MANUAL),
|
||||
'scheduled': entry.get(SOURCE_SCHEDULED),
|
||||
'coordination': entry.get(SOURCE_COORDINATION),
|
||||
'overdue_days': overdue_days.get(customer.id, 0),
|
||||
'last_visit': visits.get(customer.id),
|
||||
'group': (
|
||||
'D' if (SOURCE_MANUAL in entry and (
|
||||
SOURCE_SCHEDULED in entry or SOURCE_COORDINATION in entry))
|
||||
else 'B' if SOURCE_MANUAL in entry
|
||||
else 'C' if (SOURCE_SCHEDULED in entry or SOURCE_COORDINATION in entry)
|
||||
else 'A'
|
||||
),
|
||||
# تعداد منابع (۰ تا ۳) — قالب بر اساس آن برچسب وضعیت را میسازد.
|
||||
'source_count': len(flags),
|
||||
}
|
||||
|
||||
return render(request, 'admin_panel/entezar.html', {
|
||||
|
||||
Reference in new issue
Block a user