aa3fbca710
新增: - 历史周报: Dashboard/周报详情支持周选择器翻看往周,归档只读 - 图片预览: 全屏大图(移动端+PC端),点击遮罩关闭 - PC端照片管理: 编辑时可上传/删除照片 - 客户导入改为更新模式: 重名自动更新信息,显示操作明细 - 导入跳过原因: 旧周报导入也显示每条跳过原因 优化: - 填报进度权限: 经理只看到自己,支局长/领导看全员 - 客户经理暖灰色hash标签列 - 用户去重合并(4组),数据完整迁移 - CLAUDE.md 更新到最新状态 Co-Authored-By: Claude <noreply@anthropic.com>
82 lines
3.4 KiB
Python
82 lines
3.4 KiB
Python
import uuid
|
|
from datetime import datetime, date
|
|
from typing import Any
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
from sqlalchemy import select
|
|
import openpyxl
|
|
from app.models.customer import Customer
|
|
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.user import User
|
|
|
|
|
|
async def import_from_excel(db: AsyncSession, file_bytes: bytes, manager_id: uuid.UUID) -> dict:
|
|
"""Parse old weekly report Excel and import data. Returns summary stats."""
|
|
wb = openpyxl.load_workbook(io.BytesIO(file_bytes), data_only=True)
|
|
stats = {"visits": 0, "work_plans": 0, "mini_business": 0, "key_visits": 0, "skipped": 0, "skip_reasons": []}
|
|
|
|
# Resolve customer name -> id cache
|
|
customers_result = await db.execute(select(Customer.id, Customer.name))
|
|
customer_map = {c.name: c.id for c in customers_result.all()}
|
|
|
|
# ── Parse Sheet 1: 每日拜访记录 ──
|
|
if "每日拜访记录" in wb.sheetnames:
|
|
ws = wb["每日拜访记录"]
|
|
for row in ws.iter_rows(min_row=2, values_only=True):
|
|
if not row[0]:
|
|
continue
|
|
try:
|
|
cust_name, visit_date_str, visit_method, time_range, visitor_name, visitor_phone, content, demand, companions_str, mgr_str = \
|
|
row[0], str(row[1]) if row[1] else str(date.today()), \
|
|
str(row[2]) if row[2] else "上门", str(row[3]) if row[3] else "", \
|
|
str(row[4]) if row[4] else "", str(row[5]) if row[5] else "", \
|
|
str(row[6]) if row[6] else "", str(row[7]) if row[7] else "", \
|
|
str(row[8]) if row[8] else "", str(row[9]) if row[9] else ""
|
|
|
|
customer_id = customer_map.get(cust_name)
|
|
if not customer_id:
|
|
stats["skipped"] += 1
|
|
stats["skip_reasons"].append(f"客户「{cust_name}」不存在,跳过")
|
|
continue
|
|
|
|
try:
|
|
visit_date = datetime.strptime(visit_date_str[:10], "%Y-%m-%d").date()
|
|
except ValueError:
|
|
visit_date = date.today()
|
|
|
|
existing = await db.execute(
|
|
select(Visit).where(
|
|
Visit.visit_date == visit_date,
|
|
Visit.manager_id == manager_id,
|
|
Visit.customer_id == customer_id,
|
|
)
|
|
)
|
|
if existing.scalar_one_or_none():
|
|
stats["skipped"] += 1
|
|
stats["skip_reasons"].append(f"重复:{cust_name} {visit_date} 已存在")
|
|
continue
|
|
|
|
visit = Visit(
|
|
customer_id=customer_id,
|
|
visit_date=visit_date,
|
|
visit_method=visit_method if visit_method in ["上门", "电话", "微信", "出差"] else "上门",
|
|
time_range=time_range,
|
|
visitor_name=visitor_name,
|
|
visitor_phone=visitor_phone,
|
|
communication_content=content,
|
|
customer_demand=demand,
|
|
manager_id=manager_id,
|
|
)
|
|
db.add(visit)
|
|
stats["visits"] += 1
|
|
except Exception:
|
|
stats["skipped"] += 1
|
|
|
|
await db.commit()
|
|
return stats
|
|
|
|
|
|
import io
|