"""地磅计划按月自动生成 — 迁移自 simple-dibang-xiaoshou/auto-create-plan/auto_create_monthly_plan.py""" from __future__ import annotations import logging from datetime import date, datetime, timedelta from sqlalchemy import and_, or_ from sqlalchemy.orm import Session from app.core.utils import generate_uuid from app.models.db_models import DbPurchasePlan from app.services.db.plan_helpers import default_execution_status, generate_plan_no logger = logging.getLogger(__name__) def month_range(dt: date) -> tuple[date, date]: """返回 dt 所在月份的首日、末日。""" first = dt.replace(day=1) if first.month == 12: next_month = first.replace(year=first.year + 1, month=1, day=1) else: next_month = first.replace(month=first.month + 1, day=1) last = next_month - timedelta(days=1) return first, last def get_last_month_range(now: date) -> tuple[date, date]: """返回上个月的首日、末日。""" first_this, _ = month_range(now) last_prev = first_this - timedelta(days=1) return month_range(last_prev) def run_auto_generate_monthly_plans( db: Session, *, force: bool = False, now: date | None = None, ) -> dict: """ 按月自动生成采购计划。 - 默认仅在每月 1 日执行(force=True 时跳过该限制,供手动触发验收) - 遍历所有租户,按上月计划模板生成本月计划 """ today = now or date.today() if today.day != 1 and not force: logger.info("当前不是每月 1 日且未 force,跳过自动生成计划") return { "created_count": 0, "skipped_reason": "not_first_day_of_month", "current_month": today.strftime("%Y-%m"), } current_month = today.strftime("%Y-%m") this_month_first, this_month_last = month_range(today) last_month_first, last_month_last = get_last_month_range(today) logger.info( "开始执行自动生成计划任务,current_month=%s, last_month=[%s, %s], this_month=[%s, %s], force=%s", current_month, last_month_first, last_month_last, this_month_first, this_month_last, force, ) plans = ( db.query(DbPurchasePlan) .filter( and_( DbPurchasePlan.del_flag == 0, DbPurchasePlan.auto_generate_enabled == 1, or_( DbPurchasePlan.auto_generate_last_month.is_(None), DbPurchasePlan.auto_generate_last_month != current_month, ), or_( DbPurchasePlan.auto_generate_end_month.is_(None), DbPurchasePlan.auto_generate_end_month >= current_month, ), DbPurchasePlan.start_date == last_month_first, DbPurchasePlan.end_date == last_month_last, ) ) .all() ) if not plans: logger.info("未找到需要自动生成下月计划的记录") return { "created_count": 0, "skipped_reason": None, "current_month": current_month, } logger.info("找到 %d 条可自动生成的上月计划", len(plans)) created_count = 0 for source in plans: new_plan = DbPurchasePlan( id=generate_uuid(), tenant_no=source.tenant_no, plan_no=generate_plan_no(), plan_date=this_month_first, start_date=this_month_first, end_date=this_month_last, business_type=source.business_type, product_id=source.product_id, product=source.product, specification=source.specification, planned_quantity=source.planned_quantity, vehicle_no=source.vehicle_no or "", customer_name=source.customer_name, remark=source.remark, execution_status=default_execution_status(source.business_type), executed_at=None, last_weighing_slip_id=None, executed_quantity=0, auto_generate_enabled=source.auto_generate_enabled, auto_generate_end_month=source.auto_generate_end_month, auto_generate_last_month=None, create_by=None, create_dept_id="", ) db.add(new_plan) source.auto_generate_last_month = current_month created_count += 1 db.commit() logger.info("本次自动生成完成,共生成 %d 条新计划", created_count) return { "created_count": created_count, "skipped_reason": None, "current_month": current_month, }