feat: add shadow-only SQL Server package

This commit is contained in:
zgw
2026-08-11 19:16:07 +08:00
parent aab6b8e601
commit 8b050ac9d6
10 changed files with 928 additions and 2413 deletions

View File

@@ -0,0 +1,77 @@
/* Read-only field diagnostics and export queries for the FT546 shadow package. */
SET NOCOUNT ON;
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
SELECT CAST(SERVERPROPERTY('ProductVersion') AS varchar(30)) AS product_version,
DB_NAME() AS database_name,
SYSUTCDATETIME() AS checked_at_utc;
SELECT schema_name(object.schema_id) AS schema_name,
object.name AS object_name,
object.type_desc
FROM sys.objects object
WHERE schema_name(object.schema_id) = N'alg'
ORDER BY schema_name(object.schema_id), object.type_desc, object.name;
SELECT warehouse_id,
COUNT(*) AS lane_state_count,
SUM(CASE WHEN pending_outbound = 1 THEN 1 ELSE 0 END) AS pending_lane_count,
SUM(CASE WHEN has_gap = 1 THEN 1 ELSE 0 END) AS gap_lane_count,
MAX(rebuilt_at) AS latest_rebuild_at
FROM alg.lane_state
GROUP BY warehouse_id
ORDER BY warehouse_id;
SELECT warehouse_id,
COUNT(*) AS inventory_item_count,
COUNT(DISTINCT space_id) AS inventory_space_count,
MAX(synchronized_at) AS latest_inventory_sync_at
FROM alg.inventory_item_snapshot
GROUP BY warehouse_id
ORDER BY warehouse_id;
SELECT warehouse_id,
COUNT(*) AS space_state_count,
SUM(CASE WHEN inbound_available = 1 THEN 1 ELSE 0 END) AS inbound_available_count,
SUM(CASE WHEN pending_outbound = 1 THEN 1 ELSE 0 END) AS pending_outbound_count,
SUM(CASE WHEN tray_conflict = 1 OR project_conflict = 1 OR product_conflict = 1
THEN 1 ELSE 0 END) AS conflict_count
FROM alg.v_space_state
GROUP BY warehouse_id
ORDER BY warehouse_id;
/* Legacy checksums should remain stable apart from normal live traffic. */
SELECT 'bas_cargo_space' AS table_name,
COUNT_BIG(*) AS row_count,
CHECKSUM_AGG(BINARY_CHECKSUM(cargo_space_id, warehouse_id, group_coding_no,
group_coding, status, tray_code)) AS row_checksum
FROM dbo.bas_cargo_space
UNION ALL
SELECT 'bas_stock', COUNT_BIG(*),
CHECKSUM_AGG(BINARY_CHECKSUM(stock_id, cargo_space_id, tray_code, spare4,
product_id, lot_no, in_date))
FROM dbo.bas_stock
UNION ALL
SELECT 'mydo_job', COUNT_BIG(*),
CHECKSUM_AGG(BINARY_CHECKSUM(job_id, status, task_no, tray_code,
cargo_space_id, target_cargo_space_id,
in_or_out, warehouse_id))
FROM dbo.mydo_job;
/* Locks held on the three observed Legacy tables. */
SELECT request_session_id, resource_type, request_mode, request_status,
object_name(partition.object_id, lock.resource_database_id) AS object_name
FROM sys.dm_tran_locks lock
LEFT JOIN sys.partitions partition
ON partition.hobt_id = lock.resource_associated_entity_id
WHERE lock.resource_database_id = DB_ID()
AND object_name(partition.object_id, lock.resource_database_id)
IN (N'bas_cargo_space', N'bas_stock', N'mydo_job')
ORDER BY request_session_id, object_name, request_mode;
/* Export this result before uninstalling. */
SELECT decision_id, warehouse_id, decision_type, input_version,
result_code, reason, created_at
FROM alg.decision_log
WHERE decision_type IN ('INBOUND', 'OUTBOUND', 'INBOUND_COMPARE', 'OUTBOUND_COMPARE')
ORDER BY created_at, decision_id;

View File

@@ -1,224 +0,0 @@
/* Read-only validation queries for a SQL Server 2016 installation. */
SET NOCOUNT ON;
SELECT CAST(SERVERPROPERTY('ProductVersion') AS varchar(30)) AS product_version,
DB_NAME() AS database_name;
SELECT o.type_desc, COUNT(*) AS object_count
FROM sys.objects o
JOIN sys.schemas s ON s.schema_id = o.schema_id
WHERE s.name = N'alg' AND o.type IN ('U', 'V', 'P')
GROUP BY o.type_desc
ORDER BY o.type_desc;
SELECT installed.object_kind, installed.object_name
FROM (
SELECT o.type_desc AS object_kind, o.name AS object_name
FROM sys.objects o
JOIN sys.schemas s ON s.schema_id = o.schema_id
WHERE s.name = N'alg' AND o.type IN ('U', 'V', 'P')
UNION ALL
SELECT 'INDEX' AS object_kind, i.name AS object_name
FROM sys.indexes i
JOIN sys.objects o ON o.object_id = i.object_id
JOIN sys.schemas s ON s.schema_id = o.schema_id
WHERE s.name = N'alg' AND i.name IS NOT NULL
) installed
ORDER BY installed.object_kind, installed.object_name;
SELECT warehouse_id,
COUNT_BIG(*) AS excluded_from_algorithm_topology_count,
SUM(CASE WHEN group_coding_no IS NULL OR LTRIM(RTRIM(group_coding_no)) = ''
THEN 1 ELSE 0 END) AS missing_lane_code_count,
SUM(CASE WHEN TRY_CONVERT(int, group_coding) IS NULL
THEN 1 ELSE 0 END) AS invalid_depth_count
FROM dbo.bas_cargo_space
WHERE group_coding_no IS NULL OR LTRIM(RTRIM(group_coding_no)) = ''
OR TRY_CONVERT(int, group_coding) IS NULL
GROUP BY warehouse_id
ORDER BY warehouse_id;
SELECT warehouse_id,
COUNT(*) AS space_state_count,
SUM(CASE WHEN space_status = 2 THEN 1 ELSE 0 END) AS full_space_count,
SUM(CASE WHEN inbound_available = 1 THEN 1 ELSE 0 END) AS inbound_available_count,
SUM(CASE WHEN pending_outbound = 1 THEN 1 ELSE 0 END) AS pending_outbound_space_count,
SUM(CASE WHEN has_gap = 1 THEN 1 ELSE 0 END) AS gap_space_count,
SUM(CASE WHEN tray_conflict = 1 OR project_conflict = 1 OR product_conflict = 1 THEN 1 ELSE 0 END) AS conflict_space_count
FROM alg.v_space_state
GROUP BY warehouse_id
ORDER BY warehouse_id;
SELECT warehouse_id, COUNT(*) AS topology_count
FROM alg.space_topology
GROUP BY warehouse_id
ORDER BY warehouse_id;
SELECT warehouse_id,
COUNT(*) AS inventory_detail_snapshot_count,
COUNT(DISTINCT space_id) AS inventory_space_snapshot_count,
MAX(synchronized_at) AS last_inventory_synchronized_at
FROM alg.inventory_item_snapshot
GROUP BY warehouse_id
ORDER BY warehouse_id;
;WITH warehouse_floors AS (
SELECT DISTINCT warehouse_id, the_floor AS floor_no
FROM dbo.bas_cargo_space
), role_counts AS (
SELECT cargo.warehouse_id, cargo.the_floor AS floor_no,
SUM(CASE WHEN role.role = 'RELOCATION_BUFFER' THEN 1 ELSE 0 END) AS relocation_buffer_count,
SUM(CASE WHEN role.role = 'OUTBOUND_BUFFER' THEN 1 ELSE 0 END) AS outbound_buffer_count,
SUM(CASE WHEN role.role = 'PROTECTED_CORE' THEN 1 ELSE 0 END) AS protected_core_count
FROM dbo.bas_cargo_space cargo
LEFT JOIN alg.space_role role ON role.space_id = cargo.cargo_space_id
GROUP BY cargo.warehouse_id, cargo.the_floor
)
SELECT floors.warehouse_id, floors.floor_no,
ISNULL(counts.relocation_buffer_count, 0) AS relocation_buffer_count,
CAST(4 AS int) AS relocation_buffer_target,
CASE WHEN ISNULL(counts.relocation_buffer_count, 0) >= 4 THEN 0
ELSE 4 - ISNULL(counts.relocation_buffer_count, 0) END AS relocation_buffer_shortage,
ISNULL(counts.outbound_buffer_count, 0) AS outbound_buffer_count,
ISNULL(counts.protected_core_count, 0) AS protected_core_count
FROM warehouse_floors floors
LEFT JOIN role_counts counts ON counts.warehouse_id = floors.warehouse_id
AND counts.floor_no = floors.floor_no
ORDER BY floors.warehouse_id, floors.floor_no;
SELECT cargo.warehouse_id, cargo.the_floor AS floor_no, role.space_id, role.role, role.updated_at
FROM alg.space_role role
JOIN dbo.bas_cargo_space cargo ON cargo.cargo_space_id = role.space_id
WHERE role.role IN ('RELOCATION_BUFFER', 'OUTBOUND_BUFFER', 'PROTECTED_CORE')
ORDER BY cargo.warehouse_id, cargo.the_floor, role.role, role.space_id;
SELECT warehouse_id, COUNT(*) AS lane_state_count,
SUM(CASE WHEN pending_outbound = 1 THEN 1 ELSE 0 END) AS pending_lane_count,
SUM(CASE WHEN has_gap = 1 THEN 1 ELSE 0 END) AS gap_lane_count
FROM alg.lane_state
GROUP BY warehouse_id
ORDER BY warehouse_id;
SELECT 'bas_cargo_space' AS table_name, COUNT_BIG(*) AS row_count,
CHECKSUM_AGG(BINARY_CHECKSUM(cargo_space_id, warehouse_id, group_coding_no, group_coding, status, tray_code)) AS row_checksum
FROM dbo.bas_cargo_space
UNION ALL
SELECT 'bas_stock', COUNT_BIG(*),
CHECKSUM_AGG(BINARY_CHECKSUM(stock_id, cargo_space_id, tray_code, spare4, product_id, lot_no, in_date))
FROM dbo.bas_stock
UNION ALL
SELECT 'mydo_job', COUNT_BIG(*),
CHECKSUM_AGG(BINARY_CHECKSUM(job_id, status, task_no, tray_code, cargo_space_id, target_cargo_space_id, in_or_out, warehouse_id))
FROM dbo.mydo_job;
;WITH pallet_details AS (
SELECT cargo_space_id,
MIN(NULLIF(LTRIM(RTRIM(tray_code)), '')) AS tray_code,
MIN(NULLIF(LTRIM(RTRIM(spare4)), '')) AS project_no,
MIN(NULLIF(LTRIM(RTRIM(product_id)), '')) AS first_product_no,
MAX(NULLIF(LTRIM(RTRIM(product_id)), '')) AS last_product_no,
COUNT(DISTINCT NULLIF(LTRIM(RTRIM(product_id)), '')) AS product_count
FROM dbo.bas_stock
GROUP BY cargo_space_id
)
SELECT TOP 5 state.space_id, details.tray_code, details.project_no,
details.first_product_no, details.last_product_no, details.product_count
FROM pallet_details details
JOIN alg.v_space_state state ON state.space_id = details.cargo_space_id
WHERE details.product_count > 1
AND state.space_status = '2'
AND state.stock_tray_count = 1
AND state.stock_project_count = 1
AND state.tray_conflict = 0
AND state.project_conflict = 0
AND state.pending_outbound = 0
AND state.reserved = 0
AND state.has_gap = 0
ORDER BY state.space_id;
SELECT space_id, warehouse_id, lane_code, depth, space_status, role,
tray_conflict, project_conflict, product_conflict, pending_outbound, reserved, has_gap,
inbound_available
FROM alg.v_space_state
WHERE tray_conflict = 1 OR project_conflict = 1 OR product_conflict = 1
OR pending_outbound = 1 OR has_gap = 1
ORDER BY warehouse_id, lane_code, depth;
SELECT reservation_id, warehouse_id, lane_code, space_id, project_no, status,
created_at, released_at
FROM alg.reservation
ORDER BY created_at DESC;
SELECT lock_id, request_id, warehouse_id, floor_no, status, created_at, released_at
FROM alg.deep_workflow_lock
ORDER BY created_at DESC;
SELECT warehouse_id, lane_code, topology_dirty, reason_mask, change_count,
enqueued_at, last_changed_at
FROM alg.dirty_lane_queue
ORDER BY last_changed_at, warehouse_id, lane_code;
SELECT workflow_id, request_id, stockout_detail_id, warehouse_id, source_stock_id,
selected_tray_code, source_space_id, lane_code, floor_no, exit_id,
status, current_step, total_steps, created_at, updated_at, completed_at
FROM alg.outbound_workflow
WHERE status <> 'COMPLETED'
ORDER BY created_at, workflow_id;
SELECT step.workflow_id, step.assignment_id, step.phase, step.source_space_id,
step.target_space_id, step.workflow_step, step.workflow_total_steps,
step.depends_on_assignment_id, step.stock_id, step.tray_code, step.status,
step.wms_job_id, step.created_at, step.started_at, step.at_exit_at, step.completed_at
FROM alg.outbound_workflow_step step
JOIN alg.outbound_workflow workflow ON workflow.workflow_id = step.workflow_id
WHERE workflow.status <> 'COMPLETED' OR step.status <> 'COMPLETED'
ORDER BY step.workflow_id, step.workflow_step;
SELECT mode.warehouse_id, mode.requested_enabled, mode.effective_state,
mode.status_reason, mode.mode_version, mode.updated_by, mode.updated_at
FROM alg.tidy_mode mode
ORDER BY mode.warehouse_id;
SELECT queue.warehouse_id, queue.lane_code, queue.dirty_version, queue.queue_status,
queue.reason_mask, queue.change_count, queue.last_dirty_at, queue.claimed_version,
queue.claimed_by, queue.claimed_at, queue.lease_expires_at, queue.last_error
FROM alg.tidy_lane_queue queue
ORDER BY queue.last_dirty_at, queue.warehouse_id, queue.lane_code;
SELECT workflow.workflow_id, workflow.warehouse_id, workflow.lane_code, workflow.origin,
workflow.candidate_type, workflow.source_space_id, workflow.target_space_id,
workflow.stock_id, workflow.tray_code, workflow.status, workflow.claimed_dirty_version,
workflow.vehicle_id, workflow.wcs_task_id, workflow.status_reason,
workflow.created_at, workflow.updated_at, workflow.dispatched_at, workflow.completed_at
FROM alg.tidy_workflow workflow
WHERE workflow.completed_at IS NULL
OR workflow.status IN ('PAUSED_RECOVERY', 'CANCELLED_RECONCILED')
ORDER BY workflow.created_at, workflow.workflow_id;
SELECT step.workflow_id, step.step_id, step.step_no, step.source_space_id,
step.target_space_id, step.stock_id, step.tray_code, step.status,
step.wms_job_id, step.wcs_task_id, step.expected_event_stage,
step.created_at, step.started_at, step.completed_at
FROM alg.tidy_step step
JOIN alg.tidy_workflow workflow ON workflow.workflow_id = step.workflow_id
WHERE workflow.completed_at IS NULL OR step.completed_at IS NULL
ORDER BY step.workflow_id, step.step_no;
SELECT lock.workflow_id, lock.warehouse_id, lock.resource_type, lock.resource_id,
lock.status, lock.locked_at, lock.released_at
FROM alg.tidy_space_lock lock
WHERE lock.status = 'ACTIVE'
ORDER BY lock.warehouse_id, lock.resource_type, lock.resource_id;
SELECT event.workflow_id, event.step_id, event.wcs_task_id, event.event_stage,
COUNT(*) AS callback_count, MIN(event.received_at) AS first_received_at,
MAX(event.received_at) AS last_received_at
FROM alg.tidy_callback_event event
GROUP BY event.workflow_id, event.step_id, event.wcs_task_id, event.event_stage
HAVING COUNT(*) > 1
ORDER BY event.workflow_id, event.step_id, event.event_stage;
SELECT workflow.origin, workflow.status, COUNT(*) AS workflow_count
FROM alg.tidy_workflow workflow
GROUP BY workflow.origin, workflow.status
ORDER BY workflow.origin, workflow.status;

View File

@@ -0,0 +1,654 @@
/*
FT546 shadow-only read model for SQL Server 2016 RTM (13.0).
This package creates no Legacy-table triggers and never inserts, updates,
or deletes Legacy WMS business rows.
*/
SET NOCOUNT ON;
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
IF SCHEMA_ID(N'alg') IS NULL EXEC(N'CREATE SCHEMA alg');
GO
IF OBJECT_ID(N'alg.warehouse_policy', N'U') IS NULL
CREATE TABLE alg.warehouse_policy (
warehouse_id varchar(80) NOT NULL PRIMARY KEY,
target_fill_rate decimal(9,4) NULL,
fifo_tolerance_hours int NULL,
updated_at datetime2 NOT NULL CONSTRAINT DF_alg_warehouse_policy_updated_at DEFAULT SYSUTCDATETIME()
);
GO
IF OBJECT_ID(N'alg.project_policy', N'U') IS NULL
CREATE TABLE alg.project_policy (
project_no varchar(255) NOT NULL PRIMARY KEY,
is_isolated bit NOT NULL CONSTRAINT DF_alg_project_policy_isolated DEFAULT 0,
is_common bit NOT NULL CONSTRAINT DF_alg_project_policy_common DEFAULT 0,
allow_mixed bit NOT NULL CONSTRAINT DF_alg_project_policy_mixed DEFAULT 0,
updated_at datetime2 NOT NULL CONSTRAINT DF_alg_project_policy_updated_at DEFAULT SYSUTCDATETIME()
);
GO
IF OBJECT_ID(N'alg.space_role', N'U') IS NULL
CREATE TABLE alg.space_role (
space_id varchar(80) NOT NULL PRIMARY KEY,
role varchar(40) NOT NULL,
updated_at datetime2 NOT NULL CONSTRAINT DF_alg_space_role_updated_at DEFAULT SYSUTCDATETIME(),
CONSTRAINT CK_alg_space_role_role CHECK (role IN ('STORAGE', 'RELOCATION_BUFFER', 'OUTBOUND_BUFFER', 'PROTECTED_CORE'))
);
GO
IF OBJECT_ID(N'alg.space_topology', N'U') IS NULL
CREATE TABLE alg.space_topology (
warehouse_id varchar(80) NOT NULL,
space_id varchar(80) NOT NULL CONSTRAINT PK_alg_space_topology PRIMARY KEY,
lane_code varchar(80) NOT NULL,
lane_depth int NOT NULL,
base_zone varchar(20) NOT NULL,
rebuilt_at datetime2 NOT NULL CONSTRAINT DF_alg_space_topology_rebuilt_at DEFAULT SYSUTCDATETIME(),
CONSTRAINT CK_alg_space_topology_base_zone CHECK (base_zone IN ('CACHE', 'RESERVE', 'MAIN', 'FLEX'))
);
GO
IF NOT EXISTS (SELECT 1 FROM sys.indexes WHERE object_id = OBJECT_ID(N'alg.space_topology') AND name = N'IX_alg_space_topology_warehouse_lane')
CREATE INDEX IX_alg_space_topology_warehouse_lane ON alg.space_topology(warehouse_id, lane_code);
GO
IF OBJECT_ID(N'alg.inventory_item_snapshot', N'U') IS NULL
CREATE TABLE alg.inventory_item_snapshot (
warehouse_id varchar(80) NOT NULL,
stock_id varchar(80) NOT NULL,
space_id varchar(80) NOT NULL,
tray_code varchar(255) NULL,
project_no varchar(255) NULL,
product_no varchar(255) NULL,
lot_no varchar(80) NULL,
inbound_time_raw varchar(80) NULL,
inbound_at datetime2 NULL,
quantity decimal(38,6) NULL,
out_quantity decimal(38,6) NULL,
synchronized_at datetime2 NOT NULL CONSTRAINT DF_alg_inventory_item_snapshot_sync DEFAULT SYSUTCDATETIME(),
CONSTRAINT PK_alg_inventory_item_snapshot PRIMARY KEY (warehouse_id, stock_id)
);
GO
IF NOT EXISTS (SELECT 1 FROM sys.indexes WHERE object_id = OBJECT_ID(N'alg.inventory_item_snapshot') AND name = N'IX_alg_inventory_item_snapshot_space')
CREATE INDEX IX_alg_inventory_item_snapshot_space ON alg.inventory_item_snapshot(warehouse_id, space_id);
GO
IF NOT EXISTS (SELECT 1 FROM sys.indexes WHERE object_id = OBJECT_ID(N'alg.inventory_item_snapshot') AND name = N'IX_alg_inventory_item_snapshot_tray')
CREATE INDEX IX_alg_inventory_item_snapshot_tray ON alg.inventory_item_snapshot(warehouse_id, tray_code) INCLUDE (space_id, project_no, product_no);
GO
IF NOT EXISTS (SELECT 1 FROM sys.indexes WHERE object_id = OBJECT_ID(N'alg.inventory_item_snapshot') AND name = N'IX_alg_inventory_item_snapshot_project_product')
CREATE INDEX IX_alg_inventory_item_snapshot_project_product ON alg.inventory_item_snapshot(warehouse_id, project_no, product_no) INCLUDE (space_id, tray_code, inbound_at);
GO
IF OBJECT_ID(N'alg.space_inventory_summary', N'U') IS NULL
CREATE TABLE alg.space_inventory_summary (
warehouse_id varchar(80) NOT NULL,
space_id varchar(80) NOT NULL,
stock_detail_count int NOT NULL,
stock_tray_count int NOT NULL,
stock_project_count int NOT NULL,
stock_product_count int NOT NULL,
synchronized_at datetime2 NOT NULL CONSTRAINT DF_alg_space_inventory_summary_sync DEFAULT SYSUTCDATETIME(),
CONSTRAINT PK_alg_space_inventory_summary PRIMARY KEY (warehouse_id, space_id)
);
GO
IF NOT EXISTS (SELECT 1 FROM sys.indexes WHERE object_id = OBJECT_ID(N'alg.space_inventory_summary') AND name = N'IX_alg_space_inventory_summary_space')
CREATE INDEX IX_alg_space_inventory_summary_space ON alg.space_inventory_summary(space_id, warehouse_id);
GO
IF OBJECT_ID(N'alg.lane_state', N'U') IS NULL
CREATE TABLE alg.lane_state (
warehouse_id varchar(80) NOT NULL,
lane_code varchar(80) NOT NULL,
space_count int NOT NULL CONSTRAINT DF_alg_lane_state_space_count DEFAULT 0,
occupied_count int NOT NULL CONSTRAINT DF_alg_lane_state_occupied_count DEFAULT 0,
available_count int NOT NULL CONSTRAINT DF_alg_lane_state_available_count DEFAULT 0,
pending_outbound bit NOT NULL CONSTRAINT DF_alg_lane_state_pending_outbound DEFAULT 0,
has_gap bit NOT NULL CONSTRAINT DF_alg_lane_state_has_gap DEFAULT 0,
semantic_fingerprint varbinary(32) NULL,
state_version rowversion NOT NULL,
rebuilt_at datetime2 NOT NULL CONSTRAINT DF_alg_lane_state_rebuilt_at DEFAULT SYSUTCDATETIME(),
CONSTRAINT PK_alg_lane_state PRIMARY KEY (warehouse_id, lane_code)
);
GO
IF COL_LENGTH(N'alg.lane_state', N'semantic_fingerprint') IS NULL
ALTER TABLE alg.lane_state ADD semantic_fingerprint varbinary(32) NULL;
GO
IF OBJECT_ID(N'alg.reservation', N'U') IS NULL
CREATE TABLE alg.reservation (
reservation_id uniqueidentifier NOT NULL CONSTRAINT PK_alg_reservation PRIMARY KEY,
warehouse_id varchar(80) NOT NULL,
lane_code varchar(80) NOT NULL,
space_id varchar(80) NOT NULL,
project_no varchar(255) NULL,
status varchar(20) NOT NULL,
created_at datetime2 NOT NULL CONSTRAINT DF_alg_reservation_created_at DEFAULT SYSUTCDATETIME(),
released_at datetime2 NULL,
CONSTRAINT CK_alg_reservation_status CHECK (status IN ('ACTIVE', 'RELEASED', 'EXPIRED'))
);
GO
IF NOT EXISTS (SELECT 1 FROM sys.indexes WHERE object_id = OBJECT_ID(N'alg.reservation') AND name = N'UX_alg_reservation_active_space')
CREATE UNIQUE INDEX UX_alg_reservation_active_space ON alg.reservation(space_id) WHERE status = 'ACTIVE';
GO
IF OBJECT_ID(N'alg.deep_workflow_lock', N'U') IS NULL
CREATE TABLE alg.deep_workflow_lock (
lock_id uniqueidentifier NOT NULL CONSTRAINT PK_alg_deep_workflow_lock PRIMARY KEY,
request_id varchar(80) NOT NULL,
warehouse_id varchar(80) NOT NULL,
floor_no int NOT NULL,
status varchar(20) NOT NULL,
created_at datetime2 NOT NULL CONSTRAINT DF_alg_deep_workflow_lock_created_at DEFAULT SYSUTCDATETIME(),
released_at datetime2 NULL,
CONSTRAINT CK_alg_deep_workflow_lock_status CHECK (status IN ('ACTIVE', 'RELEASED'))
);
GO
IF NOT EXISTS (SELECT 1 FROM sys.indexes WHERE object_id = OBJECT_ID(N'alg.deep_workflow_lock') AND name = N'UX_alg_deep_workflow_lock_active_floor')
CREATE UNIQUE INDEX UX_alg_deep_workflow_lock_active_floor
ON alg.deep_workflow_lock(warehouse_id, floor_no) WHERE status = 'ACTIVE';
GO
IF OBJECT_ID(N'alg.decision_log', N'U') IS NULL
CREATE TABLE alg.decision_log (
decision_id uniqueidentifier NOT NULL CONSTRAINT PK_alg_decision_log PRIMARY KEY,
warehouse_id varchar(80) NULL,
decision_type varchar(40) NOT NULL,
input_version binary(8) NULL,
result_code varchar(80) NULL,
reason varchar(2000) NULL,
created_at datetime2 NOT NULL CONSTRAINT DF_alg_decision_log_created_at DEFAULT SYSUTCDATETIME()
);
GO
IF OBJECT_ID(N'alg.usp_rebuild_space_topology', N'P') IS NOT NULL DROP PROCEDURE alg.usp_rebuild_space_topology;
GO
EXEC(N'
CREATE PROCEDURE alg.usp_rebuild_space_topology
@warehouse_id varchar(80) = NULL
AS
BEGIN
SET NOCOUNT ON;
;WITH lane_topology AS (
SELECT warehouse_id, group_coding_no AS lane_code,
COUNT(*) AS lane_depth,
MAX(the_column) AS column_no,
MIN(the_platoon) AS row_no,
MIN(the_floor) AS floor_no,
MIN(cargo_space_id) AS stable_id
FROM dbo.bas_cargo_space
WHERE NULLIF(LTRIM(RTRIM(group_coding_no)), '''') IS NOT NULL
AND TRY_CONVERT(int, group_coding) IS NOT NULL
AND (@warehouse_id IS NULL OR warehouse_id = @warehouse_id)
GROUP BY warehouse_id, group_coding_no
), ranked_lanes AS (
SELECT lane_topology.*,
CASE WHEN lane_depth = 1 THEN ROW_NUMBER() OVER (
PARTITION BY warehouse_id
ORDER BY CASE WHEN lane_depth = 1 THEN 0 ELSE 1 END,
column_no DESC, row_no ASC, floor_no ASC, stable_id ASC
) END AS single_depth_rank
FROM lane_topology
), source AS (
SELECT c.warehouse_id, c.cargo_space_id AS space_id, c.group_coding_no AS lane_code,
ranked_lanes.lane_depth,
CASE WHEN ranked_lanes.lane_depth = 1 AND ranked_lanes.single_depth_rank <= 80 THEN ''CACHE''
WHEN ranked_lanes.lane_depth = 1 AND ranked_lanes.single_depth_rank <= 133 THEN ''RESERVE''
WHEN ranked_lanes.lane_depth = 5 THEN ''MAIN''
ELSE ''FLEX'' END AS base_zone
FROM dbo.bas_cargo_space c
JOIN ranked_lanes ON ranked_lanes.warehouse_id = c.warehouse_id
AND ranked_lanes.lane_code = c.group_coding_no
WHERE NULLIF(LTRIM(RTRIM(c.group_coding_no)), '''') IS NOT NULL
AND TRY_CONVERT(int, c.group_coding) IS NOT NULL
AND (@warehouse_id IS NULL OR c.warehouse_id = @warehouse_id)
)
MERGE alg.space_topology AS target
USING source ON source.space_id = target.space_id
WHEN MATCHED AND (target.warehouse_id <> source.warehouse_id OR target.lane_code <> source.lane_code
OR target.lane_depth <> source.lane_depth OR target.base_zone <> source.base_zone)
THEN UPDATE SET warehouse_id = source.warehouse_id, lane_code = source.lane_code,
lane_depth = source.lane_depth, base_zone = source.base_zone,
rebuilt_at = SYSUTCDATETIME()
WHEN NOT MATCHED BY TARGET THEN
INSERT (warehouse_id, space_id, lane_code, lane_depth, base_zone)
VALUES (source.warehouse_id, source.space_id, source.lane_code, source.lane_depth, source.base_zone)
WHEN NOT MATCHED BY SOURCE AND (@warehouse_id IS NULL OR target.warehouse_id = @warehouse_id)
THEN DELETE;
END;');
GO
IF OBJECT_ID(N'alg.usp_sync_inventory_snapshot', N'P') IS NOT NULL DROP PROCEDURE alg.usp_sync_inventory_snapshot;
GO
EXEC(N'
CREATE PROCEDURE alg.usp_sync_inventory_snapshot
@warehouse_id varchar(80) = NULL
AS
BEGIN
SET NOCOUNT ON;
SET XACT_ABORT ON;
BEGIN TRANSACTION;
DELETE summary
FROM alg.space_inventory_summary summary
WHERE @warehouse_id IS NULL OR summary.warehouse_id = @warehouse_id;
DELETE item
FROM alg.inventory_item_snapshot item
WHERE @warehouse_id IS NULL OR item.warehouse_id = @warehouse_id;
INSERT INTO alg.inventory_item_snapshot (
warehouse_id, stock_id, space_id, tray_code, project_no, product_no, lot_no,
inbound_time_raw, inbound_at, quantity, out_quantity, synchronized_at)
SELECT space.warehouse_id, stock.stock_id, stock.cargo_space_id, stock.tray_code,
stock.spare4, stock.product_id, stock.lot_no, CONVERT(varchar(80), stock.in_date),
TRY_CONVERT(datetime2, stock.in_date), stock.quantity, stock.out_quantity, SYSUTCDATETIME()
FROM dbo.bas_stock stock
JOIN dbo.bas_cargo_space space ON space.cargo_space_id = stock.cargo_space_id
WHERE @warehouse_id IS NULL OR space.warehouse_id = @warehouse_id;
INSERT INTO alg.space_inventory_summary (
warehouse_id, space_id, stock_detail_count, stock_tray_count,
stock_project_count, stock_product_count, synchronized_at)
SELECT item.warehouse_id, item.space_id, COUNT(*),
COUNT(DISTINCT NULLIF(LTRIM(RTRIM(item.tray_code)), '''')),
COUNT(DISTINCT NULLIF(LTRIM(RTRIM(item.project_no)), '''')),
COUNT(DISTINCT NULLIF(LTRIM(RTRIM(item.product_no)), '''')),
SYSUTCDATETIME()
FROM alg.inventory_item_snapshot item
WHERE @warehouse_id IS NULL OR item.warehouse_id = @warehouse_id
GROUP BY item.warehouse_id, item.space_id;
COMMIT TRANSACTION;
END;');
GO
IF OBJECT_ID(N'alg.v_inventory_item', N'V') IS NOT NULL DROP VIEW alg.v_inventory_item;
GO
EXEC(N'
CREATE VIEW alg.v_inventory_item AS
SELECT stock_id, space_id, tray_code, project_no, product_no, lot_no,
inbound_time_raw, inbound_at, quantity, out_quantity
FROM alg.inventory_item_snapshot;');
GO
IF OBJECT_ID(N'alg.v_active_job', N'V') IS NOT NULL DROP VIEW alg.v_active_job;
GO
EXEC(N'
CREATE VIEW alg.v_active_job AS
WITH tray_candidates AS (
SELECT j.job_id,
COUNT(DISTINCT s.space_id) AS space_count,
MIN(s.space_id) AS cargo_space_id,
COUNT(DISTINCT NULLIF(LTRIM(RTRIM(s.project_no)), '''')) AS project_count,
MIN(NULLIF(LTRIM(RTRIM(s.project_no)), '''')) AS project_no,
COUNT(DISTINCT NULLIF(LTRIM(RTRIM(s.product_no)), '''')) AS product_count,
MIN(NULLIF(LTRIM(RTRIM(s.product_no)), '''')) AS product_no
FROM dbo.mydo_job j
JOIN alg.inventory_item_snapshot s ON s.warehouse_id = j.warehouse_id
AND s.tray_code = NULLIF(LTRIM(RTRIM(j.tray_code)), '''')
GROUP BY j.job_id
), resolved_jobs AS (
SELECT j.job_id, j.task_no, j.tray_code, j.target_cargo_space_id, j.warehouse_id,
j.status, j.in_or_out, j.create_time AS create_time_raw,
COALESCE(source_space.cargo_space_id,
CASE WHEN tray_candidates.space_count = 1 THEN tray_candidates.cargo_space_id END) AS source_space_id,
CASE WHEN source_space.cargo_space_id IS NOT NULL THEN ''EXPLICIT''
WHEN tray_candidates.space_count = 1 THEN ''TRAY''
WHEN tray_candidates.space_count > 1 THEN ''AMBIGUOUS_TRAY''
ELSE ''UNRESOLVED'' END AS source_resolution
FROM dbo.mydo_job j
LEFT JOIN dbo.bas_cargo_space source_space
ON source_space.cargo_space_id = NULLIF(LTRIM(RTRIM(j.cargo_space_id)), '''')
AND source_space.warehouse_id = j.warehouse_id
LEFT JOIN tray_candidates ON tray_candidates.job_id = j.job_id
WHERE j.finish_time IS NULL OR LTRIM(RTRIM(j.finish_time)) = ''''
), source_scope AS (
SELECT resolved_jobs.job_id,
COUNT(DISTINCT NULLIF(LTRIM(RTRIM(stock.project_no)), '''')) AS project_count,
MIN(NULLIF(LTRIM(RTRIM(stock.project_no)), '''')) AS project_no,
COUNT(DISTINCT NULLIF(LTRIM(RTRIM(stock.product_no)), '''')) AS product_count,
MIN(NULLIF(LTRIM(RTRIM(stock.product_no)), '''')) AS product_no
FROM resolved_jobs
JOIN alg.inventory_item_snapshot stock ON stock.warehouse_id = resolved_jobs.warehouse_id
AND stock.space_id = resolved_jobs.source_space_id
GROUP BY resolved_jobs.job_id
)
SELECT resolved_jobs.job_id, resolved_jobs.task_no, resolved_jobs.tray_code,
CASE WHEN resolved_jobs.source_space_id IS NOT NULL
THEN CASE WHEN source_scope.project_count = 1 THEN source_scope.project_no END
ELSE CASE WHEN tray_candidates.project_count = 1 THEN tray_candidates.project_no END END AS project_no,
CASE WHEN resolved_jobs.source_space_id IS NOT NULL
THEN CASE WHEN source_scope.product_count = 1 THEN source_scope.product_no END
ELSE CASE WHEN tray_candidates.product_count = 1 THEN tray_candidates.product_no END END AS product_no,
resolved_jobs.source_space_id, resolved_jobs.target_cargo_space_id, resolved_jobs.warehouse_id,
resolved_jobs.status, resolved_jobs.in_or_out, resolved_jobs.create_time_raw,
resolved_space.group_coding_no AS source_lane_code, resolved_jobs.source_resolution
FROM resolved_jobs
LEFT JOIN tray_candidates ON tray_candidates.job_id = resolved_jobs.job_id
LEFT JOIN source_scope ON source_scope.job_id = resolved_jobs.job_id
LEFT JOIN dbo.bas_cargo_space resolved_space ON resolved_space.cargo_space_id = resolved_jobs.source_space_id;');
GO
IF OBJECT_ID(N'alg.v_space_state', N'V') IS NOT NULL DROP VIEW alg.v_space_state;
GO
EXEC(N'
CREATE VIEW alg.v_space_state AS
WITH outbound_lanes AS (
SELECT DISTINCT warehouse_id, source_lane_code AS lane_code
FROM alg.v_active_job
WHERE in_or_out >= 20 AND source_lane_code IS NOT NULL
), lane_gaps AS (
SELECT DISTINCT inner_space.warehouse_id, inner_space.group_coding_no AS lane_code
FROM dbo.bas_cargo_space inner_space
JOIN dbo.bas_cargo_space outer_space
ON outer_space.warehouse_id = inner_space.warehouse_id
AND outer_space.group_coding_no = inner_space.group_coding_no
AND TRY_CONVERT(int, outer_space.group_coding) > TRY_CONVERT(int, inner_space.group_coding)
WHERE TRY_CONVERT(int, inner_space.group_coding) IS NOT NULL
AND TRY_CONVERT(int, outer_space.group_coding) IS NOT NULL
AND NULLIF(LTRIM(RTRIM(inner_space.tray_code)), '''') IS NULL
AND NOT EXISTS (
SELECT 1 FROM alg.inventory_item_snapshot inner_stock
WHERE inner_stock.warehouse_id = inner_space.warehouse_id
AND inner_stock.space_id = inner_space.cargo_space_id
)
AND (
NULLIF(LTRIM(RTRIM(outer_space.tray_code)), '''') IS NOT NULL
OR EXISTS (
SELECT 1 FROM alg.inventory_item_snapshot outer_stock
WHERE outer_stock.warehouse_id = outer_space.warehouse_id
AND outer_stock.space_id = outer_space.cargo_space_id
)
)
)
SELECT c.cargo_space_id AS space_id,
c.warehouse_id,
c.group_coding_no AS lane_code,
c.cargo_area_id AS area_id,
CASE
WHEN ISNULL(role.role, ''STORAGE'') IN (''RELOCATION_BUFFER'', ''OUTBOUND_BUFFER'') THEN ''CACHE''
WHEN ISNULL(role.role, ''STORAGE'') = ''PROTECTED_CORE'' THEN ''RESERVE''
ELSE topology.base_zone
END AS zone_code,
c.roadway_id,
c.cargo_space_name AS display_name,
c.the_platoon AS platoon,
c.the_column AS column_no,
c.the_floor AS floor_no,
c.group_coding AS depth_raw,
TRY_CONVERT(int, c.group_coding) AS depth,
c.status AS space_status,
c.locked,
c.tray_code AS space_tray_code,
ISNULL(role.role, ''STORAGE'') AS role,
ISNULL(stock.stock_detail_count, 0) AS stock_detail_count,
ISNULL(stock.stock_tray_count, 0) AS stock_tray_count,
ISNULL(stock.stock_project_count, 0) AS stock_project_count,
ISNULL(stock.stock_product_count, 0) AS stock_product_count,
CONVERT(bit, CASE WHEN ISNULL(stock.stock_tray_count, 0) > 1 THEN 1 ELSE 0 END) AS tray_conflict,
CONVERT(bit, CASE WHEN ISNULL(stock.stock_project_count, 0) > 1 THEN 1 ELSE 0 END) AS project_conflict,
CONVERT(bit, 0) AS product_conflict,
CONVERT(bit, CASE WHEN outbound_lanes.lane_code IS NULL THEN 0 ELSE 1 END) AS pending_outbound,
active_reservations.reservation_id,
CONVERT(bit, CASE WHEN active_reservations.reservation_id IS NULL THEN 0 ELSE 1 END) AS reserved,
CONVERT(bit, CASE WHEN lane_gaps.lane_code IS NULL THEN 0 ELSE 1 END) AS has_gap,
CONVERT(bit, CASE
WHEN c.status IS NULL OR c.status <> ''1'' THEN 0
WHEN NULLIF(LTRIM(RTRIM(c.locked)), '''') IS NOT NULL AND LTRIM(RTRIM(c.locked)) NOT IN (''0'', ''N'') THEN 0
WHEN NULLIF(LTRIM(RTRIM(c.tray_code)), '''') IS NOT NULL THEN 0
WHEN ISNULL(stock.stock_detail_count, 0) <> 0 THEN 0
WHEN TRY_CONVERT(int, c.group_coding) IS NULL THEN 0
WHEN ISNULL(role.role, ''STORAGE'') <> ''STORAGE'' THEN 0
WHEN outbound_lanes.lane_code IS NOT NULL THEN 0
WHEN active_reservations.reservation_id IS NOT NULL THEN 0
WHEN lane_gaps.lane_code IS NOT NULL THEN 0
WHEN EXISTS (
SELECT 1
FROM dbo.bas_cargo_space inner_candidate
LEFT JOIN alg.space_inventory_summary inner_stock
ON inner_stock.warehouse_id = inner_candidate.warehouse_id
AND inner_stock.space_id = inner_candidate.cargo_space_id
WHERE inner_candidate.warehouse_id = c.warehouse_id
AND inner_candidate.group_coding_no = c.group_coding_no
AND TRY_CONVERT(int, inner_candidate.group_coding) < TRY_CONVERT(int, c.group_coding)
AND inner_candidate.status = ''1''
AND NULLIF(LTRIM(RTRIM(inner_candidate.tray_code)), '''') IS NULL
AND inner_stock.space_id IS NULL
) THEN 0
ELSE 1 END) AS inbound_available
FROM dbo.bas_cargo_space c
JOIN alg.space_topology topology ON topology.space_id = c.cargo_space_id
LEFT JOIN alg.space_inventory_summary stock ON stock.warehouse_id = c.warehouse_id
AND stock.space_id = c.cargo_space_id
LEFT JOIN alg.space_role role ON role.space_id = c.cargo_space_id
LEFT JOIN outbound_lanes ON outbound_lanes.warehouse_id = c.warehouse_id AND outbound_lanes.lane_code = c.group_coding_no
LEFT JOIN alg.reservation active_reservations ON active_reservations.space_id = c.cargo_space_id AND active_reservations.status = ''ACTIVE''
LEFT JOIN lane_gaps ON lane_gaps.warehouse_id = c.warehouse_id AND lane_gaps.lane_code = c.group_coding_no;');
GO
IF OBJECT_ID(N'alg.usp_compute_lane_semantic_fingerprint', N'P') IS NOT NULL DROP PROCEDURE alg.usp_compute_lane_semantic_fingerprint;
GO
EXEC(N'
CREATE PROCEDURE alg.usp_compute_lane_semantic_fingerprint
@warehouse_id varchar(80),
@lane_code varchar(80),
@semantic_fingerprint varbinary(32) OUTPUT,
@inventory_source varchar(20) = ''LIVE''
AS
BEGIN
SET NOCOUNT ON;
DECLARE @payload nvarchar(max);
CREATE TABLE #fingerprint_inventory (
stock_id varchar(80) NOT NULL,
space_id varchar(80) NOT NULL,
tray_code varchar(255) NULL,
project_no varchar(255) NULL,
product_no varchar(255) NULL,
lot_no varchar(80) NULL,
inbound_time varchar(80) NULL,
quantity decimal(38,6) NULL,
out_quantity decimal(38,6) NULL
);
IF @inventory_source = ''SNAPSHOT''
BEGIN
INSERT INTO #fingerprint_inventory (
stock_id, space_id, tray_code, project_no, product_no, lot_no,
inbound_time, quantity, out_quantity)
SELECT stock.stock_id, stock.space_id, stock.tray_code, stock.project_no,
stock.product_no, stock.lot_no, stock.inbound_time_raw,
CONVERT(decimal(38,6), stock.quantity), CONVERT(decimal(38,6), stock.out_quantity)
FROM alg.inventory_item_snapshot stock
JOIN dbo.bas_cargo_space cargo ON cargo.cargo_space_id = stock.space_id
AND cargo.warehouse_id = stock.warehouse_id
WHERE cargo.warehouse_id = @warehouse_id AND cargo.group_coding_no = @lane_code;
END
ELSE IF @inventory_source = ''LIVE''
BEGIN
INSERT INTO #fingerprint_inventory (
stock_id, space_id, tray_code, project_no, product_no, lot_no,
inbound_time, quantity, out_quantity)
SELECT stock.stock_id, stock.cargo_space_id, stock.tray_code, stock.spare4,
stock.product_id, stock.lot_no, CONVERT(varchar(80), stock.in_date),
CONVERT(decimal(38,6), stock.quantity), CONVERT(decimal(38,6), stock.out_quantity)
FROM dbo.bas_stock stock
JOIN dbo.bas_cargo_space cargo ON cargo.cargo_space_id = stock.cargo_space_id
WHERE cargo.warehouse_id = @warehouse_id AND cargo.group_coding_no = @lane_code;
END
ELSE
BEGIN
THROW 50001, ''Unsupported inventory fingerprint source'', 1;
END;
SELECT @payload = ISNULL(CONVERT(nvarchar(max), (
SELECT cargo.cargo_space_id AS [@space_id], cargo.status AS [@status],
cargo.locked AS [@locked], cargo.tray_code AS [@tray_code],
cargo.group_coding AS [@depth], cargo.the_platoon AS [@platoon],
cargo.the_column AS [@column], cargo.the_floor AS [@floor],
topology.lane_depth AS [@lane_depth], topology.base_zone AS [@base_zone],
ISNULL(role.role, ''STORAGE'') AS [@role]
FROM dbo.bas_cargo_space cargo
LEFT JOIN alg.space_topology topology ON topology.space_id = cargo.cargo_space_id
LEFT JOIN alg.space_role role ON role.space_id = cargo.cargo_space_id
WHERE cargo.warehouse_id = @warehouse_id AND cargo.group_coding_no = @lane_code
ORDER BY TRY_CONVERT(int, cargo.group_coding), cargo.cargo_space_id
FOR XML PATH(''space''), ROOT(''spaces''), TYPE
)), N'''');
SET @payload = @payload + N''|INVENTORY|'' + ISNULL(CONVERT(nvarchar(max), (
SELECT stock.stock_id AS [@stock_id], stock.space_id AS [@space_id],
stock.tray_code AS [@tray_code], stock.project_no AS [@project_no],
stock.product_no AS [@product_no], stock.lot_no AS [@lot_no],
stock.inbound_time AS [@inbound_time],
stock.quantity AS [@quantity], stock.out_quantity AS [@out_quantity]
FROM #fingerprint_inventory stock
ORDER BY stock.space_id, stock.stock_id
FOR XML PATH(''item''), ROOT(''inventory''), TYPE
)), N'''');
SET @payload = @payload + N''|ACTIVE_JOBS|'' + ISNULL(CONVERT(nvarchar(max), (
SELECT job.job_id AS [@job_id], job.task_no AS [@task_no], job.tray_code AS [@tray_code],
job.source_space_id AS [@source_space_id], job.target_cargo_space_id AS [@target_space_id],
job.status AS [@status], job.in_or_out AS [@in_or_out],
job.create_time_raw AS [@create_time]
FROM alg.v_active_job job
WHERE job.warehouse_id = @warehouse_id AND job.source_lane_code = @lane_code
ORDER BY job.job_id
FOR XML PATH(''job''), ROOT(''jobs''), TYPE
)), N'''');
SET @payload = @payload + N''|PROJECT_POLICY|'' + ISNULL(CONVERT(nvarchar(max), (
SELECT policy.project_no AS [@project_no], policy.is_isolated AS [@is_isolated],
policy.is_common AS [@is_common], policy.allow_mixed AS [@allow_mixed]
FROM alg.project_policy policy
ORDER BY policy.project_no
FOR XML PATH(''project''), ROOT(''project_policy''), TYPE
)), N'''');
SET @payload = @payload + N''|WAREHOUSE_POLICY|'' + ISNULL(CONVERT(nvarchar(max), (
SELECT policy.warehouse_id AS [@warehouse_id], policy.target_fill_rate AS [@target_fill_rate],
policy.fifo_tolerance_hours AS [@fifo_tolerance_hours]
FROM alg.warehouse_policy policy
WHERE policy.warehouse_id = @warehouse_id
FOR XML PATH(''warehouse''), ROOT(''warehouse_policy''), TYPE
)), N'''');
SET @payload = @payload + N''|RESERVATIONS|'' + ISNULL(CONVERT(nvarchar(max), (
SELECT reservation.reservation_id AS [@reservation_id], reservation.space_id AS [@space_id],
reservation.project_no AS [@project_no], reservation.status AS [@status]
FROM alg.reservation reservation
WHERE reservation.warehouse_id = @warehouse_id AND reservation.lane_code = @lane_code
AND reservation.status = ''ACTIVE''
ORDER BY reservation.space_id, reservation.reservation_id
FOR XML PATH(''reservation''), ROOT(''reservations''), TYPE
)), N'''');
SET @semantic_fingerprint = HASHBYTES(''SHA2_256'', CONVERT(varbinary(max), @payload));
END;');
GO
IF OBJECT_ID(N'alg.usp_rebuild_lane_state', N'P') IS NOT NULL DROP PROCEDURE alg.usp_rebuild_lane_state;
GO
EXEC(N'
CREATE PROCEDURE alg.usp_rebuild_lane_state
@warehouse_id varchar(80) = NULL,
@lane_code varchar(80) = NULL
AS
BEGIN
SET NOCOUNT ON;
SELECT warehouse_id, lane_code,
COUNT(*) AS space_count,
SUM(CASE WHEN space_status = ''2'' OR stock_detail_count > 0 THEN 1 ELSE 0 END) AS occupied_count,
SUM(CASE WHEN inbound_available = 1 THEN 1 ELSE 0 END) AS available_count,
MAX(CASE WHEN pending_outbound = 1 THEN 1 ELSE 0 END) AS pending_outbound,
MAX(CASE WHEN has_gap = 1 THEN 1 ELSE 0 END) AS has_gap,
CAST(NULL AS varbinary(32)) AS semantic_fingerprint
INTO #source_state
FROM alg.v_space_state
WHERE (@warehouse_id IS NULL OR warehouse_id = @warehouse_id)
AND (@lane_code IS NULL OR lane_code = @lane_code)
GROUP BY warehouse_id, lane_code;
DECLARE @fingerprint_warehouse varchar(80), @fingerprint_lane varchar(80),
@fingerprint varbinary(32);
DECLARE fingerprint_cursor CURSOR LOCAL FAST_FORWARD FOR
SELECT warehouse_id, lane_code FROM #source_state ORDER BY warehouse_id, lane_code;
OPEN fingerprint_cursor;
FETCH NEXT FROM fingerprint_cursor INTO @fingerprint_warehouse, @fingerprint_lane;
WHILE @@FETCH_STATUS = 0
BEGIN
EXEC alg.usp_compute_lane_semantic_fingerprint
@warehouse_id = @fingerprint_warehouse,
@lane_code = @fingerprint_lane,
@semantic_fingerprint = @fingerprint OUTPUT,
@inventory_source = ''SNAPSHOT'';
UPDATE #source_state SET semantic_fingerprint = @fingerprint
WHERE warehouse_id = @fingerprint_warehouse AND lane_code = @fingerprint_lane;
FETCH NEXT FROM fingerprint_cursor INTO @fingerprint_warehouse, @fingerprint_lane;
END;
CLOSE fingerprint_cursor;
DEALLOCATE fingerprint_cursor;
MERGE alg.lane_state WITH (HOLDLOCK) AS target
USING #source_state AS source
ON target.warehouse_id = source.warehouse_id AND target.lane_code = source.lane_code
WHEN MATCHED AND (target.space_count <> source.space_count OR
target.occupied_count <> source.occupied_count OR
target.available_count <> source.available_count OR
target.pending_outbound <> source.pending_outbound OR
target.has_gap <> source.has_gap OR
target.semantic_fingerprint IS NULL OR
target.semantic_fingerprint <> source.semantic_fingerprint)
THEN UPDATE SET space_count = source.space_count,
occupied_count = source.occupied_count,
available_count = source.available_count,
pending_outbound = source.pending_outbound,
has_gap = source.has_gap,
semantic_fingerprint = source.semantic_fingerprint,
rebuilt_at = SYSUTCDATETIME()
WHEN NOT MATCHED BY TARGET THEN
INSERT (warehouse_id, lane_code, space_count, occupied_count, available_count,
pending_outbound, has_gap, semantic_fingerprint)
VALUES (source.warehouse_id, source.lane_code, source.space_count, source.occupied_count,
source.available_count, source.pending_outbound, source.has_gap, source.semantic_fingerprint)
WHEN NOT MATCHED BY SOURCE
AND (@warehouse_id IS NULL OR target.warehouse_id = @warehouse_id)
AND (@lane_code IS NULL OR target.lane_code = @lane_code)
THEN DELETE;
END;');
GO
IF OBJECT_ID(N'alg.usp_refresh_dirty_lanes', N'P') IS NOT NULL DROP PROCEDURE alg.usp_refresh_dirty_lanes;
GO
EXEC(N'
CREATE PROCEDURE alg.usp_refresh_dirty_lanes
@warehouse_id varchar(80)
AS
BEGIN
SET NOCOUNT ON;
IF NULLIF(LTRIM(RTRIM(@warehouse_id)), '''') IS NULL
THROW 50002, ''warehouse_id is required'', 1;
EXEC alg.usp_rebuild_space_topology @warehouse_id = @warehouse_id;
EXEC alg.usp_sync_inventory_snapshot @warehouse_id = @warehouse_id;
EXEC alg.usp_rebuild_lane_state @warehouse_id = @warehouse_id;
END;');
GO
IF NOT EXISTS (SELECT 1 FROM alg.space_topology)
AND NOT EXISTS (SELECT 1 FROM alg.inventory_item_snapshot)
AND NOT EXISTS (SELECT 1 FROM alg.lane_state)
BEGIN
EXEC alg.usp_rebuild_space_topology;
EXEC alg.usp_sync_inventory_snapshot;
EXEC alg.usp_rebuild_lane_state;
END;
GO

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,51 @@
/*
Removes only the FT546 shadow package. Export alg.decision_log first.
No Legacy WMS business row is modified by this script.
*/
SET NOCOUNT ON;
IF OBJECT_ID(N'alg.usp_refresh_dirty_lanes', N'P') IS NOT NULL
DROP PROCEDURE alg.usp_refresh_dirty_lanes;
IF OBJECT_ID(N'alg.usp_rebuild_lane_state', N'P') IS NOT NULL
DROP PROCEDURE alg.usp_rebuild_lane_state;
IF OBJECT_ID(N'alg.usp_compute_lane_semantic_fingerprint', N'P') IS NOT NULL
DROP PROCEDURE alg.usp_compute_lane_semantic_fingerprint;
IF OBJECT_ID(N'alg.usp_sync_inventory_snapshot', N'P') IS NOT NULL
DROP PROCEDURE alg.usp_sync_inventory_snapshot;
IF OBJECT_ID(N'alg.usp_rebuild_space_topology', N'P') IS NOT NULL
DROP PROCEDURE alg.usp_rebuild_space_topology;
GO
IF OBJECT_ID(N'alg.v_space_state', N'V') IS NOT NULL
DROP VIEW alg.v_space_state;
IF OBJECT_ID(N'alg.v_active_job', N'V') IS NOT NULL
DROP VIEW alg.v_active_job;
IF OBJECT_ID(N'alg.v_inventory_item', N'V') IS NOT NULL
DROP VIEW alg.v_inventory_item;
GO
IF OBJECT_ID(N'alg.decision_log', N'U') IS NOT NULL
DROP TABLE alg.decision_log;
IF OBJECT_ID(N'alg.deep_workflow_lock', N'U') IS NOT NULL
DROP TABLE alg.deep_workflow_lock;
IF OBJECT_ID(N'alg.reservation', N'U') IS NOT NULL
DROP TABLE alg.reservation;
IF OBJECT_ID(N'alg.lane_state', N'U') IS NOT NULL
DROP TABLE alg.lane_state;
IF OBJECT_ID(N'alg.space_inventory_summary', N'U') IS NOT NULL
DROP TABLE alg.space_inventory_summary;
IF OBJECT_ID(N'alg.inventory_item_snapshot', N'U') IS NOT NULL
DROP TABLE alg.inventory_item_snapshot;
IF OBJECT_ID(N'alg.space_topology', N'U') IS NOT NULL
DROP TABLE alg.space_topology;
IF OBJECT_ID(N'alg.space_role', N'U') IS NOT NULL
DROP TABLE alg.space_role;
IF OBJECT_ID(N'alg.project_policy', N'U') IS NOT NULL
DROP TABLE alg.project_policy;
IF OBJECT_ID(N'alg.warehouse_policy', N'U') IS NOT NULL
DROP TABLE alg.warehouse_policy;
GO
IF SCHEMA_ID(N'alg') IS NOT NULL
EXEC(N'DROP SCHEMA alg');
GO

View File

@@ -1,53 +0,0 @@
/* Removes only objects owned by schema alg. Run independently of install.sql. */
SET NOCOUNT ON;
IF OBJECT_ID(N'dbo.trg_alg_dirty_bas_stock', N'TR') IS NOT NULL DROP TRIGGER dbo.trg_alg_dirty_bas_stock;
IF OBJECT_ID(N'dbo.trg_alg_dirty_bas_cargo_space', N'TR') IS NOT NULL DROP TRIGGER dbo.trg_alg_dirty_bas_cargo_space;
IF OBJECT_ID(N'dbo.trg_alg_dirty_mydo_job', N'TR') IS NOT NULL DROP TRIGGER dbo.trg_alg_dirty_mydo_job;
IF OBJECT_ID(N'alg.trg_alg_dirty_space_role', N'TR') IS NOT NULL DROP TRIGGER alg.trg_alg_dirty_space_role;
IF OBJECT_ID(N'alg.trg_alg_dirty_project_policy', N'TR') IS NOT NULL DROP TRIGGER alg.trg_alg_dirty_project_policy;
IF OBJECT_ID(N'alg.trg_alg_dirty_warehouse_policy', N'TR') IS NOT NULL DROP TRIGGER alg.trg_alg_dirty_warehouse_policy;
GO
IF OBJECT_ID(N'alg.usp_refresh_dirty_lanes', N'P') IS NOT NULL DROP PROCEDURE alg.usp_refresh_dirty_lanes;
IF OBJECT_ID(N'alg.usp_mark_dirty_lane', N'P') IS NOT NULL DROP PROCEDURE alg.usp_mark_dirty_lane;
IF OBJECT_ID(N'alg.usp_claim_tidy_lane', N'P') IS NOT NULL DROP PROCEDURE alg.usp_claim_tidy_lane;
IF OBJECT_ID(N'alg.usp_release_tidy_lane', N'P') IS NOT NULL DROP PROCEDURE alg.usp_release_tidy_lane;
IF OBJECT_ID(N'alg.usp_complete_move_job', N'P') IS NOT NULL DROP PROCEDURE alg.usp_complete_move_job;
IF OBJECT_ID(N'alg.usp_sync_replay_sequences', N'P') IS NOT NULL DROP PROCEDURE alg.usp_sync_replay_sequences;
IF OBJECT_ID(N'alg.usp_release_reservation', N'P') IS NOT NULL DROP PROCEDURE alg.usp_release_reservation;
IF OBJECT_ID(N'alg.usp_try_reserve_inbound', N'P') IS NOT NULL DROP PROCEDURE alg.usp_try_reserve_inbound;
IF OBJECT_ID(N'alg.usp_apply_space_role', N'P') IS NOT NULL DROP PROCEDURE alg.usp_apply_space_role;
IF OBJECT_ID(N'alg.usp_release_deep_workflow_lock', N'P') IS NOT NULL DROP PROCEDURE alg.usp_release_deep_workflow_lock;
IF OBJECT_ID(N'alg.usp_acquire_deep_workflow_lock', N'P') IS NOT NULL DROP PROCEDURE alg.usp_acquire_deep_workflow_lock;
IF OBJECT_ID(N'alg.usp_rebuild_lane_state', N'P') IS NOT NULL DROP PROCEDURE alg.usp_rebuild_lane_state;
IF OBJECT_ID(N'alg.usp_compute_lane_semantic_fingerprint', N'P') IS NOT NULL DROP PROCEDURE alg.usp_compute_lane_semantic_fingerprint;
IF OBJECT_ID(N'alg.usp_sync_inventory_snapshot', N'P') IS NOT NULL DROP PROCEDURE alg.usp_sync_inventory_snapshot;
IF OBJECT_ID(N'alg.usp_rebuild_space_topology', N'P') IS NOT NULL DROP PROCEDURE alg.usp_rebuild_space_topology;
GO
IF OBJECT_ID(N'alg.v_space_state', N'V') IS NOT NULL DROP VIEW alg.v_space_state;
IF OBJECT_ID(N'alg.v_active_job', N'V') IS NOT NULL DROP VIEW alg.v_active_job;
IF OBJECT_ID(N'alg.v_inventory_item', N'V') IS NOT NULL DROP VIEW alg.v_inventory_item;
GO
IF OBJECT_ID(N'alg.outbound_workflow_space', N'U') IS NOT NULL DROP TABLE alg.outbound_workflow_space;
IF OBJECT_ID(N'alg.outbound_workflow_step', N'U') IS NOT NULL DROP TABLE alg.outbound_workflow_step;
IF OBJECT_ID(N'alg.outbound_workflow', N'U') IS NOT NULL DROP TABLE alg.outbound_workflow;
IF OBJECT_ID(N'alg.tidy_callback_event', N'U') IS NOT NULL DROP TABLE alg.tidy_callback_event;
IF OBJECT_ID(N'alg.tidy_space_lock', N'U') IS NOT NULL DROP TABLE alg.tidy_space_lock;
IF OBJECT_ID(N'alg.tidy_step', N'U') IS NOT NULL DROP TABLE alg.tidy_step;
IF OBJECT_ID(N'alg.tidy_workflow', N'U') IS NOT NULL DROP TABLE alg.tidy_workflow;
IF OBJECT_ID(N'alg.tidy_lane_queue', N'U') IS NOT NULL DROP TABLE alg.tidy_lane_queue;
IF OBJECT_ID(N'alg.tidy_mode', N'U') IS NOT NULL DROP TABLE alg.tidy_mode;
IF OBJECT_ID(N'alg.decision_log', N'U') IS NOT NULL DROP TABLE alg.decision_log;
IF OBJECT_ID(N'alg.deep_workflow_lock', N'U') IS NOT NULL DROP TABLE alg.deep_workflow_lock;
IF OBJECT_ID(N'alg.reservation', N'U') IS NOT NULL DROP TABLE alg.reservation;
IF OBJECT_ID(N'alg.dirty_lane_queue', N'U') IS NOT NULL DROP TABLE alg.dirty_lane_queue;
IF OBJECT_ID(N'alg.lane_state', N'U') IS NOT NULL DROP TABLE alg.lane_state;
IF OBJECT_ID(N'alg.space_inventory_summary', N'U') IS NOT NULL DROP TABLE alg.space_inventory_summary;
IF OBJECT_ID(N'alg.inventory_item_snapshot', N'U') IS NOT NULL DROP TABLE alg.inventory_item_snapshot;
IF OBJECT_ID(N'alg.space_topology', N'U') IS NOT NULL DROP TABLE alg.space_topology;
IF OBJECT_ID(N'alg.space_role', N'U') IS NOT NULL DROP TABLE alg.space_role;
IF OBJECT_ID(N'alg.project_policy', N'U') IS NOT NULL DROP TABLE alg.project_policy;
IF OBJECT_ID(N'alg.warehouse_policy', N'U') IS NOT NULL DROP TABLE alg.warehouse_policy;
GO
IF SCHEMA_ID(N'alg') IS NOT NULL EXEC(N'DROP SCHEMA alg');
GO

View File

@@ -0,0 +1,143 @@
package com.ruoyi.algorithm.repository;
import org.junit.jupiter.api.Test;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.LinkedHashSet;
import java.util.Locale;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import static org.assertj.core.api.Assertions.assertThat;
class ShadowSqlServerMigrationScriptTest {
private static final Set<String> ALLOWED_INSTALL_OBJECTS = Set.of(
"table alg.warehouse_policy",
"table alg.project_policy",
"table alg.space_role",
"table alg.space_topology",
"table alg.inventory_item_snapshot",
"table alg.space_inventory_summary",
"table alg.lane_state",
"table alg.reservation",
"table alg.deep_workflow_lock",
"table alg.decision_log",
"view alg.v_inventory_item",
"view alg.v_active_job",
"view alg.v_space_state",
"procedure alg.usp_rebuild_space_topology",
"procedure alg.usp_sync_inventory_snapshot",
"procedure alg.usp_compute_lane_semantic_fingerprint",
"procedure alg.usp_rebuild_lane_state",
"procedure alg.usp_refresh_dirty_lanes");
@Test
void installCreatesOnlyAllowListedShadowObjects() throws IOException {
String install = resource("db/sqlserver/install-shadow.sql");
assertThat(createdObjects(install)).containsExactlyInAnyOrderElementsOf(ALLOWED_INSTALL_OBJECTS);
assertThat(install.toLowerCase(Locale.ROOT)).doesNotContain(
"usp_try_reserve_inbound",
"usp_release_reservation",
"usp_acquire_deep_workflow_lock",
"usp_release_deep_workflow_lock",
"usp_complete_move_job",
"usp_sync_replay_sequences",
"tidy_",
"outbound_workflow",
"proc_yk_complete");
}
@Test
void installNeverMutatesLegacyBusinessTables() throws IOException {
String install = normalize(resource("db/sqlserver/install-shadow.sql"));
assertThat(install).doesNotContain(
"insert into dbo.",
"update dbo.",
"delete from dbo.",
"merge dbo.",
"create trigger",
"on dbo.",
"dirty_lane_queue");
}
@Test
void installAndUninstallOwnExactlyTheSameObjects() throws IOException {
Set<String> installed = createdObjects(resource("db/sqlserver/install-shadow.sql"));
Set<String> removed = droppedObjects(resource("db/sqlserver/uninstall-shadow.sql"));
assertThat(removed).containsExactlyInAnyOrderElementsOf(installed);
}
@Test
void refreshPerformsFullSynchronizationWithoutLegacyTriggers() throws IOException {
String install = normalize(resource("db/sqlserver/install-shadow.sql"));
String refresh = install.substring(install.indexOf("create procedure alg.usp_refresh_dirty_lanes"));
assertThat(refresh).contains(
"exec alg.usp_rebuild_space_topology",
"exec alg.usp_sync_inventory_snapshot",
"exec alg.usp_rebuild_lane_state");
assertThat(refresh).doesNotContain("dirty_lane_queue", "tidy_lane_queue");
assertThat(install).contains(
"exec alg.usp_rebuild_space_topology",
"exec alg.usp_sync_inventory_snapshot",
"exec alg.usp_rebuild_lane_state");
}
@Test
void diagnosticsSupportsSafetyChecksAndDecisionExport() throws IOException {
String diagnostics = normalize(resource("db/sqlserver/diagnostics-shadow.sql"));
assertThat(diagnostics).contains(
"from alg.decision_log",
"inbound_compare",
"outbound_compare",
"from sys.dm_tran_locks",
"from dbo.bas_cargo_space",
"from dbo.bas_stock",
"from dbo.mydo_job");
assertThat(diagnostics).doesNotContain("insert ", "update ", "delete ", "merge ");
}
@Test
void installationAndDiagnosticsReadLegacyWithoutTakingSharedLocks() throws IOException {
String readUncommitted = "set transaction isolation level read uncommitted";
assertThat(normalize(resource("db/sqlserver/install-shadow.sql"))).contains(readUncommitted);
assertThat(normalize(resource("db/sqlserver/diagnostics-shadow.sql"))).contains(readUncommitted);
}
private Set<String> createdObjects(String sql) {
return objects(sql, Pattern.compile(
"(?i)create\\s+(table|view|procedure|trigger)\\s+([a-z0-9_]+\\.[a-z0-9_]+)"));
}
private Set<String> droppedObjects(String sql) {
return objects(sql, Pattern.compile(
"(?i)drop\\s+(table|view|procedure|trigger)\\s+([a-z0-9_]+\\.[a-z0-9_]+)"));
}
private Set<String> objects(String sql, Pattern pattern) {
Matcher matcher = pattern.matcher(sql);
Set<String> objects = new LinkedHashSet<>();
while (matcher.find()) {
objects.add((matcher.group(1) + " " + matcher.group(2)).toLowerCase(Locale.ROOT));
}
return objects;
}
private String normalize(String sql) {
return sql.toLowerCase(Locale.ROOT).replaceAll("\\s+", " ");
}
private String resource(String name) throws IOException {
try (var stream = getClass().getClassLoader().getResourceAsStream(name)) {
assertThat(stream).as("resource %s", name).isNotNull();
return new String(stream.readAllBytes(), StandardCharsets.UTF_8);
}
}
}

View File

@@ -1,200 +0,0 @@
package com.ruoyi.algorithm.repository;
import org.junit.jupiter.api.Test;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import static org.assertj.core.api.Assertions.assertThat;
class SqlServerMigrationScriptTest {
@Test
void installDefinesPersistentOutboundWorkflowSchema() throws IOException {
String install = resource("db/sqlserver/install.sql");
assertThat(install).contains(
"CREATE TABLE alg.outbound_workflow (",
"CREATE TABLE alg.outbound_workflow_step (",
"CREATE TABLE alg.outbound_workflow_space (",
"CK_alg_outbound_workflow_status",
"CK_alg_outbound_workflow_step_phase",
"CK_alg_outbound_workflow_step_status",
"UX_alg_outbound_workflow_active_request",
"UX_alg_outbound_workflow_step_wms_job",
"UX_alg_outbound_workflow_space_active");
}
@Test
void installDefinesDirtyLaneQueueRefreshAndAllMutationSources() throws IOException {
String install = resource("db/sqlserver/install.sql");
assertThat(install).contains(
"CREATE TABLE alg.dirty_lane_queue (",
"CREATE PROCEDURE alg.usp_mark_dirty_lane",
"CREATE PROCEDURE alg.usp_refresh_dirty_lanes",
"CREATE TRIGGER dbo.trg_alg_dirty_bas_stock",
"CREATE TRIGGER dbo.trg_alg_dirty_bas_cargo_space",
"CREATE TRIGGER dbo.trg_alg_dirty_mydo_job",
"CREATE TRIGGER alg.trg_alg_dirty_space_role",
"CREATE TRIGGER alg.trg_alg_dirty_project_policy",
"CREATE TRIGGER alg.trg_alg_dirty_warehouse_policy");
}
@Test
void installDefinesPersistentTidyRuntimeSchemaAndConstraints() throws IOException {
String install = resource("db/sqlserver/install.sql");
assertThat(install).contains(
"CREATE TABLE alg.tidy_mode (",
"CREATE TABLE alg.tidy_lane_queue (",
"CREATE TABLE alg.tidy_workflow (",
"CREATE TABLE alg.tidy_step (",
"CREATE TABLE alg.tidy_space_lock (",
"CREATE TABLE alg.tidy_callback_event (",
"CK_alg_tidy_mode_effective_state",
"CK_alg_tidy_workflow_origin",
"CK_alg_tidy_workflow_status",
"CK_alg_tidy_space_lock_resource_type",
"UX_alg_tidy_space_lock_active",
"UX_alg_tidy_callback_event_key",
"UX_alg_tidy_workflow_active_auto",
"FK_alg_tidy_callback_event_step_workflow");
}
@Test
void installDefinesVersionedTidyLaneClaimAndSequenceSynchronization() throws IOException {
String install = resource("db/sqlserver/install.sql");
assertThat(install).contains(
"CREATE PROCEDURE alg.usp_claim_tidy_lane",
"@eligible_lane_codes varchar(max) = NULL",
"STRING_SPLIT(@eligible_lane_codes, '','')",
"CREATE PROCEDURE alg.usp_release_tidy_lane",
"CREATE PROCEDURE alg.usp_sync_replay_sequences",
"sp_getapplock",
"CK202607210002");
}
@Test
void installCompletesAlgorithmOwnedMoveJobsWithoutChangingTheLegacyProcedure() throws IOException {
String install = resource("db/sqlserver/install.sql");
assertThat(install).contains(
"CREATE PROCEDURE alg.usp_complete_move_job",
"in_or_out = ''28''",
"UPDATE dbo.bas_stock",
"INSERT INTO dbo.mydo_job_finish",
"DELETE FROM dbo.mydo_job");
assertThat(install).doesNotContain("ALTER PROCEDURE dbo.proc_yk_complete");
}
@Test
void dirtyLaneRefreshPublishesRebuiltLanesToPersistentTidyQueue() throws IOException {
String install = resource("db/sqlserver/install.sql");
String procedure = install.substring(
install.indexOf("CREATE PROCEDURE alg.usp_refresh_dirty_lanes"),
install.indexOf("IF OBJECT_ID(N'alg.usp_try_reserve_inbound'"));
int rebuildLaneState = procedure.lastIndexOf("EXEC alg.usp_rebuild_lane_state");
int publishTidyLane = procedure.indexOf("MERGE alg.tidy_lane_queue WITH (HOLDLOCK)");
assertThat(rebuildLaneState).isGreaterThanOrEqualTo(0);
assertThat(publishTidyLane).isGreaterThan(rebuildLaneState);
assertThat(procedure).contains(
"dirty_version = target.dirty_version + 1",
"queue_status = ''PENDING''");
}
@Test
void dirtyLaneRefreshReleasesQueueLocksBeforeReadingWarehouseTablesAndRequeuesFailures()
throws IOException {
String install = resource("db/sqlserver/install.sql");
String procedure = install.substring(
install.indexOf("CREATE PROCEDURE alg.usp_refresh_dirty_lanes"),
install.indexOf("IF OBJECT_ID(N'alg.usp_try_reserve_inbound'"));
int claimDelete = procedure.indexOf("DELETE queue");
int claimCommit = procedure.indexOf("COMMIT TRANSACTION;", claimDelete);
int warehouseRead = procedure.indexOf("FROM dbo.bas_cargo_space", claimDelete);
assertThat(claimDelete).isGreaterThanOrEqualTo(0);
assertThat(claimCommit).isBetween(claimDelete + 1, warehouseRead - 1);
assertThat(procedure).contains(
"BEGIN TRY",
"BEGIN CATCH",
"MERGE alg.dirty_lane_queue WITH (HOLDLOCK) AS target",
"THROW;");
}
@Test
void topologyRebuildExcludesUnconfiguredCargoSpacesAndDiagnosticsReportsThem()
throws IOException {
String install = resource("db/sqlserver/install.sql");
String diagnostics = resource("db/sqlserver/diagnostics.sql");
String procedure = install.substring(
install.indexOf("CREATE PROCEDURE alg.usp_rebuild_space_topology"),
install.indexOf("IF OBJECT_ID(N'alg.usp_sync_inventory_snapshot'"));
assertThat(procedure).contains(
"NULLIF(LTRIM(RTRIM(group_coding_no)), '''') IS NOT NULL",
"TRY_CONVERT(int, group_coding) IS NOT NULL");
assertThat(diagnostics).contains(
"excluded_from_algorithm_topology_count",
"group_coding_no IS NULL OR LTRIM(RTRIM(group_coding_no)) = ''",
"TRY_CONVERT(int, group_coding) IS NULL");
}
@Test
void diagnosticsAndUninstallCoverNewRuntimeObjects() throws IOException {
String diagnostics = resource("db/sqlserver/diagnostics.sql");
String uninstall = resource("db/sqlserver/uninstall.sql");
assertThat(diagnostics).contains(
"FROM alg.dirty_lane_queue",
"FROM alg.outbound_workflow",
"FROM alg.tidy_mode",
"FROM alg.tidy_lane_queue",
"FROM alg.tidy_workflow",
"FROM alg.tidy_space_lock",
"PAUSED_RECOVERY");
assertThat(uninstall).contains(
"DROP TRIGGER dbo.trg_alg_dirty_bas_stock",
"DROP PROCEDURE alg.usp_refresh_dirty_lanes",
"DROP PROCEDURE alg.usp_claim_tidy_lane",
"DROP PROCEDURE alg.usp_release_tidy_lane",
"DROP PROCEDURE alg.usp_sync_replay_sequences",
"DROP TABLE alg.tidy_callback_event",
"DROP TABLE alg.tidy_space_lock",
"DROP TABLE alg.tidy_step",
"DROP TABLE alg.tidy_workflow",
"DROP TABLE alg.tidy_lane_queue",
"DROP TABLE alg.tidy_mode",
"DROP TABLE alg.outbound_workflow_space",
"DROP TABLE alg.outbound_workflow_step",
"DROP TABLE alg.outbound_workflow",
"DROP TABLE alg.dirty_lane_queue");
}
@Test
void repeatedInstallDoesNotDiscardPendingDirtyLanes() throws IOException {
String install = resource("db/sqlserver/install.sql");
String initialization = install.substring(install.lastIndexOf("IF NOT EXISTS (SELECT 1 FROM alg.space_topology)"));
assertThat(initialization).contains(
"AND NOT EXISTS (SELECT 1 FROM alg.inventory_item_snapshot)",
"AND NOT EXISTS (SELECT 1 FROM alg.lane_state)");
assertThat(initialization.indexOf("DELETE FROM alg.dirty_lane_queue"))
.isLessThan(initialization.indexOf("END;"));
assertThat(initialization.substring(initialization.indexOf("END;") + 4))
.doesNotContain("DELETE FROM alg.dirty_lane_queue");
}
private String resource(String name) throws IOException {
try (var stream = getClass().getClassLoader().getResourceAsStream(name)) {
if (stream == null) {
throw new IOException("Missing test resource " + name);
}
return new String(stream.readAllBytes(), StandardCharsets.UTF_8);
}
}
}