/*
Customer-specific review draft for the lserp_AI compatibility-level-100 database.

This file is intentionally inert. SET NOEXEC ON prevents the definition from being
applied and @customer_dba_reviewed remains 0. purchase.open_sources contains a bounded
candidate query only so that its field, permission and allocation contract can be
compiled and reviewed without enabling it.
The customer DBA must create a separate approved deployment script after reviewing the
database, account-book, subsystem, permission, status, currency, row-scope, calendar and
source-allocation rules. Do not edit this evidence draft in place.

Payload parsing happens in the trusted ERP process. This procedure accepts only fixed,
typed scalar parameters and therefore does not depend on JSON functions unavailable at
database compatibility level 100.
*/
SET NOEXEC ON;
GO

CREATE PROCEDURE dbo.p_lserp_agent_workflow_read_compat100
    @workflow VARCHAR(32),
    @action VARCHAR(64),
    @module_code NVARCHAR(64),
    @account_book NVARCHAR(64),
    @subsystem_id NVARCHAR(32),
    @user_id NVARCHAR(64),
    @reference NVARCHAR(500) = NULL,
    @tax_id NVARCHAR(50) = NULL,
    @line_id NVARCHAR(64) = NULL,
    @specification NVARCHAR(200) = NULL,
    @unit NVARCHAR(100) = NULL,
    @supplier_code NVARCHAR(64) = NULL,
    @currency_code NVARCHAR(64) = NULL,
    @invoice_number NVARCHAR(128) = NULL,
    @query NVARCHAR(500) = NULL,
    @employee_id NVARCHAR(64) = NULL,
    @local_date NVARCHAR(10) = NULL,
    @day_part VARCHAR(16) = NULL,
    @leave_type_code NVARCHAR(64) = NULL,
    @flow_type_query NVARCHAR(500) = NULL,
    @calculated_hours DECIMAL(18, 6) = NULL,
    @flow_type_code NVARCHAR(64) = NULL,
    @start_local DATETIME = NULL,
    @end_local DATETIME = NULL,
    @record_id NVARCHAR(128) = NULL
AS
BEGIN
    SET NOCOUNT ON;

    DECLARE @customer_dba_reviewed BIT;
    SET @customer_dba_reviewed = 0;

    IF DB_NAME() <> N'lserp_AI'
    BEGIN
        RAISERROR(N'workflow_database_not_allowed', 16, 1);
        RETURN;
    END;

    IF NULLIF(LTRIM(RTRIM(@account_book)), N'') IS NULL
       OR NULLIF(LTRIM(RTRIM(@user_id)), N'') IS NULL
    BEGIN
        RAISERROR(N'workflow_context_invalid', 16, 1);
        RETURN;
    END;

    IF @workflow = 'purchase'
    BEGIN
        IF @module_code <> N'acc_1007' OR @subsystem_id <> N'5'
        BEGIN
            RAISERROR(N'purchase_scope_not_allowed', 16, 1);
            RETURN;
        END;
        IF @action NOT IN
           ('resolve_supplier', 'resolve_currency', 'resolve_material',
            'invoice_exists', 'open_sources')
        BEGIN
            RAISERROR(N'purchase_action_not_allowed', 16, 1);
            RETURN;
        END;
    END
    ELSE IF @workflow = 'leave'
    BEGIN
        IF @module_code <> N'hr_4011' OR @subsystem_id <> N'7'
        BEGIN
            RAISERROR(N'leave_scope_not_allowed', 16, 1);
            RETURN;
        END;
        IF @action NOT IN
           ('context', 'resolve_type', 'resolve_flow_type',
            'resolve_calendar_range', 'type_enabled', 'flow_type_enabled',
            'calculate_hours', 'has_conflict', 'can_submit')
        BEGIN
            RAISERROR(N'leave_action_not_allowed', 16, 1);
            RETURN;
        END;
    END
    ELSE
    BEGIN
        RAISERROR(N'workflow_not_allowed', 16, 1);
        RETURN;
    END;

    IF @customer_dba_reviewed <> 1
    BEGIN
        RAISERROR(N'customer_dba_review_required', 16, 1);
        RETURN;
    END;

    IF LEN(@user_id) > 10 OR @user_id LIKE N'%[^0-9]%'
    BEGIN
        RAISERROR(N'erp_employee_context_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
    BEGIN
        RAISERROR(N'erp_employee_context_invalid', 16, 1);
        RETURN;
    END;

    DECLARE @current_employee_id INT;
    DECLARE @current_employee_name NVARCHAR(50);
    DECLARE @current_department_id INT;
    SET @current_employee_id = CONVERT(INT, @user_number);

    SELECT
        @current_employee_name = CONVERT(NVARCHAR(50), EmployeeName),
        @current_department_id = Departmentid
    FROM dbo.p_employeetab
    WHERE employeeid = @current_employee_id
      AND ISNULL(p_emp_status, '') <> N'离职';

    IF @current_employee_name IS NULL
    BEGIN
        RAISERROR(N'erp_employee_context_invalid', 16, 1);
        RETURN;
    END;

    DECLARE @permission_text NVARCHAR(MAX);
    SET @permission_text = N',';
    SELECT TOP (1)
        @permission_text =
            N',' + CONVERT(NVARCHAR(MAX), ISNULL(wmsPurview, '')) + N','
            + ISNULL(KcPurview, '') + N',' + ISNULL(crmPurview, '') + N','
            + ISNULL(bomPurview, '') + N',' + ISNULL(mpsPurview, '') + N','
            + ISNULL(mrpPurview, '') + N',' + ISNULL(scmPurview, '') + N','
            + ISNULL(sellPurview, '') + N',' + ISNULL(RdmPurview, '') + N','
            + ISNULL(hrPurview, '') + N',' + ISNULL(costPurview, '') + N','
            + ISNULL(eamPurview, '') + N',' + ISNULL(accPurview, '') + N','
            + ISNULL(oaPurview, '') + N',' + ISNULL(BiPurview, '') + N','
            + ISNULL(QmsPurview, '') + N',' + ISNULL(FmPurview, '') + N','
            + ISNULL(Ppurview, '') + N',' + ISNULL(EMApurview, '') + N','
            + ISNULL(GYSpurview, '') + N',' + ISNULL(PdaPurview, '') + N','
            + ISNULL(GFPTPurview, '') + N',' + ISNULL(scmtestPurview, '') + N','
            + ISNULL(CBpurview, '') + N',' + ISNULL(PMPurview, '') + N','
            + ISNULL(SMSPurview, '') + N',' + ISNULL(AMSPurview, '') + N','
            + ISNULL(cmspurview, '') + N',' + ISNULL(MEspurview, '') + N','
            + ISNULL(APSPurview, '') + N',' + ISNULL(KFpurview, '') + N','
            + ISNULL(qmcPurview, '') + N','
    FROM dbo.p_SubsysPurviewTab
    WHERE employeeid = @current_employee_id;

    DECLARE @required_menu_id INT;
    SET @required_menu_id =
        CASE WHEN @workflow = 'purchase' THEN 18848 ELSE 16629 END;

    IF (@current_employee_id <> 1 OR @current_employee_name <> N'管理员')
       AND CHARINDEX(N',' + CONVERT(NVARCHAR(20), @required_menu_id) + N',',
                     @permission_text) = 0
       AND CHARINDEX(N',' + CONVERT(NVARCHAR(20), @required_menu_id) + N'|,',
                     @permission_text) = 0
    BEGIN
        RAISERROR(N'workflow_module_permission_denied', 16, 1);
        RETURN;
    END;

    IF @workflow = 'purchase' AND @action = 'resolve_supplier'
    BEGIN
        SET @reference = NULLIF(LTRIM(RTRIM(@reference)), N'');
        SET @tax_id = NULLIF(LTRIM(RTRIM(@tax_id)), N'');
        IF @reference IS NULL AND @tax_id IS NULL
        BEGIN
            RAISERROR(N'purchase_supplier_reference_required', 16, 1);
            RETURN;
        END;

        IF
        (
            SELECT COUNT_BIG(*)
            FROM dbo.p_providertab
            WHERE ISNULL(IsDisabled, 0) = 0
              AND ISNULL(ban, 0) = 0
              AND ISNULL(cancelFlag, 0) = 0
              AND
              (
                  @reference IS NULL
                  OR CONVERT(NVARCHAR(32), id) = @reference
                  OR CONVERT(NVARCHAR(20), Coid) = @reference
                  OR CONVERT(NVARCHAR(100), CoCode) = @reference
                  OR CONVERT(NVARCHAR(500), CoName) = @reference
                  OR CONVERT(NVARCHAR(500), CoAlias) = @reference
              )
              AND (@tax_id IS NULL OR CONVERT(NVARCHAR(50), taxid) = @tax_id)
        ) > 20
        BEGIN
            RAISERROR(N'purchase_supplier_candidates_exceeded', 16, 1);
            RETURN;
        END;

        SELECT
            CONVERT(NVARCHAR(32), id) AS supplier_code,
            CONVERT(NVARCHAR(500), CoName) AS supplier_name,
            CONVERT(NVARCHAR(50), taxid) AS supplier_tax_id
        FROM dbo.p_providertab
        WHERE ISNULL(IsDisabled, 0) = 0
          AND ISNULL(ban, 0) = 0
          AND ISNULL(cancelFlag, 0) = 0
          AND
          (
              @reference IS NULL
              OR CONVERT(NVARCHAR(32), id) = @reference
              OR CONVERT(NVARCHAR(20), Coid) = @reference
              OR CONVERT(NVARCHAR(100), CoCode) = @reference
              OR CONVERT(NVARCHAR(500), CoName) = @reference
              OR CONVERT(NVARCHAR(500), CoAlias) = @reference
          )
          AND (@tax_id IS NULL OR CONVERT(NVARCHAR(50), taxid) = @tax_id)
        ORDER BY id;
        RETURN;
    END;

    IF @workflow = 'purchase' AND @action = 'resolve_currency'
    BEGIN
        SET @reference = NULLIF(LTRIM(RTRIM(@reference)), N'');
        IF @reference IS NULL OR LEN(@reference) > 40
        BEGIN
            RAISERROR(N'purchase_currency_reference_invalid', 16, 1);
            RETURN;
        END;

        /* Purchase orders and acc_1007 use different currency dictionaries. Never
           expose a source currency to matching unless finance/DBA has approved its
           exact target mapping. OCR may supply either dictionary's exact id, code or
           name, but the result deliberately remains the source P_CurrencyType id used
           by open_sources and by the signed write wrapper. */
        IF OBJECT_ID(N'dbo.p_agent_purchase_currency_crosswalk', N'U') IS NULL
           OR COL_LENGTH(N'dbo.p_agent_purchase_currency_crosswalk', N'source_currency_id') IS NULL
           OR COL_LENGTH(N'dbo.p_agent_purchase_currency_crosswalk', N'target_currency_id') IS NULL
           OR COL_LENGTH(N'dbo.p_agent_purchase_currency_crosswalk', N'is_active') IS NULL
           OR COL_LENGTH(N'dbo.p_agent_purchase_currency_crosswalk', N'approval_evidence_sha256') IS NULL
           OR NOT EXISTS
           (
               SELECT 1 FROM sys.indexes
               WHERE object_id = OBJECT_ID(N'dbo.p_agent_purchase_currency_crosswalk')
                 AND name = N'UX_p_agent_purchase_currency_active'
                 AND is_unique = 1 AND has_filter = 1 AND is_disabled = 0
           )
        BEGIN
            RAISERROR(N'purchase_agent_evidence_schema_missing', 16, 1);
            RETURN;
        END;

        IF NOT EXISTS
        (
            SELECT 1
            FROM dbo.P_CurrencyType AS known_source_currency
            WHERE ISNULL(known_source_currency.is_active, 1) = 1
              AND
              (
                  CONVERT(NVARCHAR(32), known_source_currency.id) = @reference
                  OR CONVERT(NVARCHAR(3), known_source_currency.currency_code) = @reference
                  OR known_source_currency.currency_name = @reference
              )
        )
        AND NOT EXISTS
        (
            SELECT 1
            FROM dbo.P_BaseMixInfoTab AS known_target_currency
            WHERE known_target_currency.Tag = 'L000101'
              AND ISNULL(known_target_currency.Ban, 0) = 0
              AND ISNULL(known_target_currency.Delid, 0) = 0
              AND
              (
                  CONVERT(NVARCHAR(32), known_target_currency.ID) = @reference
                  OR CONVERT(NVARCHAR(100), known_target_currency.Mix_NO) = @reference
                  OR CONVERT(NVARCHAR(500), known_target_currency.Mix_apellation) = @reference
              )
        )
        BEGIN
            /* Unknown references remain an empty candidate set so the trusted ERP
               adapter can return purchase_currency_not_resolved without disclosing
               dictionary contents. */
            SELECT
                CONVERT(NVARCHAR(32), NULL) AS currency_code,
                CONVERT(NVARCHAR(40), NULL) AS currency_name
            WHERE 1 = 0;
            RETURN;
        END;

        IF NOT EXISTS
        (
            SELECT 1
            FROM dbo.P_CurrencyType AS approved_source_currency
            INNER JOIN dbo.p_agent_purchase_currency_crosswalk AS approved_mapping
                ON approved_mapping.source_currency_id = approved_source_currency.id
            INNER JOIN dbo.P_BaseMixInfoTab AS approved_target_currency
                ON approved_target_currency.ID = approved_mapping.target_currency_id
               AND approved_target_currency.Tag = 'L000101'
            WHERE approved_mapping.is_active = 1
              AND LEN(approved_mapping.approval_evidence_sha256) = 64
              AND approved_mapping.approval_evidence_sha256
                  COLLATE Latin1_General_100_BIN2 NOT LIKE '%[^0-9a-f]%'
              AND 1 =
              (
                  SELECT COUNT_BIG(*)
                  FROM dbo.p_agent_purchase_currency_crosswalk AS source_mapping_count
                  WHERE source_mapping_count.source_currency_id = approved_source_currency.id
                    AND source_mapping_count.is_active = 1
                    AND LEN(source_mapping_count.approval_evidence_sha256) = 64
                    AND source_mapping_count.approval_evidence_sha256
                        COLLATE Latin1_General_100_BIN2 NOT LIKE '%[^0-9a-f]%'
              )
              AND ISNULL(approved_source_currency.is_active, 1) = 1
              AND ISNULL(approved_target_currency.Ban, 0) = 0
              AND ISNULL(approved_target_currency.Delid, 0) = 0
              AND
              (
                  CONVERT(NVARCHAR(32), approved_source_currency.id) = @reference
                  OR CONVERT(NVARCHAR(3), approved_source_currency.currency_code) = @reference
                  OR approved_source_currency.currency_name = @reference
                  OR CONVERT(NVARCHAR(32), approved_target_currency.ID) = @reference
                  OR CONVERT(NVARCHAR(100), approved_target_currency.Mix_NO) = @reference
                  OR CONVERT(NVARCHAR(500), approved_target_currency.Mix_apellation) = @reference
              )
        )
        BEGIN
            RAISERROR(N'purchase_currency_crosswalk_not_approved', 16, 1);
            RETURN;
        END;

        IF
        (
            SELECT COUNT_BIG(*)
            FROM
            (
                SELECT approved_source_currency.id
                FROM dbo.P_CurrencyType AS approved_source_currency
                INNER JOIN dbo.p_agent_purchase_currency_crosswalk AS approved_mapping
                    ON approved_mapping.source_currency_id = approved_source_currency.id
                INNER JOIN dbo.P_BaseMixInfoTab AS approved_target_currency
                    ON approved_target_currency.ID = approved_mapping.target_currency_id
                   AND approved_target_currency.Tag = 'L000101'
                WHERE approved_mapping.is_active = 1
                  AND LEN(approved_mapping.approval_evidence_sha256) = 64
                  AND approved_mapping.approval_evidence_sha256
                      COLLATE Latin1_General_100_BIN2 NOT LIKE '%[^0-9a-f]%'
                  AND 1 =
                  (
                      SELECT COUNT_BIG(*)
                      FROM dbo.p_agent_purchase_currency_crosswalk AS source_mapping_count
                      WHERE source_mapping_count.source_currency_id = approved_source_currency.id
                        AND source_mapping_count.is_active = 1
                        AND LEN(source_mapping_count.approval_evidence_sha256) = 64
                        AND source_mapping_count.approval_evidence_sha256
                            COLLATE Latin1_General_100_BIN2 NOT LIKE '%[^0-9a-f]%'
                  )
                  AND ISNULL(approved_source_currency.is_active, 1) = 1
                  AND ISNULL(approved_target_currency.Ban, 0) = 0
                  AND ISNULL(approved_target_currency.Delid, 0) = 0
                  AND
                  (
                      CONVERT(NVARCHAR(32), approved_source_currency.id) = @reference
                      OR CONVERT(NVARCHAR(3), approved_source_currency.currency_code) = @reference
                      OR approved_source_currency.currency_name = @reference
                      OR CONVERT(NVARCHAR(32), approved_target_currency.ID) = @reference
                      OR CONVERT(NVARCHAR(100), approved_target_currency.Mix_NO) = @reference
                      OR CONVERT(NVARCHAR(500), approved_target_currency.Mix_apellation) = @reference
                  )
                GROUP BY approved_source_currency.id
            ) AS approved_currency_candidates
        ) > 20
        BEGIN
            RAISERROR(N'purchase_currency_candidates_exceeded', 16, 1);
            RETURN;
        END;

        SELECT
            CONVERT(NVARCHAR(32), approved_source_currency.id) AS currency_code,
            CONVERT(NVARCHAR(40), approved_source_currency.currency_name) AS currency_name
        FROM dbo.P_CurrencyType AS approved_source_currency
        INNER JOIN dbo.p_agent_purchase_currency_crosswalk AS approved_mapping
            ON approved_mapping.source_currency_id = approved_source_currency.id
        INNER JOIN dbo.P_BaseMixInfoTab AS approved_target_currency
            ON approved_target_currency.ID = approved_mapping.target_currency_id
           AND approved_target_currency.Tag = 'L000101'
        WHERE approved_mapping.is_active = 1
          AND LEN(approved_mapping.approval_evidence_sha256) = 64
          AND approved_mapping.approval_evidence_sha256
              COLLATE Latin1_General_100_BIN2 NOT LIKE '%[^0-9a-f]%'
          AND 1 =
          (
              SELECT COUNT_BIG(*)
              FROM dbo.p_agent_purchase_currency_crosswalk AS source_mapping_count
              WHERE source_mapping_count.source_currency_id = approved_source_currency.id
                AND source_mapping_count.is_active = 1
                AND LEN(source_mapping_count.approval_evidence_sha256) = 64
                AND source_mapping_count.approval_evidence_sha256
                    COLLATE Latin1_General_100_BIN2 NOT LIKE '%[^0-9a-f]%'
          )
          AND ISNULL(approved_source_currency.is_active, 1) = 1
          AND ISNULL(approved_target_currency.Ban, 0) = 0
          AND ISNULL(approved_target_currency.Delid, 0) = 0
          AND
          (
              CONVERT(NVARCHAR(32), approved_source_currency.id) = @reference
              OR CONVERT(NVARCHAR(3), approved_source_currency.currency_code) = @reference
              OR approved_source_currency.currency_name = @reference
              OR CONVERT(NVARCHAR(32), approved_target_currency.ID) = @reference
              OR CONVERT(NVARCHAR(100), approved_target_currency.Mix_NO) = @reference
              OR CONVERT(NVARCHAR(500), approved_target_currency.Mix_apellation) = @reference
          )
        GROUP BY approved_source_currency.id, approved_source_currency.currency_name
        ORDER BY approved_source_currency.id;
        RETURN;
    END;

    IF @workflow = 'purchase' AND @action = 'resolve_material'
    BEGIN
        SET @reference = NULLIF(LTRIM(RTRIM(@reference)), N'');
        SET @specification = NULLIF(LTRIM(RTRIM(@specification)), N'');
        SET @unit = NULLIF(LTRIM(RTRIM(@unit)), N'');
        SET @supplier_code = NULLIF(LTRIM(RTRIM(@supplier_code)), N'');

        IF @reference IS NULL OR @supplier_code IS NULL
           OR LEN(@supplier_code) > 10 OR @supplier_code LIKE N'%[^0-9]%'
        BEGIN
            RAISERROR(N'purchase_material_reference_invalid', 16, 1);
            RETURN;
        END;

        DECLARE @material_supplier_number DECIMAL(10, 0);
        DECLARE @material_supplier_id INT;
        SET @material_supplier_number = CONVERT(DECIMAL(10, 0), @supplier_code);
        IF @material_supplier_number < 1 OR @material_supplier_number > 2147483647
        BEGIN
            RAISERROR(N'purchase_material_reference_invalid', 16, 1);
            RETURN;
        END;
        SET @material_supplier_id = CONVERT(INT, @material_supplier_number);

        IF
        (
            SELECT COUNT_BIG(*)
            FROM dbo.P_productTab
            WHERE ISNULL(delid, 0) = 0
              AND ISNULL(IsLocked, 0) = 0
              AND ISNULL(IsDisabled, 0) = 0
              AND ISNULL(ban, 0) = 0
              AND ISNULL(cancelFlag, 0) = 0
              AND
              (
                  CONVERT(NVARCHAR(32), id) = @reference
                  OR CONVERT(NVARCHAR(200), productid) = @reference
                  OR CONVERT(NVARCHAR(500), appellation) = @reference
              )
              AND
              (
                  @specification IS NULL
                  OR CONVERT(NVARCHAR(200), spec) = @specification
                  OR CONVERT(NVARCHAR(200), model) = @specification
              )
              AND
              (
                  @unit IS NULL
                  OR CONVERT(NVARCHAR(20), productunitname) = @unit
                  OR CONVERT(NVARCHAR(100), pur_unit) = @unit
              )
              AND (pur_providerid IS NULL OR pur_providerid = @material_supplier_id)
        ) > 20
        BEGIN
            RAISERROR(N'purchase_material_candidates_exceeded', 16, 1);
            RETURN;
        END;

        SELECT
            CONVERT(NVARCHAR(200), productid) AS material_code,
            CONVERT(NVARCHAR(500), appellation) AS material_name,
            CONVERT(NVARCHAR(200), COALESCE(NULLIF(spec, ''), model, '')) AS specification,
            CONVERT(NVARCHAR(100), COALESCE(NULLIF(productunitname, ''), pur_unit, '')) AS unit
        FROM dbo.P_productTab
        WHERE ISNULL(delid, 0) = 0
          AND ISNULL(IsLocked, 0) = 0
          AND ISNULL(IsDisabled, 0) = 0
          AND ISNULL(ban, 0) = 0
          AND ISNULL(cancelFlag, 0) = 0
          AND
          (
              CONVERT(NVARCHAR(32), id) = @reference
              OR CONVERT(NVARCHAR(200), productid) = @reference
              OR CONVERT(NVARCHAR(500), appellation) = @reference
          )
          AND
          (
              @specification IS NULL
              OR CONVERT(NVARCHAR(200), spec) = @specification
              OR CONVERT(NVARCHAR(200), model) = @specification
          )
          AND
          (
              @unit IS NULL
              OR CONVERT(NVARCHAR(20), productunitname) = @unit
              OR CONVERT(NVARCHAR(100), pur_unit) = @unit
          )
          AND (pur_providerid IS NULL OR pur_providerid = @material_supplier_id)
        ORDER BY productid;
        RETURN;
    END;

    IF @workflow = 'purchase' AND @action = 'invoice_exists'
    BEGIN
        SET @supplier_code = NULLIF(LTRIM(RTRIM(@supplier_code)), N'');
        SET @invoice_number = NULLIF(LTRIM(RTRIM(@invoice_number)), N'');
        IF @supplier_code IS NULL OR @invoice_number IS NULL
           OR LEN(@supplier_code) > 10 OR @supplier_code LIKE N'%[^0-9]%'
        BEGIN
            RAISERROR(N'purchase_invoice_identity_invalid', 16, 1);
            RETURN;
        END;

        DECLARE @invoice_supplier_number DECIMAL(10, 0);
        DECLARE @invoice_supplier_id INT;
        SET @invoice_supplier_number = CONVERT(DECIMAL(10, 0), @supplier_code);
        IF @invoice_supplier_number < 1 OR @invoice_supplier_number > 2147483647
        BEGIN
            RAISERROR(N'purchase_invoice_identity_invalid', 16, 1);
            RETURN;
        END;
        SET @invoice_supplier_id = CONVERT(INT, @invoice_supplier_number);

        SELECT CONVERT(BIT,
            CASE WHEN EXISTS
            (
                SELECT 1
                FROM dbo.ACC_billscmInvoicemainPIDHxtab
                WHERE acc_mphhscm_Providerid = @invoice_supplier_id
                  AND CONVERT(NVARCHAR(MAX), acc_mphhscm_invoice) = @invoice_number
                  AND ISNULL(acc_mphhscm_cancelFlag, 0) = 0
                  AND ISNULL(acc_mphhscm_wasteoper, 0) = 0
            )
            OR EXISTS
            (
                SELECT 1
                FROM dbo.ACC_billscminvoicemaintab
                WHERE acc_mscmi_Providerid = @invoice_supplier_id
                  AND CONVERT(NVARCHAR(50), acc_mscmi_invoice) = @invoice_number
                  AND ISNULL(acc_mscmi_cancelFlag, 0) = 0
                  AND ISNULL(acc_mscmi_wasteoper, 0) = 0
            )
            THEN 1 ELSE 0 END) AS [exists];
        RETURN;
    END;

    IF @workflow = 'purchase' AND @action = 'open_sources'
    BEGIN
        SET @supplier_code = NULLIF(LTRIM(RTRIM(@supplier_code)), N'');
        SET @currency_code = NULLIF(LTRIM(RTRIM(@currency_code)), N'');
        IF @supplier_code IS NULL OR @currency_code IS NULL
           OR LEN(@supplier_code) > 10 OR @supplier_code LIKE N'%[^0-9]%'
           OR LEN(@currency_code) > 10 OR @currency_code LIKE N'%[^0-9]%'
        BEGIN
            RAISERROR(N'purchase_source_identity_invalid', 16, 1);
            RETURN;
        END;

        DECLARE @source_supplier_number DECIMAL(10, 0);
        DECLARE @source_currency_number DECIMAL(10, 0);
        DECLARE @source_supplier_id INT;
        DECLARE @source_currency_id INT;
        SET @source_supplier_number = CONVERT(DECIMAL(10, 0), @supplier_code);
        SET @source_currency_number = CONVERT(DECIMAL(10, 0), @currency_code);
        IF @source_supplier_number < 1 OR @source_supplier_number > 2147483647
           OR @source_currency_number < 1 OR @source_currency_number > 2147483647
        BEGIN
            RAISERROR(N'purchase_source_identity_invalid', 16, 1);
            RETURN;
        END;
        SET @source_supplier_id = CONVERT(INT, @source_supplier_number);
        SET @source_currency_id = CONVERT(INT, @source_currency_number);

        IF NOT EXISTS
        (
            SELECT 1 FROM dbo.P_CurrencyType
            WHERE id = @source_currency_id
        )
        BEGIN
            RAISERROR(N'purchase_source_currency_invalid', 16, 1);
            RETURN;
        END;

        /* Reading purchase-order rows requires the PUR_5001 menu in addition to
           the acc_1007 permission checked above. Every returned row must also match
           one currently active, signed procurement scope tuple; administrators do
           not bypass this data boundary. */
        IF (@current_employee_id <> 1 OR @current_employee_name <> N'管理员')
           AND CHARINDEX(N',187,', @permission_text) = 0
           AND CHARINDEX(N',187|,', @permission_text) = 0
        BEGIN
            RAISERROR(N'purchase_source_permission_denied', 16, 1);
            RETURN;
        END;

        IF OBJECT_ID(N'dbo.p_agent_purchase_row_scope', N'U') IS NULL
           OR COL_LENGTH(N'dbo.p_agent_purchase_row_scope', N'account_book') IS NULL
           OR COL_LENGTH(N'dbo.p_agent_purchase_row_scope', N'subsystem_id') IS NULL
           OR COL_LENGTH(N'dbo.p_agent_purchase_row_scope', N'erp_user_id') IS NULL
           OR COL_LENGTH(N'dbo.p_agent_purchase_row_scope', N'group_id') IS NULL
           OR COL_LENGTH(N'dbo.p_agent_purchase_row_scope', N'department_id') IS NULL
           OR COL_LENGTH(N'dbo.p_agent_purchase_row_scope', N'purchase_user_id') IS NULL
           OR COL_LENGTH(N'dbo.p_agent_purchase_row_scope', N'is_active') IS NULL
           OR COL_LENGTH(N'dbo.p_agent_purchase_row_scope', N'approval_evidence_sha256') IS NULL
           OR COL_LENGTH(N'dbo.p_agent_purchase_row_scope', N'valid_from_utc') IS NULL
           OR COL_LENGTH(N'dbo.p_agent_purchase_row_scope', N'valid_to_utc') IS NULL
           OR NOT EXISTS
           (
               SELECT 1 FROM sys.indexes
               WHERE object_id = OBJECT_ID(N'dbo.p_agent_purchase_row_scope')
                 AND name = N'UX_p_agent_purchase_row_scope_active'
                 AND is_unique = 1 AND has_filter = 1 AND is_disabled = 0
           )
        BEGIN
            RAISERROR(N'purchase_agent_evidence_schema_missing', 16, 1);
            RETURN;
        END;

        IF NOT EXISTS
        (
            SELECT 1
            FROM dbo.p_agent_purchase_row_scope
            WHERE account_book = @account_book
              AND subsystem_id = @subsystem_id
              AND erp_user_id = @user_id
              AND is_active = 1
              AND valid_from_utc <= SYSUTCDATETIME()
              AND (valid_to_utc IS NULL OR valid_to_utc > SYSUTCDATETIME())
              AND LEN(approval_evidence_sha256) = 64
              AND approval_evidence_sha256 COLLATE Latin1_General_100_BIN2
                  NOT LIKE '%[^0-9a-f]%'
        )
        BEGIN
            RAISERROR(N'purchase_row_scope_denied', 16, 1);
            RETURN;
        END;

        ;WITH active_invoice_quantity AS
        (
            SELECT
                invoice_line.acc_lphhscm_ScmPoid AS source_line_id,
                SUM(ISNULL(invoice_line.acc_lphhscm_amount, 0)) AS reserved_quantity
            FROM dbo.ACC_billscmInvoicelistPIDHxtab AS invoice_line
            INNER JOIN dbo.ACC_billscmInvoicemainPIDHxtab AS invoice_main
                ON invoice_main.acc_mphhscm_billdocument_id =
                   invoice_line.acc_lphhscm_billdocument_id
            WHERE invoice_line.acc_lphhscm_ScmPoid IS NOT NULL
              AND ISNULL(invoice_main.acc_mphhscm_cancelFlag, 0) = 0
              AND ISNULL(invoice_main.acc_mphhscm_wasteoper, 0) = 0
              AND ISNULL(invoice_main.acc_mphhscm_delid, 0) = 0
            GROUP BY invoice_line.acc_lphhscm_ScmPoid
        ),
        source_candidates AS
        (
            SELECT
                order_line.scm_lpo_Billdocument_id,
                order_main.scm_mpo_handcraftId,
                order_line.scm_lpo_id,
                order_main.scm_mpo_Providerid,
                order_main.scm_mpo_Currency,
                order_line.scm_lpo_Productid,
                order_line.scm_lpo_ProductUnitName,
                CONVERT(DECIMAL(19, 8),
                    ISNULL(order_line.scm_lpo_Amount, 0)
                    - ISNULL(reserved.reserved_quantity, 0)) AS remaining_quantity,
                CONVERT(DECIMAL(19, 12), ISNULL(order_line.scm_lpo_Fprice, 0)) AS unit_price,
                CONVERT(DECIMAL(19, 8), ISNULL(order_line.scm_lpo_taxRate, 0)) AS tax_rate,
                CONVERT(DECIMAL(19, 8), COALESCE(
                    NULLIF(order_line.scm_lpo_ExchangeRate, 0),
                    order_main.scm_mpo_ExchangeRate,
                    0)) AS exchange_rate
            FROM dbo.scm_BillPolistTab AS order_line
            INNER JOIN dbo.scm_BillPoMainTab AS order_main
                ON order_main.scm_mpo_Billdocument_Id =
                   order_line.scm_lpo_Billdocument_id
            LEFT JOIN active_invoice_quantity AS reserved
                ON reserved.source_line_id = order_line.scm_lpo_id
            WHERE order_main.scm_mpo_Providerid = @source_supplier_id
              AND LTRIM(RTRIM(CONVERT(NVARCHAR(20), order_main.scm_mpo_Currency))) =
                  CONVERT(NVARCHAR(20), @source_currency_id)
              AND ISNULL(order_main.scm_mpo_stepover, 0) = 1
              AND ISNULL(order_main.scm_mpo_cancelFlag, 0) = 0
              AND ISNULL(order_main.scm_mpo_wasteoper, 0) = 0
              AND ISNULL(order_main.scm_mpo_ban, 0) = 0
              AND EXISTS
              (
                  SELECT 1
                  FROM dbo.p_agent_purchase_row_scope AS allowed_scope
                  WHERE allowed_scope.account_book = @account_book
                    AND allowed_scope.subsystem_id = @subsystem_id
                    AND allowed_scope.erp_user_id = @user_id
                    AND CONVERT(NVARCHAR(20), allowed_scope.group_id) =
                        LTRIM(RTRIM(CONVERT(NVARCHAR(20), order_main.scm_mpo_Groupid)))
                    AND CONVERT(NVARCHAR(20), allowed_scope.department_id) =
                        LTRIM(RTRIM(CONVERT(NVARCHAR(20), order_main.scm_mpo_Departmentid)))
                    AND CONVERT(NVARCHAR(20), allowed_scope.purchase_user_id) =
                        LTRIM(RTRIM(CONVERT(NVARCHAR(20), order_main.scm_mpo_PurchaseUserId)))
                    AND allowed_scope.is_active = 1
                    AND allowed_scope.valid_from_utc <= SYSUTCDATETIME()
                    AND (allowed_scope.valid_to_utc IS NULL
                         OR allowed_scope.valid_to_utc > SYSUTCDATETIME())
                    AND LEN(allowed_scope.approval_evidence_sha256) = 64
                    AND allowed_scope.approval_evidence_sha256
                        COLLATE Latin1_General_100_BIN2 NOT LIKE '%[^0-9a-f]%'
              )
        )
        SELECT TOP (10001)
            CONVERT(NVARCHAR(50), scm_lpo_Billdocument_id) AS source_order_id,
            CONVERT(NVARCHAR(50), COALESCE(
                NULLIF(LTRIM(RTRIM(scm_mpo_handcraftId)), ''),
                scm_lpo_Billdocument_id)) AS source_order_number,
            CONVERT(NVARCHAR(32), scm_lpo_id) AS source_line_id,
            CONVERT(NVARCHAR(32), scm_mpo_Providerid) AS supplier_code,
            CONVERT(NVARCHAR(32), @source_currency_id) AS currency_code,
            CONVERT(NVARCHAR(200), scm_lpo_Productid) AS material_code,
            CONVERT(NVARCHAR(100), scm_lpo_ProductUnitName) AS unit,
            remaining_quantity,
            unit_price,
            tax_rate,
            exchange_rate,
            CONVERT(BIT, 0) AS closed
        FROM source_candidates
        WHERE remaining_quantity > 0
        ORDER BY scm_lpo_Billdocument_id, scm_lpo_id;
        RETURN;
    END;

    IF @workflow = 'leave' AND @action = 'context'
    BEGIN
        SELECT
            CONVERT(NVARCHAR(32), @current_employee_id) AS current_employee_id,
            CONVERT(BIT, 0) AS can_apply_for_others,
            CONVERT(DATETIME, DATEADD(HOUR, 8, SYSUTCDATETIME())) AS now_local;
        RETURN;
    END;

    IF @workflow = 'leave' AND @action = 'resolve_type'
    BEGIN
        SET @query = NULLIF(LTRIM(RTRIM(@query)), N'');
        IF @query IS NULL
        BEGIN
            RAISERROR(N'leave_type_query_invalid', 16, 1);
            RETURN;
        END;

        ;WITH leave_types AS
        (
            SELECT
                CONVERT(NVARCHAR(64), 1) AS leave_type_code,
                CONVERT(NVARCHAR(128), CONVERT(NVARCHAR(4), YEAR(GETDATE()))
                    + N'年假剩余' + CONVERT(NVARCHAR(30), ISNULL(p_emp_holidaybalance, 0))
                    + N'小时') AS leave_type_name,
                CONVERT(NVARCHAR(64), N'本年年假') AS alias_one,
                CONVERT(NVARCHAR(64), N'年假') AS alias_two
            FROM dbo.p_employeetab WHERE employeeid = @current_employee_id
            UNION ALL
            SELECT CONVERT(NVARCHAR(64), 2),
                CONVERT(NVARCHAR(128), CONVERT(NVARCHAR(4), YEAR(GETDATE()) - 1)
                    + N'年假剩余' + CONVERT(NVARCHAR(30), ISNULL(p_emp_lastholidaybalance, 0))
                    + N'小时'),
                CONVERT(NVARCHAR(64), N'上年年假'), CONVERT(NVARCHAR(64), N'年假')
            FROM dbo.p_employeetab WHERE employeeid = @current_employee_id
            UNION ALL
            SELECT CONVERT(NVARCHAR(64), 3),
                CONVERT(NVARCHAR(128), CONVERT(NVARCHAR(4), YEAR(GETDATE()))
                    + N'年调休剩余' + CONVERT(NVARCHAR(30), ISNULL(p_emp_txholidaybalance, 0))
                    + N'小时'),
                CONVERT(NVARCHAR(64), N'本年调休'), CONVERT(NVARCHAR(64), N'调休')
            FROM dbo.p_employeetab WHERE employeeid = @current_employee_id
            UNION ALL
            SELECT CONVERT(NVARCHAR(64), 4),
                CONVERT(NVARCHAR(128), CONVERT(NVARCHAR(4), YEAR(GETDATE()) - 1)
                    + N'年调休剩余' + CONVERT(NVARCHAR(30), ISNULL(p_emp_lasttxholidaybalance, 0))
                    + N'小时'),
                CONVERT(NVARCHAR(64), N'上年调休'), CONVERT(NVARCHAR(64), N'调休')
            FROM dbo.p_employeetab WHERE employeeid = @current_employee_id
            UNION ALL
            SELECT CONVERT(NVARCHAR(64), ID),
                CONVERT(NVARCHAR(128), Mix_apellation),
                CONVERT(NVARCHAR(64), Mix_NO),
                CONVERT(NVARCHAR(64), Mix_apellation)
            FROM dbo.P_BaseMixInfoTab
            WHERE Tag = '7001801' AND Ban = 0 AND ID NOT IN (1, 2, 3, 4)
        )
        SELECT leave_type_code, leave_type_name
        FROM leave_types
        WHERE @query IN (leave_type_code, leave_type_name, alias_one, alias_two)
        ORDER BY CONVERT(INT, leave_type_code), leave_type_code;
        RETURN;
    END;

    IF @workflow = 'leave' AND @action = 'type_enabled'
    BEGIN
        SET @leave_type_code = NULLIF(LTRIM(RTRIM(@leave_type_code)), N'');
        IF @leave_type_code IS NULL OR LEN(@leave_type_code) > 10
           OR @leave_type_code LIKE N'%[^0-9]%'
        BEGIN
            SELECT CONVERT(BIT, 0) AS enabled;
            RETURN;
        END;

        DECLARE @leave_type_number DECIMAL(10, 0);
        DECLARE @leave_type_id INT;
        SET @leave_type_number = CONVERT(DECIMAL(10, 0), @leave_type_code);
        IF @leave_type_number < 1 OR @leave_type_number > 2147483647
        BEGIN
            SELECT CONVERT(BIT, 0) AS enabled;
            RETURN;
        END;
        SET @leave_type_id = CONVERT(INT, @leave_type_number);

        SELECT CONVERT(BIT,
            CASE WHEN @leave_type_id IN (1, 2, 3, 4)
                      OR EXISTS
                         (
                             SELECT 1 FROM dbo.P_BaseMixInfoTab
                             WHERE ID = @leave_type_id
                               AND Tag = '7001801' AND Ban = 0
                         )
                 THEN 1 ELSE 0 END) AS enabled;
        RETURN;
    END;

    IF @workflow = 'leave' AND @action = 'resolve_flow_type'
    BEGIN
        SET @employee_id = NULLIF(LTRIM(RTRIM(@employee_id)), N'');
        SET @flow_type_query = NULLIF(LTRIM(RTRIM(@flow_type_query)), N'');
        IF @employee_id IS NULL OR LEN(@employee_id) > 10
           OR @employee_id LIKE N'%[^0-9]%'
           OR @calculated_hours IS NULL
           OR @calculated_hours <= 0 OR @calculated_hours > 744
        BEGIN
            RAISERROR(N'leave_flow_type_request_invalid', 16, 1);
            RETURN;
        END;

        DECLARE @flow_employee_number DECIMAL(10, 0);
        SET @flow_employee_number = CONVERT(DECIMAL(10, 0), @employee_id);
        IF @flow_employee_number <> @current_employee_id
        BEGIN
            RAISERROR(N'leave_flow_type_request_invalid', 16, 1);
            RETURN;
        END;

        IF
        (
            SELECT COUNT_BIG(*)
            FROM dbo.p_systemdlltabflowtype AS flow_type
            WHERE flow_type.typeCode = 'hr_4011'
              AND ISNULL(flow_type.billflowban, 0) = 0
              AND EXISTS
              (
                  SELECT 1
                  FROM dbo.p_systemdlltabflowtypestep AS flow_step
                  WHERE flow_step.typeCode = 'hr_4011'
                    AND flow_step.billType = CONVERT(VARCHAR(10), flow_type.id)
                    AND ISNULL(flow_step.stepClosed, 0) = 0
              )
              AND
              (
                  @flow_type_query IS NULL
                  OR CONVERT(NVARCHAR(64), flow_type.id) = @flow_type_query
                  OR CONVERT(NVARCHAR(128), flow_type.billTypeName) = @flow_type_query
              )
        ) > 20
        BEGIN
            RAISERROR(N'leave_flow_type_candidates_exceeded', 16, 1);
            RETURN;
        END;

        /*
        calculated_hours is deliberately not used to infer an approval route. The
        customer has multiple rank/day categories but no reviewed deterministic
        employee-rank mapping. An empty query therefore returns every enabled choice.
        */
        SELECT
            CONVERT(NVARCHAR(64), flow_type.id) AS flow_type_code,
            CONVERT(NVARCHAR(128), flow_type.billTypeName) AS flow_type_name
        FROM dbo.p_systemdlltabflowtype AS flow_type
        WHERE flow_type.typeCode = 'hr_4011'
          AND ISNULL(flow_type.billflowban, 0) = 0
          AND EXISTS
          (
              SELECT 1
              FROM dbo.p_systemdlltabflowtypestep AS flow_step
              WHERE flow_step.typeCode = 'hr_4011'
                AND flow_step.billType = CONVERT(VARCHAR(10), flow_type.id)
                AND ISNULL(flow_step.stepClosed, 0) = 0
          )
          AND
          (
              @flow_type_query IS NULL
              OR CONVERT(NVARCHAR(64), flow_type.id) = @flow_type_query
              OR CONVERT(NVARCHAR(128), flow_type.billTypeName) = @flow_type_query
          )
        ORDER BY flow_type.id;
        RETURN;
    END;

    IF @workflow = 'leave' AND @action = 'flow_type_enabled'
    BEGIN
        SET @flow_type_code = NULLIF(LTRIM(RTRIM(@flow_type_code)), N'');
        IF @flow_type_code IS NULL OR LEN(@flow_type_code) > 10
           OR @flow_type_code LIKE N'%[^0-9]%'
        BEGIN
            SELECT CONVERT(BIT, 0) AS enabled;
            RETURN;
        END;

        SELECT CONVERT(BIT, CASE WHEN EXISTS
        (
            SELECT 1
            FROM dbo.p_systemdlltabflowtype
            WHERE CONVERT(NVARCHAR(64), id) = @flow_type_code
              AND typeCode = 'hr_4011'
              AND ISNULL(billflowban, 0) = 0
              AND EXISTS
              (
                  SELECT 1
                  FROM dbo.p_systemdlltabflowtypestep
                  WHERE typeCode = 'hr_4011'
                    AND billType = @flow_type_code
                    AND ISNULL(stepClosed, 0) = 0
              )
        ) THEN 1 ELSE 0 END) AS enabled;
        RETURN;
    END;

    IF @workflow = 'leave' AND @action = 'resolve_calendar_range'
    BEGIN
        SET @employee_id = NULLIF(LTRIM(RTRIM(@employee_id)), N'');
        IF @employee_id IS NULL OR LEN(@employee_id) > 10
           OR @employee_id LIKE N'%[^0-9]%'
           OR @local_date IS NULL OR LEN(@local_date) <> 10
           OR SUBSTRING(@local_date, 5, 1) <> N'-'
           OR SUBSTRING(@local_date, 8, 1) <> N'-'
           OR STUFF(STUFF(@local_date, 8, 1, N''), 5, 1, N'') LIKE N'%[^0-9]%'
           OR ISDATE(@local_date) <> 1
           OR @day_part NOT IN ('morning', 'afternoon', 'full_day')
        BEGIN
            RAISERROR(N'leave_calendar_request_invalid', 16, 1);
            RETURN;
        END;

        DECLARE @calendar_employee_number DECIMAL(10, 0);
        DECLARE @calendar_employee_id INT;
        DECLARE @calendar_date DATE;
        SET @calendar_employee_number = CONVERT(DECIMAL(10, 0), @employee_id);
        IF @calendar_employee_number <> @current_employee_id
        BEGIN
            RAISERROR(N'leave_calendar_request_invalid', 16, 1);
            RETURN;
        END;
        SET @calendar_employee_id = CONVERT(INT, @calendar_employee_number);
        SET @calendar_date = CONVERT(DATE, @local_date, 23);

        DECLARE @schedule_count BIGINT;
        SELECT @schedule_count = COUNT_BIG(*) FROM dbo.HR_ScheduleTab
        WHERE HR_Schedule_employeeid = @calendar_employee_id
          AND HR_Schedule_Date = @calendar_date;

        IF @schedule_count = 0
        BEGIN
            SELECT CONVERT(BIT, 0) AS available,
                CONVERT(VARCHAR(64), 'schedule_missing') AS reason_code,
                CONVERT(DATETIME, NULL) AS start_local,
                CONVERT(DATETIME, NULL) AS end_local,
                CONVERT(DECIMAL(18, 4), NULL) AS hours,
                CONVERT(NVARCHAR(128), NULL) AS time_zone_id;
            RETURN;
        END;
        IF @schedule_count <> 1
        BEGIN
            RAISERROR(N'leave_calendar_duplicate_schedule', 16, 1);
            RETURN;
        END;

        DECLARE @is_workday INT;
        DECLARE @am_start_raw DATETIME;
        DECLARE @am_end_raw DATETIME;
        DECLARE @pm_start_raw DATETIME;
        DECLARE @pm_end_raw DATETIME;
        SELECT
            @is_workday = HR_Schedule_IsWorkday,
            @am_start_raw = HR_Schedule_AMStartTime,
            @am_end_raw = HR_Schedule_AMEndTime,
            @pm_start_raw = HR_Schedule_PMStartTime,
            @pm_end_raw = HR_Schedule_PMEndTime
        FROM dbo.HR_ScheduleTab
        WHERE HR_Schedule_employeeid = @calendar_employee_id
          AND HR_Schedule_Date = @calendar_date;

        IF ISNULL(@is_workday, 0) <> 1
        BEGIN
            SELECT CONVERT(BIT, 0) AS available,
                CONVERT(VARCHAR(64), 'not_workday') AS reason_code,
                CONVERT(DATETIME, NULL) AS start_local,
                CONVERT(DATETIME, NULL) AS end_local,
                CONVERT(DECIMAL(18, 4), NULL) AS hours,
                CONVERT(NVARCHAR(128), NULL) AS time_zone_id;
            RETURN;
        END;

        IF @am_start_raw IS NULL OR @am_end_raw IS NULL
           OR @pm_start_raw IS NULL OR @pm_end_raw IS NULL
        BEGIN
            RAISERROR(N'leave_calendar_shift_invalid', 16, 1);
            RETURN;
        END;

        DECLARE @am_start DATETIME;
        DECLARE @am_end DATETIME;
        DECLARE @pm_start DATETIME;
        DECLARE @pm_end DATETIME;
        SET @am_start = DATEADD(MILLISECOND,
            DATEDIFF(MILLISECOND, CONVERT(DATE, @am_start_raw), @am_start_raw),
            CONVERT(DATETIME, @calendar_date));
        SET @am_end = DATEADD(MILLISECOND,
            DATEDIFF(MILLISECOND, CONVERT(DATE, @am_end_raw), @am_end_raw),
            CONVERT(DATETIME, @calendar_date));
        SET @pm_start = DATEADD(MILLISECOND,
            DATEDIFF(MILLISECOND, CONVERT(DATE, @pm_start_raw), @pm_start_raw),
            CONVERT(DATETIME, @calendar_date));
        SET @pm_end = DATEADD(MILLISECOND,
            DATEDIFF(MILLISECOND, CONVERT(DATE, @pm_end_raw), @pm_end_raw),
            CONVERT(DATETIME, @calendar_date));

        IF @am_start >= @am_end OR @pm_start >= @pm_end OR @am_end > @pm_start
        BEGIN
            RAISERROR(N'leave_calendar_shift_invalid', 16, 1);
            RETURN;
        END;

        DECLARE @range_start DATETIME;
        DECLARE @range_end DATETIME;
        DECLARE @range_seconds BIGINT;
        SET @range_start =
            CASE WHEN @day_part = 'afternoon' THEN @pm_start ELSE @am_start END;
        SET @range_end =
            CASE WHEN @day_part = 'morning' THEN @am_end ELSE @pm_end END;
        SET @range_seconds =
            CASE WHEN @day_part = 'morning'
                 THEN DATEDIFF(SECOND, @am_start, @am_end)
                 WHEN @day_part = 'afternoon'
                 THEN DATEDIFF(SECOND, @pm_start, @pm_end)
                 ELSE DATEDIFF(SECOND, @am_start, @am_end)
                    + DATEDIFF(SECOND, @pm_start, @pm_end)
            END;

        SELECT CONVERT(BIT, 1) AS available,
            CONVERT(VARCHAR(64), 'ok') AS reason_code,
            @range_start AS start_local,
            @range_end AS end_local,
            CONVERT(DECIMAL(18, 4), @range_seconds / 3600.0) AS hours,
            CONVERT(NVARCHAR(128), N'China Standard Time') AS time_zone_id;
        RETURN;
    END;

    IF @workflow = 'leave' AND @action IN ('calculate_hours', 'has_conflict')
    BEGIN
        SET @employee_id = NULLIF(LTRIM(RTRIM(@employee_id)), N'');
        IF @employee_id IS NULL OR LEN(@employee_id) > 10
           OR @employee_id 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)) > 366
        BEGIN
            RAISERROR(N'leave_time_range_invalid', 16, 1);
            RETURN;
        END;

        DECLARE @time_employee_number DECIMAL(10, 0);
        DECLARE @time_employee_id INT;
        SET @time_employee_number = CONVERT(DECIMAL(10, 0), @employee_id);
        IF @time_employee_number <> @current_employee_id
        BEGIN
            RAISERROR(N'leave_time_range_invalid', 16, 1);
            RETURN;
        END;
        SET @time_employee_id = CONVERT(INT, @time_employee_number);

        IF @action = 'has_conflict'
        BEGIN
            SELECT CONVERT(BIT, CASE WHEN EXISTS
            (
                SELECT 1
                FROM dbo.HR_EmpLeaveAloneTab
                WHERE hr_ela_empid = @time_employee_id
                  AND hr_ela_starttime < @end_local
                  AND hr_ela_finishtime > @start_local
                  AND ISNULL(hr_ela_cancelFlag, 0) = 0
            ) THEN 1 ELSE 0 END) AS has_conflict;
            RETURN;
        END;

        IF EXISTS
        (
            SELECT 1
            FROM
            (
                SELECT TOP (DATEDIFF(DAY, CONVERT(DATE, @start_local),
                                              CONVERT(DATE, @end_local)) + 1)
                    DATEADD(DAY, ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) - 1,
                            CONVERT(DATE, @start_local)) AS work_date
                FROM sys.all_objects
            ) AS requested_dates
            WHERE NOT EXISTS
            (
                SELECT 1 FROM dbo.HR_ScheduleTab
                WHERE HR_Schedule_employeeid = @time_employee_id
                  AND HR_Schedule_Date = requested_dates.work_date
            )
        )
        BEGIN
            RAISERROR(N'leave_calendar_schedule_missing', 16, 1);
            RETURN;
        END;

        IF EXISTS
        (
            SELECT HR_Schedule_Date
            FROM dbo.HR_ScheduleTab
            WHERE HR_Schedule_employeeid = @time_employee_id
              AND HR_Schedule_Date BETWEEN CONVERT(DATE, @start_local)
                                       AND CONVERT(DATE, @end_local)
            GROUP BY HR_Schedule_Date
            HAVING COUNT_BIG(*) <> 1
        )
        BEGIN
            RAISERROR(N'leave_calendar_schedule_duplicate', 16, 1);
            RETURN;
        END;

        IF EXISTS
        (
            SELECT 1 FROM dbo.HR_ScheduleTab
            WHERE HR_Schedule_employeeid = @time_employee_id
              AND HR_Schedule_Date BETWEEN CONVERT(DATE, @start_local)
                                       AND CONVERT(DATE, @end_local)
              AND ISNULL(HR_Schedule_IsWorkday, 0) = 1
              AND
              (
                  HR_Schedule_AMStartTime IS NULL OR HR_Schedule_AMEndTime IS NULL
                  OR HR_Schedule_PMStartTime IS NULL OR HR_Schedule_PMEndTime IS NULL
                  OR CONVERT(TIME, HR_Schedule_AMStartTime)
                     >= CONVERT(TIME, HR_Schedule_AMEndTime)
                  OR CONVERT(TIME, HR_Schedule_PMStartTime)
                     >= CONVERT(TIME, HR_Schedule_PMEndTime)
                  OR CONVERT(TIME, HR_Schedule_AMEndTime)
                     > CONVERT(TIME, HR_Schedule_PMStartTime)
              )
        )
        BEGIN
            RAISERROR(N'leave_calendar_shift_invalid', 16, 1);
            RETURN;
        END;

        ;WITH schedule_days AS
        (
            SELECT
                HR_Schedule_Date AS work_date,
                HR_Schedule_AMStartTime AS am_start_raw,
                HR_Schedule_AMEndTime AS am_end_raw,
                HR_Schedule_PMStartTime AS pm_start_raw,
                HR_Schedule_PMEndTime AS pm_end_raw
            FROM dbo.HR_ScheduleTab
            WHERE HR_Schedule_employeeid = @time_employee_id
              AND HR_Schedule_Date BETWEEN CONVERT(DATE, @start_local)
                                       AND CONVERT(DATE, @end_local)
              AND ISNULL(HR_Schedule_IsWorkday, 0) = 1
        ),
        work_segments AS
        (
            SELECT
                DATEADD(MILLISECOND,
                    DATEDIFF(MILLISECOND, CONVERT(DATE, am_start_raw), am_start_raw),
                    CONVERT(DATETIME, work_date)) AS segment_start,
                DATEADD(MILLISECOND,
                    DATEDIFF(MILLISECOND, CONVERT(DATE, am_end_raw), am_end_raw),
                    CONVERT(DATETIME, work_date)) AS segment_end
            FROM schedule_days
            UNION ALL
            SELECT
                DATEADD(MILLISECOND,
                    DATEDIFF(MILLISECOND, CONVERT(DATE, pm_start_raw), pm_start_raw),
                    CONVERT(DATETIME, work_date)),
                DATEADD(MILLISECOND,
                    DATEDIFF(MILLISECOND, CONVERT(DATE, pm_end_raw), pm_end_raw),
                    CONVERT(DATETIME, work_date))
            FROM schedule_days
        ),
        overlaps AS
        (
            SELECT
                CASE WHEN @start_local > segment_start THEN @start_local
                     ELSE segment_start END AS overlap_start,
                CASE WHEN @end_local < segment_end THEN @end_local
                     ELSE segment_end END AS overlap_end
            FROM work_segments
            WHERE segment_start < segment_end
              AND @start_local < segment_end
              AND @end_local > segment_start
        )
        SELECT CONVERT(DECIMAL(18, 4),
            ISNULL(SUM(CASE WHEN overlap_start < overlap_end
                            THEN DATEDIFF(SECOND, overlap_start, overlap_end)
                            ELSE 0 END), 0) / 3600.0) AS hours
        FROM overlaps;
        RETURN;
    END;

    IF @workflow = 'leave' AND @action = 'can_submit'
    BEGIN
        SET @record_id = NULLIF(LTRIM(RTRIM(@record_id)), N'');
        IF @record_id IS NULL
        BEGIN
            RAISERROR(N'leave_record_id_invalid', 16, 1);
            RETURN;
        END;

        IF
        (
            SELECT COUNT_BIG(*) FROM dbo.HR_EmpLeaveAloneTab
            WHERE CONVERT(NVARCHAR(32), hr_ela_id) = @record_id
               OR CONVERT(NVARCHAR(50), hr_ela_no) = @record_id
        ) > 1
        BEGIN
            RAISERROR(N'leave_record_identity_ambiguous', 16, 1);
            RETURN;
        END;

        DECLARE @record_employee_id INT;
        DECLARE @record_operator_id INT;
        DECLARE @record_affirmer INT;
        DECLARE @record_ban INT;
        DECLARE @record_stepover INT;
        DECLARE @record_cancel INT;
        DECLARE @record_stepdirection CHAR(1);
        DECLARE @record_start DATETIME;
        DECLARE @record_end DATETIME;
        DECLARE @record_hours FLOAT;

        SELECT
            @record_employee_id = hr_ela_empid,
            @record_operator_id = hr_ela_operatorid,
            @record_affirmer = hr_ela_Affirmer,
            @record_ban = hr_ela_ban,
            @record_stepover = hr_ela_stepover,
            @record_cancel = hr_ela_cancelFlag,
            @record_stepdirection = hr_ela_stepdirection,
            @record_start = hr_ela_starttime,
            @record_end = hr_ela_finishtime,
            @record_hours = hr_ela_totals
        FROM dbo.HR_EmpLeaveAloneTab
        WHERE CONVERT(NVARCHAR(32), hr_ela_id) = @record_id
           OR CONVERT(NVARCHAR(50), hr_ela_no) = @record_id;

        DECLARE @can_submit BIT;
        DECLARE @submit_reason NVARCHAR(128);
        SET @can_submit = 1;
        SET @submit_reason = N'ok';

        IF @record_employee_id IS NULL
        BEGIN
            SET @can_submit = 0;
            SET @submit_reason = N'record_not_found';
        END
        ELSE IF @record_employee_id <> @current_employee_id
             AND ISNULL(@record_operator_id, -1) <> @current_employee_id
             AND (@current_employee_id <> 1
                  OR @current_employee_name <> N'管理员')
             AND ISNULL(@current_department_id, -1) <> 1
        BEGIN
            SET @can_submit = 0;
            SET @submit_reason = N'record_owner_mismatch';
        END
        ELSE IF ISNULL(@record_cancel, 0) <> 0
        BEGIN
            SET @can_submit = 0;
            SET @submit_reason = N'record_cancelled';
        END
        ELSE IF ISNULL(@record_affirmer, 0) <> 0
             OR ISNULL(@record_stepover, 0) <> 0
             OR ISNULL(@record_ban, 0) <> 0
             OR ISNULL(@record_stepdirection, '') <> ''
        BEGIN
            SET @can_submit = 0;
            SET @submit_reason = N'record_workflow_locked';
        END
        ELSE IF @record_start IS NULL OR @record_end IS NULL
             OR @record_end <= @record_start OR ISNULL(@record_hours, 0) <= 0
        BEGIN
            SET @can_submit = 0;
            SET @submit_reason = N'record_content_invalid';
        END;

        SELECT @can_submit AS can_submit, @submit_reason AS reason;
        RETURN;
    END;

    RAISERROR(N'workflow_action_not_implemented', 16, 1);
    RETURN;
END;
GO
