v0.2.0: CRM/ERP 系统升级 - 清理 .gitignore 并移除误提交的 venv/env/db 文件
- 更新 .gitignore:全面覆盖环境变量、数据库、日志、缓存、上传文件 - 移除误跟踪的 server/venv/、crm_data.db、.env 文件 - 新增 server/.env.example 模板 - 新增合同管理、利润核算、AI教练等功能模块 - 新增 Playwright e2e 测试套件 - 前后端多项功能升级和 bug 修复
This commit is contained in:
@@ -0,0 +1,113 @@
|
||||
"""Phase B: contract management + order/invoice linkage
|
||||
|
||||
Revision ID: e5f6a7b8c9d0
|
||||
Revises: d4e5f6a7b8c9
|
||||
Create Date: 2026-03-27
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy.dialects.postgresql import UUID, JSONB
|
||||
|
||||
revision = "e5f6a7b8c9d0"
|
||||
down_revision = "d4e5f6a7b8c9"
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
# ── 1. sys_companies 新增 full_info ──
|
||||
op.add_column(
|
||||
"sys_companies",
|
||||
sa.Column("full_info", JSONB, nullable=True,
|
||||
comment="公司完整信息: full_name/address/phone/bank_name/bank_account/tax_id"),
|
||||
)
|
||||
|
||||
# ── 2. erp_contracts 主表 ──
|
||||
op.create_table(
|
||||
"erp_contracts",
|
||||
sa.Column("id", UUID(as_uuid=True), primary_key=True),
|
||||
sa.Column("contract_no", sa.String(30), unique=True, nullable=False),
|
||||
sa.Column("buyer_customer_id", UUID(as_uuid=True), sa.ForeignKey("crm_customers.id"), nullable=False),
|
||||
sa.Column("seller_company_id", UUID(as_uuid=True), sa.ForeignKey("sys_companies.id"), nullable=False),
|
||||
sa.Column("company_id", UUID(as_uuid=True), sa.ForeignKey("sys_companies.id"), nullable=False, index=True),
|
||||
sa.Column("total_amount_excl_tax", sa.Numeric(14, 2), default=0),
|
||||
sa.Column("total_amount_incl_tax", sa.Numeric(14, 2), default=0),
|
||||
sa.Column("total_amount_cn", sa.String(100), nullable=True),
|
||||
sa.Column("payment_terms", sa.String(50), nullable=False, server_default="货到付全款"),
|
||||
sa.Column("shipping_terms", sa.String(50), nullable=False, server_default="买方自提"),
|
||||
sa.Column("status", sa.String(20), nullable=False, server_default="draft"),
|
||||
sa.Column("is_signed", sa.Boolean, default=False, server_default="false"),
|
||||
sa.Column("signed_file_url", sa.String(500), nullable=True),
|
||||
sa.Column("linked_order_id", UUID(as_uuid=True), sa.ForeignKey("erp_orders.id"), nullable=True),
|
||||
sa.Column("salesperson_id", UUID(as_uuid=True), sa.ForeignKey("sys_users.id"), nullable=True),
|
||||
sa.Column("sign_date", sa.Date, nullable=True),
|
||||
sa.Column("remark", sa.Text, nullable=True),
|
||||
sa.Column("created_at", sa.DateTime, server_default=sa.func.now()),
|
||||
sa.Column("updated_at", sa.DateTime, server_default=sa.func.now()),
|
||||
sa.Column("is_deleted", sa.Boolean, default=False, server_default="false"),
|
||||
)
|
||||
|
||||
# ── 3. erp_contract_items 明细行 ──
|
||||
op.create_table(
|
||||
"erp_contract_items",
|
||||
sa.Column("id", UUID(as_uuid=True), primary_key=True),
|
||||
sa.Column("contract_id", UUID(as_uuid=True), sa.ForeignKey("erp_contracts.id"), nullable=False),
|
||||
sa.Column("sku_id", UUID(as_uuid=True), sa.ForeignKey("erp_product_skus.id"), nullable=False),
|
||||
sa.Column("qty", sa.Numeric(12, 2), nullable=False),
|
||||
sa.Column("unit_price", sa.Numeric(12, 2), nullable=False),
|
||||
sa.Column("sub_total", sa.Numeric(14, 2), nullable=False),
|
||||
sa.Column("created_at", sa.DateTime, server_default=sa.func.now()),
|
||||
sa.Column("updated_at", sa.DateTime, server_default=sa.func.now()),
|
||||
sa.Column("is_deleted", sa.Boolean, default=False, server_default="false"),
|
||||
)
|
||||
|
||||
# ── 4. erp_contract_attachments 附件 ──
|
||||
op.create_table(
|
||||
"erp_contract_attachments",
|
||||
sa.Column("id", UUID(as_uuid=True), primary_key=True),
|
||||
sa.Column("contract_id", UUID(as_uuid=True), sa.ForeignKey("erp_contracts.id"), nullable=False),
|
||||
sa.Column("file_name", sa.String(200), nullable=False),
|
||||
sa.Column("file_url", sa.String(500), nullable=False),
|
||||
sa.Column("file_type", sa.String(30), nullable=False, server_default="signed_copy"),
|
||||
sa.Column("uploader_id", UUID(as_uuid=True), sa.ForeignKey("sys_users.id"), nullable=True),
|
||||
sa.Column("created_at", sa.DateTime, server_default=sa.func.now()),
|
||||
sa.Column("is_deleted", sa.Boolean, default=False, server_default="false"),
|
||||
)
|
||||
|
||||
# ── 5. erp_orders 新增 contract_id ──
|
||||
op.add_column(
|
||||
"erp_orders",
|
||||
sa.Column("contract_id", UUID(as_uuid=True),
|
||||
sa.ForeignKey("erp_contracts.id"), nullable=True,
|
||||
comment="来源合同(一键推单后回填)"),
|
||||
)
|
||||
|
||||
# ── 6. finance_sales_invoices 新增 order_id / shipping_record_id / payment_due_date ──
|
||||
op.add_column(
|
||||
"finance_sales_invoices",
|
||||
sa.Column("order_id", UUID(as_uuid=True),
|
||||
sa.ForeignKey("erp_orders.id"), nullable=True,
|
||||
comment="关联订单"),
|
||||
)
|
||||
op.add_column(
|
||||
"finance_sales_invoices",
|
||||
sa.Column("shipping_record_id", UUID(as_uuid=True),
|
||||
sa.ForeignKey("erp_shipping_records.id"), nullable=True,
|
||||
comment="关联发货单"),
|
||||
)
|
||||
op.add_column(
|
||||
"finance_sales_invoices",
|
||||
sa.Column("payment_due_date", sa.Date, nullable=True,
|
||||
comment="回款截止日(根据合同付款条件自动推算)"),
|
||||
)
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
op.drop_column("finance_sales_invoices", "payment_due_date")
|
||||
op.drop_column("finance_sales_invoices", "shipping_record_id")
|
||||
op.drop_column("finance_sales_invoices", "order_id")
|
||||
op.drop_column("erp_orders", "contract_id")
|
||||
op.drop_table("erp_contract_attachments")
|
||||
op.drop_table("erp_contract_items")
|
||||
op.drop_table("erp_contracts")
|
||||
op.drop_column("sys_companies", "full_info")
|
||||
Reference in New Issue
Block a user