/* Customer-specific review draft for hr_4011 leave writes in the lserp_AI compatibility-level-100 database. THIS FILE IS INERT: 1. SET NOEXEC ON prevents CREATE PROCEDURE from being applied. 2. @customer_dba_reviewed is deliberately fixed to 0. 3. The script must never be edited into a production deployment artifact. A customer DBA must create and sign a separate script after reviewing every dependency below. Reviewed design assumptions that still require customer DBA acceptance: - p_lserp_agent_workflow_read_compat100 is the approved fixed-parameter read contract; - hr_4011 is a newVer=1 base module backed by HR_EmpLeaveAloneTab; - p_BaseSave70 is the exact ERP create path and generates hr_ela_no through the configured document-number rules; - p_baseApply is the exact ERP submit path; - p_SubsysPurviewTab.hrPurview is the customer menu permission source, menu 16629 is hr_4011, and only the edit token (without the read-only "|" suffix) authorizes a write; - p_agent_command_idempotency, p_agent_business_audit and p_agent_integration_outbox were created from 001_agent_business_idempotency.sql; - the legacy procedures may emit diagnostic result sets; the trusted ERP gateway discards those and accepts exactly one result set whose ten columns exactly match the fixed BusinessWriteResult contract; - the fixed INSERT column list below matches the approved UI save payload, including nullable department/post and secondary-hours fields that are intentionally omitted. No table name, column name, SQL fragment or procedure name is accepted from the Agent. The only dynamic SQL string is an internally constructed, fixed-template @base_sql required by the legacy p_BaseSave70 contract. Every text value is length checked and escaped inside this wrapper. */ SET NOEXEC ON; GO CREATE PROCEDURE dbo.p_lserp_agent_workflow_write_leave_compat100 @action VARCHAR(64), @module_code NVARCHAR(64), @account_book NVARCHAR(64), @subsystem_id NVARCHAR(32), @user_id NVARCHAR(64), @correlation_id VARCHAR(128), @idempotency_key VARCHAR(128), @input_fingerprint CHAR(64), @employee_id NVARCHAR(64) = NULL, @leave_type_code NVARCHAR(64) = NULL, @flow_type_code NVARCHAR(64) = NULL, @start_local DATETIME = NULL, @end_local DATETIME = NULL, @requested_hours DECIMAL(18, 6) = NULL, @reason NVARCHAR(500) = NULL, @submit_after_save_intent BIT = NULL, @record_id NVARCHAR(128) = NULL AS BEGIN SET NOCOUNT ON; SET XACT_ABORT ON; DECLARE @customer_dba_reviewed BIT; SET @customer_dba_reviewed = 0; IF @customer_dba_reviewed <> 1 BEGIN RAISERROR(N'customer_dba_review_required', 16, 1); RETURN; END; IF DB_NAME() <> N'lserp_AI' OR @module_code <> N'hr_4011' OR @subsystem_id <> N'7' OR @action NOT IN ('create_draft', 'submit') OR NULLIF(LTRIM(RTRIM(@account_book)), N'') IS NULL OR NULLIF(LTRIM(RTRIM(@user_id)), N'') IS NULL OR @@TRANCOUNT < 1 OR XACT_STATE() <> 1 BEGIN RAISERROR(N'leave_write_scope_invalid', 16, 1); RETURN; END; IF @correlation_id IS NULL OR LEN(@correlation_id) < 8 OR LEN(@correlation_id) > 128 OR @correlation_id LIKE '%[^A-Za-z0-9_.:-]%' OR @idempotency_key IS NULL OR LEN(@idempotency_key) < 8 OR LEN(@idempotency_key) > 128 OR @idempotency_key LIKE '%[^A-Za-z0-9_.:-]%' OR @input_fingerprint IS NULL OR LEN(@input_fingerprint) <> 64 OR @input_fingerprint LIKE '%[^A-Fa-f0-9]%' BEGIN RAISERROR(N'leave_write_evidence_invalid', 16, 1); RETURN; END; DECLARE @context TABLE ( current_employee_id NVARCHAR(64) NOT NULL, can_apply_for_others BIT NOT NULL, now_local DATETIME NOT NULL ); INSERT INTO @context (current_employee_id, can_apply_for_others, now_local) EXEC dbo.p_lserp_agent_workflow_read_compat100 @workflow = 'leave', @action = 'context', @module_code = @module_code, @account_book = @account_book, @subsystem_id = @subsystem_id, @user_id = @user_id; IF (SELECT COUNT_BIG(*) FROM @context) <> 1 BEGIN RAISERROR(N'leave_context_contract_invalid', 16, 1); RETURN; END; DECLARE @current_employee_text NVARCHAR(64); DECLARE @can_apply_for_others BIT; DECLARE @now_local DATETIME; SELECT @current_employee_text = current_employee_id, @can_apply_for_others = can_apply_for_others, @now_local = now_local FROM @context; IF LEN(@current_employee_text) > 10 OR @current_employee_text LIKE N'%[^0-9]%' BEGIN RAISERROR(N'leave_context_contract_invalid', 16, 1); RETURN; END; DECLARE @current_employee_number DECIMAL(10, 0); DECLARE @current_employee_id INT; SET @current_employee_number = CONVERT(DECIMAL(10, 0), @current_employee_text); IF @current_employee_number < 1 OR @current_employee_number > 2147483647 BEGIN RAISERROR(N'leave_context_contract_invalid', 16, 1); RETURN; END; SET @current_employee_id = CONVERT(INT, @current_employee_number); IF @user_id IS NULL OR LEN(@user_id) > 10 OR @user_id LIKE N'%[^0-9]%' BEGIN RAISERROR(N'leave_user_scope_invalid', 16, 1); RETURN; END; DECLARE @user_number DECIMAL(10, 0); SET @user_number = CONVERT(DECIMAL(10, 0), @user_id); IF @user_number < 1 OR @user_number > 2147483647 OR CONVERT(INT, @user_number) <> @current_employee_id BEGIN RAISERROR(N'leave_user_scope_invalid', 16, 1); RETURN; END; DECLARE @operator_name NVARCHAR(50); SELECT @operator_name = CONVERT(NVARCHAR(50), EmployeeName) FROM dbo.p_employeetab WITH (HOLDLOCK) WHERE employeeid = @current_employee_id AND ISNULL(p_emp_status, '') <> N'离职'; IF @operator_name IS NULL BEGIN RAISERROR(N'leave_context_contract_invalid', 16, 1); RETURN; END; /* Read-only menu tokens end in "|". A write must have the exact edit token ",16629,"; the trusted WinForm authorizer performs the same check before confirmation, and this database check closes the time-of-check/use gap. */ DECLARE @leave_permission_text NVARCHAR(MAX); SET @leave_permission_text = N','; SELECT TOP (1) @leave_permission_text = N',' + CONVERT(NVARCHAR(MAX), ISNULL(hrPurview, '')) + N',' FROM dbo.p_SubsysPurviewTab WITH (UPDLOCK, HOLDLOCK) WHERE employeeid = @current_employee_id; IF (@current_employee_id <> 1 OR @operator_name <> N'管理员') AND CHARINDEX(N',16629,', @leave_permission_text) = 0 BEGIN RAISERROR(N'leave_write_permission_denied', 16, 1); RETURN; END; DECLARE @command_name VARCHAR(128); SET @command_name = CASE WHEN @action = 'create_draft' THEN 'hr.leave.create' ELSE 'hr.leave.submit' END; IF OBJECT_ID(N'dbo.p_agent_command_idempotency', N'U') IS NULL OR OBJECT_ID(N'dbo.p_agent_business_audit', N'U') IS NULL OR OBJECT_ID(N'dbo.p_agent_integration_outbox', N'U') IS NULL BEGIN RAISERROR(N'leave_agent_evidence_schema_missing', 16, 1); RETURN; END; DECLARE @application_lock_result INT; DECLARE @application_lock_resource NVARCHAR(255); SET @application_lock_resource = N'lserp-agent:leave:' + LEFT(@account_book, 40) + N':' + LEFT(@subsystem_id, 20) + N':' + LEFT(@user_id, 10) + N':' + @command_name + N':' + CONVERT(NVARCHAR(128), @idempotency_key); EXEC @application_lock_result = sys.sp_getapplock @Resource = @application_lock_resource, @LockMode = 'Exclusive', @LockOwner = 'Transaction', @LockTimeout = 10000; IF @application_lock_result < 0 BEGIN RAISERROR(N'leave_idempotency_lock_failed', 16, 1); RETURN; END; DECLARE @idempotency_id BIGINT; DECLARE @stored_fingerprint CHAR(64); DECLARE @stored_status TINYINT; DECLARE @stored_result_code VARCHAR(128); DECLARE @stored_record_id NVARCHAR(128); DECLARE @stored_transaction_id VARCHAR(128); DECLARE @stored_audit_id VARCHAR(128); SELECT @idempotency_id = id, @stored_fingerprint = input_fingerprint, @stored_status = status, @stored_result_code = result_code, @stored_record_id = record_id, @stored_transaction_id = transaction_evidence_id, @stored_audit_id = business_audit_id FROM dbo.p_agent_command_idempotency WITH (UPDLOCK, HOLDLOCK) WHERE account_book = @account_book AND subsystem_id = @subsystem_id AND user_id = @user_id AND command_name = @command_name AND idempotency_key = @idempotency_key; IF @idempotency_id IS NOT NULL AND @stored_fingerprint <> @input_fingerprint BEGIN SELECT CONVERT(BIT, 0) AS success, CONVERT(VARCHAR(128), 'idempotency_key_conflict') AS code, CONVERT(NVARCHAR(1000), N'同一幂等键已绑定不同的业务输入。') AS message, CONVERT(NVARCHAR(128), NULL) AS record_id, CONVERT(BIT, 0) AS needs_ui, CONVERT(BIT, 0) AS idempotency_replayed, @idempotency_key AS applied_idempotency_key, @input_fingerprint AS applied_input_fingerprint, CONVERT(VARCHAR(128), NULL) AS transaction_evidence_id, CONVERT(VARCHAR(128), NULL) AS business_audit_id; RETURN; END; IF @idempotency_id IS NOT NULL AND @stored_status = 1 BEGIN SELECT CONVERT(BIT, 1) AS success, @stored_result_code AS code, CONVERT(NVARCHAR(1000), N'已返回同一幂等请求的原事务结果。') AS message, @stored_record_id AS record_id, CONVERT(BIT, 0) AS needs_ui, CONVERT(BIT, 1) AS idempotency_replayed, @idempotency_key AS applied_idempotency_key, @input_fingerprint AS applied_input_fingerprint, @stored_transaction_id AS transaction_evidence_id, @stored_audit_id AS business_audit_id; RETURN; END; IF @idempotency_id IS NOT NULL BEGIN SELECT CONVERT(BIT, 0) AS success, CONVERT(VARCHAR(128), 'idempotency_request_not_replayable') AS code, CONVERT(NVARCHAR(1000), N'同一幂等请求尚未形成可重放的成功结果。') AS message, CONVERT(NVARCHAR(128), NULL) AS record_id, CONVERT(BIT, 0) AS needs_ui, CONVERT(BIT, 0) AS idempotency_replayed, @idempotency_key AS applied_idempotency_key, @input_fingerprint AS applied_input_fingerprint, CONVERT(VARCHAR(128), NULL) AS transaction_evidence_id, CONVERT(VARCHAR(128), NULL) AS business_audit_id; RETURN; END; INSERT INTO dbo.p_agent_command_idempotency ( account_book, subsystem_id, user_id, command_name, idempotency_key, input_fingerprint, status ) VALUES ( @account_book, @subsystem_id, @user_id, @command_name, @idempotency_key, @input_fingerprint, 0 ); SET @idempotency_id = CONVERT(BIGINT, SCOPE_IDENTITY()); DECLARE @effective_record_id NVARCHAR(128); DECLARE @result_code VARCHAR(128); DECLARE @result_message NVARCHAR(1000); IF @action = 'create_draft' BEGIN SET @employee_id = NULLIF(LTRIM(RTRIM(@employee_id)), N''); SET @leave_type_code = NULLIF(LTRIM(RTRIM(@leave_type_code)), N''); SET @flow_type_code = NULLIF(LTRIM(RTRIM(@flow_type_code)), N''); SET @reason = NULLIF(LTRIM(RTRIM(@reason)), N''); IF @employee_id IS NULL OR LEN(@employee_id) > 10 OR @employee_id LIKE N'%[^0-9]%' OR @leave_type_code IS NULL OR LEN(@leave_type_code) > 10 OR @leave_type_code LIKE N'%[^0-9]%' OR @flow_type_code IS NULL OR LEN(@flow_type_code) > 10 OR @flow_type_code LIKE N'%[^0-9]%' OR @start_local IS NULL OR @end_local IS NULL OR @end_local <= @start_local OR DATEDIFF(DAY, CONVERT(DATE, @start_local), CONVERT(DATE, @end_local)) > 31 OR @requested_hours IS NULL OR @requested_hours < 0 OR @requested_hours > 744 OR @reason IS NULL OR LEN(@reason) < 2 OR LEN(@reason) > 500 OR DATALENGTH(CONVERT(VARCHAR(8000), @reason)) > 500 OR CONVERT(NVARCHAR(500), CONVERT(VARCHAR(500), @reason)) <> @reason BEGIN RAISERROR(N'leave_create_input_invalid', 16, 1); RETURN; END; DECLARE @control_code INT; SET @control_code = 1; WHILE @control_code <= 31 BEGIN IF CHARINDEX(NCHAR(@control_code), @reason) > 0 BEGIN RAISERROR(N'leave_create_input_invalid', 16, 1); RETURN; END; SET @control_code = @control_code + 1; END; IF CHARINDEX(NCHAR(127), @reason) > 0 BEGIN RAISERROR(N'leave_create_input_invalid', 16, 1); RETURN; END; DECLARE @requested_employee_number DECIMAL(10, 0); DECLARE @leave_type_number DECIMAL(10, 0); DECLARE @flow_type_number DECIMAL(10, 0); SET @requested_employee_number = CONVERT(DECIMAL(10, 0), @employee_id); SET @leave_type_number = CONVERT(DECIMAL(10, 0), @leave_type_code); SET @flow_type_number = CONVERT(DECIMAL(10, 0), @flow_type_code); IF @requested_employee_number < 1 OR @requested_employee_number > 2147483647 OR @leave_type_number < 1 OR @leave_type_number > 2147483647 OR @flow_type_number < 1 OR @flow_type_number > 2147483647 OR (@requested_employee_number <> @current_employee_id AND ISNULL(@can_apply_for_others, 0) <> 1) BEGIN RAISERROR(N'leave_apply_for_others_denied', 16, 1); RETURN; END; IF @start_local < CONVERT(DATE, @now_local) BEGIN RAISERROR(N'leave_past_start_denied', 16, 1); RETURN; END; DECLARE @type_state TABLE (enabled BIT NOT NULL); INSERT INTO @type_state(enabled) EXEC dbo.p_lserp_agent_workflow_read_compat100 @workflow = 'leave', @action = 'type_enabled', @module_code = @module_code, @account_book = @account_book, @subsystem_id = @subsystem_id, @user_id = @user_id, @leave_type_code = @leave_type_code; IF (SELECT COUNT_BIG(*) FROM @type_state) <> 1 OR NOT EXISTS (SELECT 1 FROM @type_state WHERE enabled = 1) BEGIN RAISERROR(N'leave_type_not_enabled', 16, 1); RETURN; END; DECLARE @flow_state TABLE (enabled BIT NOT NULL); INSERT INTO @flow_state(enabled) EXEC dbo.p_lserp_agent_workflow_read_compat100 @workflow = 'leave', @action = 'flow_type_enabled', @module_code = @module_code, @account_book = @account_book, @subsystem_id = @subsystem_id, @user_id = @user_id, @flow_type_code = @flow_type_code; IF (SELECT COUNT_BIG(*) FROM @flow_state) <> 1 OR NOT EXISTS (SELECT 1 FROM @flow_state WHERE enabled = 1) BEGIN RAISERROR(N'leave_flow_type_not_enabled', 16, 1); RETURN; END; DECLARE @hours_state TABLE (hours DECIMAL(18, 6) NOT NULL); INSERT INTO @hours_state(hours) EXEC dbo.p_lserp_agent_workflow_read_compat100 @workflow = 'leave', @action = 'calculate_hours', @module_code = @module_code, @account_book = @account_book, @subsystem_id = @subsystem_id, @user_id = @user_id, @employee_id = @employee_id, @start_local = @start_local, @end_local = @end_local; IF (SELECT COUNT_BIG(*) FROM @hours_state) <> 1 BEGIN RAISERROR(N'leave_hours_contract_invalid', 16, 1); RETURN; END; DECLARE @calculated_hours DECIMAL(18, 6); SELECT @calculated_hours = hours FROM @hours_state; IF @calculated_hours <= 0 OR (@requested_hours > 0 AND ABS(@requested_hours - @calculated_hours) > 0.01) BEGIN RAISERROR(N'leave_hours_mismatch', 16, 1); RETURN; END; DECLARE @conflict_state TABLE (has_conflict BIT NOT NULL); INSERT INTO @conflict_state(has_conflict) EXEC dbo.p_lserp_agent_workflow_read_compat100 @workflow = 'leave', @action = 'has_conflict', @module_code = @module_code, @account_book = @account_book, @subsystem_id = @subsystem_id, @user_id = @user_id, @employee_id = @employee_id, @start_local = @start_local, @end_local = @end_local; IF (SELECT COUNT_BIG(*) FROM @conflict_state) <> 1 OR EXISTS (SELECT 1 FROM @conflict_state WHERE has_conflict = 1) BEGIN RAISERROR(N'leave_time_conflict', 16, 1); RETURN; END; DECLARE @employee_name NVARCHAR(50); DECLARE @employee_department_id INT; DECLARE @employee_post_id INT; SELECT @employee_name = CONVERT(NVARCHAR(50), EmployeeName), @employee_department_id = Departmentid, @employee_post_id = p_emp_postid FROM dbo.p_employeetab WITH (HOLDLOCK) WHERE employeeid = CONVERT(INT, @requested_employee_number) AND ISNULL(p_emp_status, '') <> N'离职'; IF @employee_name IS NULL OR @employee_department_id IS NULL OR @employee_post_id IS NULL OR DATALENGTH(CONVERT(VARCHAR(8000), @employee_name)) > 50 OR DATALENGTH(CONVERT(VARCHAR(8000), @operator_name)) > 10 BEGIN RAISERROR(N'leave_employee_not_available', 16, 1); RETURN; END; DECLARE @standard_day_hours DECIMAL(18, 6); DECLARE @calculated_days DECIMAL(18, 6); SELECT TOP (1) @standard_day_hours = CONVERT(DECIMAL(18, 6), CASE WHEN ISNULL(check_param.shichang, 0) = 0 THEN 7.5 ELSE check_param.shichang END) FROM dbo.HR_ScheduleTab AS schedule WITH (HOLDLOCK) LEFT JOIN dbo.P_SystemCheckParamTab AS check_param WITH (HOLDLOCK) ON schedule.HR_Schedule_billdocument_d = check_param.Billdocument_Id WHERE schedule.HR_Schedule_employeeid = CONVERT(INT, @requested_employee_number) AND schedule.HR_Schedule_Date <= CONVERT(DATE, @start_local) ORDER BY schedule.HR_Schedule_Date DESC; IF @standard_day_hours IS NULL OR @standard_day_hours <= 0 BEGIN RAISERROR(N'leave_day_hours_not_configured', 16, 1); RETURN; END; SET @calculated_days = ROUND( @calculated_hours / @standard_day_hours, 1); IF @calculated_days <= 0 OR @calculated_days > 366 BEGIN RAISERROR(N'leave_calculated_days_invalid', 16, 1); RETURN; END; DECLARE @number_lock_result INT; EXEC @number_lock_result = sys.sp_getapplock @Resource = N'lserp-agent:hr_4011:document-number', @LockMode = 'Exclusive', @LockOwner = 'Transaction', @LockTimeout = 10000; IF @number_lock_result < 0 BEGIN RAISERROR(N'leave_document_number_lock_failed', 16, 1); RETURN; END; /* Hold the identity tail until the outer transaction completes. The legacy save procedure can overwrite @msg after assigning the document number, so the created row is identified by the protected identity interval instead of trusting that output text. */ DECLARE @identity_before INT; SELECT @identity_before = ISNULL(MAX(hr_ela_id), 0) FROM dbo.HR_EmpLeaveAloneTab WITH (UPDLOCK, HOLDLOCK); DECLARE @base_sql NVARCHAR(MAX); SET @base_sql = N'INSERT INTO dbo.HR_EmpLeaveAloneTab ' + N'(hr_ela_empid,hr_ela_employeename,hr_ela_depid,hr_ela_postid,' + N'hr_ela_type,hr_ela_billtype,' + N'hr_ela_Leavebak,hr_ela_starttime,hr_ela_finishtime,' + N'hr_ela_totals,hr_ela_totals1,hr_ela_operatorid,hr_ela_operatorname,' + N'hr_ela_operatedate) VALUES (' + CONVERT(NVARCHAR(20), CONVERT(INT, @requested_employee_number)) + N',N''' + REPLACE(@employee_name, N'''', N'''''') + N''',' + N'N''' + CONVERT(NVARCHAR(20), @employee_department_id) + N''',' + N'N''' + CONVERT(NVARCHAR(20), @employee_post_id) + N''',' + CONVERT(NVARCHAR(20), CONVERT(INT, @leave_type_number)) + N',' + CONVERT(NVARCHAR(20), CONVERT(INT, @flow_type_number)) + N',' + N'N''' + REPLACE(@reason, N'''', N'''''') + N''',''' + CONVERT(NVARCHAR(23), @start_local, 121) + N''',''' + CONVERT(NVARCHAR(23), @end_local, 121) + N''',' + CONVERT(NVARCHAR(50), @calculated_hours) + N',' + CONVERT(NVARCHAR(50), @calculated_days) + N',' + CONVERT(NVARCHAR(20), @current_employee_id) + N',N''' + REPLACE(@operator_name, N'''', N'''''') + N''',''' + CONVERT(NVARCHAR(23), @now_local, 121) + N''')'; DECLARE @legacy_message NVARCHAR(4000); DECLARE @legacy_return INT; SET @legacy_message = N''; EXEC @legacy_return = dbo.p_BaseSave70 @baseSql = @base_sql, @saveType = 1, @modid = 'hr_4011', @tablename = 'HR_EmpLeaveAloneTab', @keyfield = 'hr_ela_id', @keyvalue = '', @Operatorid = @current_employee_id, @operatorName = @operator_name, @msg = @legacy_message OUTPUT, @comfirmFlag = 0; IF @legacy_return <> 1 OR @@TRANCOUNT < 1 OR XACT_STATE() <> 1 BEGIN RAISERROR(N'leave_legacy_create_failed', 16, 1); RETURN; END; DECLARE @created_record_count BIGINT; DECLARE @created_identity INT; SELECT @created_record_count = COUNT_BIG(*), @created_identity = MAX(hr_ela_id), @effective_record_id = MAX(CONVERT(NVARCHAR(50), hr_ela_no)) FROM dbo.HR_EmpLeaveAloneTab WITH (UPDLOCK, HOLDLOCK) WHERE hr_ela_id > @identity_before; IF @created_record_count <> 1 OR @created_identity IS NULL OR NULLIF(LTRIM(RTRIM(@effective_record_id)), N'') IS NULL OR NOT EXISTS ( SELECT 1 FROM dbo.HR_EmpLeaveAloneTab WITH (UPDLOCK, HOLDLOCK) WHERE hr_ela_id = @created_identity AND CONVERT(NVARCHAR(50), hr_ela_no) = @effective_record_id AND hr_ela_empid = CONVERT(INT, @requested_employee_number) AND hr_ela_employeename = CONVERT(VARCHAR(50), @employee_name) AND hr_ela_depid = CONVERT(VARCHAR(20), @employee_department_id) AND hr_ela_postid = CONVERT(VARCHAR(20), @employee_post_id) AND hr_ela_type = CONVERT(INT, @leave_type_number) AND hr_ela_billtype = CONVERT(INT, @flow_type_number) AND hr_ela_starttime = @start_local AND hr_ela_finishtime = @end_local AND ABS(ISNULL(hr_ela_totals, 0) - @calculated_hours) <= 0.01 AND ABS(ISNULL(hr_ela_totals1, 0) - @calculated_days) <= 0.01 AND hr_ela_Leavebak = CONVERT(VARCHAR(500), @reason) AND hr_ela_operatorid = @current_employee_id AND ISNULL(hr_ela_cancelFlag, 0) = 0 ) BEGIN RAISERROR(N'leave_legacy_create_evidence_invalid', 16, 1); RETURN; END; SET @result_code = 'leave_draft_created'; SET @result_message = N'请假申请草稿已通过 ERP 原保存链创建。'; /* submit_after_save_intent is intentionally never executed here. */ SET @submit_after_save_intent = ISNULL(@submit_after_save_intent, 0); END ELSE BEGIN SET @record_id = NULLIF(LTRIM(RTRIM(@record_id)), N''); IF @record_id IS NULL OR LEN(@record_id) > 128 BEGIN RAISERROR(N'leave_submit_record_invalid', 16, 1); RETURN; END; IF DATALENGTH(CONVERT(VARCHAR(8000), @operator_name)) > 20 BEGIN RAISERROR(N'leave_submit_operator_invalid', 16, 1); RETURN; END; /* p_baseApply contains legacy self-migration branches; never let an Agent request reach those branches. Schema repair remains a DBA operation. */ IF COL_LENGTH('dbo.p_systemdlltabflowtypestep', 'autoStep') IS NULL OR COL_LENGTH('dbo.p_systemdlltabflowtypestep', 'comfirmOper') IS NULL OR COL_LENGTH('dbo.p_systemNotification', 'stepCode') IS NULL BEGIN RAISERROR(N'leave_approval_schema_not_ready', 16, 1); RETURN; END; DECLARE @submit_state TABLE ( can_submit BIT NOT NULL, reason NVARCHAR(1000) NULL ); INSERT INTO @submit_state(can_submit, reason) EXEC dbo.p_lserp_agent_workflow_read_compat100 @workflow = 'leave', @action = 'can_submit', @module_code = @module_code, @account_book = @account_book, @subsystem_id = @subsystem_id, @user_id = @user_id, @record_id = @record_id; IF (SELECT COUNT_BIG(*) FROM @submit_state) <> 1 OR NOT EXISTS (SELECT 1 FROM @submit_state WHERE can_submit = 1) BEGIN RAISERROR(N'leave_submit_not_allowed', 16, 1); RETURN; END; DECLARE @submit_employee_id INT; DECLARE @submit_record_count BIGINT; SELECT @submit_record_count = COUNT_BIG(*), @submit_employee_id = MAX(hr_ela_empid), @effective_record_id = MAX(CONVERT(NVARCHAR(50), hr_ela_no)) FROM dbo.HR_EmpLeaveAloneTab WITH (UPDLOCK, HOLDLOCK) WHERE CONVERT(NVARCHAR(32), hr_ela_id) = @record_id OR CONVERT(NVARCHAR(50), hr_ela_no) = @record_id; IF @submit_record_count <> 1 OR @submit_employee_id <> @current_employee_id OR NULLIF(@effective_record_id, N'') IS NULL BEGIN RAISERROR(N'leave_submit_record_scope_invalid', 16, 1); RETURN; END; DECLARE @apply_message NVARCHAR(4000); DECLARE @apply_return INT; SET @apply_message = N''; EXEC @apply_return = dbo.p_baseApply @typeCode = 'hr_4011', @billDocumentId = @effective_record_id, @Operatorid = @current_employee_id, @operatorName = @operator_name, @comfirmType = 1, @msg = @apply_message OUTPUT; IF @apply_return <> 1 OR @@TRANCOUNT < 1 OR XACT_STATE() <> 1 BEGIN RAISERROR(N'leave_legacy_submit_failed', 16, 1); RETURN; END; IF NOT EXISTS ( SELECT 1 FROM dbo.HR_EmpLeaveAloneTab AS leave_record WITH (UPDLOCK, HOLDLOCK) JOIN dbo.p_baseflowOper AS flow_record WITH (UPDLOCK, HOLDLOCK) ON flow_record.modid = 'hr_4011' AND flow_record.keyvalue = leave_record.hr_ela_no AND flow_record.billtype = leave_record.hr_ela_billtype WHERE leave_record.hr_ela_no = @effective_record_id AND leave_record.hr_ela_empid = @current_employee_id ) BEGIN RAISERROR(N'leave_legacy_submit_evidence_invalid', 16, 1); RETURN; END; SET @result_code = 'leave_submitted'; SET @result_message = N'请假申请已通过 ERP 原审批流提交。'; END; DECLARE @guid_text VARCHAR(32); DECLARE @transaction_evidence_id VARCHAR(128); DECLARE @business_audit_id VARCHAR(128); DECLARE @outbox_event_id VARCHAR(128); SET @guid_text = LOWER(REPLACE(CONVERT(VARCHAR(36), NEWID()), '-', '')); SET @transaction_evidence_id = 'tx-leave-' + @guid_text; SET @business_audit_id = 'audit-leave-' + @guid_text; SET @outbox_event_id = 'outbox-leave-' + @guid_text; INSERT INTO dbo.p_agent_business_audit ( business_audit_id, transaction_evidence_id, correlation_id, idempotency_id, account_book, subsystem_id, user_id, command_name, module_code, action_name, record_id, input_fingerprint ) VALUES ( @business_audit_id, @transaction_evidence_id, @correlation_id, @idempotency_id, @account_book, @subsystem_id, @user_id, @command_name, @module_code, @action, @effective_record_id, @input_fingerprint ); INSERT INTO dbo.p_agent_integration_outbox ( event_id, business_audit_id, correlation_id, account_book, subsystem_id, module_code, action_name, record_id, status ) VALUES ( @outbox_event_id, @business_audit_id, @correlation_id, @account_book, @subsystem_id, @module_code, @action, @effective_record_id, 0 ); UPDATE dbo.p_agent_command_idempotency SET status = 1, result_code = @result_code, record_id = @effective_record_id, transaction_evidence_id = @transaction_evidence_id, business_audit_id = @business_audit_id, completed_at_utc = SYSUTCDATETIME() WHERE id = @idempotency_id AND status = 0; IF @@ROWCOUNT <> 1 BEGIN RAISERROR(N'leave_idempotency_completion_failed', 16, 1); RETURN; END; SELECT CONVERT(BIT, 1) AS success, @result_code AS code, @result_message AS message, @effective_record_id AS record_id, CONVERT(BIT, 0) AS needs_ui, CONVERT(BIT, 0) AS idempotency_replayed, @idempotency_key AS applied_idempotency_key, @input_fingerprint AS applied_input_fingerprint, @transaction_evidence_id AS transaction_evidence_id, @business_audit_id AS business_audit_id; END; GO