feat(T+): 完成数据贯通与自动接入闭环
打通 T+ 快照、业务、发货、回款和核算准备链路,保留来源差异并提供待补状态。 新增业务自动接入任务、跨平台读取服务托管、权限复核、来源关联和前端核对页面。 补充迁移、隔离集成测试、浏览器模拟验收及角色授权边界修复。
This commit is contained in:
1 parent
3efe72c344
commit
1e53792ad6
151 files changed
+8842
-401
No files matched your search
@@ -0,0 +1,47 @@
|
||||
"""V29 回款独立人工确认及默认开启开关;不自动确认历史回款。"""
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
from alembic import op
|
||||
from sqlalchemy import inspect, text
|
||||
|
||||
|
||||
revision = "29b0c1d2e3f4"
|
||||
down_revision = "28a9b0c1d2e3"
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
sql = Path(__file__).resolve().parents[3] / "db/migration/V29__receipt_manual_confirmation.sql"
|
||||
columns = {column["name"]: column for column in inspect(op.get_bind()).get_columns("receipts")}
|
||||
existing = {name for name in ("confirmation_status", "confirmed_by", "confirmed_at", "confirmed_revision") if name in columns}
|
||||
if existing and len(existing) != 4:
|
||||
raise RuntimeError("回款确认字段不完整,请先核对数据库结构")
|
||||
if existing:
|
||||
expected = {"confirmation_status": ("TINYINT", None, False), "confirmed_by": ("VARCHAR", 64, True),
|
||||
"confirmed_at": ("DATETIME", None, True), "confirmed_revision": ("VARCHAR", 64, True)}
|
||||
for name, (kind, length, nullable) in expected.items():
|
||||
column = columns[name]
|
||||
actual_type = column["type"]
|
||||
if (actual_type.__visit_name__.upper() != kind or getattr(actual_type, "length", None) != length
|
||||
or column["nullable"] != nullable):
|
||||
raise RuntimeError(f"回款确认字段 {name} 与迁移定义不一致,请先核对数据库结构")
|
||||
if str(columns["confirmation_status"]["default"]).strip("'\"") != "0":
|
||||
raise RuntimeError("回款确认状态默认值不是待确认,请先核对数据库结构")
|
||||
for statement in sql.read_text(encoding="utf-8").split(";\n"):
|
||||
clean = "\n".join(line for line in statement.splitlines() if not line.lstrip().startswith("--")).strip()
|
||||
if clean and not (existing and clean.startswith("ALTER TABLE receipts ADD COLUMN")):
|
||||
op.get_bind().execute(text(clean))
|
||||
|
||||
|
||||
def downgrade():
|
||||
bind = op.get_bind()
|
||||
if bind.scalar(text("SELECT COUNT(*) FROM receipts WHERE confirmed_at IS NOT NULL OR confirmation_status<>0")):
|
||||
raise RuntimeError("已有回款人工确认记录,禁止删除确认依据;请仅回退应用并保留结构")
|
||||
if bind.scalar(text("SELECT COUNT(*) FROM param_values v JOIN param_definitions d ON d.id=v.param_id WHERE d.param_code='receipt.confirmation.enabled'")):
|
||||
raise RuntimeError("已维护人工确认开关版本,禁止删除历史参数")
|
||||
bind.execute(text("DELETE FROM param_definitions WHERE param_code='receipt.confirmation.enabled'"))
|
||||
bind.execute(text("UPDATE permissions SET status=0 WHERE perm_code='business:receipts:confirm'"))
|
||||
for column in ("confirmed_revision", "confirmed_at", "confirmed_by", "confirmation_status"):
|
||||
op.drop_column("receipts", column)
|
||||
@@ -0,0 +1,40 @@
|
||||
"""V30 业务接入与核算准备解耦;无默认回填。"""
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
from alembic import op
|
||||
from sqlalchemy import text
|
||||
|
||||
revision = "30c1d2e3f4a5"
|
||||
down_revision = "29b0c1d2e3f4"
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
script = Path(__file__).resolve().parents[3] / "db/migration/V30__tplus_business_sync.sql"
|
||||
for statement in script.read_text(encoding="utf-8").split(";\n"):
|
||||
sql = "\n".join(line for line in statement.splitlines() if not line.lstrip().startswith("--")).strip()
|
||||
if sql:
|
||||
op.get_bind().execute(text(sql))
|
||||
|
||||
|
||||
def downgrade():
|
||||
connection = op.get_bind()
|
||||
for table, condition in (("tplus_business_documents", "1=1"), ("products", "product_category IS NULL"),
|
||||
("customers", "customer_type IS NULL"), ("business_items", "product_category IS NULL"),
|
||||
("shipments", "freight_amount IS NULL")):
|
||||
if connection.scalar(text(f"SELECT COUNT(*) FROM {table} WHERE {condition}")):
|
||||
raise RuntimeError("已有接入或待补资料,请关闭接入并回退兼容版本;不删除业务数据以降级结构")
|
||||
op.drop_table("tplus_business_documents")
|
||||
op.drop_table("tplus_business_sync_configs")
|
||||
for index in ("idx_tplus_source_heads_parent", "idx_tplus_source_heads_customer_date", "idx_tplus_source_heads_date"):
|
||||
op.drop_index(index, "tplus_source_heads")
|
||||
for column in ("source_parent_id", "source_customer_id", "source_date"):
|
||||
op.drop_column("tplus_source_heads", column)
|
||||
op.drop_column("businesses", "integration_json")
|
||||
for table, column, sql_type, default in (("products", "product_category", "VARCHAR(32)", ""),
|
||||
("customers", "customer_type", "VARCHAR(32)", " DEFAULT 'personal'"),
|
||||
("business_items", "product_category", "VARCHAR(32)", ""),
|
||||
("shipments", "freight_amount", "DECIMAL(14,2)", " DEFAULT 0")):
|
||||
connection.execute(text(f"ALTER TABLE {table} MODIFY {column} {sql_type} NOT NULL{default}"))
|
||||
@@ -0,0 +1,14 @@
|
||||
"""合并业务接入和回款确认迁移分支。"""
|
||||
|
||||
revision = "31d2e3f4a5b6"
|
||||
down_revision = ("29b0c1d2e3f4", "30c1d2e3f4a5")
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
pass
|
||||
|
||||
|
||||
def downgrade():
|
||||
pass
|
||||
Reference in new issue
Block a user