{
  "validationRules": [
    {
      "id": "vrule_lead_post_convert_requires_view_edit_permission",
      "objectApiName": "Lead",
      "name": "Lead_Post_Convert_Requires_AllowViewEditConvertedLeads",
      "description": "Disabled for PingCAP: the tenant Lead lockRule keeps every converted Lead read-only, including for System Administrator. Corrections to conversion anchors use a scoped, audited repair instead of ordinary record editing.",
      "active": false,
      "condition": "PRIORVALUE(IsConverted) = TRUE && $Permission.AllowViewEditConvertedLeads != true",
      "errorMessage": "Editing a converted Lead requires the View and Edit Converted Leads permission."
    },
    {
      "id": "vrule_lead_isconverted_immutable_post_convert",
      "objectApiName": "Lead",
      "name": "Lead_IsConverted_Immutable_Post_Convert",
      "description": "Salesforce v63 parity: once a Lead is converted, IsConverted cannot be toggled back to false. Other Lead fields remain editable; per-anchor rules cover the other convert-anchor fields.",
      "active": true,
      "condition": "PRIORVALUE(IsConverted) = TRUE && ISCHANGED(IsConverted)",
      "errorMessage": "Cannot un-convert a converted lead."
    },
    {
      "id": "vrule_lead_converted_account_immutable",
      "objectApiName": "Lead",
      "name": "Lead_ConvertedAccountId_Immutable_Post_Convert",
      "description": "Salesforce v63 parity: ConvertedAccountId is stamped by the convert process and cannot be modified afterward.",
      "active": true,
      "condition": "PRIORVALUE(IsConverted) = TRUE && ISCHANGED(ConvertedAccountId)",
      "errorMessage": "ConvertedAccountId cannot be changed after lead conversion."
    },
    {
      "id": "vrule_lead_converted_contact_immutable",
      "objectApiName": "Lead",
      "name": "Lead_ConvertedContactId_Immutable_Post_Convert",
      "description": "Salesforce v63 parity: ConvertedContactId is stamped by the convert process and cannot be modified afterward.",
      "active": true,
      "condition": "PRIORVALUE(IsConverted) = TRUE && ISCHANGED(ConvertedContactId)",
      "errorMessage": "ConvertedContactId cannot be changed after lead conversion."
    },
    {
      "id": "vrule_lead_converted_opportunity_immutable",
      "objectApiName": "Lead",
      "name": "Lead_ConvertedOpportunityId_Immutable_Post_Convert",
      "description": "Salesforce v63 parity: ConvertedOpportunityId is stamped by the convert process and cannot be modified afterward.",
      "active": true,
      "condition": "PRIORVALUE(IsConverted) = TRUE && ISCHANGED(ConvertedOpportunityId)",
      "errorMessage": "ConvertedOpportunityId cannot be changed after lead conversion."
    },
    {
      "id": "vrule_lead_converted_date_immutable",
      "objectApiName": "Lead",
      "name": "Lead_ConvertedDate_Immutable_Post_Convert",
      "description": "Salesforce v63 parity: ConvertedDate is stamped by the convert process and cannot be modified afterward.",
      "active": true,
      "condition": "PRIORVALUE(IsConverted) = TRUE && ISCHANGED(ConvertedDate)",
      "errorMessage": "ConvertedDate cannot be changed after lead conversion."
    },
    {
      "id": "vrule_order_activate_requires_contract",
      "objectApiName": "Order",
      "name": "Order_Activate_Requires_Contract",
      "description": "Disabled for PingCAP: activation validates Account-level governing Contracts instead of Order.ContractId.",
      "active": false,
      "condition": "ISCHANGED(Status) && Status == \"Activated\" && ISBLANK(ContractId)",
      "errorMessage": "Cannot activate order: no Contract is associated"
    },
    {
      "id": "vrule_order_activate_requires_permission",
      "objectApiName": "Order",
      "name": "Order_Activate_Requires_Permission",
      "description": "Order activation requires the ActivateOrder permission on direct record updates as well as UI action invocation.",
      "active": true,
      "condition": "ISCHANGED(Status) && Status == \"Activated\" && $Permission.ActivateOrder != true",
      "errorMessage": "Cannot activate order: missing ActivateOrder permission"
    },
    {
      "id": "vrule_order_activate_contract_status",
      "objectApiName": "Order",
      "name": "Order_Activate_Contract_Status",
      "description": "Disabled for PingCAP: activation validates Account-level governing Contracts instead of Order.ContractId.",
      "active": false,
      "condition": "ISCHANGED(Status) && Status == \"Activated\" && !ISBLANK(ContractId) && Contract.Status != \"Activated\" && Contract.Status != \"Effective\"",
      "errorMessage": "Cannot activate order: the associated Contract must be in Activated/Effective status"
    },
    {
      "id": "vrule_order_activate_contract_start",
      "objectApiName": "Order",
      "name": "Order_Activate_Contract_Start",
      "description": "Disabled for PingCAP: activation validates Account-level governing Contracts instead of Order.ContractId.",
      "active": false,
      "condition": "ISCHANGED(Status) && Status == \"Activated\" && !ISBLANK(EffectiveDate) && (ISBLANK(Contract.StartDate) || Contract.StartDate > EffectiveDate)",
      "errorMessage": "Cannot activate order: the associated Contract must start on or before the Order EffectiveDate"
    },
    {
      "id": "vrule_order_activate_contract_end",
      "objectApiName": "Order",
      "name": "Order_Activate_Contract_End",
      "description": "Disabled for PingCAP: activation validates Account-level governing Contracts instead of Order.ContractId.",
      "active": false,
      "condition": "ISCHANGED(Status) && Status == \"Activated\" && !ISBLANK(EndDate) && (ISBLANK(Contract.EndDate) || Contract.EndDate < EndDate)",
      "errorMessage": "Cannot activate order: the associated Contract must end on or after the Order EndDate"
    },
    {
      "id": "vrule_order_post_activate_read_only",
      "objectApiName": "Order",
      "name": "Order_Post_Activate_Readonly",
      "description": "Prevents non-admin edits to activated Order records while allowing rollup-maintained fields to update: the finance rollups (Revenue and Collection child writes) and the OrderItem product rollups (TotalAmount, Count_Product_Lines__c, Net_Fees__c, Overall_Start_Date__c, Overall_End_Date__c \u2014 re-sourced to OrderItem in PR-B3). The engine performs those header writes in system context, where $Profile.Name is empty and the admin exemption can never match, so without these carve-outs any OrderItem change on an activated order rolls back the entire child transaction. The carve-outs open no user-edit hole: every exempted field is a rollup (createable:false, updateable:false), so caller-supplied writes are rejected at the field layer before validation runs.",
      "active": true,
      "condition": "NOT($SystemContext.IsDerivedRecompute) && (\n    PRIORVALUE(Status) == \"Activated\" && $Profile.Name <> \"System Administrator\" && NOT(OR(ISCHANGED(Accumulated_Credit_Amount__c), ISCHANGED(Accumulated_Payable_Amount__c), ISCHANGED(Accumulated_Revenue_Before_Adjust__c), ISCHANGED(Cash_Collected_Amount_USD__c), ISCHANGED(Cash_Collected_Amount__c), ISCHANGED(Cash_Collected_USD_exc_tax__c), ISCHANGED(Cash_Collected_exc_tax_Amount__c), ISCHANGED(Latest_Payable_Date__c), ISCHANGED(Payable_Amount_USD_exc_tax__c), ISCHANGED(Recognized_Revenue_Amount_USD__c), ISCHANGED(Recognized_Revenue_Amount__c), ISCHANGED(TotalAmount), ISCHANGED(Count_Product_Lines__c), ISCHANGED(Net_Fees__c), ISCHANGED(Overall_Start_Date__c), ISCHANGED(Overall_End_Date__c)))\n)",
      "errorMessage": "This order has already been activated. No further changes are allowed."
    },
    {
      "id": "vrule_contract_post_activate_lock",
      "objectApiName": "Contract",
      "name": "Contract_Post_Activate_Lifecycle_Lock",
      "description": "Locks core lifecycle fields on activated/effective contracts while still allowing lifecycle status progression.",
      "active": true,
      "condition": "(PRIORVALUE(Status) == \"Activated\" || PRIORVALUE(Status) == \"Effective\" || PRIORVALUE(Status) == \"Expired\" || PRIORVALUE(Status) == \"Terminated\" || Status == \"Activated\" || Status == \"Effective\" || Status == \"Expired\" || Status == \"Terminated\") && (ISCHANGED(AccountId) || ISCHANGED(StartDate) || ISCHANGED(ContractTerm) || ISCHANGED(EndDate) || ISCHANGED(Pricebook2Id))",
      "errorMessage": "Activated contracts lock core lifecycle fields; only allowed status transitions may proceed."
    },
    {
      "id": "vrule_sharing_rule_target_exactly_one",
      "objectApiName": "SharingRule",
      "name": "SharingRule_Target_Exactly_One",
      "description": "SharingRule records must target exactly one role or group through ShareWithRoleId or ShareWithGroupId.",
      "active": true,
      "condition": "(ISBLANK(ShareWithRoleId) && ISBLANK(ShareWithGroupId)) || (!ISBLANK(ShareWithRoleId) && !ISBLANK(ShareWithGroupId))",
      "errorMessage": "sharing rule must target exactly one role or group"
    },
    {
      "id": "vrule_sharing_rule_criteria_forbids_shared_from",
      "objectApiName": "SharingRule",
      "name": "SharingRule_Criteria_Forbids_SharedFrom",
      "description": "Criteria-based SharingRule records are defined by Criteria and cannot also carry SharedFromRoleId or SharedFromGroupId.",
      "active": true,
      "condition": "!ISBLANK(Criteria) && (!ISBLANK(SharedFromRoleId) || !ISBLANK(SharedFromGroupId))",
      "errorMessage": "criteria-based sharing rules cannot define SharedFromRoleId or SharedFromGroupId"
    },
    {
      "id": "vrule_sharing_rule_owner_based_requires_one_shared_from",
      "objectApiName": "SharingRule",
      "name": "SharingRule_Owner_Based_Requires_One_SharedFrom",
      "description": "Owner-based SharingRule records must define exactly one owner source through SharedFromRoleId or SharedFromGroupId.",
      "active": true,
      "condition": "ISBLANK(Criteria) && ((ISBLANK(SharedFromRoleId) && ISBLANK(SharedFromGroupId)) || (!ISBLANK(SharedFromRoleId) && !ISBLANK(SharedFromGroupId)))",
      "errorMessage": "owner-based sharing rules must define exactly one SharedFromRoleId or SharedFromGroupId"
    },
    {
      "id": "vrule_identitysyncsetting_singleton_name",
      "objectApiName": "IdentitySyncSetting",
      "name": "IdentitySyncSetting_Name_Must_Be_Default",
      "description": "IdentitySyncSetting is a singleton: the only permitted Name is \"default\". Combined with the UNIQUE constraint on Name, this makes at most one configuration row possible (Name=\"default\"). Public standard REST mutation surfaces are closed by the CRM dialect; the deploying L2 pack authors the default row and the serving layer reads only Name=\"default\".",
      "active": true,
      "condition": "Name != \"default\"",
      "errorMessage": "IdentitySyncSetting is a singleton; the only permitted Name is \"default\"."
    },
    {
      "id": "vrule_xml_account_fillincountry",
      "objectApiName": "Account",
      "name": "FillinCountry",
      "description": "",
      "active": false,
      "condition": "ISBLANK( BillingCountry)  &&  $Profile.Name != 'System Administrator'",
      "errorDisplayField": "BillingCountry",
      "errorMessage": "Please Complete this field."
    },
    {
      "id": "vrule_xml_account_require_billing_country_on_create",
      "objectApiName": "Account",
      "name": "Require_Billing_Country_On_Create",
      "description": "Require Billing Country when a NEW Account is created (ISNEW) \u2014 applies to manual, API, and lead-conversion account creation. Bypass: System Administrator, Integration User (automation / API / data loads). Creation-scoped only; edits of existing accounts are not affected. INTENDED & verified: NexusCRM has no 'enforce validation on lead convert' setting and lead conversion creates the Account via persistence.Insert, which always runs custom validation rules \u2014 so converting a country-less lead is blocked until its country is set (leads that already carry a country convert normally). STAGED: active=false; confirm in staging, then enable.",
      "active": false,
      "condition": "AND(ISNEW(), ISBLANK(BillingCountry), $Profile.Name != 'System Administrator', $Profile.Name != 'Integration User')",
      "errorDisplayField": "BillingCountry",
      "errorMessage": "Billing Country is required when creating an Account \u2014 the Quote / Order / Contract number is generated from its country code. Please select a Billing Country."
    },
    {
      "id": "vrule_xml_quote_require_account_country_code",
      "objectApiName": "Quote",
      "name": "Require_Account_Country_Code",
      "description": "Block creating a Quote whose Account has no Billing Country Code. The Quote number format is {Quote.Account_Country_Code__c}-QT-{YY}{MM}{DD}{00} (per_prefix), so a blank code yields a malformed number and a colliding empty-prefix counter. Bypass: System Administrator, Integration User. STAGED: active=false. DEV: verify in staging that Account_Country_Code__c is resolved at insert-time VR evaluation before enabling.",
      "active": false,
      "condition": "AND(ISNEW(), ISBLANK(Account_Country_Code__c), $Profile.Name != 'System Administrator', $Profile.Name != 'Integration User')",
      "errorDisplayField": null,
      "errorMessage": "This Quote's Account has no Billing Country Code, which the quote number is generated from. Set Billing Country on the Account first."
    },
    {
      "id": "vrule_xml_order_require_account_country_code",
      "objectApiName": "Order",
      "name": "Require_Account_Country_Code",
      "description": "Block creating an Order whose Account has no Billing Country Code. The Order number format is {Order.Account_Country_Code__c}-OF-{YY}{MM}{DD}{00} (per_prefix). Bypass: System Administrator, Integration User. STAGED: active=false. DEV: verify Account_Country_Code__c insert-time availability in staging before enabling.",
      "active": false,
      "condition": "AND(ISNEW(), ISBLANK(Account_Country_Code__c), $Profile.Name != 'System Administrator', $Profile.Name != 'Integration User')",
      "errorDisplayField": null,
      "errorMessage": "This Order's Account has no Billing Country Code, which the order number is generated from. Set Billing Country on the Account first."
    },
    {
      "id": "vrule_xml_contract_require_account_country_code",
      "objectApiName": "Contract",
      "name": "Require_Account_Country_Code",
      "description": "Block creating a Contract whose Account has no Billing Country Code. The Contract number format is {Contract.Account_Country_Code__c}-{RecordType.RecordTypeCode}-{YY}{MM}{DD}{00} (per_prefix). Bypass: System Administrator, Integration User. STAGED: active=false. DEV: verify Account_Country_Code__c insert-time availability in staging before enabling.",
      "active": false,
      "condition": "AND(ISNEW(), ISBLANK(Account_Country_Code__c), $Profile.Name != 'System Administrator', $Profile.Name != 'Integration User')",
      "errorDisplayField": null,
      "errorMessage": "This Contract's Account has no Billing Country Code, which the contract number is generated from. Set Billing Country on the Account first."
    },
    {
      "id": "vrule_xml_account_ska_and_ka_account_plan_is_required",
      "objectApiName": "Account",
      "name": "SKA_and_KA_Account_Plan_is_required",
      "description": "Account Plan information for SKA&KA is required",
      "active": false,
      "condition": "AND(OR(ISPICKVAL(Region__c,\"NA\"), ISPICKVAL(Region__c,\"EMEA\")), Account_Plan__c ='', OR(ISPICKVAL(Segmentation_For_NA_EMEA_Reference__c,\"SKA\"), ISPICKVAL(Segmentation_For_NA_EMEA_Reference__c,\"KA\")))",
      "errorDisplayField": "Account_Plan__c",
      "errorMessage": "Account Plan information for SKA&KA is required"
    },
    {
      "id": "vrule_xml_account_tam",
      "objectApiName": "Account",
      "name": "TAM",
      "description": "AND(\n    OR(\n        AND(ISNEW(), NOT(ISBLANK(TAM__c) )),\n        ISCHANGED(TAM__c)\n    ),\n    NOT(\n        OR(\n            $User.Id = OwnerId\n        )\n    )\n)",
      "active": true,
      "condition": "AND(\n    OR(\n        ISCHANGED(TAM__c)\n    ),\n    NOT(\n        OR(\n            $Profile.Name = \"System Administrator\",\n            $Profile.Name = \"PoC Profile\",\n            $User.Id = OwnerId\n        )\n    )\n)",
      "errorDisplayField": "TAM__c",
      "errorMessage": "Only account owners can edit."
    },
    {
      "id": "vrule_xml_account_salescannotchangeaccountowner",
      "objectApiName": "Account",
      "name": "salescannotchangeaccountowner",
      "description": "\u9500\u552e\u4e0d\u53ef\u4ee5\u53d8\u66f4\u5ba2\u6237\u6240\u6709\u4eba",
      "active": false,
      "condition": "ISCHANGED( OwnerId )  &&  $Profile.Name =\"sales\"",
      "errorDisplayField": null,
      "errorMessage": "If you need to change Account owner , please contact DM."
    },
    {
      "id": "vrule_xml_contact_validation_associateopportunitytocontact",
      "objectApiName": "Contact",
      "name": "Validation_AssociateOpportunitytoContact",
      "description": "on  Contact if anyone tries to change the value of Person Status to Open Opportunity, Customer, or Closed Lost,  and there is no opportunity associated with the contact",
      "active": false,
      "condition": "AND(\n!OpportunityAssociated__c,\nOR(\nISPICKVAL(Person_Status__c , 'Open Opportunity'),\nISPICKVAL(Person_Status__c , 'Customer'),\nISPICKVAL(Person_Status__c , 'Closed-Lost')\n))",
      "errorDisplayField": null,
      "errorMessage": "Please associate the contact with an opportunity before updating the Person Status to Open Opportunity, Customer, or Closed-Lost."
    },
    {
      "id": "vrule_xml_contract_contracttermsnolessthan12",
      "objectApiName": "Contract",
      "name": "ContractTermsnolessthan12",
      "description": "",
      "active": true,
      "condition": "ContractTerm < 12",
      "errorDisplayField": "ContractTerm",
      "errorMessage": "Contract Term (months) needs to be no less than 12."
    },
    {
      "id": "vrule_xml_invoice_collection_check_whether_has_actual_billing_date",
      "objectApiName": "Invoice_Collection__c",
      "name": "Check_whether_has_Actual_Billing_Date",
      "description": "",
      "active": true,
      "condition": "AND(OR(TEXT(Invoice_Status__c) = \"Issued\", LEN(Invoice_Number__c) >6),TEXT(Actual_Billing_Date__c)=\"\")",
      "errorDisplayField": "Actual_Billing_Date__c",
      "errorMessage": "Need Actual Billing Date."
    },
    {
      "id": "vrule_xml_lead_ae",
      "objectApiName": "Lead",
      "name": "AE",
      "description": "When \"IQM Scheduled\" box is checked, this field is required to be filled.",
      "active": true,
      "condition": "AND(OR( ISPICKVAL( Region__c , \"EMEA\") , ISPICKVAL( Region__c , \"NA\")),  IQM__c=TRUE, ISBLANK(AE__c)=TRUE)",
      "errorDisplayField": "AE__c",
      "errorMessage": "When \"IQM Scheduled\" box is checked, this field is required to be filled."
    },
    {
      "id": "vrule_xml_lead_ae_feedback_notes",
      "objectApiName": "Lead",
      "name": "AE_Feedback_Notes",
      "description": "AE Feedback Notes is required to convert NA/EMEA leads when IQM Completed is checked.",
      "active": false,
      "condition": "AND(\n  OR(\n    ISPICKVAL(Region__c, \"NA\"),\n    ISPICKVAL(Region__c, \"EMEA\")\n  ),\n  IQM_Completed__c,\n  LEN(TRIM(AE_Feedback_Notes__c)) = 0\n)",
      "errorDisplayField": "AE_Feedback_Notes__c",
      "errorMessage": "AE Feedback Notes is required to convert NA/EMEA leads when IQM Completed is checked."
    },
    {
      "id": "vrule_xml_lead_ae_feedback_score",
      "objectApiName": "Lead",
      "name": "AE_Feedback_Score",
      "description": "AE Feedback Score is required to convert NA or EMEA leads when IQM is completed.",
      "active": false,
      "condition": "AND(\n  OR(\n    ISPICKVAL(Region__c, \"EMEA\"),\n    ISPICKVAL(Region__c, \"NA\")\n  ),\n  IQM_Completed__c = TRUE,\n  ISBLANK(TEXT(AE_Feedback_Score__c))\n)",
      "errorDisplayField": "AE_Feedback_Score__c",
      "errorMessage": "AE Feedback Score is required to convert NA or EMEA leads when IQM is completed."
    },
    {
      "id": "vrule_xml_lead_ae_feedback_test",
      "objectApiName": "Lead",
      "name": "AE_Feedback_test",
      "description": "",
      "active": false,
      "condition": "AND(\n  IsConverted,\n  IQM_Completed__c,\n  LEN(TRIM(AE_Feedback_Notes__c)) = 0\n)",
      "errorDisplayField": null,
      "errorMessage": "AE Feedback Notes is required to convert NA/EMEA leads when IQM Completed is checked."
    },
    {
      "id": "vrule_xml_lead_ai_processed_lock",
      "objectApiName": "Lead",
      "name": "AI_Processed_Lock",
      "description": "",
      "active": true,
      "condition": "OR(\n    /* \u903b\u8f91A: \u5982\u679c\u5df2\u9501\u5b9a\uff0c\u7981\u6b62\u4fee\u6539\u90a3\u4e9b\u4e1a\u52a1\u5b57\u6bb5 */\n    AND(\n        AI_Processed__c = TRUE,\n$Profile.Name <> \"System Administrator\",   /* \u5141\u8bb8\u7ba1\u7406\u5458\u4fee\u6539 */\n    $Profile.Name <> \"Integration User\",       /* \u5141\u8bb8\u96c6\u6210\u8d26\u53f7/API\u4fee\u6539 */\n        OR(\n            ISCHANGED( Primary_Need_Pain__c ),\n            ISCHANGED( Need_Notes__c ),\n            ISCHANGED( Budget__c ),\n            ISCHANGED( Budget_Notes__c ),\n            ISCHANGED( Authority__c ),\n            ISCHANGED( Authority_Notes__c ),\n            ISCHANGED( Timing__c ),\n            ISCHANGED( Timing_Notes__c ),\n            ISCHANGED( Team__c ),\n            ISCHANGED( Team_Notes__c ),\n            ISCHANGED( Next_Steps__c ),\n            ISCHANGED( Next_Steps_Notes__c )\n        )\n    )\n)",
      "errorDisplayField": null,
      "errorMessage": "\"Since 'AI Processed' is checked, core qualification fields (BANT/Notes) are locked and cannot be modified.\""
    },
    {
      "id": "vrule_xml_lead_apac_region_converted_lead_rule",
      "objectApiName": "Lead",
      "name": "APAC_region_converted_lead_rule",
      "description": "",
      "active": false,
      "condition": "text(Approvalstatus__c)  <> \"Approved\" &&  text(Region__c) =\"APAC\" &&  IsConverted =TRUE && ISCHANGED(IsConverted)",
      "errorDisplayField": null,
      "errorMessage": "Only approved lead can converted."
    },
    {
      "id": "vrule_xml_lead_createoppuponconversion",
      "objectApiName": "Lead",
      "name": "CreateOppUponConversion",
      "description": "IsConverted && ConvertedOpportunityId = null  &&  ISCHANGED(IsConverted)",
      "active": true,
      "condition": "AND(\n    /* \u8df3\u8fc7\uff1aSystem Administrator / Sales Ops */\n    NOT(\n        OR(\n            $Profile.Name = \"System Administrator\",\n            $Profile.Name = \"Sales Ops\",\n            $Profile.Name = \"Legal\"\n        )\n    ),\n\n    IsConverted,\n    ISBLANK(ConvertedOpportunityId),\n    ISCHANGED(IsConverted)\n)",
      "errorDisplayField": null,
      "errorMessage": "All converted leads must have an opportunity."
    },
    {
      "id": "vrule_xml_lead_disqualified_date",
      "objectApiName": "Lead",
      "name": "Disqualified_Date",
      "description": "",
      "active": true,
      "condition": "AND(OR( ISPICKVAL( Region__c , \"EMEA\") , ISPICKVAL( Region__c , \"NA\")),   ISPICKVAL(Lifecycle_Stage__c, \"68053680\"), ISBLANK( Disqualified_Date__c  )=TRUE, OR(ISNEW(), TEXT(PRIORVALUE(Lifecycle_Stage__c)) <> \"68053680\"))",
      "errorDisplayField": "Disqualified_Date__c",
      "errorMessage": "When Lead Life Stage is moved to \"Disqualified\", this field for \"Disqualified Date\" is required to be filled."
    },
    {
      "id": "vrule_xml_lead_existing_database",
      "objectApiName": "Lead",
      "name": "Existing_Database",
      "description": "Existing Database information from IQM",
      "active": true,
      "condition": "AND( OR( ISPICKVAL( Region__c , \"EMEA\") , ISPICKVAL( Region__c , \"NA\")),IQM_Completed__c =TRUE, ISBLANK( Existing_Database__c )=TRUE )",
      "errorDisplayField": "Existing_Database__c",
      "errorMessage": "Completed IQM must fill the \"Existing Database\" field"
    },
    {
      "id": "vrule_xml_lead_iqm_completed_date",
      "objectApiName": "Lead",
      "name": "IQM_Completed_Date",
      "description": "Completed IQM must fill the \"IQM Completed Date\" field",
      "active": true,
      "condition": "AND(OR( ISPICKVAL( Region__c , \"EMEA\") , ISPICKVAL( Region__c , \"NA\")), IQM_Completed__c=TRUE,  ISBLANK( IQM_Completed_Date__c ) =TRUE)",
      "errorDisplayField": "IQM_Completed_Date__c",
      "errorMessage": "Completed IQM must fill the \"IQM Completed Date\" field"
    },
    {
      "id": "vrule_xml_lead_iqm_scheduled_date",
      "objectApiName": "Lead",
      "name": "IQM_Scheduled_Date",
      "description": "When \"IQM Scheduled\" box is checked, this field is required to be filled.",
      "active": true,
      "condition": "AND(OR( ISPICKVAL( Region__c , \"EMEA\") , ISPICKVAL( Region__c , \"NA\")),  IQM__c=TRUE, ISBLANK( IQM_Date__c )=TRUE)",
      "errorDisplayField": "IQM_Date__c",
      "errorMessage": "When \"IQM Scheduled\" box is checked, this field is required to be filled."
    },
    {
      "id": "vrule_xml_lead_lead_cloud_vendor",
      "objectApiName": "Lead",
      "name": "Lead_Cloud_Vendor",
      "description": "Prospect Cloud Vendor information from IQM",
      "active": true,
      "condition": "AND(OR( ISPICKVAL( Region__c , \"EMEA\") , ISPICKVAL( Region__c , \"NA\")), IQM_Completed__c=TRUE, ISPICKVAL( Cloudd_Vendor__c, ''))",
      "errorDisplayField": "Cloudd_Vendor__c",
      "errorMessage": "Completed IQM must fill the \"Cloud Vendor\" field"
    },
    {
      "id": "vrule_xml_lead_lead_iqm_note",
      "objectApiName": "Lead",
      "name": "Lead_IQM_Note",
      "description": "Note from IQM",
      "active": false,
      "condition": "AND(OR( ISPICKVAL( Region__c , \"EMEA\") , ISPICKVAL( Region__c , \"NA\")), IQM_Completed__c =TRUE, ISBLANK( IQM_Note_NA_Region_Use_Only__c )=TRUE )",
      "errorDisplayField": "IQM_Note_NA_Region_Use_Only__c",
      "errorMessage": "Completed IQM must fill the \"IQM Note (NA&EMEA Region Use Only)\" field"
    },
    {
      "id": "vrule_xml_lead_lead_industry",
      "objectApiName": "Lead",
      "name": "Lead_Industry",
      "description": "Prospect Industry information from IQM",
      "active": true,
      "condition": "AND(OR( ISPICKVAL( Region__c , \"EMEA\") , ISPICKVAL( Region__c , \"NA\")), IQM_Completed__c=TRUE, ISPICKVAL(  Industry , \"\"))",
      "errorDisplayField": "Industry",
      "errorMessage": "Completed IQM must fill the \"Industry\" field"
    },
    {
      "id": "vrule_xml_lead_lead_job_role",
      "objectApiName": "Lead",
      "name": "Lead_Job_Role",
      "description": "Job Role is required if IQM has been completed",
      "active": true,
      "condition": "AND(OR( ISPICKVAL( Region__c , \"EMEA\") , ISPICKVAL( Region__c , \"NA\")), IQM_Completed__c=TRUE, ISPICKVAL( Job_Role__c, ''))",
      "errorDisplayField": "Job_Role__c",
      "errorMessage": "Completed IQM must fill the \"Job Role\" field"
    },
    {
      "id": "vrule_xml_lead_lead_mysql_family_capture",
      "objectApiName": "Lead",
      "name": "Lead_MySQL_Family_Capture",
      "description": "Prospect MySQL Family Capture information from IQM",
      "active": true,
      "condition": "AND(OR( ISPICKVAL( Region__c , \"EMEA\") , ISPICKVAL( Region__c , \"NA\")), IQM_Completed__c=TRUE, ISPICKVAL( MySQL_Family_Capture__c , ''))",
      "errorDisplayField": "MySQL_Family_Capture__c",
      "errorMessage": "Completed IQM must fill the \"MySQL Family Capture\" field"
    },
    {
      "id": "vrule_xml_lead_lead_status_to_meeting_completed",
      "objectApiName": "Lead",
      "name": "Lead_Status_to_Meeting_Completed",
      "description": "Completed IQM must move Lead Status to \u201cMeeting Completed\u201d",
      "active": true,
      "condition": "AND(OR( ISPICKVAL( Region__c , \"EMEA\") , ISPICKVAL( Region__c , \"NA\")), IQM_Completed__c=TRUE, ISPICKVAL( Status , \"\"))",
      "errorDisplayField": "Status",
      "errorMessage": "Completed IQM must move Lead Status to \u201cMeeting Completed\u201d"
    },
    {
      "id": "vrule_xml_lead_lead_tcp",
      "objectApiName": "Lead",
      "name": "Lead_TCP",
      "description": "Prospect TCP information from IQM",
      "active": true,
      "condition": "AND(OR( ISPICKVAL( Region__c , \"EMEA\") , ISPICKVAL( Region__c , \"NA\")), IQM_Completed__c=TRUE, ISPICKVAL( TCP_NA__c , ''))",
      "errorDisplayField": "TCP_NA__c",
      "errorMessage": "Completed IQM must fill the \"TCP (For NA&EMEA Only)\" field"
    },
    {
      "id": "vrule_xml_lead_nurture_marketing_date",
      "objectApiName": "Lead",
      "name": "Nurture_Marketing_Date",
      "description": "Nurture - Marketing Date",
      "active": false,
      "condition": "AND(OR( ISPICKVAL( Region__c , \"EMEA\") , ISPICKVAL( Region__c , \"NA\")), ISPICKVAL( Nurture_Reason__c , \"Nuture - Marketing\"), ISBLANK(  Nurture_Marketing_Date__c  )=TRUE)",
      "errorDisplayField": "Nurture_Marketing_Date__c",
      "errorMessage": "When Nurture Reason is \"Nurture - Marketing\", this field \"Nurture - Marketing Date\" is required to be filled."
    },
    {
      "id": "vrule_xml_lead_nurture_sales_date",
      "objectApiName": "Lead",
      "name": "Nurture_Sales_Date",
      "description": "Nurture - Sales Date",
      "active": false,
      "condition": "AND(OR( ISPICKVAL( Region__c , \"EMEA\") , ISPICKVAL( Region__c , \"NA\")), ISPICKVAL( Nurture_Reason__c , \"Nuture - Sales\"), ISBLANK(  Nurture_Sales_Date__c )=TRUE)",
      "errorDisplayField": "Nurture_Sales_Date__c",
      "errorMessage": "When Nurture Reason is \"Nurture - Sales\", this field \"Nurture Reason Date\" is required to be filled."
    },
    {
      "id": "vrule_xml_lead_reassign_lead_owner_to_the_assigned_ae",
      "objectApiName": "Lead",
      "name": "Reassign_Lead_Owner_to_the_assigned_AE",
      "description": "Completed IQM must Reassign \u201cLead Owner\u201d to the assigned AE\u201d",
      "active": false,
      "condition": "AND(OR( ISPICKVAL( Region__c , \"EMEA\") , ISPICKVAL( Region__c , \"NA\")), IQM_Completed__c=TRUE,  AE__c <> OwnerId)",
      "errorDisplayField": "OwnerId",
      "errorMessage": "Completed IQM must Reassign \u201cLead Owner\u201d to the assigned AE\u201d"
    },
    {
      "id": "vrule_xml_lead_sdr_na_and_emea_only",
      "objectApiName": "Lead",
      "name": "SDR_NA_and_EMEA_ONLY",
      "description": "When \"IQM Scheduled\" box is checked, this field is required to be filled.",
      "active": true,
      "condition": "AND(OR( ISPICKVAL( Region__c , \"EMEA\") , ISPICKVAL( Region__c , \"NA\")),  IQM__c=TRUE, ISBLANK( SDR_For_NA_Only__c)=TRUE)",
      "errorDisplayField": "SDR_For_NA_Only__c",
      "errorMessage": "When \"IQM Scheduled\" box is checked, this field is required to be filled."
    },
    {
      "id": "vrule_xml_lead_sdr_note_na_and_emea_only",
      "objectApiName": "Lead",
      "name": "SDR_Note_NA_and_EMEA_ONLY",
      "description": "When \"IQM Scheduled\" box is checked, this field is required to be filled.",
      "active": true,
      "condition": "AND(OR( ISPICKVAL( Region__c , \"EMEA\") , ISPICKVAL( Region__c , \"NA\")),  IQM__c=TRUE, ISBLANK(SDR_Note_NA_EMEA_ONLY__c)=TRUE)",
      "errorDisplayField": "SDR_Note_NA_EMEA_ONLY__c",
      "errorMessage": "When \"IQM Scheduled\" box is checked, this field is required to be filled."
    },
    {
      "id": "vrule_xml_lead_segmentation",
      "objectApiName": "Lead",
      "name": "Segmentation",
      "description": "Prospect Segmentation information from IQM",
      "active": true,
      "condition": "AND(OR( ISPICKVAL( Region__c , \"EMEA\") , ISPICKVAL( Region__c , \"NA\")),  IQM__c=TRUE, ISPICKVAL( Segmentation_For_NA_EMEA_Reference__c , ''))",
      "errorDisplayField": "Segmentation_For_NA_EMEA_Reference__c",
      "errorMessage": "Completed IQM must fill the Segmentation\" field"
    },
    {
      "id": "vrule_xml_lead_sourcecanonlyupdatedbycreater",
      "objectApiName": "Lead",
      "name": "SourcecanonlyupdatedbyCreater",
      "description": "",
      "active": false,
      "condition": "OR(AND(OR(ISCHANGED( Person_Source__c ),ISCHANGED(  Person_Source_Detail__c  ),ISCHANGED(  Recent_Source__c  ),ISCHANGED(  Recent_Source_Detail__c  )), CreatedBy.Id  <>  LastModifiedById) , AND(OR(ISCHANGED( Person_Source__c ),ISCHANGED(  Person_Source_Detail__c  ),ISCHANGED(  Recent_Source__c  ),ISCHANGED(  Recent_Source_Detail__c  )), LastModifiedBy.UserRole.Name <> \"Business_Ops\"))",
      "errorDisplayField": null,
      "errorMessage": "Source can only be updated by who created the Lead."
    },
    {
      "id": "vrule_xml_lead_validation_accountportalid_not_null",
      "objectApiName": "Lead",
      "name": "Validation_AccountPortalId_Not_Null",
      "description": "",
      "active": false,
      "condition": "LeadValidation__c",
      "errorDisplayField": null,
      "errorMessage": "This Account is already associated with a TiDB Cloud Org ID. Pls convert this Lead to a new Account. Any questions pls contact your Salesforce Admin."
    },
    {
      "id": "vrule_xml_lead_validation_convertleadtocontact",
      "objectApiName": "Lead",
      "name": "Validation_ConvertLeadtoContact",
      "description": "On Lead if anyone tries to change the value of Person Status to Open Opportunity, Customer, or Closed Lost",
      "active": false,
      "condition": "AND(OR(\nISPICKVAL(Person_Status__c , 'Open Opportunity'), \nISPICKVAL(Person_Status__c , 'Customer'),\nISPICKVAL(Person_Status__c , 'Closed-Lost')\n),IsConverted = false)",
      "errorDisplayField": null,
      "errorMessage": "Please convert the lead to a contact, and associate it with an account and an opportunity"
    },
    {
      "id": "vrule_xml_opportunity_arramountvalidationrule",
      "objectApiName": "Opportunity",
      "name": "ARRAmountValidationRule",
      "description": "",
      "active": true,
      "condition": "NOT($SystemContext.IsDerivedRecompute) && (\n    (RecordType.DeveloperName = 'Downscale' || RecordType.DeveloperName = 'Churn' ) &&  ARR_Amount_USD__c >=0\n)",
      "errorDisplayField": "ARR_Amount_USD__c",
      "errorMessage": "Downscale/Churn record type requires Negative ARR AMT entry (-)."
    },
    {
      "id": "vrule_xml_opportunity_allowclosewon",
      "objectApiName": "Opportunity",
      "name": "AllowCloseWon",
      "description": "",
      "active": true,
      "condition": "AND(\n  NOT(CONTAINS($Profile.Name, \"System Administrator\")),\n  TEXT(StageName) = \"Deal Closed\",\n  ISCHANGED(StageName),\n  OR(\n    AND(\n      Allow_Close__c = TRUE,\n      NOT(\n        OR(\n          CONTAINS($Profile.Name , \"DM\"),\n          CONTAINS($UserRole.Name , \"Finance\"),\n          $UserRole.Name = \"Business Ops\"\n        )\n      )\n    ),\n    Allow_Close__c = FALSE\n  )\n)",
      "errorDisplayField": null,
      "errorMessage": "Only DM Can mark Deal Closed When Order & Contract are Signed."
    },
    {
      "id": "vrule_xml_opportunity_allowlaunch",
      "objectApiName": "Opportunity",
      "name": "AllowLaunch",
      "description": "",
      "active": false,
      "condition": "text(StageName) = \"Launched\" \n&& ISCHANGED(StageName)  \n&& NOT(CONTAINS( $Profile.Name , \"System Administrator\" ))",
      "errorDisplayField": null,
      "errorMessage": "Sales cannot change to Launch stage directly, please ensure you input an accurate Launch Date."
    },
    {
      "id": "vrule_xml_opportunity_aurora_take_out_data_volume",
      "objectApiName": "Opportunity",
      "name": "Aurora_take_out_data_volume",
      "description": "",
      "active": false,
      "condition": "text( Taking_Aurora_s_share__c )='Yes'&&isblank(  TEXT(Aurora_Data_Volume__c  ))",
      "errorDisplayField": "Aurora_Data_Volume__c",
      "errorMessage": "Aurora take out pipelines must input Data Volume."
    },
    {
      "id": "vrule_xml_opportunity_aurora_take_out_instance",
      "objectApiName": "Opportunity",
      "name": "Aurora_take_out_instance",
      "description": "",
      "active": false,
      "condition": "text(  Taking_Aurora_s_share__c  )='Yes'&&isblank(  Aurora_Instance_Type_1st__c  )",
      "errorDisplayField": "Aurora_Instance_Type_1st__c",
      "errorMessage": "Aurora take out pipelines must input Instance Type."
    },
    {
      "id": "vrule_xml_opportunity_cloud_order_type_for_40_opp",
      "objectApiName": "Opportunity",
      "name": "Cloud_Order_Type_for_40_Opp",
      "description": "",
      "active": true,
      "condition": "NOT($SystemContext.IsDerivedRecompute) && (\n    AND(ISBLANK(HubSpot_Id__c), AND(Probability>=0.4, $Profile.Name <> 'System Administrator', ISBLANK( TEXT( Cloud_Order_Type__c  ))=TRUE,  TEXT(Product_Family__c) =\"TiDB Cloud\" ))\n)",
      "errorDisplayField": "Cloud_Order_Type__c",
      "errorMessage": "Opportunities with Probability >=40% must input Cloud Order Type."
    },
    {
      "id": "vrule_xml_opportunity_cloud_vendor_for_40_opp",
      "objectApiName": "Opportunity",
      "name": "Cloud_Vendor_for_40_Opp",
      "description": "",
      "active": true,
      "condition": "NOT($SystemContext.IsDerivedRecompute) && (\n    AND(ISBLANK(HubSpot_Id__c), AND(Probability>=0.4,$Profile.Name <> 'System Administrator',  ISBLANK( Cloud_Vendor__c )=TRUE,  TEXT(Product_Family__c) =\"TiDB Cloud\" ))\n)",
      "errorDisplayField": "Cloud_Vendor__c",
      "errorMessage": "Opportunities with Probability >=40% must chose Cloud Vendor."
    },
    {
      "id": "vrule_xml_opportunity_commit_risk_rating",
      "objectApiName": "Opportunity",
      "name": "Commit_Risk_Rating",
      "description": "",
      "active": true,
      "condition": "NOT($SystemContext.IsDerivedRecompute) && (\n    OR(text(  Forecast_Type__c )='Commit',text(  Forecast_Type__c )='Upside')&&isblank( text(Risk_Rating__c ))\n)",
      "errorDisplayField": "Risk_Rating__c",
      "errorMessage": "Commit or Upside Opportunities must have Risk Rating"
    },
    {
      "id": "vrule_xml_opportunity_data_volume_size_for_40_opp",
      "objectApiName": "Opportunity",
      "name": "Data_Volume_Size_for_40_Opp",
      "description": "AND(Probability>=0.4, ISBLANK(  Data_Volume_Size__c )=TRUE)",
      "active": true,
      "condition": "NOT($SystemContext.IsDerivedRecompute) && (\n    AND(ISBLANK(HubSpot_Id__c), AND(\n        $Profile.Name <> 'System Administrator',\n        Probability >= 0.4,\n        ISBLANK(Data_Volume_Size__c)\n    ))\n)",
      "errorDisplayField": "Data_Volume_Size__c",
      "errorMessage": "Opportunities with Probability >=40% must input Data Volume Size."
    },
    {
      "id": "vrule_xml_opportunity_existing_db_delpoyment_remarks",
      "objectApiName": "Opportunity",
      "name": "Existing_DB_Delpoyment_Remarks",
      "description": "",
      "active": true,
      "condition": "NOT($SystemContext.IsDerivedRecompute) && (\n    INCLUDES(  Existing_Database_Deployment__c ,'Others')&&isblank(  Existing_Database_Deployment_Remarks__c )\n)",
      "errorDisplayField": "Existing_Database_Deployment_Remarks__c",
      "errorMessage": "Pls specify which deployment in this field."
    },
    {
      "id": "vrule_xml_opportunity_existing_database_deployment_remarks",
      "objectApiName": "Opportunity",
      "name": "Existing_Database_Deployment_Remarks",
      "description": "",
      "active": true,
      "condition": "NOT($SystemContext.IsDerivedRecompute) && (\n    OR(\n      /* \u9009\u4e86 Others \u4f46\u5907\u6ce8\u6ca1\u586b */\n      AND(\n        INCLUDES(Existing_Database_Deployment__c, \"Others\"),\n        ISBLANK(Existing_Database_Deployment_Remarks__c)\n      )\n\n  \n    )\n)",
      "errorDisplayField": "Existing_Database_Deployment_Remarks__c",
      "errorMessage": "Please specify which Cloud / Infra Provider in the Remarks field only if \"Others\" is selected in the Cloud / Infra Provider."
    },
    {
      "id": "vrule_xml_opportunity_existing_database_deployment_for_40_opp",
      "objectApiName": "Opportunity",
      "name": "Existing_database_deployment_for_40_Opp",
      "description": "",
      "active": false,
      "condition": "AND(Probability>=0.4,$Profile.Name <> 'System Administrator', ISNULL( Existing_Database_Deployment__c )=TRUE)",
      "errorDisplayField": "Existing_Database_Deployment__c",
      "errorMessage": "Opportunities with Probability >=40% must chose Existing Database Deployment."
    },
    {
      "id": "vrule_xml_opportunity_forecast_type",
      "objectApiName": "Opportunity",
      "name": "Forecast_Type",
      "description": "OR(AND(text( Forecast_Type__c )=\"Upside\", OR (text (StageName) = \"Prospecting\",text(StageName)=\"Evaluation\")),AND(text( Forecast_Type__c )=\"Commit\", OR (text (StageName) = \"Prospecting\",text(StageName)=\"Evaluation\",text(StageName)=\"Qualification\")))",
      "active": true,
      "condition": "NOT($SystemContext.IsDerivedRecompute) && (\n    OR(AND(text( Forecast_Type__c )=\"Upside\", OR (text (StageName) = \"Prospecting\",text(StageName)=\"Discovery & Qualification\")),AND(text( Forecast_Type__c )=\"Commit\", OR (text (StageName) = \"Prospecting\",text(StageName)=\"Discovery & Qualification\",text(StageName)=\"Technical Validation\",text(StageName)=\"Business Validation\")))\n)",
      "errorDisplayField": null,
      "errorMessage": "Forecast Type inconsistant with Sales Stage."
    },
    {
      "id": "vrule_xml_opportunity_fulfillment_channel_with_50_opp",
      "objectApiName": "Opportunity",
      "name": "Fulfillment_Channel_with_50_Opp",
      "description": "AND(Probability>=0.5, ISBLANK( TEXT( Fulfillment_Channel__c  ))=TRUE )",
      "active": true,
      "condition": "NOT($SystemContext.IsDerivedRecompute) && (\n    AND(ISBLANK(HubSpot_Id__c), AND(\n        $Profile.Name <> 'System Administrator',\n        Probability >= 0.5,\n        ISBLANK(TEXT(Fulfillment_Channel__c))\n    ))\n)",
      "errorDisplayField": "Fulfillment_Channel__c",
      "errorMessage": "Opportunities with Probability >=50% must chose Fulfillment Channel."
    },
    {
      "id": "vrule_xml_opportunity_launchdaterequired",
      "objectApiName": "Opportunity",
      "name": "LaunchDateRequired",
      "description": "stage3-6,Launch Date required",
      "active": true,
      "condition": "NOT($SystemContext.IsDerivedRecompute) && (\n    ISBLANK(Launch_Date__c) &&\n    (\n      ISPICKVAL(StageName, \"Validation\") ||\n      ISPICKVAL(StageName, \"Negotiating\") ||\n      ISPICKVAL(StageName, \"Contract Signed\") ||\n      ISPICKVAL(StageName, \"Launched\")\n    )\n)",
      "errorDisplayField": "Launch_Date__c",
      "errorMessage": "Launch Date is mandatory"
    },
    {
      "id": "vrule_xml_opportunity_loss_reason",
      "objectApiName": "Opportunity",
      "name": "Loss_Reason",
      "description": "",
      "active": true,
      "condition": "NOT($SystemContext.IsDerivedRecompute) && (\n    AND (ISPICKVAL(StageName, \"Closed Lost\"),\n    ISBLANK(TEXT(Loss_Reason__c))\n    )\n)",
      "errorDisplayField": "Loss_Reason__c",
      "errorMessage": "Closed Lost Opp pls input Loss Reason"
    },
    {
      "id": "vrule_xml_opportunity_loss_reason_notes",
      "objectApiName": "Opportunity",
      "name": "Loss_Reason_Notes",
      "description": "",
      "active": true,
      "condition": "AND(\n    ISCHANGED(StageName),\n    ISPICKVAL(StageName, \"Closed Lost\"),\n    LEN(Loss_Reason_Notes__c) < 15\n)",
      "errorDisplayField": "Loss_Reason_Notes__c",
      "errorMessage": "More detail needed. Loss Reason Notes must contain at least 15 characters."
    },
    {
      "id": "vrule_xml_opportunity_notcloneclosedopp",
      "objectApiName": "Opportunity",
      "name": "NotCloneClosedOpp",
      "description": "",
      "active": true,
      "condition": "AND(\nISCLONE(),\nISNEW(),\nOR(\nISPICKVAL(StageName,\"Closed Won\"),\nISPICKVAL(StageName,\"Closed Lost\")\n))",
      "errorDisplayField": null,
      "errorMessage": "Closed Opportunities cannot be Cloned."
    },
    {
      "id": "vrule_xml_opportunity_opportunitynewmustprospecting",
      "objectApiName": "Opportunity",
      "name": "OpportunityNewMustProspecting",
      "description": "",
      "active": true,
      "condition": "ISNEW() &&  TEXT(StageName) != 'Prospecting' &&  ISBLANK(awsapn__APN_CRM_Id__c)",
      "errorDisplayField": "StageName",
      "errorMessage": "Opportunity needs to start from Stage 1 - Prospecting."
    },
    {
      "id": "vrule_xml_opportunity_partner_validation",
      "objectApiName": "Opportunity",
      "name": "Partner_Validation",
      "description": "",
      "active": true,
      "condition": "NOT($SystemContext.IsDerivedRecompute) && (\n    text( Sales_Channel_Type__c )='Indirect'&&isblank( Partner_Name__c )\n)",
      "errorDisplayField": "Partner_Name__c",
      "errorMessage": "Indirect deals must have a valid Partner Name."
    },
    {
      "id": "vrule_xml_opportunity_pingcap_contract_entity_emea_only",
      "objectApiName": "Opportunity",
      "name": "PingCAP_Contract_Entity_EMEA_only",
      "description": "",
      "active": false,
      "condition": "Region__c='EMEA'&&isblank(text( PingCAP_Contracting_Entity_EMEA_only__c ))&& Probability>0.1",
      "errorDisplayField": "PingCAP_Contracting_Entity_EMEA_only__c",
      "errorMessage": "For EMEA region, it is mandatory to select the PingCAP Contract Entity before the Quote is created, to determine whether the Self-Sustained process applies to this deal."
    },
    {
      "id": "vrule_xml_opportunity_replacing_db_remarks",
      "objectApiName": "Opportunity",
      "name": "Replacing_DB_Remarks",
      "description": "OR(\n  /* \u9009\u4e86 Others \u4f46\u5907\u6ce8\u6ca1\u586b */\n  AND(\n    INCLUDES(Replacing_Database__c, \"Others\"),\n    ISBLANK(Replacing_Database_Remarks__c)\n\nPls specify which Databases in this field.",
      "active": true,
      "condition": "NOT($SystemContext.IsDerivedRecompute) && (\n    OR(\n      /* \u9009\u4e86 Others \u4f46\u5907\u6ce8\u6ca1\u586b */\n      AND(\n        INCLUDES(Replacing_Database__c, \"Others\"),\n        ISBLANK(Replacing_Database_Remarks__c)\n      )\n    )\n)",
      "errorDisplayField": "Replacing_Database_Remarks__c",
      "errorMessage": "Please specify which Databases in the Remarks field only if \"Others\" is selected in Replacing Database."
    },
    {
      "id": "vrule_xml_opportunity_require_arr_amount_usd",
      "objectApiName": "Opportunity",
      "name": "Require_ARR_Amount_USD",
      "description": "",
      "active": true,
      "condition": "NOT($SystemContext.IsDerivedRecompute) && (\n    AND(\n        $Profile.Name <> 'System Administrator',\n        NOT(ISPICKVAL(StageName, 'Prospecting')),\n        NOT(ISPICKVAL(StageName, 'Discovery & Qualification')),\n        ISBLANK(ARR_Amount_USD__c),\n        NOT(\n            OR(\n                ISCHANGED(Elvance__Elvance_Owner__c),\n                ISCHANGED(Elvance_Scorecard_Score__c),\n                ISCHANGED(Elvance__Elvance_Stage__c)\n            )\n        )\n    )\n)",
      "errorDisplayField": "ARR_Amount_USD__c",
      "errorMessage": "ARR Amount (USD) should be specified for New/Expansion opportunities on/beyond stage Technical Validation."
    },
    {
      "id": "vrule_xml_opportunity_require_opportunity_source",
      "objectApiName": "Opportunity",
      "name": "Require_Opportunity_Source",
      "description": "AND(\n    $Profile.Name <> 'System Administrator',\n    NOT(ISPICKVAL(StageName, 'Prospecting')),\n    NOT(ISPICKVAL(StageName, 'Discovery & Qualification')),\n    ISBLANK(TEXT(Opportunity_Source__c))\n)",
      "active": true,
      "condition": "NOT($SystemContext.IsDerivedRecompute) && (\n    AND(\n        $Profile.Name <> 'System Administrator',\n        NOT(ISPICKVAL(StageName, 'Prospecting')),\n        NOT(ISPICKVAL(StageName, 'Discovery & Qualification')),\n        ISBLANK(TEXT(Opportunity_Source__c)),\n        OR(\n            RecordType.DeveloperName = 'New_Subscription',\n            RecordType.DeveloperName = 'Expansion'\n        )\n    )\n)",
      "errorDisplayField": "Opportunity_Source__c",
      "errorMessage": "Opportunity Source should be specified for New/Expansion opportunities on/beyond stage Technical Validation."
    },
    {
      "id": "vrule_xml_opportunity_risk_and_netx_step",
      "objectApiName": "Opportunity",
      "name": "Risk_and_Netx_Step",
      "description": "Risk and Next Step must have minimum of 50 characters.",
      "active": false,
      "condition": "AND(( Region__c =\"NA\"),   (TEXT( StageName )<> \"Prospecting\"), LEN( Risk_and_Next_Step__c )<50)",
      "errorDisplayField": "Risk_and_Next_Step__c",
      "errorMessage": "Risk and Next Step must have minimum of 50 characters."
    },
    {
      "id": "vrule_xml_opportunity_sync_with_aws_launched",
      "objectApiName": "Opportunity",
      "name": "Sync_with_AWS_Launched",
      "description": "NOT(ISBLANK( awsapn__APN_CRM_Id__c )) &&",
      "active": false,
      "condition": "awsapn__Sync_with_Partner_Central__c = True && $Profile.Id != '00e5g000001QgR8' &&  \n NOT(ISBLANK( awsapn__APN_CRM_Id__c )) &&\ntext(PingCAP_Stage__c) = \"Launched\" && \nOR(ISBLANK( text(Procurement_Type__c )),\nISBLANK( Customer_Software_Value__c ),\nISBLANK( Contract_Start_Date__c ),\nISBLANK(  Contract_End_Date__c  ))",
      "errorDisplayField": null,
      "errorMessage": "When Sycn with AWS and Stage is Launched, pls input Procurement Type, Customer Software Value, Contract Start Date and Contract End Date."
    },
    {
      "id": "vrule_xml_opportunity_sync_with_aws_lostreason",
      "objectApiName": "Opportunity",
      "name": "Sync_with_AWS_LostReason",
      "description": "awsapn__Sync_with_Partner_Central__c = True &&\nISBLANK(awsapn__APN_CRM_Id__c) &&\ntext(PingCAP_Stage__c) = \"Closed Lost\" && \nISBLANK( text( AWS_Lost_Reason__c  ))",
      "active": false,
      "condition": "awsapn__Sync_with_Partner_Central__c = True &&\ntext(PingCAP_Stage__c) = \"Closed Lost\" && \nISBLANK( text( AWS_Lost_Reason__c  ))",
      "errorDisplayField": "AWS_Lost_Reason__c",
      "errorMessage": "When Sycn with AWS and Stage is Closed Lost, pls input Lost Reason."
    },
    {
      "id": "vrule_xml_opportunity_sync_with_aws_mktfunded",
      "objectApiName": "Opportunity",
      "name": "Sync_with_AWS_MKTFunded",
      "description": "",
      "active": false,
      "condition": "awsapn__Sync_with_Partner_Central__c = True &&\nISBLANK(awsapn__APN_CRM_Id__c) &&\nISBLANK( Text(Is_Marketing_Development_Funded__c )) &&\n Text(Is_Opp_From_Marketing_Activity__c) = \"Yes\"",
      "errorDisplayField": "Is_Marketing_Development_Funded__c",
      "errorMessage": "\"isMarketingDevelopmentFunded\" is mandatory like either yes or no if IsOppFromMarketingActivity is Yes"
    },
    {
      "id": "vrule_xml_opportunity_sync_with_aws_mandatory",
      "objectApiName": "Opportunity",
      "name": "Sync_with_AWS_Mandatory",
      "description": "",
      "active": false,
      "condition": "awsapn__Sync_with_Partner_Central__c = True &&   \n ISBLANK( awsapn__APN_CRM_Id__c ) && \n(OR(ISBLANK(Company_Name_for_AWS__c) ,\nISBLANK( text(Industry_for_AWS__c )),\nISBLANK( text(Country_for_AWS__c )),\nISBLANK( Postal_Code_for_AWS__c ),\nISBLANK( Website_for_AWS__c ),\nISBLANK(  AWS_Project_Title__c ),\nISBLANK( AWS_Project_Description__c ),\nISBLANK(text( Partner_Primary_Need_From_AWS__c) ),\nISBLANK( text(AWS_Use_Case__c )),\nISBLANK( Expected_Monthly_AWS_Revenue__c ),\nISBLANK(text( Is_Net_New_Business_For_Company__c)),\nISBLANK(text( AWS_Field_Engagement__c )),\nISBLANK( text(Is_This_For_Marketplace__c )),\nISBLANK( text(Is_Opp_From_Marketing_Activity__c ))\n))",
      "errorDisplayField": null,
      "errorMessage": "Sync with AWS Opportunities must input Company Name (for AWS),Industry (for AWS),Country (for AWS),Postal Code (for AWS),Website (for AWS),Project Name,AWS Project Description,Partner Primary Need From AWS,AWS Use Case,Expected Monthly AWS Revenue,etc."
    },
    {
      "id": "vrule_xml_opportunity_sync_with_aws_mandatory_us_states",
      "objectApiName": "Opportunity",
      "name": "Sync_with_AWS_Mandatory_US_States",
      "description": "",
      "active": false,
      "condition": "awsapn__Sync_with_Partner_Central__c = True &&\nISBLANK(awsapn__APN_CRM_Id__c) &&\nTEXT( Country_for_AWS__c) = \"United States\" && \nISBLANK( TEXT(State_for_AWS__c ))",
      "errorDisplayField": "State_for_AWS__c",
      "errorMessage": "State is mandantory for Sync-with-AWS Opportunities for United Status."
    },
    {
      "id": "vrule_xml_opportunity_sync_with_aws_mandatory_for_public_secto",
      "objectApiName": "Opportunity",
      "name": "Sync_with_AWS_Mandatory_for_Public_Secto",
      "description": "",
      "active": false,
      "condition": "awsapn__Sync_with_Partner_Central__c = True &&\nISBLANK(awsapn__APN_CRM_Id__c) &&\n(OR(TEXT(Industry_for_AWS__c) = \"Education\",\nTEXT(Industry_for_AWS__c) = \"Government\")) && \n(OR(ISBLANK( AWS_Contract_Vehicle__c ) ,\nISBLANK( RFX_Solicitation_Number__c  ))\n)",
      "errorDisplayField": "AWS_Contract_Vehicle__c",
      "errorMessage": "AWS Contract Vehicle and RFX Solicitation Number are mandantory for Sync-with-AWS Opportunities from Public Sector (Education or Government)."
    },
    {
      "id": "vrule_xml_opportunity_sync_with_aws_not_from_mkt",
      "objectApiName": "Opportunity",
      "name": "Sync_with_AWS_Not_From_MKT",
      "description": "",
      "active": false,
      "condition": "awsapn__Sync_with_Partner_Central__c = True &&\nISBLANK(awsapn__APN_CRM_Id__c) &&\nText( Is_Opp_From_Marketing_Activity__c )=\"No\" &&\nOR (ISBLANK ( Text(Is_Marketing_Development_Funded__c )) = FALSE,\nISBLANK ( Text(AWS_Campaign_Name__c)  ) = FALSE)",
      "errorDisplayField": "Is_Marketing_Development_Funded__c",
      "errorMessage": "If IsOppFromMarketingActivity is set to No, is Marketing Development Funded and AWS Campaign Name have to be blank."
    },
    {
      "id": "vrule_xml_opportunity_sync_with_aws_project_description",
      "objectApiName": "Opportunity",
      "name": "Sync_with_AWS_Project_Description",
      "description": "",
      "active": false,
      "condition": "awsapn__Sync_with_Partner_Central__c = True &&\nISBLANK(awsapn__APN_CRM_Id__c) &&\nLEN( AWS_Project_Description__c )<50",
      "errorDisplayField": "AWS_Project_Description__c",
      "errorMessage": "AWS Project Description must have minimum of 50 characters."
    },
    {
      "id": "vrule_xml_opportunity_sync_with_aws_rejectreason",
      "objectApiName": "Opportunity",
      "name": "Sync_with_AWS_RejectReason",
      "description": "",
      "active": false,
      "condition": "awsapn__Sync_with_Partner_Central__c = True &&\ntext(PingCAP_Acceptance_Status__c) = \"Rejected\" && \nISBLANK( text(AWS_Rejection_reason__c))",
      "errorDisplayField": "AWS_Rejection_reason__c",
      "errorMessage": "When Sycn with AWS and Status is Rejected, pls input Rejection Reason."
    },
    {
      "id": "vrule_xml_opportunity_total_of_mysql_clusters_for_40_opp",
      "objectApiName": "Opportunity",
      "name": "Total_of_MySQL_Clusters_for_40_Opp",
      "description": "",
      "active": true,
      "condition": "NOT($SystemContext.IsDerivedRecompute) && (\n    AND( TEXT(Data_Consolidation__c)=\"Yes\",\n    TEXT(MySQL_Family_Capture__c)=\"Yes\",  \n    ISBLANK(  Total_of_MySQL_Clusters__c  )=TRUE)\n)",
      "errorDisplayField": "Total_of_MySQL_Clusters__c",
      "errorMessage": "Opportunities with \"Data Consolidation\" must input Total # of MySQL Clusters."
    },
    {
      "id": "vrule_xml_opportunity_othersolutionoffered",
      "objectApiName": "Opportunity",
      "name": "otherSolutionOffered",
      "description": "",
      "active": false,
      "condition": "AND( Solution_offered_for_AWS__c = 'Other', ISBLANK( Other_Solution_Offered_for_AWS__c ) )",
      "errorDisplayField": "Other_Solution_Offered_for_AWS__c",
      "errorMessage": "When solutionOffered = \"Other\"  Other Solution Offered is required"
    },
    {
      "id": "vrule_xml_opportunity_required_to_fill_in_meddic",
      "objectApiName": "Opportunity",
      "name": "required_to_fill_in_MEDDIC",
      "description": "",
      "active": false,
      "condition": "and(text( StageName )!=\"Prospecting\" ,text( StageName )!=\"Discovery\" ) && ( LEN( Metrics__c)=0 ||  LEN( Economic_Buyer__c ) =0   ||  LEN( Identify_Pain__c ) =0   ||  LEN(Competition__c ) =0   ||  LEN( Champion__c ) =0)",
      "errorDisplayField": null,
      "errorMessage": "Pls fill in the MEDDIC details, especially the information of Metrics, Economic Buyer, Indentify Pain, Champion and Competition."
    },
    {
      "id": "vrule_order_create_requires_quote_service_period",
      "objectApiName": "Order",
      "name": "Order_Create_Requires_Quote_Service_Period",
      "description": "Quote-sourced Order creation requires a canonical governing service period before checking governing Contracts.",
      "active": true,
      "condition": "ISNEW() && !ISBLANK(QuoteId) && (Governing_Contract_Family__c == \"Cloud\" || Governing_Contract_Family__c == \"OP\") && (ISBLANK(Governing_Service_Start_Date__c) || ISBLANK(Governing_Service_End_Date__c))",
      "errorMessage": "Cannot create order: the Quote must have a complete service period."
    },
    {
      "id": "vrule_order_create_requires_covering_cloud_csa",
      "objectApiName": "Order",
      "name": "Order_Create_Requires_Covering_Cloud_CSA",
      "description": "Cloud Order creation from a Quote requires the Account to have a CSA/CSAO Contract covering the governing service period; Contract status is ignored at creation.",
      "active": true,
      "condition": "ISNEW() && !ISBLANK(QuoteId) && Governing_Contract_Family__c == \"Cloud\" && !ISBLANK(Governing_Service_Start_Date__c) && !ISBLANK(Governing_Service_End_Date__c) && NOT(HasCoveringCloudCSAForOrder)",
      "errorMessage": "Cannot create order: the Account must have a CSA Contract covering the Order service period.",
      "recordSetBindings": [
        {
          "name": "HasCoveringCloudCSAForOrder",
          "mode": "exists",
          "objectApiName": "Contract",
          "filters": [
            {
              "field": "AccountId",
              "operator": "equals",
              "valueSource": "recordPath",
              "value": "AccountId"
            },
            {
              "field": "RecordType.Name",
              "operator": "in",
              "valueSource": "literal",
              "value": [
                "TiDB Cloud Services Agreement",
                "TiDB Cloud Services Agreement Online"
              ]
            },
            {
              "field": "StartDate",
              "operator": "lessOrEqual",
              "valueSource": "recordPath",
              "value": "Governing_Service_Start_Date__c"
            },
            {
              "field": "EndDate",
              "operator": "greaterOrEqual",
              "valueSource": "recordPath",
              "value": "Governing_Service_End_Date__c"
            }
          ]
        }
      ]
    },
    {
      "id": "vrule_order_create_requires_covering_op_ma",
      "objectApiName": "Order",
      "name": "Order_Create_Requires_Covering_OP_MA",
      "description": "OP Order creation from a Quote requires the Account to have an MA Contract covering the governing service period; Contract status is ignored at creation.",
      "active": true,
      "condition": "ISNEW() && !ISBLANK(QuoteId) && Governing_Contract_Family__c == \"OP\" && !ISBLANK(Governing_Service_Start_Date__c) && !ISBLANK(Governing_Service_End_Date__c) && NOT(HasCoveringOPMAForOrder)",
      "errorMessage": "Cannot create order: the Account must have a Master Agreement covering the Order service period.",
      "recordSetBindings": [
        {
          "name": "HasCoveringOPMAForOrder",
          "mode": "exists",
          "objectApiName": "Contract",
          "filters": [
            {
              "field": "AccountId",
              "operator": "equals",
              "valueSource": "recordPath",
              "value": "AccountId"
            },
            {
              "field": "RecordType.Name",
              "operator": "equals",
              "valueSource": "literal",
              "value": "PingCAP Master Agreement"
            },
            {
              "field": "StartDate",
              "operator": "lessOrEqual",
              "valueSource": "recordPath",
              "value": "Governing_Service_Start_Date__c"
            },
            {
              "field": "EndDate",
              "operator": "greaterOrEqual",
              "valueSource": "recordPath",
              "value": "Governing_Service_End_Date__c"
            }
          ]
        }
      ]
    },
    {
      "id": "vrule_order_activate_requires_order_service_period",
      "objectApiName": "Order",
      "name": "Order_Activate_Requires_Order_Service_Period",
      "description": "Order activation requires a canonical governing service period before checking governing Contracts.",
      "active": true,
      "condition": "ISCHANGED(Status) && Status == \"Activated\" && (Governing_Contract_Family__c == \"Cloud\" || Governing_Contract_Family__c == \"OP\") && (ISBLANK(Governing_Service_Start_Date__c) || ISBLANK(Governing_Service_End_Date__c))",
      "errorMessage": "Cannot activate order: the Order must have a complete service period."
    },
    {
      "id": "vrule_order_activate_requires_covering_cloud_csa",
      "objectApiName": "Order",
      "name": "Order_Activate_Requires_Covering_Cloud_CSA",
      "description": "Quote-sourced Cloud Order activation requires the Account to have a CSA/CSAO Contract covering the governing service period.",
      "active": true,
      "condition": "ISCHANGED(Status) && Status == \"Activated\" && !ISBLANK(QuoteId) && Governing_Contract_Family__c == \"Cloud\" && !ISBLANK(Governing_Service_Start_Date__c) && !ISBLANK(Governing_Service_End_Date__c) && NOT(HasCoveringCloudCSAForOrder)",
      "errorMessage": "Cannot activate order: the Account must have a CSA Contract covering the Order service period.",
      "recordSetBindings": [
        {
          "name": "HasCoveringCloudCSAForOrder",
          "mode": "exists",
          "objectApiName": "Contract",
          "filters": [
            {
              "field": "AccountId",
              "operator": "equals",
              "valueSource": "recordPath",
              "value": "AccountId"
            },
            {
              "field": "RecordType.Name",
              "operator": "in",
              "valueSource": "literal",
              "value": [
                "TiDB Cloud Services Agreement",
                "TiDB Cloud Services Agreement Online"
              ]
            },
            {
              "field": "StartDate",
              "operator": "lessOrEqual",
              "valueSource": "recordPath",
              "value": "Governing_Service_Start_Date__c"
            },
            {
              "field": "EndDate",
              "operator": "greaterOrEqual",
              "valueSource": "recordPath",
              "value": "Governing_Service_End_Date__c"
            }
          ]
        }
      ]
    },
    {
      "id": "vrule_order_activate_requires_effective_cloud_csa",
      "objectApiName": "Order",
      "name": "Order_Activate_Requires_Effective_Cloud_CSA",
      "description": "Quote-sourced Cloud Order activation requires a covering CSA/CSAO Contract to be Effective.",
      "active": true,
      "condition": "ISCHANGED(Status) && Status == \"Activated\" && !ISBLANK(QuoteId) && Governing_Contract_Family__c == \"Cloud\" && HasCoveringCloudCSAForOrder && NOT(HasEffectiveCoveringCloudCSAForOrder)",
      "errorMessage": "Cannot activate order: the covering CSA Contract must be Effective.",
      "recordSetBindings": [
        {
          "name": "HasCoveringCloudCSAForOrder",
          "mode": "exists",
          "objectApiName": "Contract",
          "filters": [
            {
              "field": "AccountId",
              "operator": "equals",
              "valueSource": "recordPath",
              "value": "AccountId"
            },
            {
              "field": "RecordType.Name",
              "operator": "in",
              "valueSource": "literal",
              "value": [
                "TiDB Cloud Services Agreement",
                "TiDB Cloud Services Agreement Online"
              ]
            },
            {
              "field": "StartDate",
              "operator": "lessOrEqual",
              "valueSource": "recordPath",
              "value": "Governing_Service_Start_Date__c"
            },
            {
              "field": "EndDate",
              "operator": "greaterOrEqual",
              "valueSource": "recordPath",
              "value": "Governing_Service_End_Date__c"
            }
          ]
        },
        {
          "name": "HasEffectiveCoveringCloudCSAForOrder",
          "mode": "exists",
          "objectApiName": "Contract",
          "filters": [
            {
              "field": "AccountId",
              "operator": "equals",
              "valueSource": "recordPath",
              "value": "AccountId"
            },
            {
              "field": "RecordType.Name",
              "operator": "in",
              "valueSource": "literal",
              "value": [
                "TiDB Cloud Services Agreement",
                "TiDB Cloud Services Agreement Online"
              ]
            },
            {
              "field": "Status",
              "operator": "equals",
              "valueSource": "literal",
              "value": "Effective"
            },
            {
              "field": "StartDate",
              "operator": "lessOrEqual",
              "valueSource": "recordPath",
              "value": "Governing_Service_Start_Date__c"
            },
            {
              "field": "EndDate",
              "operator": "greaterOrEqual",
              "valueSource": "recordPath",
              "value": "Governing_Service_End_Date__c"
            }
          ]
        }
      ]
    },
    {
      "id": "vrule_order_activate_requires_covering_op_ma",
      "objectApiName": "Order",
      "name": "Order_Activate_Requires_Covering_OP_MA",
      "description": "OP Order activation requires the Account to have an MA Contract covering the governing service period.",
      "active": true,
      "condition": "ISCHANGED(Status) && Status == \"Activated\" && Governing_Contract_Family__c == \"OP\" && !ISBLANK(Governing_Service_Start_Date__c) && !ISBLANK(Governing_Service_End_Date__c) && NOT(HasCoveringOPMAForOrder)",
      "errorMessage": "Cannot activate order: the Account must have a Master Agreement covering the Order service period.",
      "recordSetBindings": [
        {
          "name": "HasCoveringOPMAForOrder",
          "mode": "exists",
          "objectApiName": "Contract",
          "filters": [
            {
              "field": "AccountId",
              "operator": "equals",
              "valueSource": "recordPath",
              "value": "AccountId"
            },
            {
              "field": "RecordType.Name",
              "operator": "equals",
              "valueSource": "literal",
              "value": "PingCAP Master Agreement"
            },
            {
              "field": "StartDate",
              "operator": "lessOrEqual",
              "valueSource": "recordPath",
              "value": "Governing_Service_Start_Date__c"
            },
            {
              "field": "EndDate",
              "operator": "greaterOrEqual",
              "valueSource": "recordPath",
              "value": "Governing_Service_End_Date__c"
            }
          ]
        }
      ]
    },
    {
      "id": "vrule_order_activate_requires_effective_op_ma",
      "objectApiName": "Order",
      "name": "Order_Activate_Requires_Effective_OP_MA",
      "description": "OP Order activation requires a covering MA Contract to be Effective.",
      "active": true,
      "condition": "ISCHANGED(Status) && Status == \"Activated\" && Governing_Contract_Family__c == \"OP\" && HasCoveringOPMAForOrder && NOT(HasEffectiveCoveringOPMAForOrder)",
      "errorMessage": "Cannot activate order: the covering Master Agreement must be Effective.",
      "recordSetBindings": [
        {
          "name": "HasCoveringOPMAForOrder",
          "mode": "exists",
          "objectApiName": "Contract",
          "filters": [
            {
              "field": "AccountId",
              "operator": "equals",
              "valueSource": "recordPath",
              "value": "AccountId"
            },
            {
              "field": "RecordType.Name",
              "operator": "equals",
              "valueSource": "literal",
              "value": "PingCAP Master Agreement"
            },
            {
              "field": "StartDate",
              "operator": "lessOrEqual",
              "valueSource": "recordPath",
              "value": "Governing_Service_Start_Date__c"
            },
            {
              "field": "EndDate",
              "operator": "greaterOrEqual",
              "valueSource": "recordPath",
              "value": "Governing_Service_End_Date__c"
            }
          ]
        },
        {
          "name": "HasEffectiveCoveringOPMAForOrder",
          "mode": "exists",
          "objectApiName": "Contract",
          "filters": [
            {
              "field": "AccountId",
              "operator": "equals",
              "valueSource": "recordPath",
              "value": "AccountId"
            },
            {
              "field": "RecordType.Name",
              "operator": "equals",
              "valueSource": "literal",
              "value": "PingCAP Master Agreement"
            },
            {
              "field": "Status",
              "operator": "equals",
              "valueSource": "literal",
              "value": "Effective"
            },
            {
              "field": "StartDate",
              "operator": "lessOrEqual",
              "valueSource": "recordPath",
              "value": "Governing_Service_Start_Date__c"
            },
            {
              "field": "EndDate",
              "operator": "greaterOrEqual",
              "valueSource": "recordPath",
              "value": "Governing_Service_End_Date__c"
            }
          ]
        }
      ]
    },
    {
      "id": "vrule_xml_order_product_canotlowerthanthequotedprice",
      "objectApiName": "Order_Product__c",
      "name": "Canotlowerthanthequotedprice",
      "description": "",
      "active": true,
      "condition": "NOT(ISBLANK(Unit_Net_Price__c)) && NOT(ISBLANK(Quote_Product__r.Unit_Net_Price__c)) && Unit_Net_Price__c < Quote_Product__r.Unit_Net_Price__c",
      "errorDisplayField": null,
      "errorMessage": "Order Price Can't Lower Than Quoted Price."
    },
    {
      "id": "vrule_xml_poc_cloud_id_required_for_cloud_poc",
      "objectApiName": "PoC__c",
      "name": "Cloud_ID_Required_for_Cloud_PoC",
      "description": "",
      "active": false,
      "condition": "AND(!ISNULL(TiDB_Cloud_Account_ID_to_run_This_PoC__c),   CONTAINS(RecordType.DeveloperName, \"Cloud\"),\nText(ApprovalStatus__c) = \"In Process\")",
      "errorDisplayField": null,
      "errorMessage": "Cloud PoC must have valid Cloud ID before submission for approval."
    },
    {
      "id": "vrule_xml_poc_modify_poc_autoapprove",
      "objectApiName": "PoC__c",
      "name": "Modify_PoC_Autoapprove",
      "description": "",
      "active": true,
      "condition": "AND(\n(ISCHANGED( Presales_Auto_approve__c )&& \nNOT(CONTAINS( $Profile.Name , \"System Administrator\" ))\n),\n(ISCHANGED( Presales_Auto_approve__c )&&\nNOT(CONTAINS( $Profile.Name , \"Pre-Sales\" ))\n))",
      "errorDisplayField": null,
      "errorMessage": "Only Pre-sales Can  Modify Presales Auto-approve Field"
    },
    {
      "id": "vrule_xml_poc_poc_failed_reason",
      "objectApiName": "PoC__c",
      "name": "PoC_Failed_Reason",
      "description": "",
      "active": true,
      "condition": "AND (ISPICKVAL(PoC_Status__c, \"Failed\"),\nISBLANK( PoC_Failed_Reason__c )\n)",
      "errorDisplayField": "PoC_Failed_Reason__c",
      "errorMessage": "Pls input Failed Reason or link to the PoC Doc."
    },
    {
      "id": "vrule_xml_poc_fill_in_poc_evaluation_at_approval",
      "objectApiName": "PoC__c",
      "name": "fill_in_PoC_evaluation_at_approval",
      "description": "",
      "active": false,
      "condition": "Business_Leader_Approval_or_not__c =true",
      "errorDisplayField": null,
      "errorMessage": "Pls fill in the \"Use Case & Persona Scoring\" and the \"Interest & Motivation Scoring\"."
    },
    {
      "id": "vrule_xml_quote_cloud_vendor",
      "objectApiName": "Quote",
      "name": "Cloud_Vendor",
      "description": "",
      "active": true,
      "condition": "NOT($SystemContext.IsDerivedRecompute) && (\n    AND(TEXT(Product_Family__c) = \"TiDB Cloud\", TEXT(Cloud_Vendor__c) = \"\", IsConvertQuote__c = False)\n)",
      "errorDisplayField": "Cloud_Vendor__c",
      "errorMessage": "For Quote of TiDB Cloud, \"Cloud Vendor\" is mandatory."
    },
    {
      "id": "vrule_xml_quote_customer_s_hardware_configuration",
      "objectApiName": "Quote",
      "name": "Customer_s_Hardware_Configuration",
      "description": "",
      "active": false,
      "condition": "AND(TEXT(Product_Family__c) = \"TiDB Enterprise Subscription\", LEN(Customer_s_Hardware_Configuration__c)< 6, IsConvertQuote__c = False)",
      "errorDisplayField": "Customer_s_Hardware_Configuration__c",
      "errorMessage": "For Quote of TiDB Enterprise Subscription, it is mandatory for Sales to provide the customer's server product type name, server quantity, CPU number per server and CPU product type name."
    },
    {
      "id": "vrule_xml_quote_customers_cluster_configuration",
      "objectApiName": "Quote",
      "name": "Customers_Cluster_Configuration",
      "description": "",
      "active": true,
      "condition": "NOT($SystemContext.IsDerivedRecompute) && (\n    AND(TEXT(Product_Family__c) = \"TiDB Cloud\",  LEN(Customer_s_Cluster_Configuration__c)<6, LEN(Customer_s_Consumption_in_Last_3_Months__c)<6, IsConvertQuote__c = False)\n)",
      "errorDisplayField": "Customer_s_Cluster_Configuration__c",
      "errorMessage": "For Quote of TiDB Cloud, please provide \"Customer's Cluster Configuration\" or \"Customer's Consumption in Last 3 Months\"."
    },
    {
      "id": "vrule_xml_quote_customers_consumption_in_last_3_months",
      "objectApiName": "Quote",
      "name": "Customers_Consumption_in_Last_3_Months",
      "description": "",
      "active": true,
      "condition": "NOT($SystemContext.IsDerivedRecompute) && (\n    AND(TEXT(Product_Family__c) = \"TiDB Cloud\",  LEN(Customer_s_Cluster_Configuration__c)<6, LEN(Customer_s_Consumption_in_Last_3_Months__c)<6, IsConvertQuote__c = False)\n)",
      "errorDisplayField": "Customer_s_Consumption_in_Last_3_Months__c",
      "errorMessage": "For Quote of TiDB Cloud, please provide \"Customer's Cluster Configuration\" or \"Customer's Consumption in Last 3 Months\"."
    },
    {
      "id": "vrule_xml_quote_details_of_vcpu_calculation",
      "objectApiName": "Quote",
      "name": "Details_of_vCPU_Calculation",
      "description": "",
      "active": false,
      "condition": "AND(TEXT(Product_Family__c) = \"TiDB Enterprise Subscription\", LEN(Details_of_vCPU_Calculation__c)< 6, IsConvertQuote__c = False)",
      "errorDisplayField": "Details_of_vCPU_Calculation__c",
      "errorMessage": "For Quote of TiDB Enterprise Subscription, \"Details of vCPU# Calculation\" is mandatory."
    },
    {
      "id": "vrule_xml_quote_minimum_commitment_amount",
      "objectApiName": "Quote",
      "name": "Minimum_Commitment_Amount",
      "description": "",
      "active": true,
      "condition": "NOT($SystemContext.IsDerivedRecompute) && (\n    AND(TEXT(Product_Family__c) = \"TiDB Cloud\", ISBLANK(Minimum_Commitment_Amount__c)= True, IsConvertQuote__c = False)\n)",
      "errorDisplayField": "Minimum_Commitment_Amount__c",
      "errorMessage": "For Quote of TiDB Cloud, \"Minimum Commitment Amount\" is mandatory."
    },
    {
      "id": "vrule_xml_quote_partner_margin_should_have_value",
      "objectApiName": "Quote",
      "name": "Partner_Margin_should_have_value",
      "description": "",
      "active": true,
      "condition": "NOT($SystemContext.IsDerivedRecompute) && (\n    AND(ISBLANK(Partner_Margin__c) = True, LEN(Partner_Name__c)>5, IsConvertQuote__c = False)\n)",
      "errorDisplayField": "Partner_Margin__c",
      "errorMessage": "For indirect deal, you should input \"Partner Margin (%)\" before you submit this Quote for approval. If the partner will be compensated via rebate instead of a margin, please input \"0\"."
    },
    {
      "id": "vrule_xml_quote_rebate_fee_rate",
      "objectApiName": "Quote",
      "name": "Rebate_Fee_Rate",
      "description": "",
      "active": true,
      "condition": "NOT($SystemContext.IsDerivedRecompute) && (\n    AND(ISBLANK(Rebate_Fee_Rate__c) = True, LEN(Partner_Name__c)>5, IsConvertQuote__c = False)\n)",
      "errorDisplayField": "Rebate_Fee_Rate__c",
      "errorMessage": "For indirect deal, you should input \"Rebate Fee Rate\" before you submit this Quote for approval. If the partner will be compensated via a margin instead of a rebate, please input \"0\"."
    },
    {
      "id": "vrule_xml_quote_tidb_cloud_support_tier",
      "objectApiName": "Quote",
      "name": "TiDB_Cloud_Support_Tier",
      "description": "",
      "active": true,
      "condition": "NOT($SystemContext.IsDerivedRecompute) && (\n    AND(TEXT(Product_Family__c) = \"TiDB Cloud\", TEXT(TiDB_Cloud_Support_Tier__c)=\"\",IsConvertQuote__c = False)\n)",
      "errorDisplayField": "TiDB_Cloud_Support_Tier__c",
      "errorMessage": "For Quote of TiDB Cloud, \"TiDB Cloud Support Tier\" is mandatory."
    },
    {
      "id": "vrule_xml_quote_version_of_tidb",
      "objectApiName": "Quote",
      "name": "Version_of_TiDB",
      "description": "",
      "active": false,
      "condition": "AND(TEXT(Product_Family__c) = \"TiDB Enterprise Subscription\", ISBLANK(Version_of_TiDB__c)= True, IsConvertQuote__c = False)",
      "errorDisplayField": "Version_of_TiDB__c",
      "errorMessage": "For Quote of TiDB Enterprise Subscription, \"Version of TiDB\" is mandatory."
    },
    {
      "id": "vrule_xml_task_weekly_update_progress",
      "objectApiName": "Task",
      "name": "Weekly_Update_Progress",
      "description": "Progress must have minimum of 150 characters.",
      "active": true,
      "condition": "AND(TEXT(Type) = \"Weekly Update\", (Region__c =\"NA\"), LEN( Progress__c )<150)",
      "errorDisplayField": "Progress__c",
      "errorMessage": "Progress must have minimum of 150 characters."
    },
    {
      "id": "vrule_xml_opportunity_account_must_be_approved",
      "objectApiName": "Opportunity",
      "name": "Account_Must_Be_Approved",
      "description": "Block Opportunity creation, and reparenting onto a different Account, when the parent Account has not been approved via the Account approval process.",
      "active": true,
      "condition": "AND(\n    OR(ISNEW(), ISCHANGED(AccountId)),\n    NOT($SystemContext.IsDerivedRecompute),\n    NOT(ISBLANK(AccountId)),\n    NOT(ISBLANK(TEXT(Account.ApprovalStatus__c))),\n    TEXT(Account.ApprovalStatus__c) <> \"Approved\",\n    $Profile.Name <> \"System Administrator\",\n    $Profile.Name <> \"Integration User\"\n)",
      "errorDisplayField": "AccountId",
      "errorMessage": "This Account has not been approved yet. Submit the Account for approval and wait for final approval before creating an Opportunity on it or moving an Opportunity onto it."
    },
    {
      "id": "vrule_xml_contact_account_must_be_approved",
      "objectApiName": "Contact",
      "name": "Account_Must_Be_Approved",
      "description": "Block Contact creation, and reparenting onto a different Account, when the parent Account has not been approved via the Account approval process.",
      "active": true,
      "condition": "AND(\n    OR(ISNEW(), ISCHANGED(AccountId)),\n    NOT($SystemContext.IsDerivedRecompute),\n    NOT(ISBLANK(AccountId)),\n    NOT(ISBLANK(TEXT(Account.ApprovalStatus__c))),\n    TEXT(Account.ApprovalStatus__c) <> \"Approved\",\n    $Profile.Name <> \"System Administrator\",\n    $Profile.Name <> \"Integration User\"\n)",
      "errorDisplayField": "AccountId",
      "errorMessage": "This Account has not been approved yet. Submit the Account for approval and wait for final approval before creating a Contact on it or moving a Contact onto it."
    },
    {
      "id": "vrule_xml_quote_account_must_be_approved",
      "objectApiName": "Quote",
      "name": "Account_Must_Be_Approved",
      "description": "Block Quote creation, and reparenting onto a different Account, when the parent Account has not been approved via the Account approval process.",
      "active": true,
      "condition": "AND(\n    OR(ISNEW(), ISCHANGED(AccountId)),\n    NOT($SystemContext.IsDerivedRecompute),\n    NOT(ISBLANK(AccountId)),\n    NOT(ISBLANK(TEXT(Account.ApprovalStatus__c))),\n    TEXT(Account.ApprovalStatus__c) <> \"Approved\",\n    $Profile.Name <> \"System Administrator\",\n    $Profile.Name <> \"Integration User\"\n)",
      "errorDisplayField": "AccountId",
      "errorMessage": "This Account has not been approved yet. Submit the Account for approval and wait for final approval before creating a Quote on it or moving a Quote onto it."
    },
    {
      "id": "vrule_xml_order_account_must_be_approved",
      "objectApiName": "Order",
      "name": "Account_Must_Be_Approved",
      "description": "Block Order creation, and reparenting onto a different Account, when the parent Account has not been approved via the Account approval process.",
      "active": true,
      "condition": "AND(\n    OR(ISNEW(), ISCHANGED(AccountId)),\n    NOT($SystemContext.IsDerivedRecompute),\n    NOT(ISBLANK(AccountId)),\n    NOT(ISBLANK(TEXT(Account.ApprovalStatus__c))),\n    TEXT(Account.ApprovalStatus__c) <> \"Approved\",\n    $Profile.Name <> \"System Administrator\",\n    $Profile.Name <> \"Integration User\"\n)",
      "errorDisplayField": "AccountId",
      "errorMessage": "This Account has not been approved yet. Submit the Account for approval and wait for final approval before creating an Order on it or moving an Order onto it."
    },
    {
      "id": "vrule_oli_pricebookentry_required_after_resolve",
      "objectApiName": "OpportunityLineItem",
      "name": "PricebookEntry_Required_After_Resolve",
      "description": "Fail-closed safeguard for the Add Product UX. PricebookEntryId is no longer a schema-level required field (so sales can submit a row with Product2Id + CurrencyIsoCode only and let OpportunityLineItem_PricebookEntryId_Autofill_From_Product2 resolve the PBE at triggerOrder 20). This validation rule then rejects any row that still has no PricebookEntryId after the before-save chain runs \u2014 typically because no active PricebookEntry exists for that (Product2, Pricebook2, Currency) combination in the parent Opportunity's pricebook. Without this rule the trigger-20 flow's Stamp_PBE_And_ListPrice assignment would silently null out PricebookEntryId and ListPrice on a no-match lookup, leaving the row in an inconsistent state; with it, sales gets an actionable error pointing at the missing PBE. Gated to create and to edits of the resolve inputs so legacy rows with a historically blank PBE stay editable on unrelated fields.",
      "active": true,
      "condition": "(ISNEW() || ISCHANGED(PricebookEntryId) || ISCHANGED(Product2Id) || ISCHANGED(CurrencyIsoCode)) && ISBLANK(PricebookEntryId)",
      "errorDisplayField": "PricebookEntryId",
      "errorMessage": "No active Price Book Entry was found for this Product + Currency in the Opportunity's price book. Pick a different Product, change the Currency, or have an admin create the missing Price Book Entry."
    },
    {
      "id": "vrule_credits_vr_poc_requires_opp",
      "objectApiName": "Credits__c",
      "name": "Credits__VR_PoC_Requires_Opp",
      "description": "When Credit_Type__c = 'PoC', Opportunity__c must be set. Allows future Credit_Types (CS, Trial, etc.) to have no Opportunity without enforcing it at schema level.",
      "active": true,
      "condition": "NOT($SystemContext.IsDerivedRecompute) && (\n    AND(ISPICKVAL(Credit_Type__c, 'PoC'), ISBLANK(Opportunity__c))\n)",
      "errorDisplayField": "Opportunity__c",
      "errorMessage": "An Opportunity is required for PoC credit grants."
    },
    {
      "id": "vrule_credits_vr_cloud_org_matches_account",
      "objectApiName": "Credits__c",
      "name": "Credits__VR_CloudOrg_Matches_Account",
      "description": "The selected Cloud_Org__c must belong to the same Account as this Credits record. Server-side companion to the lookup picker's account-filtered dropdown.",
      "active": true,
      "condition": "NOT($SystemContext.IsDerivedRecompute) && (\n    AND(NOT(ISBLANK(Cloud_Org__c)), Cloud_Org__r.Account__c <> Account__c)\n)",
      "errorDisplayField": "Cloud_Org__c",
      "errorMessage": "The selected Cloud Org must belong to the same Account as this Credits record."
    },
    {
      "id": "vrule_credits_vr_opp_matches_account",
      "objectApiName": "Credits__c",
      "name": "Credits__VR_Opp_Matches_Account",
      "description": "The selected Opportunity__c must belong to the same Account as this Credits record. Server-side companion to the runtime_scoped_lookup picker's account-filtered Opportunity dropdown. Catches cross-customer pairings (Credits.Account__c = customer A AND Credits.Opportunity__c = customer B's Opp) which previously slipped through Credits__VR_PoC_Requires_Opp (that VR only checks ISBLANK, not Account parity).",
      "active": true,
      "condition": "NOT($SystemContext.IsDerivedRecompute) && (\n    AND(NOT(ISBLANK(Opportunity__c)), Opportunity__r.AccountId <> Account__c)\n)",
      "errorDisplayField": "Opportunity__c",
      "errorMessage": "The selected Opportunity must belong to the same Account as this Credits record."
    },
    {
      "id": "vrule_pingcap_order_revrec_entity_on_activation",
      "objectApiName": "Order",
      "name": "RevRec_Entity_Required_When_Activated",
      "description": "PingCAP RevRec Entity drives revenue recognition and must be set before an order is activated. Normally the Order_Default_RevRec_Entity before-save flow stamps a default from the contracting entity in the same save; this rule catches the cases the flow cannot derive (Beijing contracting entity, blank account contracting entity) so an order never transitions to Activated without a revenue entity. Guarded on the transition (ISNEW/ISCHANGED, matching the Order_Activate_* rules) so edits to already-Activated orders \u2014 including automation writes like Accumulated_* billing updates and legacy orders with an empty-string value \u2014 are not blocked.",
      "active": true,
      "condition": "(ISNEW() || ISCHANGED(Status)) && Status == \"Activated\" && ISBLANK(TEXT(PingCAP_RevRec_Entity__c))",
      "errorDisplayField": "PingCAP_RevRec_Entity__c",
      "errorMessage": "PingCAP RevRec Entity is required before an order can be activated. Pick the PingCAP legal entity that will recognize this order's revenue (defaults from the account's contracting entity; ask Finance if unsure)."
    },
    {
      "id": "vrule_qli_pricebookentry_required_after_resolve",
      "objectApiName": "QuoteLineItem",
      "name": "PricebookEntry_Required_After_Resolve",
      "description": "Fail-closed safeguard for the Add Product UX. PricebookEntryId is no longer a schema-level required field (so sales can submit a row without picking a Price Book Entry (Quantity/UnitPrice stay schema-required) and let QuoteLineItem_PricebookEntryId_Autofill_From_Product2 resolve the PBE at triggerOrder 20). This validation rule then rejects any row that still has no PricebookEntryId after the before-save chain runs \u2014 typically because no active PricebookEntry exists for that (Product2, Pricebook2, Currency) combination in the parent Quote's pricebook. Without this rule the trigger-20 flow's Stamp_PBE_And_ListPrice assignment would silently null out PricebookEntryId and ListPrice on a no-match lookup, leaving the row in an inconsistent state; with it, sales gets an actionable error pointing at the missing PBE. Gated to create and to edits of the resolve inputs so legacy rows with a historically blank PBE stay editable on unrelated fields.",
      "active": true,
      "condition": "(ISNEW() || ISCHANGED(PricebookEntryId) || ISCHANGED(Product2Id) || ISCHANGED(CurrencyIsoCode)) && ISBLANK(PricebookEntryId)",
      "errorDisplayField": "PricebookEntryId",
      "errorMessage": "No active Price Book Entry was found for this Product + Currency in the Quote's price book. Pick a different Product, change the Currency, or have an admin create the missing Price Book Entry."
    },
    {
      "id": "vrule_orderitem_pricebookentry_required_after_resolve",
      "objectApiName": "OrderItem",
      "name": "PricebookEntry_Required_After_Resolve",
      "description": "Fail-closed safeguard for the Add Product UX. PricebookEntryId is no longer a schema-level required field (so sales can submit a row with Product2Id + CurrencyIsoCode only and let OrderItem_PricebookEntryId_Autofill_From_Product2 resolve the PBE at triggerOrder 20). This validation rule then rejects any row that still has no PricebookEntryId after the before-save chain runs \u2014 typically because no active PricebookEntry exists for that (Product2, Pricebook2, Currency) combination in the parent Order's pricebook. Without this rule the trigger-20 flow's Stamp_PBE_And_ListPrice assignment would silently null out PricebookEntryId and ListPrice on a no-match lookup, leaving the row in an inconsistent state; with it, sales gets an actionable error pointing at the missing PBE. Gated to create and to edits of the resolve inputs so legacy rows with a historically blank PBE stay editable on unrelated fields.",
      "active": true,
      "condition": "(ISNEW() || ISCHANGED(PricebookEntryId) || ISCHANGED(Product2Id) || ISCHANGED(CurrencyIsoCode)) && ISBLANK(PricebookEntryId)",
      "errorDisplayField": "PricebookEntryId",
      "errorMessage": "No active Price Book Entry was found for this Product + Currency in the Order's price book. Pick a different Product, change the Currency, or have an admin create the missing Price Book Entry."
    },
    {
      "id": "vrule_oli_product2_pbe_consistency",
      "objectApiName": "OpportunityLineItem",
      "name": "Product2_PricebookEntry_Consistency",
      "description": "Line-level integrity: the row's Product2Id must match its PricebookEntry's Product2Id. Prevents a line that reads as product A via Product2 but prices as product B via the price book entry (observed in production before this rule; platform does not enforce this natively). Gated to create and to edits of the two fields so pre-existing mismatched rows stay editable on unrelated fields.",
      "active": true,
      "condition": "(ISNEW() || ISCHANGED(Product2Id) || ISCHANGED(PricebookEntryId)) && NOT(ISBLANK(PricebookEntryId)) && NOT(ISBLANK(Product2Id)) && PricebookEntry.Product2Id <> Product2Id",
      "errorDisplayField": null,
      "errorMessage": "Product and Price Book Entry do not match: the selected PricebookEntry belongs to a different product."
    },
    {
      "id": "vrule_qli_product2_pbe_consistency",
      "objectApiName": "QuoteLineItem",
      "name": "Product2_PricebookEntry_Consistency",
      "description": "Line-level integrity: the row's Product2Id must match its PricebookEntry's Product2Id. Prevents a line that reads as product A via Product2 but prices as product B via the price book entry (observed in production before this rule; platform does not enforce this natively). Gated to create and to edits of the two fields so pre-existing mismatched rows stay editable on unrelated fields.",
      "active": true,
      "condition": "(ISNEW() || ISCHANGED(Product2Id) || ISCHANGED(PricebookEntryId)) && NOT(ISBLANK(PricebookEntryId)) && NOT(ISBLANK(Product2Id)) && PricebookEntry.Product2Id <> Product2Id",
      "errorDisplayField": null,
      "errorMessage": "Product and Price Book Entry do not match: the selected PricebookEntry belongs to a different product."
    },
    {
      "id": "vrule_orderitem_product2_pbe_consistency",
      "objectApiName": "OrderItem",
      "name": "Product2_PricebookEntry_Consistency",
      "description": "Line-level integrity: the row's Product2Id must match its PricebookEntry's Product2Id. Prevents a line that reads as product A via Product2 but prices as product B via the price book entry (observed in production before this rule; platform does not enforce this natively). Gated to create and to edits of the two fields so pre-existing mismatched rows stay editable on unrelated fields.",
      "active": true,
      "condition": "(ISNEW() || ISCHANGED(Product2Id) || ISCHANGED(PricebookEntryId)) && NOT(ISBLANK(PricebookEntryId)) && NOT(ISBLANK(Product2Id)) && PricebookEntry.Product2Id <> Product2Id",
      "errorDisplayField": null,
      "errorMessage": "Product and Price Book Entry do not match: the selected PricebookEntry belongs to a different product."
    },
    {
      "id": "vrule_orderitem_canotlowerthanthequotedprice",
      "objectApiName": "OrderItem",
      "name": "Canotlowerthanthequotedprice",
      "description": "Standard-chain equivalent of the legacy Order_Product__c price-floor rule (which stays active on the frozen legacy chain): an order line linked to a quote line cannot be priced below the quoted unit price. Same-currency comparison only, and gated to create/price edits so a later quote amendment cannot freeze existing order lines.",
      "active": true,
      "condition": "(ISNEW() || ISCHANGED(UnitPrice) || ISCHANGED(QuoteLineItemId)) && NOT(ISBLANK(QuoteLineItemId)) && NOT(ISBLANK(UnitPrice)) && NOT(ISBLANK(QuoteLineItem.UnitPrice)) && (QuoteLineItem.CurrencyIsoCode = CurrencyIsoCode) && UnitPrice < QuoteLineItem.UnitPrice",
      "errorDisplayField": null,
      "errorMessage": "Order Price Can't Lower Than Quoted Price."
    },
    {
      "id": "vrule_orderitem_quoteline_currency_match",
      "objectApiName": "OrderItem",
      "name": "Quote_Line_Currency_Match",
      "description": "Companion to the price-floor rule: an order line linked to a quote line must keep the quote line's currency. Without this, flipping CurrencyIsoCode would exempt the row from the same-currency price floor entirely. Gated to create and to edits of the currency/link so pre-existing cross-currency rows stay editable on unrelated fields.",
      "active": true,
      "condition": "(ISNEW() || ISCHANGED(CurrencyIsoCode) || ISCHANGED(QuoteLineItemId)) && NOT(ISBLANK(QuoteLineItemId)) && NOT(ISBLANK(QuoteLineItem.CurrencyIsoCode)) && QuoteLineItem.CurrencyIsoCode <> CurrencyIsoCode",
      "errorDisplayField": null,
      "errorMessage": "Order line currency must match the linked quote line's currency."
    },
    {
      "id": "vrule_quote_product_retired_guard",
      "objectApiName": "Quote_Product__c",
      "name": "Legacy_Table_Retired_Guard",
      "description": "P3 guardrail: Quote_Product__c was emptied by the 2026-07 product-chain cleanup and is retired. The standard chain (QuoteLineItem) is the only writable home for line items. Object permissions cannot enforce this (master-detail children inherit parent CRUD), and $Profile-based exemptions are unreliable here (the environment admin users carry a NULL Profile.Name \u2014 verified on staging, and it is why the Order post-activate lock fires even for admins). Exemption is therefore namespace-based: only rows whose Name BEGINS with the inspection prefix ('Staging Inspection') pass (prefix-anchored \u2014 an embedded mid-name phrase does not qualify); every other create/update is rejected. Break-glass: a deliberate write must use the inspection name prefix and should be cleaned up afterwards.",
      "active": true,
      "condition": "NOT(BEGINS(Name, \"Staging Inspection\"))",
      "errorDisplayField": null,
      "errorMessage": "This legacy table is retired. Create line items on the standard object (QuoteLineItem) instead."
    },
    {
      "id": "vrule_opportunity_product_retired_guard",
      "objectApiName": "Opportunity_Product__c",
      "name": "Legacy_Table_Retired_Guard",
      "description": "P3 guardrail: Opportunity_Product__c was emptied by the 2026-07 product-chain cleanup and is retired. The standard chain (OpportunityLineItem) is the only writable home for line items. Object permissions cannot enforce this (master-detail children inherit parent CRUD), and $Profile-based exemptions are unreliable here (the environment admin users carry a NULL Profile.Name \u2014 verified on staging, and it is why the Order post-activate lock fires even for admins). Exemption is therefore namespace-based: only rows whose Name BEGINS with the inspection prefix ('Staging Inspection') pass (prefix-anchored \u2014 an embedded mid-name phrase does not qualify); every other create/update is rejected. Break-glass: a deliberate write must use the inspection name prefix and should be cleaned up afterwards.",
      "active": true,
      "condition": "NOT(BEGINS(Name, \"Staging Inspection\"))",
      "errorDisplayField": null,
      "errorMessage": "This legacy table is retired. Create line items on the standard object (OpportunityLineItem) instead."
    },
    {
      "id": "vrule_order_product_retired_guard",
      "objectApiName": "Order_Product__c",
      "name": "Legacy_Table_Retired_Guard",
      "description": "D4-final guardrail (2026-07-23): the billing owner confirmed the OrderItem backfill is complete and Order_Product__c is deprecated \u2014 historical rows are KEPT as a frozen archive, but the object takes no new writes. The standard chain (OrderItem) is the only writable home for order lines; the billing cron writes OrderItem as of the 2026-07-04 cycle. Object permissions cannot enforce this (master-detail children inherit parent CRUD) and $Profile-based exemptions are unreliable (environment admins carry a NULL Profile.Name), so the exemption is namespace-based: only rows whose Name BEGINS with the inspection prefix ('Staging Inspection') pass (prefix-anchored \u2014 an embedded mid-name phrase does not qualify); every other create/update is rejected. NOTE: validation rules do not cover deletes or direct-SQL writes \u2014 the retirement watchdog case additionally asserts zero non-inspection rows created after the retirement epoch, which catches an accidentally re-enabled legacy cron path (SQL writes bypass VRs).",
      "active": true,
      "condition": "NOT(BEGINS(Name, \"Staging Inspection\"))",
      "errorDisplayField": null,
      "errorMessage": "This legacy table is retired. Create order lines on the standard object (OrderItem) instead."
    },
    {
      "id": "vrule_credits_topup_vr_poc_ledgers_only",
      "objectApiName": "Credits_Topup__c",
      "name": "Credits_Topup__VR_PoC_Ledgers_Only",
      "description": "Top-ups exist only for PoC ledgers. Credits_Topup__c carries no Credit_Type__c of its own - the program is a property of the parent engagement - so the rule reads the parent through Credits__r. WHY this rule and not just the Add Top-up button's visibilityCondition: that condition hides a button, which is a UI affordance, not a control. An API-direct create still attaches a top-up to a non-PoC ledger, and for Startup that silently defeats the one-grant-per-org rule the ledger VR enforces - the same fail-open-and-silent shape this credit family has been bitten by before. The two guards are deliberate defense in depth: the button keeps the UI honest, this rule keeps the invariant true. ALLOWLIST, not denylist: the condition blocks unless the parent is PoC, rather than blocking a named bad type. A future credit program is then non-toppable until someone deliberately opts it in, which is the safe default for a spend control; a denylist would silently admit every type added after this rule was written. It also means an unresolvable parent traversal fails CLOSED, so the local oracle must positively assert that a top-up on a PoC parent is still allowed - that assertion is what proves the rule does not over-block, and it is not implied by the blocking case passing. Event set: a top-up can land on a non-PoC ledger only by being created there or by being reparented, so the rule gates on ISNEW plus ISCHANGED(Credits__c) and leaves unrelated edits to existing rows alone - the parent's Credit_Type__c is updateable:false, so the parent cannot change type underneath a top-up and no third event exists. Bypass mirrors Credits__VR_One_Open_Ledger_Per_Org: $Permission.ModifyAllData, not $Profile.Name, because the environment admin carries no Profile.Name and a $Profile gate would be no gate at all. That bypass means acceptance testing must use a non-admin identity or it returns a false negative.",
      "active": true,
      "condition": "AND(OR(ISNEW(), ISCHANGED(Credits__c)), TEXT(Credits__r.Credit_Type__c) != \"PoC\", $Permission.ModifyAllData != true)",
      "errorDisplayField": "Credits__c",
      "errorMessage": "Top-ups are only available on PoC credit ledgers. This ledger is a different credit type, granted once per Cloud Org with no top-up path. Raise the additional grant through that program's own process."
    },
    {
      "id": "vrule_credits_vr_one_open_ledger_per_org",
      "objectApiName": "Credits__c",
      "name": "Credits__VR_One_Open_Ledger_Per_Org",
      "description": "At most one non-Cancelled Credits__c ledger per (Cloud_Org__c, Credit_Type__c). Reads Has_Open_Org_Ledger__c, stamped in the same save by the Credits_Stamp_Open_Org_Ledger before-save Flow (same flow-stamps-then-rule-reads ordering as Order_Default_RevRec_Entity + RevRec_Entity_Required_When_Activated, verified end to end on the local bootstrap oracle). Closes the cross-record gate bypass in issue #4733: approval criteria aggregate only within one ledger, so separate ledgers on one org are each gated on their own amount and the combined total is never reviewed. Structure follows directly from what the invariant is about. A record OCCUPIES a single ledger slot exactly when Credit_Lifecycle__c is not Cancelled, and WHICH slot is (Cloud_Org__c, Credit_Type__c). So the rule is: this record currently occupies (first clause) AND this save is one of the three events that can make it newly occupy or move between slots - create, Cloud_Org__c change, or leaving Cancelled (second clause) AND the target slot is already taken (the stamp). The slot is keyed per credit type because each program runs its own approval chain with its own thresholds and its own remedy for a follow-on grant, so an existing ledger is a bypass only within the same program; one org carrying both a PoC engagement and a Startup Program grant is two legitimate grants, each entering its own chain. Adding Credit_Type__c to the key does NOT add a fourth event to the triad: the field is updateable:false and locked after create, so it can never move a record between buckets - an immutability this control now depends on, pinned by a guard test rather than by this sentence. No other field can affect whether or where it occupies, and changes on the OTHER ledger are covered by that record's own three events, which is what closes the invariant with no cross-record trigger. The first clause is load-bearing, not defensive: creating or repointing a record that is itself Cancelled takes no slot, so blocking it would reject legitimate corrections to cancelled history - exactly the all-except-Cancelled behavior this design promises. PRIORVALUE distinguishes reactivation from ordinary progression, so Active -> Provisioned on a grandfathered duplicate stays allowed while only Cancelled -> anything is a retake; transitions INTO Cancelled release a slot and are never blocked, which is what keeps the recommended cleanup of the grandfathered groups possible. Bypass is $Permission.ModifyAllData, not $Profile.Name: the environment admin carries no Profile.Name, which makes a $Profile gate always-true and therefore no gate at all.",
      "active": true,
      "condition": "AND(NOT(ISPICKVAL(Credit_Lifecycle__c, \"Cancelled\")), OR(ISNEW(), ISCHANGED(Cloud_Org__c), AND(ISCHANGED(Credit_Lifecycle__c), ISPICKVAL(PRIORVALUE(Credit_Lifecycle__c), \"Cancelled\"))), Has_Open_Org_Ledger__c, $Permission.ModifyAllData != true)",
      "errorDisplayField": "Cloud_Org__c",
      "errorMessage": "This Cloud Org already has an open ledger of this credit type. For PoC, add the credit as a Top-up on that existing record so thresholds see the cumulative total. Startup is one grant per org. A new ledger needs every record of this type Cancelled."
    },
    {
      "id": "vrule_quote_submit_requires_complete_lines",
      "objectApiName": "Quote",
      "name": "Quote_Submit_Requires_Complete_Lines",
      "description": "Submit gate for the quote-side ACV chain (2026-08-13 ACV overhaul, decision B2). Fires on any save that moves Status into Pending, Approved or Accepted - not Pending alone, or a direct Draft-to-Approved edit would skip the gate entirely (Quote.Status is a plain picklist with no state machine) - and blocks it while the quote is empty or any line item is incomplete in either of the two ways that silently corrupt ACV. The empty checks are two distinct arms: ISBLANK catches quotes whose COUNT rollups have never computed (rollup fields added by this pack are not backfilled onto existing rows; any line save heals all three), and == 0 catches a quote whose last line was deleted - the count is a failure arm, not a precondition, or deleting every line would become the obvious gate escape. First: a line whose Net_fees__c never stamped - the create form requires the dead-end UnitPrice while the real money input Unit_Net_Price__c is optional, so TCV under-counts while the term still computes and a single-condition gate would wave the quote through. Second: a line missing a service date - Term_Months__c is derived from the dates and cleared when they are incomplete, so a populated term doubles as the per-line date-completeness marker; checking the header Overall_* envelope instead would pass a multi-line quote whose MAX simply skipped the dateless line and then annualize on a too-short window (the KR-QT-26052801 failure shape, multi-line edition). Reads only physical columns: three COUNT rollups (SQL COUNT over a stamped column skips NULLs) and Status - no virtual formula fields, checking the derivation's inputs rather than its output. Deliberately not a QuoteLineItem save rule: that would break the atomic Std_CreateQuoteFromOpportunity copy for quotes built from migrated opportunities, and Draft is allowed to be dirty while sales fill dates line by line. IsDerivedRecompute is exempted so rollup maintenance on a submitted quote never trips the gate.",
      "active": true,
      "condition": "NOT($SystemContext.IsDerivedRecompute) && ISCHANGED(Status) && (ISPICKVAL(Status, \"Pending\") || ISPICKVAL(Status, \"Approved\") || ISPICKVAL(Status, \"Accepted\")) && (ISBLANK(Line_Item_Count__c) || Line_Item_Count__c == 0 || Priced_Line_Item_Count__c < Line_Item_Count__c || Termed_Line_Item_Count__c < Line_Item_Count__c)",
      "errorDisplayField": null,
      "errorMessage": "Cannot submit: the quote needs at least one line item, and every line needs Service Start/End dates (the term derives from them) and a computed Net Fees (enter Unit Net Price). Complete the lines, save, then submit."
    },
    {
      "id": "vrule_quoteline_locked_after_quote_leaves_draft",
      "objectApiName": "QuoteLineItem",
      "name": "QuoteLineItem_Locked_After_Quote_Leaves_Draft",
      "description": "Companion to Quote_Submit_Requires_Complete_Lines: the submit gate certifies line completeness at the moment Status leaves Draft, and this rule keeps that certification from rotting afterwards - without it a user could clear a service date or blank a price on a Pending quote, the order-150 derivation would clear the term, the counts would drop, and no gate would re-fire because ISCHANGED(Status) is false on line saves. Ports the Order_Post_Activate_Readonly pattern to the quote family: create and edit of line items are blocked while the parent quote is out of Draft, exempting derived recomputes and sessions holding ModifyAllData (backfills and repair run through those). The bypass is $Permission.ModifyAllData, not $Profile.Name: the environment admin carries no Profile.Name, which makes a $Profile gate always-true for exactly the sessions that run repairs (Credits__VR_One_Open_Ledger_Per_Org precedent). Reparenting (ISCHANGED(QuoteId) on update) is blocked outright, not just into non-Draft quotes: a destination-only check leaves the exit hole where a line moves FROM a submitted quote TO a Draft one - the old quote's counts drop with no status change to re-fire the gate - and the prior parent's status is not expressible in a validation rule (no cross-object PRIORVALUE traversal). QuoteId is runtime-writable on this platform even though Salesforce treats it as non-reparentable master-detail, so the rule carries the parity, with ModifyAllData as the repair escape. The intended correction path is recall: set the quote back to Draft, fix the lines, resubmit through the gate. Known residual gap, deliberate: validation rules do not run on delete, so deleting a line from a submitted quote is not blocked here - the COUNT rollups keep the header arithmetically consistent, and the delete hole is accepted rather than papered over with a mechanism the platform does not have. TEXT(Quote.Status) rather than ISPICKVAL because cross-object picklists resolve as text (Canotlowerthanthequotedprice precedent for the parent traversal).",
      "active": true,
      "condition": "NOT($SystemContext.IsDerivedRecompute) && NOT(ISBLANK(QuoteId)) && $Permission.ModifyAllData != true && ((NOT(ISBLANK(TEXT(Quote.Status))) && TEXT(Quote.Status) <> \"Draft\") || (NOT(ISNEW()) && ISCHANGED(QuoteId)))",
      "errorDisplayField": null,
      "errorMessage": "Line items are locked once the quote leaves Draft (recall it to Draft to correct them), and a line cannot be moved to another quote."
    }
  ]
}
