211 lines
6.7 KiB
Python
211 lines
6.7 KiB
Python
from __future__ import annotations
|
||
|
||
import re
|
||
from datetime import datetime
|
||
|
||
from sqlalchemy import select, update
|
||
from sqlalchemy.orm import Session
|
||
|
||
from app.models.org import SystemConfig
|
||
|
||
RAW_MATERIAL_LOT_PREFIX_CONFIG_CODE = "RAW_MATERIAL_LOT_PREFIX"
|
||
RAW_MATERIAL_LOT_PREFIX_DEFAULT = "YL"
|
||
SMART_OPERATION_REPORT_CONFIG_CODE = "SMART_OPERATION_REPORT_ENABLED"
|
||
MINIAPP_OPERATION_REPORT_LAST_SYNC_CONFIG_CODE = "MINIAPP_OPERATION_REPORT_LAST_SYNCED_AT"
|
||
MINIAPP_OPERATION_REPORT_LAST_SYNC_CONFIG_NAME = "小程序报工上次同步成功时间"
|
||
MINIAPP_OPERATION_REPORT_LAST_SYNC_FORMAT = "%Y-%m-%d %H:%M:%S"
|
||
|
||
|
||
def normalize_raw_material_lot_prefix(value: str | None) -> str:
|
||
normalized = re.sub(r"[^A-Z0-9]", "", str(value or "").upper())
|
||
return normalized or RAW_MATERIAL_LOT_PREFIX_DEFAULT
|
||
|
||
|
||
def normalize_enabled_config_value(value: object) -> str:
|
||
if isinstance(value, bool):
|
||
return "开启" if value else "关闭"
|
||
normalized = str(value or "").strip().lower()
|
||
if normalized in {"开启", "启用", "true", "1", "yes", "on", "enabled"}:
|
||
return "开启"
|
||
if normalized in {"关闭", "停用", "false", "0", "no", "off", "disabled"}:
|
||
return "关闭"
|
||
return "开启"
|
||
|
||
|
||
def get_system_config_value(db: Session, config_code: str, default: str = "") -> str:
|
||
value = db.scalar(
|
||
select(SystemConfig.config_value).where(
|
||
SystemConfig.config_code == config_code,
|
||
SystemConfig.status == "ACTIVE",
|
||
)
|
||
)
|
||
return str(value) if value is not None else default
|
||
|
||
|
||
def get_raw_material_lot_prefix(db: Session) -> str:
|
||
value = get_system_config_value(
|
||
db,
|
||
RAW_MATERIAL_LOT_PREFIX_CONFIG_CODE,
|
||
RAW_MATERIAL_LOT_PREFIX_DEFAULT,
|
||
)
|
||
return normalize_raw_material_lot_prefix(value)
|
||
|
||
|
||
def get_smart_operation_report_enabled(db: Session) -> bool:
|
||
value = get_system_config_value(db, SMART_OPERATION_REPORT_CONFIG_CODE, "开启")
|
||
return normalize_enabled_config_value(value) == "开启"
|
||
|
||
|
||
def format_miniapp_operation_report_last_sync(value: datetime) -> str:
|
||
return value.strftime(MINIAPP_OPERATION_REPORT_LAST_SYNC_FORMAT)
|
||
|
||
|
||
def parse_miniapp_operation_report_last_sync(value: str | None) -> datetime | None:
|
||
normalized = str(value or "").strip()
|
||
if not normalized:
|
||
return None
|
||
return datetime.strptime(normalized, MINIAPP_OPERATION_REPORT_LAST_SYNC_FORMAT)
|
||
|
||
|
||
def get_miniapp_operation_report_last_sync(db: Session) -> datetime | None:
|
||
value = get_system_config_value(db, MINIAPP_OPERATION_REPORT_LAST_SYNC_CONFIG_CODE, "")
|
||
return parse_miniapp_operation_report_last_sync(value)
|
||
|
||
|
||
def upsert_miniapp_operation_report_last_sync(
|
||
db: Session,
|
||
timestamp: datetime,
|
||
*,
|
||
updated_by: int | None = None,
|
||
) -> SystemConfig:
|
||
return upsert_system_config(
|
||
db,
|
||
config_code=MINIAPP_OPERATION_REPORT_LAST_SYNC_CONFIG_CODE,
|
||
config_name=MINIAPP_OPERATION_REPORT_LAST_SYNC_CONFIG_NAME,
|
||
config_value=format_miniapp_operation_report_last_sync(timestamp),
|
||
remark="工序报工小程序同步 ERP 的上次成功截止时间",
|
||
updated_by=updated_by,
|
||
now=timestamp,
|
||
)
|
||
|
||
|
||
def ensure_miniapp_operation_report_last_sync_config(
|
||
db: Session,
|
||
*,
|
||
updated_by: int | None = None,
|
||
) -> SystemConfig:
|
||
row = db.scalar(select(SystemConfig).where(SystemConfig.config_code == MINIAPP_OPERATION_REPORT_LAST_SYNC_CONFIG_CODE))
|
||
if row:
|
||
return row
|
||
timestamp = datetime.now()
|
||
row = SystemConfig(
|
||
config_code=MINIAPP_OPERATION_REPORT_LAST_SYNC_CONFIG_CODE,
|
||
config_name=MINIAPP_OPERATION_REPORT_LAST_SYNC_CONFIG_NAME,
|
||
config_value="",
|
||
remark="工序报工小程序同步 ERP 的上次成功截止时间",
|
||
status="ACTIVE",
|
||
created_by=updated_by,
|
||
updated_by=updated_by,
|
||
created_at=timestamp,
|
||
updated_at=timestamp,
|
||
)
|
||
db.add(row)
|
||
db.flush()
|
||
return row
|
||
|
||
|
||
def lock_miniapp_operation_report_last_sync(db: Session) -> SystemConfig | None:
|
||
ensure_miniapp_operation_report_last_sync_config(db)
|
||
return db.scalar(
|
||
select(SystemConfig)
|
||
.where(
|
||
SystemConfig.config_code == MINIAPP_OPERATION_REPORT_LAST_SYNC_CONFIG_CODE,
|
||
SystemConfig.status == "ACTIVE",
|
||
)
|
||
.with_for_update()
|
||
)
|
||
|
||
|
||
def upsert_smart_operation_report_config(
|
||
db: Session,
|
||
*,
|
||
enabled: bool,
|
||
updated_by: int | None = None,
|
||
remark: str | None = None,
|
||
now: datetime | None = None,
|
||
) -> SystemConfig:
|
||
return upsert_system_config(
|
||
db,
|
||
config_code=SMART_OPERATION_REPORT_CONFIG_CODE,
|
||
config_name="对接智能报工小程序",
|
||
config_value=normalize_enabled_config_value(enabled),
|
||
remark=remark or "开启后显示工序报工并按小程序报工数据计算生产进度;关闭后按ERP入库数据反推生产进度",
|
||
updated_by=updated_by,
|
||
now=now,
|
||
)
|
||
|
||
|
||
def lock_raw_material_lot_prefix_config(db: Session) -> None:
|
||
"""Serialize lot-number generation on databases that support row locks."""
|
||
db.scalar(
|
||
select(SystemConfig.id)
|
||
.where(
|
||
SystemConfig.config_code == RAW_MATERIAL_LOT_PREFIX_CONFIG_CODE,
|
||
SystemConfig.status == "ACTIVE",
|
||
)
|
||
.with_for_update()
|
||
)
|
||
|
||
|
||
def upsert_system_config(
|
||
db: Session,
|
||
*,
|
||
config_code: str,
|
||
config_name: str,
|
||
config_value: str,
|
||
remark: str | None = None,
|
||
updated_by: int | None = None,
|
||
now: datetime | None = None,
|
||
) -> SystemConfig:
|
||
timestamp = now or datetime.now()
|
||
saved_value = (
|
||
normalize_raw_material_lot_prefix(config_value)
|
||
if config_code == RAW_MATERIAL_LOT_PREFIX_CONFIG_CODE
|
||
else str(config_value or "").strip()
|
||
)
|
||
row = db.scalar(select(SystemConfig).where(SystemConfig.config_code == config_code))
|
||
if not row:
|
||
row = SystemConfig(
|
||
config_code=config_code,
|
||
created_by=updated_by,
|
||
created_at=timestamp,
|
||
)
|
||
|
||
row.config_name = config_name
|
||
row.config_value = saved_value
|
||
row.remark = remark
|
||
row.status = "ACTIVE"
|
||
row.updated_by = updated_by
|
||
row.updated_at = timestamp
|
||
db.add(row)
|
||
db.flush()
|
||
return row
|
||
|
||
# Use a Core update so caller-supplied business timestamps are not overwritten by
|
||
# the global ORM before_update audit hook.
|
||
db.execute(
|
||
update(SystemConfig)
|
||
.where(SystemConfig.id == row.id)
|
||
.values(
|
||
config_name=config_name,
|
||
config_value=saved_value,
|
||
remark=remark,
|
||
status="ACTIVE",
|
||
updated_by=updated_by,
|
||
updated_at=timestamp,
|
||
)
|
||
)
|
||
db.flush()
|
||
db.refresh(row)
|
||
return row
|