Compare commits
2 Commits
bf48c5a60f
...
855642ea75
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
855642ea75 | ||
|
|
8cafca4b44 |
|
|
@ -1,34 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"database/sql"
|
|
||||||
"log"
|
|
||||||
"os"
|
|
||||||
|
|
||||||
_ "github.com/go-sql-driver/mysql"
|
|
||||||
"github.com/wmms/wmms/pkg/migration"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
dsn := os.Getenv("DB_DSN")
|
|
||||||
if dsn == "" {
|
|
||||||
log.Fatal("DB_DSN is required")
|
|
||||||
}
|
|
||||||
dir := os.Getenv("MIGRATIONS_DIR")
|
|
||||||
if dir == "" {
|
|
||||||
dir = "scripts/migrations"
|
|
||||||
}
|
|
||||||
db, err := sql.Open("mysql", dsn)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("open database: %v", err)
|
|
||||||
}
|
|
||||||
defer db.Close()
|
|
||||||
if err := db.PingContext(context.Background()); err != nil {
|
|
||||||
log.Fatalf("connect database: %v", err)
|
|
||||||
}
|
|
||||||
if err := migration.Run(context.Background(), db, dir); err != nil {
|
|
||||||
log.Fatalf("run migrations: %v", err)
|
|
||||||
}
|
|
||||||
log.Print("migrations applied successfully")
|
|
||||||
}
|
|
||||||
|
|
@ -11,6 +11,7 @@ services:
|
||||||
- "3307:3306"
|
- "3307:3306"
|
||||||
volumes:
|
volumes:
|
||||||
- mysql_data:/var/lib/mysql
|
- mysql_data:/var/lib/mysql
|
||||||
|
- ./scripts/init.sql:/docker-entrypoint-initdb.d/001-init.sql:ro
|
||||||
command: >
|
command: >
|
||||||
--default-authentication-plugin=mysql_native_password
|
--default-authentication-plugin=mysql_native_password
|
||||||
--character-set-server=utf8mb4
|
--character-set-server=utf8mb4
|
||||||
|
|
@ -35,7 +36,6 @@ services:
|
||||||
GIN_MODE: release
|
GIN_MODE: release
|
||||||
volumes:
|
volumes:
|
||||||
- uploads_data:/app/uploads
|
- uploads_data:/app/uploads
|
||||||
- ./scripts/migrations:/docker-entrypoint-initdb.d/migrations:ro
|
|
||||||
depends_on:
|
depends_on:
|
||||||
db:
|
db:
|
||||||
condition: service_healthy
|
condition: service_healthy
|
||||||
|
|
|
||||||
|
|
@ -1,31 +0,0 @@
|
||||||
# 第一阶段数据库迁移
|
|
||||||
|
|
||||||
本阶段将公司组织、业务伙伴、仓库/库位、物料基础配置和库存余额拆分到独立表。旧的
|
|
||||||
`materials.current_stock` 与 `materials.warning_stock` 暂时保留用于回滚观察,但应用代码不再读写。
|
|
||||||
|
|
||||||
## 执行顺序
|
|
||||||
|
|
||||||
1. 备份现有数据库,并暂停入库、出库和物料申请操作。
|
|
||||||
2. 为当前 shell 设置 `DB_DSN`,不要把账号密码写入仓库。
|
|
||||||
3. 在项目根目录运行:
|
|
||||||
|
|
||||||
```powershell
|
|
||||||
go run ./cmd/migrate
|
|
||||||
```
|
|
||||||
|
|
||||||
4. 确认迁移 `001` 到 `006` 均为非 dirty 状态,并检查 `006` 最后的库存差异查询没有结果。
|
|
||||||
5. 发布后端和前端,再恢复库存操作。
|
|
||||||
|
|
||||||
迁移器按文件名顺序执行并记录在 `schema_migrations`。MySQL DDL 可能隐式提交;如果某个版本为
|
|
||||||
dirty,迁移器会停止,必须根据报错的语句编号人工核对并修复,不能直接假定事务已回滚。
|
|
||||||
|
|
||||||
## 核对重点
|
|
||||||
|
|
||||||
- 每个旧用户均已分配默认组织和默认仓库。
|
|
||||||
- 旧物料数量与 `inventory_balances.on_hand` 汇总一致。
|
|
||||||
- 旧预警值与 `stock_warning_rules.minimum_available` 一致。
|
|
||||||
- `materials` API 响应不再包含库存数量和预警值。
|
|
||||||
- 公司组织位于 `organizations`,外部公司位于 `business_partners`,合作关系位于 `organization_partners`。
|
|
||||||
|
|
||||||
不要在生产库直接试跑。应先在数据库副本验证一次;当前自动化测试只验证迁移执行器和应用查询,
|
|
||||||
不替代生产数据副本的迁移核对。
|
|
||||||
|
|
@ -1,202 +0,0 @@
|
||||||
package migration
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"database/sql"
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"sort"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
const createMigrationsTable = `CREATE TABLE IF NOT EXISTS schema_migrations (
|
|
||||||
version VARCHAR(100) PRIMARY KEY,
|
|
||||||
dirty BOOLEAN NOT NULL DEFAULT TRUE,
|
|
||||||
applied_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
|
|
||||||
)`
|
|
||||||
|
|
||||||
// Run applies .sql files in lexical filename order.
|
|
||||||
// MySQL 5.7 may implicitly commit DDL, so every migration is marked dirty
|
|
||||||
// before its first statement and only marked clean after all statements pass.
|
|
||||||
func Run(ctx context.Context, db *sql.DB, dir string) error {
|
|
||||||
if db == nil {
|
|
||||||
return fmt.Errorf("migration database is nil")
|
|
||||||
}
|
|
||||||
if _, err := db.ExecContext(ctx, createMigrationsTable); err != nil {
|
|
||||||
return fmt.Errorf("create schema_migrations: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
var dirtyVersion string
|
|
||||||
err := db.QueryRowContext(ctx, `SELECT version FROM schema_migrations WHERE dirty = TRUE ORDER BY applied_at DESC LIMIT 1`).Scan(&dirtyVersion)
|
|
||||||
if err == nil {
|
|
||||||
return fmt.Errorf("migration %s is dirty; stop and perform manual repair before retrying", dirtyVersion)
|
|
||||||
}
|
|
||||||
if err != sql.ErrNoRows {
|
|
||||||
return fmt.Errorf("check dirty migration: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
applied, err := loadApplied(ctx, db)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
files, err := migrationFiles(dir)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, path := range files {
|
|
||||||
version := filepath.Base(path)
|
|
||||||
if applied[version] {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
contents, err := os.ReadFile(path)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("read migration %s: %w", version, err)
|
|
||||||
}
|
|
||||||
statements, err := splitStatements(string(contents))
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("parse migration %s: %w", version, err)
|
|
||||||
}
|
|
||||||
if _, err := db.ExecContext(ctx, `INSERT INTO schema_migrations (version, dirty) VALUES (?, TRUE)`, version); err != nil {
|
|
||||||
return fmt.Errorf("mark migration %s dirty: %w", version, err)
|
|
||||||
}
|
|
||||||
for index, statement := range statements {
|
|
||||||
if _, err := db.ExecContext(ctx, statement); err != nil {
|
|
||||||
return fmt.Errorf("migration %s statement %d failed: %w; version remains dirty because MySQL DDL cannot be transactionally rolled back; perform manual repair", version, index+1, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if _, err := db.ExecContext(ctx, `UPDATE schema_migrations SET dirty = FALSE, applied_at = CURRENT_TIMESTAMP WHERE version = ?`, version); err != nil {
|
|
||||||
return fmt.Errorf("mark migration %s clean: %w; version remains dirty and requires manual repair", version, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func loadApplied(ctx context.Context, db *sql.DB) (map[string]bool, error) {
|
|
||||||
rows, err := db.QueryContext(ctx, `SELECT version FROM schema_migrations WHERE dirty = FALSE`)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("list applied migrations: %w", err)
|
|
||||||
}
|
|
||||||
defer rows.Close()
|
|
||||||
applied := make(map[string]bool)
|
|
||||||
for rows.Next() {
|
|
||||||
var version string
|
|
||||||
if err := rows.Scan(&version); err != nil {
|
|
||||||
return nil, fmt.Errorf("scan applied migration: %w", err)
|
|
||||||
}
|
|
||||||
applied[version] = true
|
|
||||||
}
|
|
||||||
if err := rows.Err(); err != nil {
|
|
||||||
return nil, fmt.Errorf("iterate applied migrations: %w", err)
|
|
||||||
}
|
|
||||||
return applied, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func migrationFiles(dir string) ([]string, error) {
|
|
||||||
entries, err := os.ReadDir(dir)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("read migrations directory %s: %w", dir, err)
|
|
||||||
}
|
|
||||||
files := make([]string, 0, len(entries))
|
|
||||||
for _, entry := range entries {
|
|
||||||
if entry.IsDir() || strings.ToLower(filepath.Ext(entry.Name())) != ".sql" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
files = append(files, filepath.Join(dir, entry.Name()))
|
|
||||||
}
|
|
||||||
sort.Strings(files)
|
|
||||||
return files, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func splitStatements(input string) ([]string, error) {
|
|
||||||
var statements []string
|
|
||||||
var current strings.Builder
|
|
||||||
var quote rune
|
|
||||||
escaped := false
|
|
||||||
lineComment := false
|
|
||||||
blockComment := false
|
|
||||||
runes := []rune(input)
|
|
||||||
|
|
||||||
flush := func() {
|
|
||||||
statement := strings.TrimSpace(current.String())
|
|
||||||
if statement != "" {
|
|
||||||
statements = append(statements, statement)
|
|
||||||
}
|
|
||||||
current.Reset()
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := 0; i < len(runes); i++ {
|
|
||||||
ch := runes[i]
|
|
||||||
next := rune(0)
|
|
||||||
if i+1 < len(runes) {
|
|
||||||
next = runes[i+1]
|
|
||||||
}
|
|
||||||
if lineComment {
|
|
||||||
if ch == '\n' {
|
|
||||||
lineComment = false
|
|
||||||
if current.Len() > 0 {
|
|
||||||
current.WriteRune(' ')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if blockComment {
|
|
||||||
if ch == '*' && next == '/' {
|
|
||||||
blockComment = false
|
|
||||||
i++
|
|
||||||
if current.Len() > 0 {
|
|
||||||
current.WriteRune(' ')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if quote != 0 {
|
|
||||||
current.WriteRune(ch)
|
|
||||||
if escaped {
|
|
||||||
escaped = false
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if ch == '\\' {
|
|
||||||
escaped = true
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if ch == quote {
|
|
||||||
quote = 0
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if ch == '-' && next == '-' {
|
|
||||||
lineComment = true
|
|
||||||
i++
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if ch == '#' {
|
|
||||||
lineComment = true
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if ch == '/' && next == '*' {
|
|
||||||
blockComment = true
|
|
||||||
i++
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if ch == '\'' || ch == '"' || ch == '`' {
|
|
||||||
quote = ch
|
|
||||||
current.WriteRune(ch)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if ch == ';' {
|
|
||||||
flush()
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
current.WriteRune(ch)
|
|
||||||
}
|
|
||||||
if quote != 0 {
|
|
||||||
return nil, fmt.Errorf("unterminated quoted string")
|
|
||||||
}
|
|
||||||
if blockComment {
|
|
||||||
return nil, fmt.Errorf("unterminated block comment")
|
|
||||||
}
|
|
||||||
flush()
|
|
||||||
return statements, nil
|
|
||||||
}
|
|
||||||
|
|
@ -1,136 +0,0 @@
|
||||||
package migration
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"errors"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"regexp"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/DATA-DOG/go-sqlmock"
|
|
||||||
)
|
|
||||||
|
|
||||||
const createMigrationsTableSQL = `CREATE TABLE IF NOT EXISTS schema_migrations (
|
|
||||||
version VARCHAR(100) PRIMARY KEY,
|
|
||||||
dirty BOOLEAN NOT NULL DEFAULT TRUE,
|
|
||||||
applied_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
|
|
||||||
)`
|
|
||||||
|
|
||||||
func writeMigration(t *testing.T, name, sqlText string) string {
|
|
||||||
t.Helper()
|
|
||||||
dir := t.TempDir()
|
|
||||||
if err := os.WriteFile(filepath.Join(dir, name), []byte(sqlText), 0o600); err != nil {
|
|
||||||
t.Fatalf("write migration: %v", err)
|
|
||||||
}
|
|
||||||
return dir
|
|
||||||
}
|
|
||||||
|
|
||||||
func expectCleanState(mock sqlmock.Sqlmock, applied ...string) {
|
|
||||||
mock.ExpectExec(regexp.QuoteMeta(createMigrationsTableSQL)).WillReturnResult(sqlmock.NewResult(0, 0))
|
|
||||||
mock.ExpectQuery(regexp.QuoteMeta(`SELECT version FROM schema_migrations WHERE dirty = TRUE ORDER BY applied_at DESC LIMIT 1`)).
|
|
||||||
WillReturnRows(sqlmock.NewRows([]string{"version"}))
|
|
||||||
rows := sqlmock.NewRows([]string{"version"})
|
|
||||||
for _, version := range applied {
|
|
||||||
rows.AddRow(version)
|
|
||||||
}
|
|
||||||
mock.ExpectQuery(regexp.QuoteMeta(`SELECT version FROM schema_migrations WHERE dirty = FALSE`)).WillReturnRows(rows)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestRunAppliesEachMigrationOnce(t *testing.T) {
|
|
||||||
dir := writeMigration(t, "001_create_widgets.sql", `CREATE TABLE widgets (id BIGINT PRIMARY KEY);`)
|
|
||||||
db, mock, err := sqlmock.New()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("open sqlmock: %v", err)
|
|
||||||
}
|
|
||||||
t.Cleanup(func() { db.Close() })
|
|
||||||
|
|
||||||
expectCleanState(mock)
|
|
||||||
mock.ExpectExec(regexp.QuoteMeta(`INSERT INTO schema_migrations (version, dirty) VALUES (?, TRUE)`)).
|
|
||||||
WithArgs("001_create_widgets.sql").WillReturnResult(sqlmock.NewResult(1, 1))
|
|
||||||
mock.ExpectExec(regexp.QuoteMeta(`CREATE TABLE widgets (id BIGINT PRIMARY KEY)`)).
|
|
||||||
WillReturnResult(sqlmock.NewResult(0, 0))
|
|
||||||
mock.ExpectExec(regexp.QuoteMeta(`UPDATE schema_migrations SET dirty = FALSE, applied_at = CURRENT_TIMESTAMP WHERE version = ?`)).
|
|
||||||
WithArgs("001_create_widgets.sql").WillReturnResult(sqlmock.NewResult(0, 1))
|
|
||||||
|
|
||||||
if err := Run(context.Background(), db, dir); err != nil {
|
|
||||||
t.Fatalf("first run: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
expectCleanState(mock, "001_create_widgets.sql")
|
|
||||||
if err := Run(context.Background(), db, dir); err != nil {
|
|
||||||
t.Fatalf("second run: %v", err)
|
|
||||||
}
|
|
||||||
if err := mock.ExpectationsWereMet(); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestRunLeavesFailedMigrationDirty(t *testing.T) {
|
|
||||||
dir := writeMigration(t, "002_broken.sql", `CREATE TABLE broken (id BIGINT); ALTER TABLE missing ADD value INT;`)
|
|
||||||
db, mock, err := sqlmock.New()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("open sqlmock: %v", err)
|
|
||||||
}
|
|
||||||
t.Cleanup(func() { db.Close() })
|
|
||||||
|
|
||||||
expectCleanState(mock)
|
|
||||||
mock.ExpectExec(regexp.QuoteMeta(`INSERT INTO schema_migrations (version, dirty) VALUES (?, TRUE)`)).
|
|
||||||
WithArgs("002_broken.sql").WillReturnResult(sqlmock.NewResult(1, 1))
|
|
||||||
mock.ExpectExec(regexp.QuoteMeta(`CREATE TABLE broken (id BIGINT)`)).WillReturnResult(sqlmock.NewResult(0, 0))
|
|
||||||
mock.ExpectExec(regexp.QuoteMeta(`ALTER TABLE missing ADD value INT`)).WillReturnError(errors.New("missing table"))
|
|
||||||
|
|
||||||
err = Run(context.Background(), db, dir)
|
|
||||||
if err == nil {
|
|
||||||
t.Fatal("expected migration failure")
|
|
||||||
}
|
|
||||||
for _, fragment := range []string{"002_broken.sql", "statement 2", "dirty", "manual repair"} {
|
|
||||||
if !strings.Contains(strings.ToLower(err.Error()), strings.ToLower(fragment)) {
|
|
||||||
t.Fatalf("error %q does not contain %q", err, fragment)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if err := mock.ExpectationsWereMet(); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestRunStopsWhenDirtyMigrationExists(t *testing.T) {
|
|
||||||
dir := writeMigration(t, "003_should_not_run.sql", `CREATE TABLE should_not_run (id BIGINT);`)
|
|
||||||
db, mock, err := sqlmock.New()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("open sqlmock: %v", err)
|
|
||||||
}
|
|
||||||
t.Cleanup(func() { db.Close() })
|
|
||||||
|
|
||||||
mock.ExpectExec(regexp.QuoteMeta(createMigrationsTableSQL)).WillReturnResult(sqlmock.NewResult(0, 0))
|
|
||||||
mock.ExpectQuery(regexp.QuoteMeta(`SELECT version FROM schema_migrations WHERE dirty = TRUE ORDER BY applied_at DESC LIMIT 1`)).
|
|
||||||
WillReturnRows(sqlmock.NewRows([]string{"version"}).AddRow("002_broken.sql"))
|
|
||||||
|
|
||||||
err = Run(context.Background(), db, dir)
|
|
||||||
if err == nil || !strings.Contains(err.Error(), "002_broken.sql") {
|
|
||||||
t.Fatalf("expected dirty migration error, got %v", err)
|
|
||||||
}
|
|
||||||
if err := mock.ExpectationsWereMet(); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSplitStatementsPreservesSemicolonsInsideStrings(t *testing.T) {
|
|
||||||
statements, err := splitStatements("INSERT INTO notes(value) VALUES ('a;b'); -- comment\nUPDATE notes SET value = \"c;d\";")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("split statements: %v", err)
|
|
||||||
}
|
|
||||||
want := []string{
|
|
||||||
"INSERT INTO notes(value) VALUES ('a;b')",
|
|
||||||
"UPDATE notes SET value = \"c;d\"",
|
|
||||||
}
|
|
||||||
if len(statements) != len(want) {
|
|
||||||
t.Fatalf("got %d statements, want %d: %#v", len(statements), len(want), statements)
|
|
||||||
}
|
|
||||||
for i := range want {
|
|
||||||
if statements[i] != want[i] {
|
|
||||||
t.Fatalf("statement %d = %q, want %q", i+1, statements[i], want[i])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -7,11 +7,5 @@ until mysqladmin ping -h db -uroot -pwmms1234 --silent 2>/dev/null; do
|
||||||
done
|
done
|
||||||
echo "MySQL is ready."
|
echo "MySQL is ready."
|
||||||
|
|
||||||
echo "Running database migrations..."
|
|
||||||
for f in $(ls /docker-entrypoint-initdb.d/migrations/*.sql 2>/dev/null | sort); do
|
|
||||||
echo "Executing: $f"
|
|
||||||
mysql -h db -uroot -pwmms1234 wmms < "$f" 2>/dev/null && echo "OK: $(basename $f)" || echo "FAILED: $(basename $f)"
|
|
||||||
done
|
|
||||||
|
|
||||||
echo "Starting application..."
|
echo "Starting application..."
|
||||||
exec "$@"
|
exec "$@"
|
||||||
|
|
|
||||||
279
scripts/init.sql
279
scripts/init.sql
|
|
@ -1,237 +1,48 @@
|
||||||
-- ============================================
|
-- WMMS fresh schema for MySQL 8.0. Executed once for an empty Docker volume.
|
||||||
-- Warehouse Material Management System (WMMS)
|
CREATE DATABASE IF NOT EXISTS wmms CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||||
-- Database Initialization Script
|
|
||||||
-- MySQL 5.7+
|
|
||||||
-- ============================================
|
|
||||||
|
|
||||||
-- Create database
|
|
||||||
CREATE DATABASE IF NOT EXISTS wmms;
|
|
||||||
USE wmms;
|
USE wmms;
|
||||||
|
|
||||||
-- ============================================
|
CREATE TABLE users (id BIGINT AUTO_INCREMENT PRIMARY KEY, username VARCHAR(50) NOT NULL UNIQUE, password VARCHAR(255) NOT NULL, description VARCHAR(500) NOT NULL DEFAULT '', created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||||
-- Table: materials
|
CREATE TABLE organizations (id BIGINT AUTO_INCREMENT PRIMARY KEY, code VARCHAR(50) NOT NULL UNIQUE, name VARCHAR(200) NOT NULL, status VARCHAR(20) NOT NULL DEFAULT 'active', default_timezone VARCHAR(64) NOT NULL DEFAULT 'Asia/Shanghai', created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||||
-- ============================================
|
CREATE TABLE roles (id BIGINT AUTO_INCREMENT PRIMARY KEY, code VARCHAR(80) NOT NULL UNIQUE, name VARCHAR(100) NOT NULL, enabled BOOLEAN NOT NULL DEFAULT TRUE, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||||
CREATE TABLE IF NOT EXISTS materials (
|
CREATE TABLE permissions (id BIGINT AUTO_INCREMENT PRIMARY KEY, code VARCHAR(120) NOT NULL UNIQUE, name VARCHAR(150) NOT NULL, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||||
id BIGINT AUTO_INCREMENT PRIMARY KEY,
|
CREATE TABLE role_permissions (role_id BIGINT NOT NULL, permission_id BIGINT NOT NULL, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY(role_id,permission_id), FOREIGN KEY(role_id) REFERENCES roles(id) ON DELETE CASCADE, FOREIGN KEY(permission_id) REFERENCES permissions(id) ON DELETE CASCADE) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||||
material_code VARCHAR(50) NOT NULL UNIQUE,
|
CREATE TABLE user_roles (user_id BIGINT NOT NULL, role_id BIGINT NOT NULL, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY(user_id,role_id), FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE, FOREIGN KEY(role_id) REFERENCES roles(id) ON DELETE RESTRICT) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||||
name VARCHAR(200) NOT NULL,
|
CREATE TABLE user_organizations (user_id BIGINT NOT NULL, organization_id BIGINT NOT NULL, is_default BOOLEAN NOT NULL DEFAULT FALSE, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY(user_id,organization_id), FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE, FOREIGN KEY(organization_id) REFERENCES organizations(id) ON DELETE RESTRICT) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||||
material_image VARCHAR(500),
|
CREATE TABLE revoked_tokens (jti VARCHAR(64) PRIMARY KEY, user_id BIGINT NOT NULL, expires_at DATETIME NOT NULL, revoked_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, KEY idx_revoked_tokens_expires_at(expires_at), FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||||
spec VARCHAR(200),
|
CREATE TABLE material_types (id BIGINT AUTO_INCREMENT PRIMARY KEY, organization_id BIGINT NOT NULL, code VARCHAR(50) NOT NULL, name VARCHAR(100) NOT NULL, status VARCHAR(20) NOT NULL DEFAULT 'active', UNIQUE KEY uk_material_types_org_code(organization_id,code), FOREIGN KEY(organization_id) REFERENCES organizations(id) ON DELETE RESTRICT) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||||
category VARCHAR(100),
|
CREATE TABLE material_categories (id BIGINT AUTO_INCREMENT PRIMARY KEY, organization_id BIGINT NOT NULL, code VARCHAR(50) NOT NULL, name VARCHAR(100) NOT NULL, parent_id BIGINT NULL, status VARCHAR(20) NOT NULL DEFAULT 'active', UNIQUE KEY uk_material_categories_org_code(organization_id,code), FOREIGN KEY(organization_id) REFERENCES organizations(id) ON DELETE RESTRICT, FOREIGN KEY(parent_id) REFERENCES material_categories(id) ON DELETE RESTRICT) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||||
unit VARCHAR(20),
|
CREATE TABLE units (id BIGINT AUTO_INCREMENT PRIMARY KEY, organization_id BIGINT NOT NULL, code VARCHAR(50) NOT NULL, name VARCHAR(100) NOT NULL, precision_scale TINYINT NOT NULL DEFAULT 0, status VARCHAR(20) NOT NULL DEFAULT 'active', UNIQUE KEY uk_units_org_code(organization_id,code), FOREIGN KEY(organization_id) REFERENCES organizations(id) ON DELETE RESTRICT) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||||
warning_stock INT DEFAULT 0,
|
CREATE TABLE materials (id BIGINT AUTO_INCREMENT PRIMARY KEY, organization_id BIGINT NOT NULL, material_code VARCHAR(50) NOT NULL, name VARCHAR(200) NOT NULL, material_image VARCHAR(500), spec VARCHAR(200), manufacturer_model VARCHAR(200), material_type_id BIGINT, category_id BIGINT NOT NULL, base_unit_id BIGINT, unit VARCHAR(20), status VARCHAR(20) NOT NULL DEFAULT 'active', version INT NOT NULL DEFAULT 1, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, UNIQUE KEY uk_materials_org_code(organization_id,material_code), KEY idx_materials_category(category_id), FOREIGN KEY(organization_id) REFERENCES organizations(id) ON DELETE RESTRICT, FOREIGN KEY(material_type_id) REFERENCES material_types(id) ON DELETE RESTRICT, FOREIGN KEY(category_id) REFERENCES material_categories(id) ON DELETE RESTRICT, FOREIGN KEY(base_unit_id) REFERENCES units(id) ON DELETE RESTRICT) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||||
current_stock INT DEFAULT 0,
|
CREATE TABLE code_rules (id BIGINT AUTO_INCREMENT PRIMARY KEY, organization_id BIGINT NOT NULL, category_id BIGINT NOT NULL, prefix VARCHAR(30) NOT NULL, sequence_length TINYINT NOT NULL, next_sequence BIGINT NOT NULL DEFAULT 1, status VARCHAR(20) NOT NULL DEFAULT 'active', UNIQUE KEY uk_code_rules_org_category(organization_id,category_id), FOREIGN KEY(organization_id) REFERENCES organizations(id), FOREIGN KEY(category_id) REFERENCES material_categories(id)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
CREATE TABLE material_unit_conversions (id BIGINT AUTO_INCREMENT PRIMARY KEY, organization_id BIGINT NOT NULL, material_id BIGINT NOT NULL, from_unit_id BIGINT NOT NULL, to_unit_id BIGINT NOT NULL, conversion_rate DECIMAL(20,6) NOT NULL, effective_from DATE NOT NULL, effective_to DATE NULL, KEY idx_material_unit_conversions_material(organization_id,material_id), FOREIGN KEY(organization_id) REFERENCES organizations(id), FOREIGN KEY(material_id) REFERENCES materials(id), FOREIGN KEY(from_unit_id) REFERENCES units(id), FOREIGN KEY(to_unit_id) REFERENCES units(id)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||||
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
CREATE TABLE tags (id BIGINT AUTO_INCREMENT PRIMARY KEY, material_id BIGINT NOT NULL, tag_code VARCHAR(100) NOT NULL UNIQUE, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, KEY idx_tags_material(material_id), FOREIGN KEY(material_id) REFERENCES materials(id) ON DELETE CASCADE) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||||
INDEX idx_material_code (material_code),
|
CREATE TABLE business_partners (id BIGINT AUTO_INCREMENT PRIMARY KEY, legal_name VARCHAR(200) NOT NULL, unified_credit_code VARCHAR(50) UNIQUE, contact_name VARCHAR(100), contact_phone VARCHAR(50), address VARCHAR(500), created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||||
INDEX idx_category (category)
|
CREATE TABLE organization_partners (id BIGINT AUTO_INCREMENT PRIMARY KEY, organization_id BIGINT NOT NULL, business_partner_id BIGINT NOT NULL, partner_code VARCHAR(50) NOT NULL, is_supplier BOOLEAN NOT NULL DEFAULT FALSE, is_customer BOOLEAN NOT NULL DEFAULT FALSE, supplier_status VARCHAR(20), customer_status VARCHAR(20), settlement_currency CHAR(3), payment_terms VARCHAR(200), created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, UNIQUE KEY uk_organization_partners_org_partner(organization_id,business_partner_id), UNIQUE KEY uk_organization_partners_org_code(organization_id,partner_code), FOREIGN KEY(organization_id) REFERENCES organizations(id), FOREIGN KEY(business_partner_id) REFERENCES business_partners(id)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
CREATE TABLE material_suppliers (id BIGINT AUTO_INCREMENT PRIMARY KEY, organization_id BIGINT NOT NULL, material_id BIGINT NOT NULL, organization_partner_id BIGINT NOT NULL, price DECIMAL(20,6) NOT NULL, currency CHAR(3) NOT NULL, minimum_order_quantity DECIMAL(20,6) NOT NULL DEFAULT 0, lead_time_days INT NOT NULL DEFAULT 0, is_preferred BOOLEAN NOT NULL DEFAULT FALSE, effective_from DATE NOT NULL, effective_to DATE, status VARCHAR(20) NOT NULL DEFAULT 'active', KEY idx_material_suppliers_material(organization_id,material_id), FOREIGN KEY(organization_id) REFERENCES organizations(id), FOREIGN KEY(material_id) REFERENCES materials(id), FOREIGN KEY(organization_partner_id) REFERENCES organization_partners(id)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||||
|
CREATE TABLE warehouses (id BIGINT AUTO_INCREMENT PRIMARY KEY, organization_id BIGINT NOT NULL, code VARCHAR(50) NOT NULL, name VARCHAR(150) NOT NULL, status VARCHAR(20) NOT NULL DEFAULT 'active', created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, UNIQUE KEY uk_warehouses_org_code(organization_id,code), FOREIGN KEY(organization_id) REFERENCES organizations(id)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||||
|
CREATE TABLE warehouse_locations (id BIGINT AUTO_INCREMENT PRIMARY KEY, warehouse_id BIGINT NOT NULL, code VARCHAR(50) NOT NULL, name VARCHAR(150) NOT NULL, status VARCHAR(20) NOT NULL DEFAULT 'active', created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, UNIQUE KEY uk_warehouse_locations_warehouse_code(warehouse_id,code), FOREIGN KEY(warehouse_id) REFERENCES warehouses(id)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||||
|
CREATE TABLE user_warehouses (user_id BIGINT NOT NULL, warehouse_id BIGINT NOT NULL, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY(user_id,warehouse_id), FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE, FOREIGN KEY(warehouse_id) REFERENCES warehouses(id)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||||
|
CREATE TABLE inventory_balances (id BIGINT AUTO_INCREMENT PRIMARY KEY, organization_id BIGINT NOT NULL, warehouse_id BIGINT NOT NULL, location_id BIGINT NOT NULL, material_id BIGINT NOT NULL, on_hand DECIMAL(20,6) NOT NULL DEFAULT 0, reserved DECIMAL(20,6) NOT NULL DEFAULT 0, locked DECIMAL(20,6) NOT NULL DEFAULT 0, in_transit DECIMAL(20,6) NOT NULL DEFAULT 0, version INT NOT NULL DEFAULT 1, updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, UNIQUE KEY uk_inventory_balance_dimension(organization_id,warehouse_id,location_id,material_id), KEY idx_inventory_balances_material(organization_id,material_id), FOREIGN KEY(organization_id) REFERENCES organizations(id), FOREIGN KEY(warehouse_id) REFERENCES warehouses(id), FOREIGN KEY(location_id) REFERENCES warehouse_locations(id), FOREIGN KEY(material_id) REFERENCES materials(id)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||||
|
CREATE TABLE stock_warning_rules (id BIGINT AUTO_INCREMENT PRIMARY KEY, organization_id BIGINT NOT NULL, warehouse_id BIGINT NOT NULL, material_id BIGINT NOT NULL, minimum_available DECIMAL(20,6) NOT NULL DEFAULT 0, enabled BOOLEAN NOT NULL DEFAULT TRUE, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, UNIQUE KEY uk_stock_warning_rules_dimension(organization_id,warehouse_id,material_id), FOREIGN KEY(organization_id) REFERENCES organizations(id), FOREIGN KEY(warehouse_id) REFERENCES warehouses(id), FOREIGN KEY(material_id) REFERENCES materials(id)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||||
|
CREATE TABLE inbound_orders (id BIGINT AUTO_INCREMENT PRIMARY KEY, organization_id BIGINT NOT NULL DEFAULT 1, warehouse_id BIGINT NOT NULL DEFAULT 1, location_id BIGINT NOT NULL DEFAULT 1, inbound_date DATETIME NOT NULL, handler VARCHAR(100) NOT NULL, supplier VARCHAR(200), created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, KEY idx_inbound_orders_date(inbound_date), FOREIGN KEY(organization_id) REFERENCES organizations(id), FOREIGN KEY(warehouse_id) REFERENCES warehouses(id), FOREIGN KEY(location_id) REFERENCES warehouse_locations(id)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||||
|
CREATE TABLE inbound_items (id BIGINT AUTO_INCREMENT PRIMARY KEY, inbound_order_id BIGINT NOT NULL, material_id BIGINT NOT NULL, quantity INT NOT NULL, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, KEY idx_inbound_items_order(inbound_order_id), FOREIGN KEY(inbound_order_id) REFERENCES inbound_orders(id) ON DELETE CASCADE, FOREIGN KEY(material_id) REFERENCES materials(id)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||||
|
CREATE TABLE outbound_orders (id BIGINT AUTO_INCREMENT PRIMARY KEY, organization_id BIGINT NOT NULL DEFAULT 1, warehouse_id BIGINT NOT NULL DEFAULT 1, location_id BIGINT NOT NULL DEFAULT 1, outbound_date DATETIME NOT NULL, handler VARCHAR(100) NOT NULL, receiver VARCHAR(100) NOT NULL, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, KEY idx_outbound_orders_date(outbound_date), FOREIGN KEY(organization_id) REFERENCES organizations(id), FOREIGN KEY(warehouse_id) REFERENCES warehouses(id), FOREIGN KEY(location_id) REFERENCES warehouse_locations(id)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||||
|
CREATE TABLE outbound_items (id BIGINT AUTO_INCREMENT PRIMARY KEY, outbound_order_id BIGINT NOT NULL, material_id BIGINT NOT NULL, quantity INT NOT NULL, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, KEY idx_outbound_items_order(outbound_order_id), FOREIGN KEY(outbound_order_id) REFERENCES outbound_orders(id) ON DELETE CASCADE, FOREIGN KEY(material_id) REFERENCES materials(id)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||||
|
CREATE TABLE material_logs (id BIGINT AUTO_INCREMENT PRIMARY KEY, operation_type VARCHAR(50) NOT NULL, material_id BIGINT NOT NULL, old_value TEXT, new_value TEXT, operator VARCHAR(100), description TEXT, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, KEY idx_material_logs_material(material_id), FOREIGN KEY(material_id) REFERENCES materials(id) ON DELETE CASCADE) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||||
|
CREATE TABLE stock_logs (id BIGINT AUTO_INCREMENT PRIMARY KEY, material_id BIGINT NOT NULL, type ENUM('in','out') NOT NULL, quantity INT NOT NULL, related_order_type VARCHAR(50) NOT NULL, related_order_id BIGINT NOT NULL, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, KEY idx_stock_logs_material(material_id), KEY idx_stock_logs_related_order(related_order_type,related_order_id), FOREIGN KEY(material_id) REFERENCES materials(id)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||||
|
CREATE TABLE material_applications (id BIGINT AUTO_INCREMENT PRIMARY KEY, user_id BIGINT NOT NULL, purpose VARCHAR(500) NOT NULL DEFAULT '', status ENUM('pending','approved','rejected','cancelled') NOT NULL DEFAULT 'pending', rejection_reason VARCHAR(500) NOT NULL DEFAULT '', approved_by BIGINT NOT NULL DEFAULT 0, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, FOREIGN KEY(user_id) REFERENCES users(id)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||||
|
CREATE TABLE application_items (id BIGINT AUTO_INCREMENT PRIMARY KEY, application_id BIGINT NOT NULL, material_id BIGINT NOT NULL, quantity INT NOT NULL, FOREIGN KEY(application_id) REFERENCES material_applications(id) ON DELETE CASCADE, FOREIGN KEY(material_id) REFERENCES materials(id)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||||
|
|
||||||
-- ============================================
|
INSERT INTO organizations (id,code,name,status) VALUES (1,'DEFAULT','默认公司组织','active');
|
||||||
-- Table: tags
|
INSERT INTO roles (code,name) VALUES ('super_admin','超级管理员'),('warehouse_admin','仓库管理员'),('purchaser','采购人员'),('technician','技术人员'),('production','生产人员'),('sales','销售人员');
|
||||||
-- ============================================
|
INSERT INTO permissions (code,name) VALUES ('master.material.read','查看物料'),('master.material.write','维护物料'),('master.config.write','维护物料基础配置'),('organization.read','查看公司组织'),('organization.write','维护公司组织'),('organization.user.write','维护用户'),('organization.role.write','维护角色与权限'),('partner.supplier.read','查看供应商'),('partner.supplier.write','维护供应商'),('partner.customer.read','查看客户'),('partner.customer.write','维护客户'),('inventory.stock.read','查看库存'),('inventory.warning.write','维护库存预警'),('inventory.inbound.write','维护入库'),('inventory.outbound.write','维护出库');
|
||||||
CREATE TABLE IF NOT EXISTS tags (
|
INSERT INTO role_permissions (role_id,permission_id) SELECT r.id,p.id FROM roles r CROSS JOIN permissions p WHERE r.code='super_admin';
|
||||||
id BIGINT AUTO_INCREMENT PRIMARY KEY,
|
INSERT INTO material_types (organization_id,code,name) VALUES (1,'raw_material','原材料'),(1,'semi_finished','半成品'),(1,'finished_good','成品'),(1,'consumable','耗材'),(1,'service','服务');
|
||||||
material_id BIGINT NOT NULL,
|
INSERT INTO material_categories (organization_id,code,name) VALUES (1,'A01','标准件'),(1,'A02','成品件'),(1,'A03','简易加工件'),(1,'A04','精密加工件'),(1,'A05','钣金件'),(1,'A06','塑胶件'),(1,'A07','其他'),(1,'B01','外购件类'),(1,'B02','简易加工件'),(1,'B03','PCB硬板'),(1,'B04','FPC软板'),(1,'B05','线束'),(1,'B06','组装组件'),(1,'B07','其他'),(1,'C01','连接耗材'),(1,'C02','密封耗材'),(1,'C03','其他'),(1,'D01','自研程序固件'),(1,'D02','外购软件授权'),(1,'D03','授权介质'),(1,'D04','其他'),(1,'E01','成品'),(1,'E02','半成品'),(1,'F01','资料文档'),(1,'F02','标识标牌'),(1,'F03','包装物料'),(1,'F04','随机工具配件'),(1,'F05','其他');
|
||||||
tag_code VARCHAR(100) NOT NULL UNIQUE,
|
INSERT INTO material_categories (organization_id,code,name,parent_id,status)
|
||||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
SELECT organization_id,CONCAT(code,'-01'),CONCAT(name,'·通用型'),id,status FROM material_categories WHERE parent_id IS NULL;
|
||||||
INDEX idx_material_id (material_id),
|
INSERT INTO material_categories (organization_id,code,name,parent_id,status)
|
||||||
INDEX idx_tag_code (tag_code),
|
SELECT organization_id,CONCAT(code,'-02'),CONCAT(name,'·专用型'),id,status FROM material_categories WHERE parent_id IS NULL;
|
||||||
CONSTRAINT fk_tags_material FOREIGN KEY (material_id) REFERENCES materials(id) ON DELETE CASCADE
|
INSERT INTO warehouses (id,organization_id,code,name,status) VALUES (1,1,'DEFAULT','默认仓库','active');
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
INSERT INTO warehouse_locations (id,warehouse_id,code,name,status) VALUES (1,1,'MAIN','主库位','active');
|
||||||
|
|
||||||
-- ============================================
|
|
||||||
-- Table: apply_orders
|
|
||||||
-- ============================================
|
|
||||||
CREATE TABLE IF NOT EXISTS apply_orders (
|
|
||||||
id BIGINT AUTO_INCREMENT PRIMARY KEY,
|
|
||||||
applicant VARCHAR(100) NOT NULL,
|
|
||||||
apply_date DATE NOT NULL,
|
|
||||||
status ENUM('draft', 'pending', 'approved', 'rejected') DEFAULT 'draft',
|
|
||||||
approval_comment TEXT,
|
|
||||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
|
||||||
INDEX idx_applicant (applicant),
|
|
||||||
INDEX idx_status (status),
|
|
||||||
INDEX idx_apply_date (apply_date)
|
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
|
||||||
|
|
||||||
-- ============================================
|
|
||||||
-- Table: apply_items
|
|
||||||
-- ============================================
|
|
||||||
CREATE TABLE IF NOT EXISTS apply_items (
|
|
||||||
id BIGINT AUTO_INCREMENT PRIMARY KEY,
|
|
||||||
apply_order_id BIGINT NOT NULL,
|
|
||||||
material_id BIGINT NOT NULL,
|
|
||||||
quantity INT NOT NULL,
|
|
||||||
purpose TEXT,
|
|
||||||
INDEX idx_apply_order_id (apply_order_id),
|
|
||||||
INDEX idx_material_id (material_id),
|
|
||||||
CONSTRAINT fk_apply_items_order FOREIGN KEY (apply_order_id) REFERENCES apply_orders(id) ON DELETE CASCADE,
|
|
||||||
CONSTRAINT fk_apply_items_material FOREIGN KEY (material_id) REFERENCES materials(id) ON DELETE RESTRICT
|
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
|
||||||
|
|
||||||
-- ============================================
|
|
||||||
-- Table: inbound_orders
|
|
||||||
-- ============================================
|
|
||||||
CREATE TABLE IF NOT EXISTS inbound_orders (
|
|
||||||
id BIGINT AUTO_INCREMENT PRIMARY KEY,
|
|
||||||
inbound_date DATETIME NOT NULL,
|
|
||||||
handler VARCHAR(100) NOT NULL,
|
|
||||||
supplier VARCHAR(200),
|
|
||||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
|
||||||
INDEX idx_inbound_date (inbound_date),
|
|
||||||
INDEX idx_handler (handler)
|
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
|
||||||
|
|
||||||
-- ============================================
|
|
||||||
-- Table: inbound_items
|
|
||||||
-- ============================================
|
|
||||||
CREATE TABLE IF NOT EXISTS inbound_items (
|
|
||||||
id BIGINT AUTO_INCREMENT PRIMARY KEY,
|
|
||||||
inbound_order_id BIGINT NOT NULL,
|
|
||||||
material_id BIGINT NOT NULL,
|
|
||||||
quantity INT NOT NULL,
|
|
||||||
INDEX idx_inbound_order_id (inbound_order_id),
|
|
||||||
INDEX idx_material_id (material_id),
|
|
||||||
CONSTRAINT fk_inbound_items_order FOREIGN KEY (inbound_order_id) REFERENCES inbound_orders(id) ON DELETE CASCADE,
|
|
||||||
CONSTRAINT fk_inbound_items_material FOREIGN KEY (material_id) REFERENCES materials(id) ON DELETE RESTRICT
|
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
|
||||||
|
|
||||||
-- ============================================
|
|
||||||
-- Table: outbound_orders
|
|
||||||
-- ============================================
|
|
||||||
CREATE TABLE IF NOT EXISTS outbound_orders (
|
|
||||||
id BIGINT AUTO_INCREMENT PRIMARY KEY,
|
|
||||||
outbound_date DATETIME NOT NULL,
|
|
||||||
handler VARCHAR(100) NOT NULL,
|
|
||||||
receiver VARCHAR(100) NOT NULL,
|
|
||||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
|
||||||
INDEX idx_outbound_date (outbound_date),
|
|
||||||
INDEX idx_handler (handler),
|
|
||||||
INDEX idx_receiver (receiver)
|
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
|
||||||
|
|
||||||
-- ============================================
|
|
||||||
-- Table: outbound_items
|
|
||||||
-- ============================================
|
|
||||||
CREATE TABLE IF NOT EXISTS outbound_items (
|
|
||||||
id BIGINT AUTO_INCREMENT PRIMARY KEY,
|
|
||||||
outbound_order_id BIGINT NOT NULL,
|
|
||||||
material_id BIGINT NOT NULL,
|
|
||||||
quantity INT NOT NULL,
|
|
||||||
INDEX idx_outbound_order_id (outbound_order_id),
|
|
||||||
INDEX idx_material_id (material_id),
|
|
||||||
CONSTRAINT fk_outbound_items_order FOREIGN KEY (outbound_order_id) REFERENCES outbound_orders(id) ON DELETE CASCADE,
|
|
||||||
CONSTRAINT fk_outbound_items_material FOREIGN KEY (material_id) REFERENCES materials(id) ON DELETE RESTRICT
|
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
|
||||||
|
|
||||||
-- ============================================
|
|
||||||
-- Table: material_logs
|
|
||||||
-- ============================================
|
|
||||||
CREATE TABLE IF NOT EXISTS material_logs (
|
|
||||||
id BIGINT AUTO_INCREMENT PRIMARY KEY,
|
|
||||||
operation_type VARCHAR(50) NOT NULL,
|
|
||||||
material_id BIGINT NOT NULL,
|
|
||||||
old_value TEXT,
|
|
||||||
new_value TEXT,
|
|
||||||
operator VARCHAR(100),
|
|
||||||
description TEXT,
|
|
||||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
INDEX idx_operation_type (operation_type),
|
|
||||||
INDEX idx_material_id (material_id),
|
|
||||||
INDEX idx_created_at (created_at),
|
|
||||||
CONSTRAINT fk_material_logs_material FOREIGN KEY (material_id) REFERENCES materials(id) ON DELETE CASCADE
|
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
|
||||||
|
|
||||||
-- ============================================
|
|
||||||
-- Table: stock_logs
|
|
||||||
-- ============================================
|
|
||||||
CREATE TABLE IF NOT EXISTS stock_logs (
|
|
||||||
id BIGINT AUTO_INCREMENT PRIMARY KEY,
|
|
||||||
material_id BIGINT NOT NULL,
|
|
||||||
type ENUM('in', 'out') NOT NULL,
|
|
||||||
quantity INT NOT NULL,
|
|
||||||
related_order_type VARCHAR(50) NOT NULL,
|
|
||||||
related_order_id BIGINT NOT NULL,
|
|
||||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
INDEX idx_material_id (material_id),
|
|
||||||
INDEX idx_type (type),
|
|
||||||
INDEX idx_related_order (related_order_type, related_order_id),
|
|
||||||
CONSTRAINT fk_stock_logs_material FOREIGN KEY (material_id) REFERENCES materials(id) ON DELETE RESTRICT
|
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
|
||||||
|
|
||||||
-- ============================================
|
|
||||||
-- Table: users
|
|
||||||
-- ============================================
|
|
||||||
CREATE TABLE IF NOT EXISTS users (
|
|
||||||
id BIGINT AUTO_INCREMENT PRIMARY KEY,
|
|
||||||
username VARCHAR(50) NOT NULL UNIQUE,
|
|
||||||
password VARCHAR(255) NOT NULL,
|
|
||||||
role ENUM('admin','user') NOT NULL DEFAULT 'user',
|
|
||||||
description VARCHAR(500) DEFAULT '',
|
|
||||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
|
||||||
|
|
||||||
-- ============================================
|
|
||||||
-- Category Code Reference
|
|
||||||
-- A01-A07: 标准件, 成品件, 简易加工件, 精密加工件, 钣金件, 塑胶件, 其他
|
|
||||||
-- B01-B07: 成品件, 简易加工件, PCB硬板, FPC软板, 线束, 组装组件, 其他
|
|
||||||
-- C01-C03: 连接耗材, 密封耗材, 其他
|
|
||||||
-- D01-D04: 自研程序固件, 外购软件授权, 授权介质, 其他
|
|
||||||
-- E01-E02: 成品, 半成品
|
|
||||||
-- F01-F05: 资料文档, 标识标牌, 包装物料, 随机工具配件, 其他
|
|
||||||
-- ============================================
|
|
||||||
|
|
||||||
-- ============================================
|
|
||||||
-- Table: materials_category (reference table)
|
|
||||||
-- ============================================
|
|
||||||
CREATE TABLE IF NOT EXISTS materials_category (
|
|
||||||
category_code VARCHAR(10) PRIMARY KEY,
|
|
||||||
category_name VARCHAR(100) NOT NULL,
|
|
||||||
parent_category VARCHAR(10),
|
|
||||||
description VARCHAR(200)
|
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
|
||||||
|
|
||||||
-- Insert category reference data
|
|
||||||
INSERT INTO materials_category (category_code, category_name, description) VALUES
|
|
||||||
('A01', '标准件', 'Standard parts'),
|
|
||||||
('A02', '成品件', 'Finished parts'),
|
|
||||||
('A03', '简易加工件', 'Simple machined parts'),
|
|
||||||
('A04', '精密加工件', 'Precision machined parts'),
|
|
||||||
('A05', '钣金件', 'Sheet metal parts'),
|
|
||||||
('A06', '塑胶件', 'Plastic parts'),
|
|
||||||
('A07', '其他', 'Other (A series)'),
|
|
||||||
('B01', '成品件', 'Finished parts'),
|
|
||||||
('B02', '简易加工件', 'Simple machined parts'),
|
|
||||||
('B03', 'PCB硬板', 'PCB hard board'),
|
|
||||||
('B04', 'FPC软板', 'FPC flexible board'),
|
|
||||||
('B05', '线束', 'Wire harness'),
|
|
||||||
('B06', '组装组件', 'Assembly component'),
|
|
||||||
('B07', '其他', 'Other (B series)'),
|
|
||||||
('C01', '连接耗材', 'Connection consumable'),
|
|
||||||
('C02', '密封耗材', 'Sealing consumable'),
|
|
||||||
('C03', '其他', 'Other (C series)'),
|
|
||||||
('D01', '自研程序固件', 'In-house firmware'),
|
|
||||||
('D02', '外购软件授权', 'Purchased software license'),
|
|
||||||
('D03', '授权介质', 'License media'),
|
|
||||||
('D04', '其他', 'Other (D series)'),
|
|
||||||
('E01', '成品', 'Finished product'),
|
|
||||||
('E02', '半成品', 'Semi-finished product'),
|
|
||||||
('F01', '资料文档', 'Documentation'),
|
|
||||||
('F02', '标识标牌', 'Label/signage'),
|
|
||||||
('F03', '包装物料', 'Packaging material'),
|
|
||||||
('F04', '随机工具配件', 'Bundled tools/accessories'),
|
|
||||||
('F05', '其他', 'Other (F series)');
|
|
||||||
|
|
||||||
-- ============================================
|
|
||||||
-- Test Data: 5 Materials (using category codes)
|
|
||||||
-- ============================================
|
|
||||||
INSERT INTO materials (material_code, name, spec, category, unit, warning_stock, current_stock) VALUES
|
|
||||||
('MAT-001', 'Steel Plate', '1000x2000x5mm', 'A01', 'Sheet', 50, 200),
|
|
||||||
('MAT-002', 'Aluminum Rod', 'D20x1000mm', 'A01', 'Piece', 100, 500),
|
|
||||||
('MAT-003', 'Hex Bolt M10', 'M10x30mm SS304', 'A01', 'Box', 20, 80),
|
|
||||||
('MAT-004', 'Hydraulic Oil', 'ISO VG 46', 'C01', 'Barrel', 10, 25),
|
|
||||||
('MAT-005', 'Bearing 6205', '6205-2RS Deep Groove', 'A02', 'Piece', 30, 120);
|
|
||||||
|
|
|
||||||
|
|
@ -1,108 +0,0 @@
|
||||||
CREATE TABLE organizations (
|
|
||||||
id BIGINT NOT NULL AUTO_INCREMENT,
|
|
||||||
code VARCHAR(50) NOT NULL,
|
|
||||||
name VARCHAR(200) NOT NULL,
|
|
||||||
status VARCHAR(20) NOT NULL DEFAULT 'active',
|
|
||||||
default_timezone VARCHAR(64) NOT NULL DEFAULT 'Asia/Shanghai',
|
|
||||||
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
|
||||||
PRIMARY KEY (id),
|
|
||||||
UNIQUE KEY uk_organizations_code (code)
|
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
|
||||||
|
|
||||||
INSERT INTO organizations (id, code, name, status)
|
|
||||||
VALUES (1, 'DEFAULT', '默认公司组织', 'active');
|
|
||||||
|
|
||||||
CREATE TABLE roles (
|
|
||||||
id BIGINT NOT NULL AUTO_INCREMENT,
|
|
||||||
code VARCHAR(80) NOT NULL,
|
|
||||||
name VARCHAR(100) NOT NULL,
|
|
||||||
enabled BOOLEAN NOT NULL DEFAULT TRUE,
|
|
||||||
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
|
||||||
PRIMARY KEY (id),
|
|
||||||
UNIQUE KEY uk_roles_code (code)
|
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
|
||||||
|
|
||||||
CREATE TABLE permissions (
|
|
||||||
id BIGINT NOT NULL AUTO_INCREMENT,
|
|
||||||
code VARCHAR(120) NOT NULL,
|
|
||||||
name VARCHAR(150) NOT NULL,
|
|
||||||
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
PRIMARY KEY (id),
|
|
||||||
UNIQUE KEY uk_permissions_code (code)
|
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
|
||||||
|
|
||||||
CREATE TABLE role_permissions (
|
|
||||||
role_id BIGINT NOT NULL,
|
|
||||||
permission_id BIGINT NOT NULL,
|
|
||||||
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
PRIMARY KEY (role_id, permission_id),
|
|
||||||
CONSTRAINT fk_role_permissions_role FOREIGN KEY (role_id) REFERENCES roles(id) ON DELETE CASCADE,
|
|
||||||
CONSTRAINT fk_role_permissions_permission FOREIGN KEY (permission_id) REFERENCES permissions(id) ON DELETE CASCADE
|
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
|
||||||
|
|
||||||
CREATE TABLE user_roles (
|
|
||||||
user_id BIGINT NOT NULL,
|
|
||||||
role_id BIGINT NOT NULL,
|
|
||||||
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
PRIMARY KEY (user_id, role_id),
|
|
||||||
CONSTRAINT fk_user_roles_user FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
|
|
||||||
CONSTRAINT fk_user_roles_role FOREIGN KEY (role_id) REFERENCES roles(id) ON DELETE RESTRICT
|
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
|
||||||
|
|
||||||
CREATE TABLE user_organizations (
|
|
||||||
user_id BIGINT NOT NULL,
|
|
||||||
organization_id BIGINT NOT NULL,
|
|
||||||
is_default BOOLEAN NOT NULL DEFAULT FALSE,
|
|
||||||
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
PRIMARY KEY (user_id, organization_id),
|
|
||||||
CONSTRAINT fk_user_organizations_user FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
|
|
||||||
CONSTRAINT fk_user_organizations_org FOREIGN KEY (organization_id) REFERENCES organizations(id) ON DELETE RESTRICT
|
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
|
||||||
|
|
||||||
CREATE TABLE revoked_tokens (
|
|
||||||
jti VARCHAR(64) NOT NULL,
|
|
||||||
user_id BIGINT NOT NULL,
|
|
||||||
expires_at DATETIME NOT NULL,
|
|
||||||
revoked_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
PRIMARY KEY (jti),
|
|
||||||
KEY idx_revoked_tokens_expires_at (expires_at),
|
|
||||||
CONSTRAINT fk_revoked_tokens_user FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
|
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
|
||||||
|
|
||||||
INSERT INTO roles (code, name) VALUES
|
|
||||||
('super_admin', '超级管理员'),
|
|
||||||
('warehouse_admin', '仓库管理员'),
|
|
||||||
('purchaser', '采购人员'),
|
|
||||||
('technician', '技术人员'),
|
|
||||||
('production', '生产人员'),
|
|
||||||
('sales', '销售人员');
|
|
||||||
|
|
||||||
INSERT INTO permissions (code, name) VALUES
|
|
||||||
('master.material.read', '查看物料'),
|
|
||||||
('master.material.write', '维护物料'),
|
|
||||||
('master.config.write', '维护物料基础配置'),
|
|
||||||
('organization.read', '查看公司组织'),
|
|
||||||
('organization.write', '维护公司组织'),
|
|
||||||
('organization.user.write', '维护用户'),
|
|
||||||
('organization.role.write', '维护角色与权限'),
|
|
||||||
('partner.supplier.read', '查看供应商'),
|
|
||||||
('partner.supplier.write', '维护供应商'),
|
|
||||||
('partner.customer.read', '查看客户'),
|
|
||||||
('partner.customer.write', '维护客户'),
|
|
||||||
('inventory.stock.read', '查看库存'),
|
|
||||||
('inventory.warning.write', '维护库存预警'),
|
|
||||||
('inventory.inbound.write', '维护入库'),
|
|
||||||
('inventory.outbound.write', '维护出库');
|
|
||||||
|
|
||||||
INSERT INTO role_permissions (role_id, permission_id)
|
|
||||||
SELECT r.id, p.id FROM roles r CROSS JOIN permissions p WHERE r.code = 'super_admin';
|
|
||||||
|
|
||||||
INSERT INTO user_organizations (user_id, organization_id, is_default)
|
|
||||||
SELECT id, 1, TRUE FROM users;
|
|
||||||
|
|
||||||
INSERT INTO user_roles (user_id, role_id)
|
|
||||||
SELECT u.id, r.id
|
|
||||||
FROM users u
|
|
||||||
JOIN roles r ON r.code = CASE WHEN u.role = 'admin' THEN 'super_admin' ELSE 'warehouse_admin' END;
|
|
||||||
|
|
@ -1,100 +0,0 @@
|
||||||
CREATE TABLE material_types (
|
|
||||||
id BIGINT NOT NULL AUTO_INCREMENT,
|
|
||||||
organization_id BIGINT NOT NULL,
|
|
||||||
code VARCHAR(50) NOT NULL,
|
|
||||||
name VARCHAR(100) NOT NULL,
|
|
||||||
status VARCHAR(20) NOT NULL DEFAULT 'active',
|
|
||||||
PRIMARY KEY (id),
|
|
||||||
UNIQUE KEY uk_material_types_org_code (organization_id, code),
|
|
||||||
CONSTRAINT fk_material_types_org FOREIGN KEY (organization_id) REFERENCES organizations(id) ON DELETE RESTRICT
|
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
|
||||||
|
|
||||||
CREATE TABLE material_categories (
|
|
||||||
id BIGINT NOT NULL AUTO_INCREMENT,
|
|
||||||
organization_id BIGINT NOT NULL,
|
|
||||||
code VARCHAR(50) NOT NULL,
|
|
||||||
name VARCHAR(100) NOT NULL,
|
|
||||||
parent_id BIGINT NULL,
|
|
||||||
status VARCHAR(20) NOT NULL DEFAULT 'active',
|
|
||||||
PRIMARY KEY (id),
|
|
||||||
UNIQUE KEY uk_material_categories_org_code (organization_id, code),
|
|
||||||
CONSTRAINT fk_material_categories_org FOREIGN KEY (organization_id) REFERENCES organizations(id) ON DELETE RESTRICT,
|
|
||||||
CONSTRAINT fk_material_categories_parent FOREIGN KEY (parent_id) REFERENCES material_categories(id) ON DELETE RESTRICT
|
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
|
||||||
|
|
||||||
CREATE TABLE units (
|
|
||||||
id BIGINT NOT NULL AUTO_INCREMENT,
|
|
||||||
organization_id BIGINT NOT NULL,
|
|
||||||
code VARCHAR(50) NOT NULL,
|
|
||||||
name VARCHAR(100) NOT NULL,
|
|
||||||
precision_scale TINYINT NOT NULL DEFAULT 0,
|
|
||||||
status VARCHAR(20) NOT NULL DEFAULT 'active',
|
|
||||||
PRIMARY KEY (id),
|
|
||||||
UNIQUE KEY uk_units_org_code (organization_id, code),
|
|
||||||
CONSTRAINT fk_units_org FOREIGN KEY (organization_id) REFERENCES organizations(id) ON DELETE RESTRICT
|
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
|
||||||
|
|
||||||
CREATE TABLE code_rules (
|
|
||||||
id BIGINT NOT NULL AUTO_INCREMENT,
|
|
||||||
organization_id BIGINT NOT NULL,
|
|
||||||
category_id BIGINT NOT NULL,
|
|
||||||
prefix VARCHAR(30) NOT NULL,
|
|
||||||
sequence_length TINYINT NOT NULL,
|
|
||||||
next_sequence BIGINT NOT NULL DEFAULT 1,
|
|
||||||
status VARCHAR(20) NOT NULL DEFAULT 'active',
|
|
||||||
PRIMARY KEY (id),
|
|
||||||
UNIQUE KEY uk_code_rules_org_category (organization_id, category_id),
|
|
||||||
CONSTRAINT fk_code_rules_org FOREIGN KEY (organization_id) REFERENCES organizations(id) ON DELETE RESTRICT,
|
|
||||||
CONSTRAINT fk_code_rules_category FOREIGN KEY (category_id) REFERENCES material_categories(id) ON DELETE RESTRICT
|
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
|
||||||
|
|
||||||
CREATE TABLE material_unit_conversions (
|
|
||||||
id BIGINT NOT NULL AUTO_INCREMENT,
|
|
||||||
organization_id BIGINT NOT NULL,
|
|
||||||
material_id BIGINT NOT NULL,
|
|
||||||
from_unit_id BIGINT NOT NULL,
|
|
||||||
to_unit_id BIGINT NOT NULL,
|
|
||||||
conversion_rate DECIMAL(20,6) NOT NULL,
|
|
||||||
effective_from DATE NOT NULL,
|
|
||||||
effective_to DATE NULL,
|
|
||||||
PRIMARY KEY (id),
|
|
||||||
KEY idx_material_unit_conversions_material (organization_id, material_id),
|
|
||||||
CONSTRAINT fk_material_unit_conversions_org FOREIGN KEY (organization_id) REFERENCES organizations(id) ON DELETE RESTRICT,
|
|
||||||
CONSTRAINT fk_material_unit_conversions_material FOREIGN KEY (material_id) REFERENCES materials(id) ON DELETE RESTRICT,
|
|
||||||
CONSTRAINT fk_material_unit_conversions_from_unit FOREIGN KEY (from_unit_id) REFERENCES units(id) ON DELETE RESTRICT,
|
|
||||||
CONSTRAINT fk_material_unit_conversions_to_unit FOREIGN KEY (to_unit_id) REFERENCES units(id) ON DELETE RESTRICT
|
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
|
||||||
|
|
||||||
ALTER TABLE materials
|
|
||||||
ADD COLUMN organization_id BIGINT NOT NULL DEFAULT 1 AFTER id,
|
|
||||||
ADD COLUMN material_type_id BIGINT NULL AFTER category,
|
|
||||||
ADD COLUMN category_id BIGINT NULL AFTER material_type_id,
|
|
||||||
ADD COLUMN base_unit_id BIGINT NULL AFTER unit,
|
|
||||||
ADD COLUMN status VARCHAR(20) NOT NULL DEFAULT 'active' AFTER base_unit_id,
|
|
||||||
ADD COLUMN version INT NOT NULL DEFAULT 1 AFTER status,
|
|
||||||
ADD COLUMN manufacturer_model VARCHAR(200) NULL AFTER spec,
|
|
||||||
ADD CONSTRAINT fk_materials_org FOREIGN KEY (organization_id) REFERENCES organizations(id) ON DELETE RESTRICT,
|
|
||||||
ADD CONSTRAINT fk_materials_type FOREIGN KEY (material_type_id) REFERENCES material_types(id) ON DELETE RESTRICT,
|
|
||||||
ADD CONSTRAINT fk_materials_category FOREIGN KEY (category_id) REFERENCES material_categories(id) ON DELETE RESTRICT,
|
|
||||||
ADD CONSTRAINT fk_materials_base_unit FOREIGN KEY (base_unit_id) REFERENCES units(id) ON DELETE RESTRICT;
|
|
||||||
|
|
||||||
ALTER TABLE materials DROP INDEX material_code;
|
|
||||||
ALTER TABLE materials ADD UNIQUE KEY uk_materials_org_code (organization_id, material_code);
|
|
||||||
|
|
||||||
INSERT INTO material_types (organization_id, code, name) VALUES
|
|
||||||
(1, 'raw_material', '原材料'),
|
|
||||||
(1, 'semi_finished', '半成品'),
|
|
||||||
(1, 'finished_good', '成品'),
|
|
||||||
(1, 'consumable', '耗材'),
|
|
||||||
(1, 'service', '服务');
|
|
||||||
|
|
||||||
INSERT INTO material_categories (organization_id, code, name)
|
|
||||||
SELECT 1, category_code, category_name FROM materials_category;
|
|
||||||
|
|
||||||
INSERT INTO units (organization_id, code, name)
|
|
||||||
SELECT 1, unit, unit FROM materials WHERE unit IS NOT NULL AND unit <> '' GROUP BY unit;
|
|
||||||
|
|
||||||
UPDATE materials m
|
|
||||||
LEFT JOIN material_categories c ON c.organization_id = m.organization_id AND c.code = m.category
|
|
||||||
LEFT JOIN units u ON u.organization_id = m.organization_id AND u.code = m.unit
|
|
||||||
SET m.category_id = c.id, m.base_unit_id = u.id;
|
|
||||||
|
|
@ -1,52 +0,0 @@
|
||||||
CREATE TABLE business_partners (
|
|
||||||
id BIGINT NOT NULL AUTO_INCREMENT,
|
|
||||||
legal_name VARCHAR(200) NOT NULL,
|
|
||||||
unified_credit_code VARCHAR(50) NULL,
|
|
||||||
contact_name VARCHAR(100) NULL,
|
|
||||||
contact_phone VARCHAR(50) NULL,
|
|
||||||
address VARCHAR(500) NULL,
|
|
||||||
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
|
||||||
PRIMARY KEY (id),
|
|
||||||
UNIQUE KEY uk_business_partners_credit_code (unified_credit_code)
|
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
|
||||||
|
|
||||||
CREATE TABLE organization_partners (
|
|
||||||
id BIGINT NOT NULL AUTO_INCREMENT,
|
|
||||||
organization_id BIGINT NOT NULL,
|
|
||||||
business_partner_id BIGINT NOT NULL,
|
|
||||||
partner_code VARCHAR(50) NOT NULL,
|
|
||||||
is_supplier BOOLEAN NOT NULL DEFAULT FALSE,
|
|
||||||
is_customer BOOLEAN NOT NULL DEFAULT FALSE,
|
|
||||||
supplier_status VARCHAR(20) NULL,
|
|
||||||
customer_status VARCHAR(20) NULL,
|
|
||||||
settlement_currency CHAR(3) NULL,
|
|
||||||
payment_terms VARCHAR(200) NULL,
|
|
||||||
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
|
||||||
PRIMARY KEY (id),
|
|
||||||
UNIQUE KEY uk_organization_partners_org_partner (organization_id, business_partner_id),
|
|
||||||
UNIQUE KEY uk_organization_partners_org_code (organization_id, partner_code),
|
|
||||||
CONSTRAINT fk_organization_partners_org FOREIGN KEY (organization_id) REFERENCES organizations(id) ON DELETE RESTRICT,
|
|
||||||
CONSTRAINT fk_organization_partners_partner FOREIGN KEY (business_partner_id) REFERENCES business_partners(id) ON DELETE RESTRICT
|
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
|
||||||
|
|
||||||
CREATE TABLE material_suppliers (
|
|
||||||
id BIGINT NOT NULL AUTO_INCREMENT,
|
|
||||||
organization_id BIGINT NOT NULL,
|
|
||||||
material_id BIGINT NOT NULL,
|
|
||||||
organization_partner_id BIGINT NOT NULL,
|
|
||||||
price DECIMAL(20,6) NOT NULL,
|
|
||||||
currency CHAR(3) NOT NULL,
|
|
||||||
minimum_order_quantity DECIMAL(20,6) NOT NULL DEFAULT 0,
|
|
||||||
lead_time_days INT NOT NULL DEFAULT 0,
|
|
||||||
is_preferred BOOLEAN NOT NULL DEFAULT FALSE,
|
|
||||||
effective_from DATE NOT NULL,
|
|
||||||
effective_to DATE NULL,
|
|
||||||
status VARCHAR(20) NOT NULL DEFAULT 'active',
|
|
||||||
PRIMARY KEY (id),
|
|
||||||
KEY idx_material_suppliers_material (organization_id, material_id),
|
|
||||||
CONSTRAINT fk_material_suppliers_org FOREIGN KEY (organization_id) REFERENCES organizations(id) ON DELETE RESTRICT,
|
|
||||||
CONSTRAINT fk_material_suppliers_material FOREIGN KEY (material_id) REFERENCES materials(id) ON DELETE RESTRICT,
|
|
||||||
CONSTRAINT fk_material_suppliers_partner FOREIGN KEY (organization_partner_id) REFERENCES organization_partners(id) ON DELETE RESTRICT
|
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
|
||||||
|
|
@ -1,43 +0,0 @@
|
||||||
CREATE TABLE warehouses (
|
|
||||||
id BIGINT NOT NULL AUTO_INCREMENT,
|
|
||||||
organization_id BIGINT NOT NULL,
|
|
||||||
code VARCHAR(50) NOT NULL,
|
|
||||||
name VARCHAR(150) NOT NULL,
|
|
||||||
status VARCHAR(20) NOT NULL DEFAULT 'active',
|
|
||||||
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
|
||||||
PRIMARY KEY (id),
|
|
||||||
UNIQUE KEY uk_warehouses_org_code (organization_id, code),
|
|
||||||
CONSTRAINT fk_warehouses_org FOREIGN KEY (organization_id) REFERENCES organizations(id) ON DELETE RESTRICT
|
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
|
||||||
|
|
||||||
CREATE TABLE warehouse_locations (
|
|
||||||
id BIGINT NOT NULL AUTO_INCREMENT,
|
|
||||||
warehouse_id BIGINT NOT NULL,
|
|
||||||
code VARCHAR(50) NOT NULL,
|
|
||||||
name VARCHAR(150) NOT NULL,
|
|
||||||
status VARCHAR(20) NOT NULL DEFAULT 'active',
|
|
||||||
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
|
||||||
PRIMARY KEY (id),
|
|
||||||
UNIQUE KEY uk_warehouse_locations_warehouse_code (warehouse_id, code),
|
|
||||||
CONSTRAINT fk_warehouse_locations_warehouse FOREIGN KEY (warehouse_id) REFERENCES warehouses(id) ON DELETE RESTRICT
|
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
|
||||||
|
|
||||||
CREATE TABLE user_warehouses (
|
|
||||||
user_id BIGINT NOT NULL,
|
|
||||||
warehouse_id BIGINT NOT NULL,
|
|
||||||
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
PRIMARY KEY (user_id, warehouse_id),
|
|
||||||
CONSTRAINT fk_user_warehouses_user FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
|
|
||||||
CONSTRAINT fk_user_warehouses_warehouse FOREIGN KEY (warehouse_id) REFERENCES warehouses(id) ON DELETE RESTRICT
|
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
|
||||||
|
|
||||||
INSERT INTO warehouses (id, organization_id, code, name, status)
|
|
||||||
VALUES (1, 1, 'DEFAULT', '默认仓库', 'active');
|
|
||||||
|
|
||||||
INSERT INTO warehouse_locations (id, warehouse_id, code, name, status)
|
|
||||||
VALUES (1, 1, 'MAIN', '主库位', 'active');
|
|
||||||
|
|
||||||
INSERT INTO user_warehouses (user_id, warehouse_id)
|
|
||||||
SELECT id, 1 FROM users;
|
|
||||||
|
|
@ -1,36 +0,0 @@
|
||||||
CREATE TABLE inventory_balances (
|
|
||||||
id BIGINT NOT NULL AUTO_INCREMENT,
|
|
||||||
organization_id BIGINT NOT NULL,
|
|
||||||
warehouse_id BIGINT NOT NULL,
|
|
||||||
location_id BIGINT NOT NULL,
|
|
||||||
material_id BIGINT NOT NULL,
|
|
||||||
on_hand DECIMAL(20,6) NOT NULL DEFAULT 0,
|
|
||||||
reserved DECIMAL(20,6) NOT NULL DEFAULT 0,
|
|
||||||
locked DECIMAL(20,6) NOT NULL DEFAULT 0,
|
|
||||||
in_transit DECIMAL(20,6) NOT NULL DEFAULT 0,
|
|
||||||
version INT NOT NULL DEFAULT 1,
|
|
||||||
updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
|
||||||
PRIMARY KEY (id),
|
|
||||||
UNIQUE KEY uk_inventory_balance_dimension (organization_id, warehouse_id, location_id, material_id),
|
|
||||||
KEY idx_inventory_balances_material (organization_id, material_id),
|
|
||||||
CONSTRAINT fk_inventory_balances_org FOREIGN KEY (organization_id) REFERENCES organizations(id) ON DELETE RESTRICT,
|
|
||||||
CONSTRAINT fk_inventory_balances_warehouse FOREIGN KEY (warehouse_id) REFERENCES warehouses(id) ON DELETE RESTRICT,
|
|
||||||
CONSTRAINT fk_inventory_balances_location FOREIGN KEY (location_id) REFERENCES warehouse_locations(id) ON DELETE RESTRICT,
|
|
||||||
CONSTRAINT fk_inventory_balances_material FOREIGN KEY (material_id) REFERENCES materials(id) ON DELETE RESTRICT
|
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
|
||||||
|
|
||||||
CREATE TABLE stock_warning_rules (
|
|
||||||
id BIGINT NOT NULL AUTO_INCREMENT,
|
|
||||||
organization_id BIGINT NOT NULL,
|
|
||||||
warehouse_id BIGINT NOT NULL,
|
|
||||||
material_id BIGINT NOT NULL,
|
|
||||||
minimum_available DECIMAL(20,6) NOT NULL DEFAULT 0,
|
|
||||||
enabled BOOLEAN NOT NULL DEFAULT TRUE,
|
|
||||||
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
|
||||||
PRIMARY KEY (id),
|
|
||||||
UNIQUE KEY uk_stock_warning_rules_dimension (organization_id, warehouse_id, material_id),
|
|
||||||
CONSTRAINT fk_stock_warning_rules_org FOREIGN KEY (organization_id) REFERENCES organizations(id) ON DELETE RESTRICT,
|
|
||||||
CONSTRAINT fk_stock_warning_rules_warehouse FOREIGN KEY (warehouse_id) REFERENCES warehouses(id) ON DELETE RESTRICT,
|
|
||||||
CONSTRAINT fk_stock_warning_rules_material FOREIGN KEY (material_id) REFERENCES materials(id) ON DELETE RESTRICT
|
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
|
||||||
|
|
@ -1,31 +0,0 @@
|
||||||
INSERT INTO inventory_balances (organization_id, warehouse_id, location_id, material_id, on_hand)
|
|
||||||
SELECT 1, 1, 1, id, current_stock FROM materials
|
|
||||||
ON DUPLICATE KEY UPDATE material_id = VALUES(material_id);
|
|
||||||
|
|
||||||
INSERT INTO stock_warning_rules (organization_id, warehouse_id, material_id, minimum_available, enabled)
|
|
||||||
SELECT 1, 1, id, warning_stock, TRUE FROM materials
|
|
||||||
ON DUPLICATE KEY UPDATE material_id = VALUES(material_id);
|
|
||||||
|
|
||||||
ALTER TABLE inbound_orders
|
|
||||||
ADD COLUMN organization_id BIGINT NOT NULL DEFAULT 1 AFTER id,
|
|
||||||
ADD COLUMN warehouse_id BIGINT NOT NULL DEFAULT 1 AFTER organization_id,
|
|
||||||
ADD COLUMN location_id BIGINT NOT NULL DEFAULT 1 AFTER warehouse_id,
|
|
||||||
ADD CONSTRAINT fk_inbound_orders_org FOREIGN KEY (organization_id) REFERENCES organizations(id) ON DELETE RESTRICT,
|
|
||||||
ADD CONSTRAINT fk_inbound_orders_warehouse FOREIGN KEY (warehouse_id) REFERENCES warehouses(id) ON DELETE RESTRICT,
|
|
||||||
ADD CONSTRAINT fk_inbound_orders_location FOREIGN KEY (location_id) REFERENCES warehouse_locations(id) ON DELETE RESTRICT;
|
|
||||||
|
|
||||||
ALTER TABLE outbound_orders
|
|
||||||
ADD COLUMN organization_id BIGINT NOT NULL DEFAULT 1 AFTER id,
|
|
||||||
ADD COLUMN warehouse_id BIGINT NOT NULL DEFAULT 1 AFTER organization_id,
|
|
||||||
ADD COLUMN location_id BIGINT NOT NULL DEFAULT 1 AFTER warehouse_id,
|
|
||||||
ADD CONSTRAINT fk_outbound_orders_org FOREIGN KEY (organization_id) REFERENCES organizations(id) ON DELETE RESTRICT,
|
|
||||||
ADD CONSTRAINT fk_outbound_orders_warehouse FOREIGN KEY (warehouse_id) REFERENCES warehouses(id) ON DELETE RESTRICT,
|
|
||||||
ADD CONSTRAINT fk_outbound_orders_location FOREIGN KEY (location_id) REFERENCES warehouse_locations(id) ON DELETE RESTRICT;
|
|
||||||
|
|
||||||
SELECT m.id, m.material_code, m.current_stock AS legacy_stock,
|
|
||||||
COALESCE(SUM(ib.on_hand), 0) AS migrated_stock,
|
|
||||||
m.current_stock - COALESCE(SUM(ib.on_hand), 0) AS difference
|
|
||||||
FROM materials m
|
|
||||||
LEFT JOIN inventory_balances ib ON ib.material_id = m.id
|
|
||||||
GROUP BY m.id, m.material_code, m.current_stock
|
|
||||||
HAVING difference <> 0;
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
ALTER TABLE materials
|
|
||||||
DROP COLUMN current_stock,
|
|
||||||
DROP COLUMN warning_stock;
|
|
||||||
|
|
@ -1,31 +0,0 @@
|
||||||
INSERT IGNORE INTO role_permissions (role_id, permission_id)
|
|
||||||
SELECT r.id, p.id
|
|
||||||
FROM (
|
|
||||||
SELECT 'warehouse_admin' AS role_code, 'master.material.read' AS permission_code
|
|
||||||
UNION ALL SELECT 'warehouse_admin', 'inventory.stock.read'
|
|
||||||
UNION ALL SELECT 'warehouse_admin', 'inventory.warning.write'
|
|
||||||
UNION ALL SELECT 'warehouse_admin', 'inventory.inbound.write'
|
|
||||||
UNION ALL SELECT 'warehouse_admin', 'inventory.outbound.write'
|
|
||||||
|
|
||||||
UNION ALL SELECT 'purchaser', 'master.material.read'
|
|
||||||
UNION ALL SELECT 'purchaser', 'partner.supplier.read'
|
|
||||||
UNION ALL SELECT 'purchaser', 'partner.supplier.write'
|
|
||||||
UNION ALL SELECT 'purchaser', 'inventory.stock.read'
|
|
||||||
UNION ALL SELECT 'purchaser', 'inventory.warning.write'
|
|
||||||
|
|
||||||
UNION ALL SELECT 'technician', 'master.material.read'
|
|
||||||
UNION ALL SELECT 'technician', 'master.material.write'
|
|
||||||
UNION ALL SELECT 'technician', 'master.config.write'
|
|
||||||
UNION ALL SELECT 'technician', 'inventory.stock.read'
|
|
||||||
|
|
||||||
UNION ALL SELECT 'production', 'master.material.read'
|
|
||||||
UNION ALL SELECT 'production', 'inventory.stock.read'
|
|
||||||
UNION ALL SELECT 'production', 'inventory.outbound.write'
|
|
||||||
|
|
||||||
UNION ALL SELECT 'sales', 'master.material.read'
|
|
||||||
UNION ALL SELECT 'sales', 'partner.customer.read'
|
|
||||||
UNION ALL SELECT 'sales', 'partner.customer.write'
|
|
||||||
UNION ALL SELECT 'sales', 'inventory.stock.read'
|
|
||||||
) AS defaults
|
|
||||||
JOIN roles r ON r.code = defaults.role_code
|
|
||||||
JOIN permissions p ON p.code = defaults.permission_code;
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
ALTER TABLE users DROP COLUMN role;
|
|
||||||
|
|
@ -1,96 +0,0 @@
|
||||||
UPDATE material_categories
|
|
||||||
SET name = '外购件类'
|
|
||||||
WHERE code = 'B01' AND parent_id IS NULL;
|
|
||||||
|
|
||||||
INSERT INTO material_categories (organization_id, code, name, parent_id, status)
|
|
||||||
SELECT parent.organization_id,
|
|
||||||
CONCAT(parent.code, '-01'),
|
|
||||||
CASE parent.code
|
|
||||||
WHEN 'A01' THEN '紧固件类'
|
|
||||||
WHEN 'A02' THEN '轴承类'
|
|
||||||
WHEN 'A03' THEN '轴套法兰类'
|
|
||||||
WHEN 'A04' THEN '导轨丝杠类'
|
|
||||||
WHEN 'A05' THEN '机柜钣金类'
|
|
||||||
WHEN 'A06' THEN '管槽塑胶类'
|
|
||||||
WHEN 'B01' THEN '电子类'
|
|
||||||
WHEN 'B03' THEN '控制板类'
|
|
||||||
WHEN 'B04' THEN '排线类'
|
|
||||||
WHEN 'B05' THEN '电源线束类'
|
|
||||||
WHEN 'C01' THEN '接线辅材类'
|
|
||||||
WHEN 'C02' THEN '密封件类'
|
|
||||||
WHEN 'D01' THEN '控制固件类'
|
|
||||||
WHEN 'D02' THEN '工业软件类'
|
|
||||||
WHEN 'E01' THEN '电气设备类'
|
|
||||||
WHEN 'F01' THEN '技术资料类'
|
|
||||||
WHEN 'F02' THEN '设备标识类'
|
|
||||||
WHEN 'F03' THEN '包装容器类'
|
|
||||||
WHEN 'F04' THEN '手动工具类'
|
|
||||||
ELSE CONCAT(parent.name, '·通用型')
|
|
||||||
END,
|
|
||||||
parent.id,
|
|
||||||
parent.status
|
|
||||||
FROM material_categories parent
|
|
||||||
WHERE parent.parent_id IS NULL
|
|
||||||
AND NOT EXISTS (
|
|
||||||
SELECT 1 FROM material_categories child
|
|
||||||
WHERE child.organization_id = parent.organization_id
|
|
||||||
AND child.parent_id = parent.id
|
|
||||||
);
|
|
||||||
|
|
||||||
INSERT INTO material_categories (organization_id, code, name, parent_id, status)
|
|
||||||
SELECT parent.organization_id,
|
|
||||||
CONCAT(parent.code, '-02'),
|
|
||||||
CASE parent.code
|
|
||||||
WHEN 'A01' THEN '传动连接类'
|
|
||||||
WHEN 'A02' THEN '其他成品件类'
|
|
||||||
WHEN 'A03' THEN '连接加工件类'
|
|
||||||
WHEN 'A04' THEN '齿轮传动类'
|
|
||||||
WHEN 'A05' THEN '管槽接头类'
|
|
||||||
WHEN 'A06' THEN '密封防护类'
|
|
||||||
WHEN 'B01' THEN '机电类'
|
|
||||||
WHEN 'B03' THEN '功能板类'
|
|
||||||
WHEN 'B04' THEN '柔性电缆类'
|
|
||||||
WHEN 'B05' THEN '信号线束类'
|
|
||||||
WHEN 'C01' THEN '包装连接类'
|
|
||||||
WHEN 'C02' THEN '密封材料类'
|
|
||||||
WHEN 'D01' THEN '升级包类'
|
|
||||||
WHEN 'D02' THEN '设计软件类'
|
|
||||||
WHEN 'E01' THEN '操作设备类'
|
|
||||||
WHEN 'F01' THEN '管理资料类'
|
|
||||||
WHEN 'F02' THEN '包装标识类'
|
|
||||||
WHEN 'F03' THEN '缓冲防护类'
|
|
||||||
WHEN 'F04' THEN '仪器仪表类'
|
|
||||||
ELSE CONCAT(parent.name, '·专用型')
|
|
||||||
END,
|
|
||||||
parent.id,
|
|
||||||
parent.status
|
|
||||||
FROM material_categories parent
|
|
||||||
WHERE parent.parent_id IS NULL
|
|
||||||
AND EXISTS (
|
|
||||||
SELECT 1 FROM material_categories first_child
|
|
||||||
WHERE first_child.organization_id = parent.organization_id
|
|
||||||
AND first_child.parent_id = parent.id
|
|
||||||
AND first_child.code = CONCAT(parent.code, '-01')
|
|
||||||
)
|
|
||||||
AND NOT EXISTS (
|
|
||||||
SELECT 1 FROM material_categories child
|
|
||||||
WHERE child.organization_id = parent.organization_id
|
|
||||||
AND child.code = CONCAT(parent.code, '-02')
|
|
||||||
);
|
|
||||||
|
|
||||||
UPDATE materials material
|
|
||||||
JOIN material_categories parent
|
|
||||||
ON parent.id = material.category_id
|
|
||||||
AND parent.organization_id = material.organization_id
|
|
||||||
AND parent.parent_id IS NULL
|
|
||||||
JOIN material_categories child
|
|
||||||
ON child.id = (
|
|
||||||
SELECT CASE
|
|
||||||
WHEN MOD(CRC32(material.material_code), 2) = 0 THEN MIN(candidate.id)
|
|
||||||
ELSE MAX(candidate.id)
|
|
||||||
END
|
|
||||||
FROM material_categories candidate
|
|
||||||
WHERE candidate.organization_id = parent.organization_id
|
|
||||||
AND candidate.parent_id = parent.id
|
|
||||||
)
|
|
||||||
SET material.category_id = child.id;
|
|
||||||
|
|
@ -1,2 +0,0 @@
|
||||||
ALTER TABLE materials
|
|
||||||
MODIFY COLUMN category_id BIGINT NOT NULL;
|
|
||||||
|
|
@ -1,138 +0,0 @@
|
||||||
-- ============================================
|
|
||||||
-- Test Data for WMMS
|
|
||||||
-- 遵循命名规范:物料代码 MAT-XXX
|
|
||||||
-- 分类使用 materials_category 表中的标准分类码
|
|
||||||
-- ============================================
|
|
||||||
|
|
||||||
USE wmms;
|
|
||||||
|
|
||||||
-- A01 类:标准件
|
|
||||||
INSERT INTO materials (material_code, name, spec, category, unit, warning_stock, current_stock) VALUES
|
|
||||||
('MAT-006', '六角螺栓 M12', 'M12x40mm 镀锌', 'A01', '盒', 50, 300),
|
|
||||||
('MAT-007', '平垫圈 M12', 'M12 镀锌平垫', 'A01', '盒', 100, 500),
|
|
||||||
('MAT-008', '弹垫 M12', 'M12 弹簧垫圈 65Mn', 'A01', '盒', 80, 400),
|
|
||||||
('MAT-009', '内六角螺丝 M8', 'M8x25mm 不锈钢304', 'A01', '盒', 60, 250),
|
|
||||||
('MAT-010', '双头螺栓', 'M16x100mm 4.8级', 'A01', '根', 40, 180);
|
|
||||||
|
|
||||||
-- A02 类:成品件
|
|
||||||
INSERT INTO materials (material_code, name, spec, category, unit, warning_stock, current_stock) VALUES
|
|
||||||
('MAT-011', '深沟球轴承 6204', '6204-2RS 橡胶密封', 'A02', '个', 30, 120),
|
|
||||||
('MAT-012', '深沟球轴承 6206', '6206-2RS 橡胶密封', 'A02', '个', 25, 100),
|
|
||||||
('MAT-013', '角接触轴承 7205', '7205AC 接触角25°', 'A02', '个', 20, 80),
|
|
||||||
('MAT-014', '圆锥滚子轴承 30205', '30205 新疆特检院', 'A02', '个', 15, 60),
|
|
||||||
('MAT-015', '直线轴承 LM8UU', 'LM8UU 直线运动', 'A02', '个', 40, 160);
|
|
||||||
|
|
||||||
-- A03 类:简易加工件
|
|
||||||
INSERT INTO materials (material_code, name, spec, category, unit, warning_stock, current_stock) VALUES
|
|
||||||
('MAT-016', '轴套 45#钢', 'Φ25x30mm 调质处理', 'A03', '个', 50, 200),
|
|
||||||
('MAT-017', '法兰盘', 'Φ80x20mm 45#钢', 'A03', '个', 30, 120),
|
|
||||||
('MAT-018', '锁紧螺母', 'M30x1.5 尼龙锁紧', 'A03', '个', 40, 180),
|
|
||||||
('MAT-019', '调整垫片', 'Φ50/Φ25x0.5mm 45#钢', 'A03', '片', 100, 500),
|
|
||||||
('MAT-020', '连接套筒', 'Φ40x60mm 45#钢', 'A03', '个', 25, 100);
|
|
||||||
|
|
||||||
-- A04 类:精密加工件
|
|
||||||
INSERT INTO materials (material_code, name, spec, category, unit, warning_stock, current_stock) VALUES
|
|
||||||
('MAT-021', '精密丝杠', 'T12x4mm 长度500mm 研磨级', 'A04', '根', 5, 20),
|
|
||||||
('MAT-022', '导轨滑块', 'MGN12H 长度100mm', 'A04', '个', 10, 40),
|
|
||||||
('MAT-023', '分度盘', 'Φ100mm 72等分 45#钢', 'A04', '个', 3, 15),
|
|
||||||
('MAT-024', '齿轮坯料', 'M2 Z=40 45#钢 调质', 'A04', '个', 20, 80),
|
|
||||||
('MAT-025', '蜗轮坯料', '模数3 齿数50 青铜', 'A04', '个', 8, 35);
|
|
||||||
|
|
||||||
-- A05 类:钣金件
|
|
||||||
INSERT INTO materials (material_code, name, spec, category, unit, warning_stock, current_stock) VALUES
|
|
||||||
('MAT-026', '安装板', '300x200x5mm Q235A', 'A05', '块', 15, 60),
|
|
||||||
('MAT-027', '电气柜门板', '600x400x1.5mm 喷塑', 'A05', '块', 8, 30),
|
|
||||||
('MAT-028', '机箱外壳', '400x300x150mm 铝合金', 'A05', '个', 10, 40),
|
|
||||||
('MAT-029', '波纹管接头', 'DG25 不锈钢', 'A05', '个', 50, 200),
|
|
||||||
('MAT-030', '线槽', '50x50x2000mm 铝合金', 'A05', '根', 20, 80);
|
|
||||||
|
|
||||||
-- A06 类:塑胶件
|
|
||||||
INSERT INTO materials (material_code, name, spec, category, unit, warning_stock, current_stock) VALUES
|
|
||||||
('MAT-031', 'PVC管 25', 'Φ25x3mm 长度2m', 'A06', '根', 30, 120),
|
|
||||||
('MAT-032', '塑料线槽', '20x10mm PVC白色', 'A06', '根', 50, 200),
|
|
||||||
('MAT-033', '尼龙轮', 'Φ50mm 带轴承', 'A06', '个', 20, 80),
|
|
||||||
('MAT-034', '橡胶密封圈', 'Φ30x3 NBR70', 'A06', '个', 100, 400),
|
|
||||||
('MAT-035', 'PE保护盖', 'M20x1.5 塑料', 'A06', '个', 200, 800);
|
|
||||||
|
|
||||||
-- B01 类:成品件(电子)
|
|
||||||
INSERT INTO materials (material_code, name, spec, category, unit, warning_stock, current_stock) VALUES
|
|
||||||
('MAT-036', 'PLC控制器 S7-200', 'CPU SR40 DC/DC/DC', 'B01', '台', 3, 12),
|
|
||||||
('MAT-037', '变频器', '0.75kW 380V 三相', 'B01', '台', 5, 20),
|
|
||||||
('MAT-038', '伺服驱动器', '400W 220V', 'B01', '台', 4, 15),
|
|
||||||
('MAT-039', '触摸屏', '7寸 800x480 RS485', 'B01', '台', 3, 10),
|
|
||||||
('MAT-040', '开关电源', '24V 10A 导轨式', 'B01', '个', 10, 40);
|
|
||||||
|
|
||||||
-- B03 类:PCB硬板
|
|
||||||
INSERT INTO materials (material_code, name, spec, category, unit, warning_stock, current_stock) VALUES
|
|
||||||
('MAT-041', '控制板 PCB', '150x100mm 双面板', 'B03', '块', 10, 40),
|
|
||||||
('MAT-042', '驱动板 PCB', '100x80mm 四层板', 'B03', '块', 8, 30),
|
|
||||||
('MAT-043', '显示板 PCB', '80x50mm 单面板', 'B03', '块', 15, 60),
|
|
||||||
('MAT-044', '电源板 PCB', '120x90mm 双面板', 'B03', '块', 10, 35);
|
|
||||||
|
|
||||||
-- B04 类:FPC软板
|
|
||||||
INSERT INTO materials (material_code, name, spec, category, unit, warning_stock, current_stock) VALUES
|
|
||||||
('MAT-045', 'FPC排线', '0.5mm间距 50pin', 'B04', '条', 20, 80),
|
|
||||||
('MAT-046', 'FPC连接线', '1.0mm间距 30pin', 'B04', '条', 15, 60),
|
|
||||||
('MAT-047', '柔性电缆', '0.3mm间距 20pin', 'B04', '条', 25, 100);
|
|
||||||
|
|
||||||
-- B05 类:线束
|
|
||||||
INSERT INTO materials (material_code, name, spec, category, unit, warning_stock, current_stock) VALUES
|
|
||||||
('MAT-048', '电源线束', '3x2.5平方 RVV', 'B05', '条', 20, 80),
|
|
||||||
('MAT-049', '信号线束', '4x0.75平方 屏蔽', 'B05', '条', 30, 120),
|
|
||||||
('MAT-050', '电机线束', '4x4平方 耐油', 'B05', '条', 15, 60),
|
|
||||||
('MAT-051', '编码器线', '8芯 屏蔽双绞', 'B05', '条', 25, 100),
|
|
||||||
('MAT-052', '网线', 'CAT6 蓝色 2m', 'B05', '条', 50, 200);
|
|
||||||
|
|
||||||
-- C01 类:连接耗材
|
|
||||||
INSERT INTO materials (material_code, name, spec, category, unit, warning_stock, current_stock) VALUES
|
|
||||||
('MAT-053', '扎带', '3x150mm 尼龙', 'C01', '包', 20, 100),
|
|
||||||
('MAT-054', '热缩管', 'Φ10 黑色 2m/根', 'C01', '根', 30, 150),
|
|
||||||
('MAT-055', '接线端子', 'UK 2.5N 弹簧式', 'C01', '个', 200, 800),
|
|
||||||
('MAT-056', '冷压端头', 'RV2-3.5 叉形', 'C01', '个', 300, 1200),
|
|
||||||
('MAT-057', '线号管', 'Φ2.5 白色', 'C01', '米', 100, 400),
|
|
||||||
('MAT-058', '缠绕膜', '500mmx0.025mm', 'C01', '卷', 10, 40);
|
|
||||||
|
|
||||||
-- C02 类:密封耗材
|
|
||||||
INSERT INTO materials (material_code, name, spec, category, unit, warning_stock, current_stock) VALUES
|
|
||||||
('MAT-059', 'O型圈', 'Φ25x3 NBR70', 'C02', '个', 150, 600),
|
|
||||||
('MAT-060', '骨架油封', 'Φ35x50x10 TC双唇', 'C02', '个', 50, 200),
|
|
||||||
('MAT-061', '密封胶', '硅酮密封胶 300ml', 'C02', '支', 20, 80),
|
|
||||||
('MAT-062', '生料带', '18mmx0.1mmx20m', 'C02', '卷', 30, 120),
|
|
||||||
('MAT-063', '盘根', '10x10mm 柔性石墨', 'C02', '米', 20, 80);
|
|
||||||
|
|
||||||
-- D01 类:自研程序固件
|
|
||||||
INSERT INTO materials (material_code, name, spec, category, unit, warning_stock, current_stock) VALUES
|
|
||||||
('MAT-064', '主控程序', 'V2.1.0 HEX文件', 'D01', '套', 1, 5),
|
|
||||||
('MAT-065', '显示程序', 'V1.8.2 HEX文件', 'D01', '套', 1, 5),
|
|
||||||
('MAT-066', '固件升级包', 'V3.0.0 BIN文件', 'D01', '套', 2, 8);
|
|
||||||
|
|
||||||
-- D02 类:外购软件授权
|
|
||||||
INSERT INTO materials (material_code, name, spec, category, unit, warning_stock, current_stock) VALUES
|
|
||||||
('MAT-067', '组态软件授权', '昆仑通态 授权key', 'D02', '套', 2, 10),
|
|
||||||
('MAT-068', 'PLC编程软件', '西门子 V4.0授权', 'D02', '套', 1, 5),
|
|
||||||
('MAT-069', 'CAD软件授权', '年费授权', 'D02', '套', 3, 10);
|
|
||||||
|
|
||||||
-- E01 类:成品
|
|
||||||
INSERT INTO materials (material_code, name, spec, category, unit, warning_stock, current_stock) VALUES
|
|
||||||
('MAT-070', '控制柜', '800x600x2000mm', 'E01', '台', 2, 8),
|
|
||||||
('MAT-071', '操作台', '1500x800x1200mm', 'E01', '台', 1, 5),
|
|
||||||
('MAT-072', '配电箱', '400x300x150mm', 'E01', '台', 3, 12);
|
|
||||||
|
|
||||||
-- F03 类:包装物料
|
|
||||||
INSERT INTO materials (material_code, name, spec, category, unit, warning_stock, current_stock) VALUES
|
|
||||||
('MAT-073', '纸箱', '400x300x300mm', 'F03', '个', 50, 200),
|
|
||||||
('MAT-074', '气泡膜', '宽1m 厚3mm', 'F03', '卷', 10, 40),
|
|
||||||
('MAT-075', '木托盘', '1200x1000x150mm', 'F03', '个', 20, 80),
|
|
||||||
('MAT-076', '打包带', '12mmx0.8mm', 'F03', '卷', 15, 60),
|
|
||||||
('MAT-077', '标签纸', '100x60mm 白色', 'F03', '张', 500, 2000);
|
|
||||||
|
|
||||||
-- F04 类:随机工具配件
|
|
||||||
INSERT INTO materials (material_code, name, spec, category, unit, warning_stock, current_stock) VALUES
|
|
||||||
('MAT-078', '内六角扳手套装', '9件套 公制', 'F04', '套', 5, 20),
|
|
||||||
('MAT-079', '螺丝刀套装', '6支 公制+十字', 'F04', '套', 8, 30),
|
|
||||||
('MAT-080', '万用表', '数字式 VC890C+', 'F04', '个', 3, 12),
|
|
||||||
('MAT-081', '剥线钳', '自动调节 0.5-6mm²', 'F04', '把', 5, 20),
|
|
||||||
('MAT-082', '压线钳', '电子线专用 0.5-2mm²', 'F04', '把', 5, 20),
|
|
||||||
('MAT-083', '活动扳手', '10寸', 'F04', '把', 6, 25),
|
|
||||||
('MAT-084', '呆扳手套装', '8件套 8-24mm', 'F04', '套', 4, 15),
|
|
||||||
('MAT-085', 'aiman 工具箱', '18寸 塑料', 'F04', '个', 3, 10);
|
|
||||||
Loading…
Reference in New Issue
Block a user