cd47d1aed5
- light_board.classify: 自然月 → 30/60天滚动窗口 (≤30天🟢, 31-60天🟡, 61+天🔴) - LightBoard.vue: 4处文案同步 (近30天已拜访 / 上次 / 个周期) - dashboard.get_weekly_report: 补全 companion_names_resolved + visitor_name + visitor_phone + edit_log - 导入/导出模板 + VisitForm + ManagerWorkspace: 同访人员 → 相关人员
163 lines
5.9 KiB
Python
163 lines
5.9 KiB
Python
import io
|
|
from datetime import date, timedelta
|
|
from openpyxl import Workbook
|
|
from openpyxl.styles import Font, Alignment, Border, Side
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
from sqlalchemy import select
|
|
from app.models.visit import Visit
|
|
from app.models.work_plan import WorkPlan
|
|
from app.models.mini_business import MiniBusiness
|
|
from app.models.key_visit import KeyVisit
|
|
from app.models.daily_note import DailyNote
|
|
from app.models.customer import Customer
|
|
from app.models.user import User
|
|
|
|
|
|
def get_week_range(reference_date: date | None = None):
|
|
"""Get the Monday and Sunday of the week containing reference_date (defaults to today)."""
|
|
today = reference_date or date.today()
|
|
monday = today - timedelta(days=today.weekday())
|
|
sunday = monday + timedelta(days=6)
|
|
return monday, sunday
|
|
|
|
|
|
async def export_weekly_report(db: AsyncSession, reference_date: date | None = None) -> io.BytesIO:
|
|
"""Generate a 5-sheet xlsx matching the existing weekly report template."""
|
|
monday, sunday = get_week_range(reference_date)
|
|
wb = Workbook()
|
|
|
|
# Pre-fetch lookups
|
|
customers_result = await db.execute(select(Customer.id, Customer.name))
|
|
customer_map = {str(c.id): c.name for c in customers_result.all()}
|
|
users_result = await db.execute(select(User.id, User.name))
|
|
user_map = {str(u.id): u.name for u in users_result.all()}
|
|
|
|
thin_border = Border(
|
|
left=Side(style='thin'), right=Side(style='thin'),
|
|
top=Side(style='thin'), bottom=Side(style='thin')
|
|
)
|
|
header_font = Font(bold=True)
|
|
|
|
# ── Sheet 1: 每日拜访记录 ──
|
|
ws1 = wb.active
|
|
ws1.title = "每日拜访记录"
|
|
headers1 = ["客户单位", "拜访日期", "拜访方式", "时间范围", "拜访人姓名", "拜访人电话", "沟通内容", "客户需求", "相关人员", "客户经理"]
|
|
ws1.append(headers1)
|
|
for col in range(1, len(headers1) + 1):
|
|
cell = ws1.cell(row=1, column=col)
|
|
cell.font = header_font
|
|
cell.border = thin_border
|
|
|
|
visits_result = await db.execute(
|
|
select(Visit).where(Visit.visit_date >= monday, Visit.visit_date <= sunday)
|
|
)
|
|
for v in visits_result.scalars():
|
|
companions_names = [user_map.get(str(cid), str(cid)) for cid in (v.companions or [])]
|
|
companions_names.extend(v.companion_names or [])
|
|
ws1.append([
|
|
customer_map.get(str(v.customer_id), ""),
|
|
str(v.visit_date),
|
|
v.visit_method,
|
|
v.time_range,
|
|
v.visitor_name or "",
|
|
v.visitor_phone or "",
|
|
v.communication_content,
|
|
v.customer_demand,
|
|
", ".join(companions_names),
|
|
user_map.get(str(v.manager_id), ""),
|
|
])
|
|
|
|
# ── Sheet 2: 下周工作计划 ──
|
|
ws2 = wb.create_sheet("下周工作计划")
|
|
headers2 = ["客户单位", "工作计划", "计划拜访时间", "客户经理", "状态"]
|
|
ws2.append(headers2)
|
|
for col in range(1, len(headers2) + 1):
|
|
cell = ws2.cell(row=1, column=col)
|
|
cell.font = header_font
|
|
cell.border = thin_border
|
|
|
|
plans_result = await db.execute(select(WorkPlan))
|
|
for p in plans_result.scalars():
|
|
ws2.append([
|
|
customer_map.get(str(p.customer_id), ""),
|
|
p.plan_content,
|
|
str(p.plan_date),
|
|
user_map.get(str(p.manager_id), ""),
|
|
p.status,
|
|
])
|
|
|
|
# ── Sheet 3: 小微业务商机 ──
|
|
ws3 = wb.create_sheet("小微业务商机")
|
|
headers3 = ["客户单位", "产品类型", "金额", "跟进内容具体情况", "跟进状态", "客户经理", "预计列收时间"]
|
|
ws3.append(headers3)
|
|
for col in range(1, len(headers3) + 1):
|
|
cell = ws3.cell(row=1, column=col)
|
|
cell.font = header_font
|
|
cell.border = thin_border
|
|
|
|
mb_result = await db.execute(select(MiniBusiness))
|
|
for m in mb_result.scalars():
|
|
ws3.append([
|
|
customer_map.get(str(m.customer_id), ""),
|
|
m.product_type,
|
|
m.amount,
|
|
m.follow_up_detail,
|
|
m.status,
|
|
user_map.get(str(m.manager_id), ""),
|
|
m.expected_revenue_date,
|
|
])
|
|
|
|
# ── Sheet 4: 要客拜访计划 ──
|
|
ws4 = wb.create_sheet("要客拜访计划")
|
|
headers4 = ["客户单位", "紧急重要度", "内容描述", "进展状态", "计划拜访时间", "计划拜访人", "拜访对象", "客户经理"]
|
|
ws4.append(headers4)
|
|
for col in range(1, len(headers4) + 1):
|
|
cell = ws4.cell(row=1, column=col)
|
|
cell.font = header_font
|
|
cell.border = thin_border
|
|
|
|
kv_result = await db.execute(select(KeyVisit))
|
|
for k in kv_result.scalars():
|
|
ws4.append([
|
|
customer_map.get(str(k.customer_id), ""),
|
|
k.urgency_level,
|
|
k.description,
|
|
k.progress_status,
|
|
k.planned_date,
|
|
k.planned_visitor,
|
|
k.visit_target,
|
|
user_map.get(str(k.manager_id), ""),
|
|
])
|
|
|
|
# ── Sheet 5: 今日纪要 ──
|
|
ws5 = wb.create_sheet("今日纪要")
|
|
headers5 = ["日期", "分类", "内容", "时间范围", "填报人"]
|
|
ws5.append(headers5)
|
|
for col in range(1, len(headers5) + 1):
|
|
cell = ws5.cell(row=1, column=col)
|
|
cell.font = header_font
|
|
cell.border = thin_border
|
|
|
|
notes_result = await db.execute(
|
|
select(DailyNote).where(DailyNote.note_date >= monday, DailyNote.note_date <= sunday)
|
|
)
|
|
for n in notes_result.scalars():
|
|
ws5.append([
|
|
str(n.note_date),
|
|
n.category,
|
|
n.content,
|
|
n.time_range,
|
|
user_map.get(str(n.manager_id), ""),
|
|
])
|
|
|
|
# Adjust column widths
|
|
for ws in [ws1, ws2, ws3, ws4, ws5]:
|
|
for col_cells in ws.columns:
|
|
max_length = max((len(str(cell.value or "")) for cell in col_cells), default=10)
|
|
ws.column_dimensions[col_cells[0].column_letter].width = min(max_length + 4, 50)
|
|
|
|
output = io.BytesIO()
|
|
wb.save(output)
|
|
output.seek(0)
|
|
return output
|