feat: add shadow-only SQL Server package
This commit is contained in:
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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
@@ -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
|
||||
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user