package repository import ( "database/sql" "testing" "time" "github.com/wmms/wmms/internal/model" ) func TestOutboundRepoOrganizationIsolationReads(t *testing.T) { db := isolationDB(t, false) seedOrderIsolation(t, db) repo := NewOutboundRepo() ownOrder, err := repo.GetByID(102, 102) if err != nil || ownOrder.ID != 102 || ownOrder.OrganizationID != 102 { t.Fatalf("B 读自己的单据失败: %+v err=%v", ownOrder, err) } if _, err := repo.GetByID(102, 101); err != sql.ErrNoRows { t.Fatalf("B 读 A 的单据必须 ErrNoRows, got %v", err) } ordersA, totalA, err := repo.List(101, "", 20, 0) if err != nil { t.Fatal(err) } if totalA != 1 || len(ordersA) != 1 || ordersA[0].ID != 101 { t.Fatalf("A 列表=%+v total=%d", ordersA, totalA) } ordersB, totalB, err := repo.List(102, "", 20, 0) if err != nil { t.Fatal(err) } if totalB != 1 || len(ordersB) != 1 || ordersB[0].ID != 102 { t.Fatalf("B 列表=%+v total=%d", ordersB, totalB) } recordsB, err := repo.GetByMaterialID(102, 102) if err != nil || len(recordsB) != 1 { t.Fatalf("B 物料历史=%+v err=%v", recordsB, err) } recordsCross, err := repo.GetByMaterialID(102, 101) if err != nil || len(recordsCross) != 0 { t.Fatalf("B 读 A 物料历史必须为空, got %+v err=%v", recordsCross, err) } } func TestOutboundRepoCreatePersistsDimensionsAndRestrictsItems(t *testing.T) { db := isolationDB(t, false) seedOrderIsolation(t, db) repo := NewOutboundRepo() order := &model.OutboundOrder{ OrganizationID: 102, WarehouseID: 102, LocationID: 102, OutboundDate: time.Now(), Operator: "op", Handler: "h", Receiver: "r", Items: []model.OutboundItem{{MaterialID: 102, Quantity: 2}}, } if err := repo.Create(order); err != nil { t.Fatal(err) } var org, warehouse, location int64 if err := db.QueryRow(`SELECT organization_id, warehouse_id, location_id FROM outbound_orders WHERE id = ?`, order.ID).Scan(&org, &warehouse, &location); err != nil { t.Fatal(err) } if org != 102 || warehouse != 102 || location != 102 { t.Fatalf("头表维度=%d/%d/%d, want 102/102/102", org, warehouse, location) } // 塞一条 A 的物料明细:物料外键合法,但读取时的同组织关联会把它挡掉。 mustSQL(t, db, `INSERT INTO outbound_items(outbound_order_id, material_id, quantity) VALUES (?, ?, ?)`, order.ID, 101, 9) loaded, err := repo.GetByID(102, order.ID) if err != nil { t.Fatal(err) } if len(loaded.Items) != 1 || loaded.Items[0].MaterialID != 102 { t.Fatalf("跨公司物料明细必须被关联条件挡掉, got %+v", loaded.Items) } } func TestOutboundRepoUpdateAndDeleteScopeToOrganization(t *testing.T) { db := isolationDB(t, false) seedOrderIsolation(t, db) repo := NewOutboundRepo() if err := repo.Update(102, 101, &model.UpdateOutboundRequest{Handler: "hacked", Receiver: "hacked"}); err != sql.ErrNoRows { t.Fatalf("B 改 A 的单据必须 ErrNoRows, got %v", err) } var handler, receiver string if err := db.QueryRow(`SELECT handler, receiver FROM outbound_orders WHERE id = 101`).Scan(&handler, &receiver); err != nil { t.Fatal(err) } if handler != "A-out" || receiver != "recA" { t.Fatalf("A 的单据头被改动: %s/%s", handler, receiver) } var itemCount int if err := db.QueryRow(`SELECT COUNT(*) FROM outbound_items WHERE outbound_order_id = 101`).Scan(&itemCount); err != nil { t.Fatal(err) } if itemCount != 1 { t.Fatalf("A 的明细被改动: %d 行", itemCount) } if err := repo.Delete(102, 101); err != nil { t.Fatal(err) } var orderCount int if err := db.QueryRow(`SELECT COUNT(*) FROM outbound_orders WHERE id = 101`).Scan(&orderCount); err != nil { t.Fatal(err) } if orderCount != 1 { t.Fatal("B 删掉了 A 的单据") } }