feat: 消除同访草案机制,改为查询时通过 companions 数组覆盖
- 删除 POST /visits 中的草案创建逻辑(不再创建空拜访记录) - 周报查询:manager 角色也显示同访的拜访记录 - 亮灯表覆盖:同访人也算客户拜访覆盖(UNION companions) - 填报进度:同访人也算今日已填报 - 清理已有 2 条空草案记录 Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -194,22 +194,6 @@ async def create_visit(
|
|||||||
)
|
)
|
||||||
db.add(log)
|
db.add(log)
|
||||||
|
|
||||||
# Create draft copies for companions
|
|
||||||
for companion_id in data.companions:
|
|
||||||
if companion_id != uuid.UUID(current_user["user_id"]):
|
|
||||||
draft = Visit(
|
|
||||||
customer_id=data.customer_id,
|
|
||||||
visit_date=parse_date(data.visit_date),
|
|
||||||
visit_method=data.visit_method,
|
|
||||||
time_range=data.time_range,
|
|
||||||
communication_content="", # Leave blank for companion to fill
|
|
||||||
customer_demand="",
|
|
||||||
companions=[],
|
|
||||||
photos=[],
|
|
||||||
manager_id=companion_id,
|
|
||||||
)
|
|
||||||
db.add(draft)
|
|
||||||
|
|
||||||
await db.commit()
|
await db.commit()
|
||||||
await db.refresh(visit)
|
await db.refresh(visit)
|
||||||
return await _enrich_visit(visit, db)
|
return await _enrich_visit(visit, db)
|
||||||
|
|||||||
@@ -121,10 +121,14 @@ async def get_reporting_progress(db: AsyncSession, reference_date: date | None =
|
|||||||
} if leave else None,
|
} if leave else None,
|
||||||
})
|
})
|
||||||
|
|
||||||
# Check today's reporting — visits OR daily notes
|
# Check today's reporting — visits (direct + companions) OR daily notes
|
||||||
today_visits = await db.execute(
|
from sqlalchemy import union_all
|
||||||
select(Visit.manager_id).where(Visit.visit_date == today)
|
today_direct = select(Visit.manager_id).where(Visit.visit_date == today)
|
||||||
|
today_companion = select(func.unnest(Visit.companions).label("manager_id")).where(
|
||||||
|
Visit.visit_date == today, Visit.companions.isnot(None)
|
||||||
)
|
)
|
||||||
|
today_combined = union_all(today_direct, today_companion).subquery()
|
||||||
|
today_visits = await db.execute(select(today_combined.c.manager_id))
|
||||||
today_notes = await db.execute(
|
today_notes = await db.execute(
|
||||||
select(DailyNote.manager_id).where(DailyNote.note_date == today)
|
select(DailyNote.manager_id).where(DailyNote.note_date == today)
|
||||||
)
|
)
|
||||||
@@ -162,9 +166,13 @@ async def get_weekly_report(
|
|||||||
# ── Visits ──
|
# ── Visits ──
|
||||||
visit_query = select(Visit).where(Visit.visit_date >= monday, Visit.visit_date <= sunday)
|
visit_query = select(Visit).where(Visit.visit_date >= monday, Visit.visit_date <= sunday)
|
||||||
if role == "manager":
|
if role == "manager":
|
||||||
visit_query = visit_query.where(Visit.manager_id == user_id)
|
visit_query = visit_query.where(
|
||||||
|
(Visit.manager_id == user_id) | (Visit.companions.any(user_id))
|
||||||
|
)
|
||||||
elif filter_manager_id:
|
elif filter_manager_id:
|
||||||
visit_query = visit_query.where(Visit.manager_id == filter_manager_id)
|
visit_query = visit_query.where(
|
||||||
|
(Visit.manager_id == filter_manager_id) | (Visit.companions.any(filter_manager_id))
|
||||||
|
)
|
||||||
if filter_customer_id:
|
if filter_customer_id:
|
||||||
visit_query = visit_query.where(Visit.customer_id == filter_customer_id)
|
visit_query = visit_query.where(Visit.customer_id == filter_customer_id)
|
||||||
visit_query = visit_query.order_by(Visit.visit_date.desc())
|
visit_query = visit_query.order_by(Visit.visit_date.desc())
|
||||||
|
|||||||
@@ -85,19 +85,34 @@ async def get_light_board(
|
|||||||
for m in visible_managers
|
for m in visible_managers
|
||||||
}
|
}
|
||||||
|
|
||||||
# ── All customers with their last visit per manager ──
|
# ── All customers with their last visit per manager (including companions) ──
|
||||||
# Subquery: latest visit date + method per (manager_id, customer_id)
|
# UNION direct + companions (unnested), then DISTINCT ON to get the latest per (manager, customer)
|
||||||
|
from sqlalchemy import union_all, distinct
|
||||||
|
direct = select(
|
||||||
|
Visit.manager_id, Visit.customer_id, Visit.visit_date, Visit.visit_method,
|
||||||
|
)
|
||||||
|
companion = select(
|
||||||
|
func.unnest(Visit.companions).label("manager_id"),
|
||||||
|
Visit.customer_id,
|
||||||
|
Visit.visit_date,
|
||||||
|
Visit.visit_method,
|
||||||
|
).where(Visit.companions.isnot(None))
|
||||||
|
combined = union_all(direct, companion).subquery()
|
||||||
|
|
||||||
|
# Get latest visit (date + method) per (manager_id, customer_id) using DISTINCT ON
|
||||||
latest_visit = (
|
latest_visit = (
|
||||||
select(
|
select(
|
||||||
Visit.manager_id,
|
combined.c.manager_id,
|
||||||
Visit.customer_id,
|
combined.c.customer_id,
|
||||||
func.max(Visit.visit_date).label("last_visit_date"),
|
combined.c.visit_date.label("last_visit_date"),
|
||||||
|
combined.c.visit_method,
|
||||||
)
|
)
|
||||||
.group_by(Visit.manager_id, Visit.customer_id)
|
.distinct(combined.c.manager_id, combined.c.customer_id)
|
||||||
|
.order_by(combined.c.manager_id, combined.c.customer_id, combined.c.visit_date.desc())
|
||||||
.subquery()
|
.subquery()
|
||||||
)
|
)
|
||||||
|
|
||||||
# Join: assignments → customers → latest_visit → visits (for method)
|
# Join: assignments → customers → latest_visit
|
||||||
rows = await db.execute(
|
rows = await db.execute(
|
||||||
select(
|
select(
|
||||||
CustomerAssignment.manager_id,
|
CustomerAssignment.manager_id,
|
||||||
@@ -108,18 +123,13 @@ async def get_light_board(
|
|||||||
Customer.in_use_services,
|
Customer.in_use_services,
|
||||||
Customer.monthly_fee,
|
Customer.monthly_fee,
|
||||||
latest_visit.c.last_visit_date,
|
latest_visit.c.last_visit_date,
|
||||||
Visit.visit_method,
|
latest_visit.c.visit_method,
|
||||||
)
|
)
|
||||||
.join(Customer, Customer.id == CustomerAssignment.customer_id)
|
.join(Customer, Customer.id == CustomerAssignment.customer_id)
|
||||||
.outerjoin(latest_visit, and_(
|
.outerjoin(latest_visit, and_(
|
||||||
latest_visit.c.manager_id == CustomerAssignment.manager_id,
|
latest_visit.c.manager_id == CustomerAssignment.manager_id,
|
||||||
latest_visit.c.customer_id == CustomerAssignment.customer_id,
|
latest_visit.c.customer_id == CustomerAssignment.customer_id,
|
||||||
))
|
))
|
||||||
.outerjoin(Visit, and_(
|
|
||||||
Visit.manager_id == CustomerAssignment.manager_id,
|
|
||||||
Visit.customer_id == CustomerAssignment.customer_id,
|
|
||||||
Visit.visit_date == latest_visit.c.last_visit_date,
|
|
||||||
))
|
|
||||||
.where(CustomerAssignment.role == "primary")
|
.where(CustomerAssignment.role == "primary")
|
||||||
.order_by(Customer.name)
|
.order_by(Customer.name)
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user