Loading
Sisällysluettelo
Valitse suodattimet

          Ei tuloksia
          Ei tuloksia
          Tässä on joitain hakuvinkkejä

          Tarkista avainsanojesi oikeinkirjoitus.
          Käytä yleisempiä hakutermejä.
          Laajenna hakua valitsemalla vähemmän suodattimia.

          Hae koko Salesforce-ohjeesta
          Esimerkki vierailutoimintojen vahvistuskomentosarjasta

          Esimerkki vierailutoimintojen vahvistuskomentosarjasta

          Vierailutoimintojen vahvistuksen mukautetut komentosarjat auttavat varmistamaan, että käyttäjät noudattavat liiketoimintasääntöjä ennen vierailun allekirjoittamista tai lähettämistä. Nämä komentosarjat suoritetaan, kun käyttäjä valitsee Allekirjoita- tai Lähetä-toiminnon.

          Vaaditut versiot

          Käytettävissä: Lightning Experiencessa
          Käytettävissä: Enterprise Edition- ja Unlimited Edition -versioissa Life Sciences Cloudilla, Life Sciences Cloud for Customer Engagement -lisäosalisenssillä ja Life Sciences Customer Engagement -hallitulla paketilla.
          Huomautus
          Huomautus Mukautetun komentosarjan vahvistus suoritetaan vain Lähetä-, Allekirjoitus- ja Paperilomake-vierailutoiminnoille. Sitä ei suoriteta Tallenna-toiminnoille (mukaan lukien pikatoimintojen tallennukset) tai Peruuta-toiminnoille. Jos vaadit vahvistusta väliaikaisille tallenteille, käytä toista laajennettavuuspistettä.

          Jos haluat luoda mukautetun komentosarjan vierailutoimintojen vahvistukselle, luo Lightning näillä attribuuteilla:

          • Syötä komentosarjan nimeksi visitSampleScript.
          • Käytä komponentin nimeksi Lightning-komponentin nimeä, esimerkiksi visitSampleScript.
          • Käytä Tyyppi-kenttään Visit-toiminnon vahvistus.

          Lisää Lightning JavaScript-tiedostoon esimerkin mukautettu komentosarja myöhemmin ja ota Lightning käyttöön organisaatiossasi. Mukautettu komentosarja suorittaa vahvistuksen joka kerta, kun joku lähettää vierailutoiminnon ja kun se on allekirjoitettu.

          Ota huomioon:

          • Jos koskaan muutat Lightning ja tallennat sen, etsi myös tietue Mukautetut komentosarjat -osiosta ja napsauta Päivitä.
          • Jos luot useamman kuin yhden komentosarjan, jonka tyyppi on Vierailutoiminto-vahvistus, vain komentosarjan ensimmäinen versio suoritetaan. Suoritettava komentosarja perustuu tunnukseen tai luontipäivään.
          • Sama Vierailutoiminto-vahvistuskomentosarja suoritetaan sovelluksen mobiili- ja verkkoversioissa, joten muista testata molempia.

          Esimerkkiskripti sisältää kuusi vahvistussääntöä:

          Vahvistussäännöt
          Sääntö Käyttö Käynnistimet Virheviesti Onnistui
          atLeastOneSampleIsRequired Varmistaa, että vierailu sisältää vähintään yhden näytteen (ProductDisbursement). Suoritetaan aina Vierailuun täytyy lisätä vähintään yksi näyte. Näyttää vierailun näytteen määrän
          atLeastOneDetailAndSampleAreRequired Vaatii sekä näytteen että yksityiskohtaisen tuotteen (ProviderVisitProdDetailing). Suoritetaan aina Vierailuun täytyy lisätä vähintään yksi esimerkki ja yksityiskohtainen tuote. Näyttää näytteiden ja yksityiskohtaisten tuotteiden määrät
          atLeastOneMessageIsRequiredForEachVisitDetail Jokaisella yksityiskohtaisella tuotteella täytyy olla vähintään yksi viesti. Vain, kun käyttäjä on Field Sales Representative (tietty profiili) tai kun Vierailukanava-asetukseksi on määritetty Henkilökohtainen. Jokaiselle yksityiskohtaiselle tuotteelle vaaditaan vähintään yksi viesti, kun kanava on 'In-Person' ja käyttäjällä on 'Field Sales Representative' -profiili Ohittaa muut profiilit tai muut kuin henkilökohtaiset vierailut
          specificSampleDependencyCheck Jos tietty tuote on valittuna, sääntö tarkastaa, onko myös muita vaadittuja tuotteita valittu. Kun esimerkkejä on olemassa Jos vierailuun lisätään <product1>, myös <product2> täytyy lisätä. N/A
          isAtLeastOneHCP HCO (terveydenhuollon organisaatio tai laitos) -kutsujen täytyy sisältää vähintään yksi HCP (terveydenhuollon tarjoaja) -tilin osallistuja. Vain toimialueen tileille Vähintään yhden HCP-palvelun (Healthcare Professional) täytyy liittyä, kun luot vierailua HCO:lle (Healthcare Organization). Ohittaa, jos ensisijainen tili on henkilötili (HCP)
          isMoreThanOneHCO Rajoittaa vain yhteen HCO-organisaation (institution) osallistujaksi per puhelu. Suoritetaan aina Vain 1 HCO (Healthcare Organization) -osallistuja voidaan lisätä per vierailu. Näyttää HCO-tilien määrän

          Esimerkkikomentosarja sisältää myös kolme uudelleenkäytettävää avustaja-metodia:

          • getActionName(env)
            • Hakee toiminnon nimen ympäristöstä
            • Määrittää, tulisiko vahvistus suorittaa
            • Suoritetaan vain Submit-, Sign- ja runCustomScriptValidations-objekteille
          • parseContextData(record)
            • Kerää asiayhteystiedot tietueobjektista turvallisesti
            • Käsittelee JSON-merkkijonot ja objektit (mukaan lukien välityspalvelinobjektit)
            • Palauttaa tyhjän objektin virheestä
          • getFieldData(contextData, baseFieldName)
            • Tämä on sovellusalustan tietoinen kenttien datan noutomenetelmä.
            • Verkkosivulla se käyttää kentanNimi.VisitId -arvoa (esimerkiksi ProductDisbursement.VisitId).
            • Mobiilisovelluksessa se käyttää yksinkertaista kentän nimeä (esimerkiksi ProductDisbursement).
            • Metodi yrittää automaattisesti molempia ja palauttaa molempia.

          Kun toteutat komentosarjaa, sinun tulisi ottaa huomioon ja käsitellä asianmukaisesti useita seikkoja:

          • Kun Vierailutoimintojen vahvistus -komentosarja suoritetaan mobiililaitteilla ja verkossa, vierailun tiedot saattavat olla vain muistissa, joten sinun täytyy käyttää tämänhetkistä dataa käsittelemällä kentät contextData-kentässä. Voit käyttää db.query-kyselyä, mutta se tulisi varata tiedoille, joita ei muokata vierailussa, esimerkiksi tilin tietuetyypin tarkastamiseksi.
          • Mukautettuun komentosarjaan välitetyssä kontekstidatassa saattaa olla eroavaisuuksia mobiililaitteiden ja verkkojen välillä. Esimerkki mukautetusta komentosarjasta käsittelee nämä eroavaisuudet käyttämällä yleistä avustajan menetelmää getFieldData.
          Esimerkki
          Esimerkki
          
          (() => {
             // Note: Old data extraction functions removed - no longer needed
             // since businessRuleValidator provides clean, structured parameters
          
          
             // Platform detection flag - will be set in entry point
             let hasWebField = false;
          
          
             /**
              * Gets the action name from environment
              * @param {Object} env - JsEnv object for environment options
              * @returns {string} The action name or empty string
              */
             function getActionName(env) {
                 try {
                     if (env && typeof env.getOption === 'function') {
                         return env.getOption('actionName') || '';
                     }
                     return '';
                 } catch (error) {
                     return '';
                 }
             }
          
          
             // AccountDAO - Data Access Object for account-related operations
             var accountDao = (function () {
                 var instance;
                 var currentRecord;
                 var isPersonAccount;
                 var isInstitution;
                 var childCallAccounts;
                 var accountCache = new Map(); // Simple cache for account data
          
          
                 // Helper functions for accountDao - moved inside closure to access currentRecord
                 async function checkForPersonAccount() {
                     let accountId = currentRecord.stringValue("AccountId");
                    
                     // If not found, try extracting directly from context data
                     if (!accountId) {
                         try {
                             const contextData = parseContextData(currentRecord);
                            
                             // Try different possible locations for AccountId
                             accountId = contextData.ProviderVisit?.AccountId ||
                                         contextData.Visit?.AccountId ||
                                        contextData.AccountId ||
                                        contextData.Account?.Id ||
                                        contextData.Account;
                         } catch (e) {
                             // Error accessing context data, continue with null accountId
                             accountId = null;
                         }
                     }
                    
                     if (!accountId) {
                         return false; // Default to not person account if no account ID
                     }
                    
                     try {
                         let account = await selectAccountById(accountId);
                         let result = account && account.length > 0 ? account[0].boolValue("IsPersonAccount") : false;
                         return result;
                     } catch (error) {
                         return false; // Default to false on error
                     }
                 }
          
          
                 async function checkForInstitution() {
                     let accountId = currentRecord.stringValue("AccountId");
                     // If not found, try extracting directly from context data
                     if (!accountId) {
                         try {
                             const contextData = parseContextData(currentRecord);
                            
                             // Try different possible locations for AccountId
                             accountId = contextData.ProviderVisit?.AccountId ||
                                       contextData.Visit?.AccountId||
                                        contextData.AccountId ||
                                        contextData.Account?.Id ||
                                        contextData.Account;
                         } catch (e) {
                             // Error accessing context data, continue with null accountId
                         }
                     }
                    
                     if (!accountId) {
                         return false; // Default to not institution if no account ID
                     }
                    
                     try {
                         let account = await selectAccountById(accountId);
                         let isPersonAccount = account && account.length > 0 ? account[0].boolValue("IsPersonAccount") : false;
                        
                         // If it's not a Person Account, then it's a Business Account (HCO)
                         let result = !isPersonAccount;
                         return result;
                     } catch (error) {
                         return false; // Default to false on error
                     }
                 }
          
          
                 async function selectChildCallAccountsById() {
                     // Extract attendee account IDs directly from the JSON data
                     // The attendee data is in the Visit.ParentVisitId array in the JSON
                    
                     // Get the data from the current record context
                     let contextData;
                     try {
                         contextData = parseContextData(currentRecord);
                     } catch (error) {
                         return [];
                     }
                    
                     // Extract attendee account IDs from Visit.ParentVisitId array
                     const attendeeVisits = contextData?.["Visit.ParentVisitId"] || contextData?.["ChildVisit"];
                    
                     if (!Array.isArray(attendeeVisits) || attendeeVisits.length === 0) {
                         return [];
                     }
                    
                     // Extract AccountIds from the attendee visits
                     const attendeeAccountIds = attendeeVisits
                         .map(visit => visit.AccountId || visit.accountid)  // Try PascalCase first, then lowercase
                         .filter(accountId => accountId);
                                    
                     if (attendeeAccountIds.length === 0) {
                         return [];
                     }
                    
                    
                     // Query the Account records for these IDs
                     let result = await db.query(
                         "Account",
                         await new ConditionBuilder(
                             "Account",
                             new SetCondition("Id", "IN", attendeeAccountIds)
                         ).build(),
                         ["Id", "Name", "IsPersonAccount"]
                     );
                    
                     return result || [];
                 }
          
          
                 function getRecordId(record) {
                     return record ? record.stringValue("Id") : null;
                 }
          
          
                 async function selectAccountById(accountId) {
                     // Check cache first
                     if (accountCache.has(accountId)) {
                         return accountCache.get(accountId);
                     }
                    
                     // If not in cache, query database
                     let accounts = await db.query(
                         "Account",
                         await new ConditionBuilder(
                             "Account",
                             new FieldCondition("Id", "=", accountId)
                         ).build(),
                         ["Id", "Name", "IsPersonAccount"]
                     );
                    
                     // Cache the result
                     accountCache.set(accountId, accounts);
                    
                     return accounts;
                 }
          
          
                 var initialize = async function(record) {
                     currentRecord = record;
                     // Clear cache for new record context
                     accountCache.clear();
                     isPersonAccount = await checkForPersonAccount();
                     isInstitution = await checkForInstitution();
                     childCallAccounts = await selectChildCallAccountsById();
                 };
          
          
                 var getIsPersonAccount = function () {
                     return isPersonAccount;
                 };
          
          
                 var getIsInstitution = function () {
                     return isInstitution;
                 };
          
          
                 var getChildCallAccounts = function () {
                     return childCallAccounts;
                 };
          
          
                 var createInstance = function () {
                     return {
                         initialize: initialize,
                         getIsPersonAccount: getIsPersonAccount,
                         getChildCallAccounts: getChildCallAccounts,
                         getIsInstitution: getIsInstitution,
                     };
                 };
          
          
                 return {
                     getInstance: function () {
                         return instance || (instance = createInstance());
                     },
                 };
             })();
          
          
             // Main function that businessRuleValidator calls
             async function validateVisit() {
                 try {
                     if (!record) {
                         return [{
                             title: "Error in validation",
                             status: "error",
                             error: "No record provided"
                         }];
                     }
                    
                     // Use the properly provided business rule parameters from outer scope
                     const validationResults = await runValidation();
                    
                     // Platform-specific handling: Web uses Promise.all(), Mobile doesn't
                     let resolvedResults;
                     if (hasWebField) {
                         // Web platform - use Promise.all() to resolve all promises
                         resolvedResults = await Promise.all(validationResults);
                     } else {
                         // Mobile platform - use validationResults directly
                         resolvedResults = validationResults;
                     }
                    
                     // Ensure we always return an array
                     const finalResults = Array.isArray(resolvedResults) ? resolvedResults : [resolvedResults];
                     return finalResults;
                 } catch (error) {
                     return [{
                         title: "Error in validation",
                         status: "error",
                         error: error.message
                     }];
                 }
             }
            
             // Function to run the validation with data from outer scope
             async function runValidation() {
          
          
                 // Initialize accountDao with the proper record object (JsDbObject)
                 await accountDao.getInstance().initialize(record);
          
          
                 // Always validate in this version
                 const isValidationRequired = true;
          
          
                 // Only run validations if needed
                 let validationResults = [];
                 if (isValidationRequired) {
                     // Array of validation functions to run
                     // Add new validation functions to this array
                     const validationFunctions = [
                         atLeastOneSampleIsRequired,
                         atLeastOneDetailAndSampleAreRequired,
                         atLeastOneMessageIsRequiredForEachVisitDetail,
                         specificSampleDependencyCheck,
                         isAtLeastOneHCP,
                         isMoreThanOneHCO,
                         // Add new validation functions here one at a time
                         // Example: validateSampleType,
                         // Example: validateComplianceAgreement,
                     ];
                    
                     // Run all validation functions (handling both sync and async)
                  
                     validationResults = validationFunctions.map((validationFn, index) => {
                         try {
                             // Call validation functions - they access record, user, db, env from outer scope
                             const result = validationFn();
                         // If the result is a Promise, return it as is for Promise.all
                         if (result && typeof result.then === 'function') {
                             return result.then(asyncResult => {
                                 return asyncResult;
                             }).catch(error => {
                                 return {
                                     title: `Error in ${validationFn.name}: ${error.message}`,
                                     status: "error",
                                     error: error.message
                                 };
                             });
                         }
                         return result;
                     } catch (error) {
                         return {
                             title: `Error in ${validationFn.name}: ${error.message}`,
                             status: "error",
                             error: error.message
                         };
                     }
                     });
                    
                    
                     } else {
                         // Default return when validation is not required
                         validationResults = [{
                             title: "Validation not required",
                             status: "success"
                         }];
                     }
                 return validationResults;
             }
            
          
          
          
          
             // Helper function to get context data safely
             function parseContextData(record) {
                 try {
                     if (!record || typeof record.getContextData !== 'function') {
                         return {};
                     }
                    
                     const contextData = record.getContextData();
                    
                     // Handle different return types from getContextData()
                     if (typeof contextData === 'string') {
                         // If it's a JSON string, parse it
                         return JSON.parse(contextData);
                     } else if (typeof contextData === 'object' && contextData !== null) {
                         // If it's already an object (including Proxy), use it directly
                         return contextData;
                     } else {
                         return {};
                     }
                 } catch (error) {
                     return {};
                 }
             }
            
             /**
              * Helper function to get field data with web/mobile fallback
              * Web uses nested field paths (e.g., "ObjectName.VisitId")
              * Mobile uses simple field names (e.g., "ObjectName")
              *
              * @param {Object} contextData - The context data object
              * @param {string} baseFieldName - The base field name (e.g., "ProductDisbursement")
              * @returns {*} The field data from web or mobile field, or undefined
              */
             function getFieldData(contextData, baseFieldName) {
                 const webField = `${baseFieldName}.VisitId`;
                 const mobileField = baseFieldName;
                 return contextData?.[webField] || contextData?.[mobileField];
             }
            
             // Validation rule: at least one sample is required
             function atLeastOneSampleIsRequired() {
                 let hasSamples = false;
                 let sampleCount = 0;
                
                 try {
                     // Get context data from the record object (from outer scope)
                     const contextData = parseContextData(record);           
                     // Use helper to get field data with web/mobile fallback
                     const sampleData = getFieldData(contextData, "ProductDisbursement") || null;
                    
                     // Handle Proxy arrays properly
                     if (sampleData) {
                         try {
                             // Try to get length property (works for both arrays and Proxy arrays)
                             sampleCount = sampleData.length || 0;
                             hasSamples = sampleCount > 0;
          
          
                         } catch (lengthError) {
                             // Handle Proxy length access error
                             // Fallback: check if object has any enumerable properties
                             try {
                                 const keys = Object.keys(sampleData);
                                 hasSamples = keys.length > 0;
                                 sampleCount = keys.length;
                             } catch (keysError) {
                                 // Error getting keys - graceful fallback
                                 hasSamples = false;
                                 sampleCount = 0;
                             }
                         }
                     }          
                 } catch (e) {
                     // Handle validation error gracefully
                     hasSamples = false;
                     sampleCount = 0;
                 }
                
                 return {
                     title: hasSamples ?
                         `Found ${sampleCount} sample(s)` :
                         "At least one sample must be added to the visit.",
                     status: hasSamples ? "success" : "error"
                 };
             }
          
          
             // Validation rule: at least one detail and sample are required
             function atLeastOneDetailAndSampleAreRequired() {    
                 try {
                     const contextData = parseContextData(record);
                    
                     // Use helper to get field data with web/mobile fallback
                     const productDisbursementData = getFieldData(contextData, "ProductDisbursement");
                     const providerVisitProdDetailingData = getFieldData(contextData, "ProviderVisitProdDetailing");
                    
                     // Handle Proxy arrays properly for both fields
                     let sampleCount = 0;
                     let detailCount = 0;
                     let hasProductDisbursement = false;
                     let hasProviderVisitProdDetailing = false;
                    
                     // Check product disbursement
                     if (productDisbursementData) {
                         try {
                             sampleCount = productDisbursementData.length || 0;
                             hasProductDisbursement = sampleCount > 0;
                         } catch (e) {
                             // Error accessing length property
                             const keys = Object.keys(productDisbursementData || {});
                             sampleCount = keys.length;
                             hasProductDisbursement = sampleCount > 0;
                         }
                     }
                    
                     // Check provider visit prod detailing
                     if (providerVisitProdDetailingData) {
                         try {
                             detailCount = providerVisitProdDetailingData.length || 0;
                             hasProviderVisitProdDetailing = detailCount > 0;
                         } catch (e) {
                             // Error accessing length property - try Object.keys fallback
                             const keys = Object.keys(providerVisitProdDetailingData);
                             detailCount = keys.length;
                             hasProviderVisitProdDetailing = detailCount > 0;
                         }
                     }
                    
                     if (hasProductDisbursement && hasProviderVisitProdDetailing) {
                         return {
                             title: `Found ${sampleCount} sample(s) and ${detailCount} detailed product(s)`,
                             status: "success"
                         };
                     }
                    
                     return {
                         title: "At least one sample and detailed product must be added to the visit.",
                         status: "error"
                     };
                 } catch (e) {
                     return {
                         title: "At least one sample and detailed product must be added to the visit.",
                         status: "error",
                         error: e.message
                     };
                 }
             }
          
          
             // Validation rule: at least one message is required for each visit detail
             async function atLeastOneMessageIsRequiredForEachVisitDetail() {
                 try {
                     let userId;
                    
                     // Try to get user Id from user (from outer scope)
                     if (user) {
                         try {
                             userId = user.stringValue('Id');
                         } catch (error) {
                             // Try alternative access methods
                             userId = user.Id || user["Id"];
                             if (!userId) {
                                 // Error accessing userId, continue with fallback
                             }
                         }
                     }
                    
                     if (!userId) {
                         return {
                             title: 'Profile validation skipped - no userId available',
                             status: "success",
                         };
                     }
          
          
                     // As rep user don't have access to User.ProfileId and Profile table
                     // we can only check by ProfileIdentifier in UserAdditionalInfo
                     //Please replace the Profile_Id with the id of the profile you want to apply this validation
                     let targetProfileId = 'Profile_Id';
                     let userAdditionalInfoResults;
                     let isFieldSalesRep = false;
                    
                     try {
                         userAdditionalInfoResults = await db.query(
                             "UserAdditionalInfo",
                             await new ConditionBuilder(
                                 "UserAdditionalInfo",
                                 new FieldCondition("UserId", "=", userId)
                             ).build(),
                             ["Id", "ProfileIdentifier"]
                         );
                        
                        
                         if (userAdditionalInfoResults && userAdditionalInfoResults.length > 0) {
                             const profileId = userAdditionalInfoResults[0].stringValue('ProfileIdentifier');
                             isFieldSalesRep = profileId === targetProfileId;
                         } else {
                             return {
                                 title: 'Profile validation skipped - profile not found',
                                 status: "success",
                             };
                         }
                        
                     } catch (error) {
                         return {
                             title: 'Profile validation skipped - unable to query profile',
                             status: "success",
                         };
                     }
          
          
                     if (!isFieldSalesRep) {
                         return {
                             title: `Profile validation skipped - user is not Field Sales Representative`,
                             status: "success",
                         };
                     }
          
          
                     // Get visit context data from the record object
                     const visitData = parseContextData(record);
          
          
                     // Check if channel is "In-Person"
                     const visitChannel = visitData?.Visit?.channel || visitData?.ProviderVisit?.Channel || '';
          
          
                     if (visitChannel !== "In-Person") {
                         return {
                             title: `Message validation skipped - visit channel is "${visitChannel}", not "In-Person"`,
                             status: "success",
                         };
                     }
          
          
                     // Check if we have visit details to validate
                     // Use helper to get field data with web/mobile fallback
                     const visitDetails = getFieldData(visitData, "ProviderVisitProdDetailing");
          
          
                     if (!Array.isArray(visitDetails) || visitDetails.length === 0) {
                         return {
                             title: 'Message validation passed - no visit details to validate',
                             status: "success"
                         };
                     }
          
          
                     // Validate each visit detail has at least one message
                     let detailsWithoutMessages = [];
          
          
                     visitDetails.forEach((detail, index) => {
                         // Use helper to get field data with web/mobile fallback
                         const messages = getFieldData(detail, "ProviderVisitDtlProductMsg");
                         const hasMessages = Array.isArray(messages) && messages.length > 0;
          
          
                         if (!hasMessages) {
                             const detailInfo = {
                                 index: index + 1,
                                 productId: detail?.productid || 'Unknown Product',
                                 uid: detail?.uid || 'Unknown Detail'
                             };
                             detailsWithoutMessages.push(detailInfo);
                         }
                     });
          
          
                     // Set validation result
                     if (detailsWithoutMessages.length > 0) {
                         return {
                             title: "At least one message is required for each detailed product when the channel is 'In-Person' and the user has a 'Field Sales Representative' profile.",
                             status: "error"
                         };
                     } else {
                         return {
                             title: `All ${visitDetails.length} detailed products have messages - Field Sales Rep In-Person validation passed`,
                             status: "success"
                         };
                     }
          
          
                 } catch (error) {
                     return {
                         title: "At least one message is required for each detailed product when the channel is 'In-Person' and the user has a 'Field Sales Representative' profile.",
                         status: "error",
                         error: error.message
                     };
                 }
             }
          
          
             /**
              * The rule 'specificSampleDependencyCheck' blocks the user from submitting a visit.
              * Validation: If sample "Immunexis 10mg" is selected,
              * then "ADRAVIL Sample Pack 5mg" must also be selected.
              * @returns result { title: string, status: "success" | "error" };
              */
             async function specificSampleDependencyCheck() {
          
          
                 try {
                     // Get visit context data from record (from outer scope)
                     let visitData = parseContextData(record);
                    
                     // Check if we have samples to validate
                     // Use helper to get field data with web/mobile fallback
                     let samples = getFieldData(visitData, "ProductDisbursement");
                    
                     // Handle Proxy arrays properly
                     let samplesCount = 0;
                     let isValidSamples = false;
                    
                     if (samples) {
                         try {
                             samplesCount = samples.length || 0;
                             isValidSamples = samplesCount > 0;
                         } catch (e) {
                             // Error accessing samples length
                             const keys = Object.keys(samples || {});
                             samplesCount = keys.length;
                             isValidSamples = samplesCount > 0;
                         }
                     }
                    
                     if (!isValidSamples) {
                         return {
                             title: 'Sample dependency validation passed - no samples to validate',
                             status: "success"
                         };
                     }
          
          
                     // Get all product item IDs from samples
                     let productItemIds = [];
                     try {
                         if (samples && typeof samples === 'object') {
                             // Handle both array and Proxy array
                             for (let i = 0; i < samplesCount; i++) {
                                 try {
                                     const sample = samples[i];
          
          
                                     if (sample) {
                                         const productItemId = sample.ProductItemId || sample.productitemid
                                         productItemIds.push(productItemId);
                                     }
                                 } catch (sampleError) {
                                     // Error accessing sample
                                 }
                             }
                         }
                     } catch (mappingError) {
                         // Error mapping product item IDs
                     }
                    
          
          
                     if (productItemIds.length === 0) {
                         return {
                             title: 'Sample dependency validation passed - no product item IDs found',
                             status: "success"
                         };
                     }
                    
                     // Query ProductItem to get Product2Id
                     let productItems = await db.query(
                            "ProductItem",
                            await new ConditionBuilder(
                              "ProductItem",
                              new SetCondition("Id", "IN", productItemIds)
                            ).build(),
                            ["Id", "Product2Id"]
                          );
          
          
                     // Extract Product2Ids and create a map of productItemId to Product2Id
                     let product2Ids = [];
                     let productItemToProduct2Map = new Map();
                    
                     if (productItems && Array.isArray(productItems)) {
                         productItems.forEach(item => {
                             const productItemId = item.stringValue("Id");
                             const product2Id = item.stringValue("Product2Id");
                            
                             if (product2Id) {
                                 product2Ids.push(product2Id);
                                 productItemToProduct2Map.set(productItemId, product2Id);
                             }
                         });
                     }
                                
                     // Query Product2 to get product names
                     let product2Items = await db.query(
                            "Product2",
                            await new ConditionBuilder(
                              "Product2",
                              new SetCondition("Id", "IN", product2Ids)
                            ).build(),
                            ["Id", "Name"]
                          );
                    
                     // Create a map of Product2Id to Name
                     let product2NameMap = new Map();
                     if (product2Items && Array.isArray(product2Items)) {
                         product2Items.forEach(item => {
                             const id = item.stringValue("Id");
                             const name = item.stringValue("Name");
                             product2NameMap.set(id, name);
                         });
                     }
                    
                     // Create a map of productItemId to ProductName (via Product2)
                     let itemToProductNameMap = new Map();
                     productItemToProduct2Map.forEach((product2Id, productItemId) => {
                         const productName = product2NameMap.get(product2Id);
                         if (productName) {
                             itemToProductNameMap.set(productItemId, productName);
                         }
                     });
                    
                     // Get all sample names for the current visit
                     let sampleNames = [];
                     try {
                         if (samples && typeof samples === 'object') {
                             // Handle both array and Proxy array for sample names
                             for (let i = 0; i < samplesCount; i++) {
                                 try {
                                     const sample = samples[i];
                                     if (sample) {
                                         const productItemId = sample.ProductItemId || sample.productitemid;
                                         const productName = itemToProductNameMap.get(productItemId) || '';
                                         if (productName) {
                                             sampleNames.push(productName);
                                         }
                                     }
                                 } catch (sampleError) {
                                     // Error accessing sample for name mapping
                                 }
                             }
                         }
                     } catch (nameMappingError) {
                         // Error mapping sample names
                     }
                    
                     // Check if Immunexis 10mg is present
                     const targetSample = "Immunexis 10mg";
                     const requiredSample = "ADRAVIL Sample Pack 5mg";
                     let hasImmunexis = sampleNames.includes(targetSample);
          
          
                     if (hasImmunexis) {
                         // If Immunexis is present, check if ADRAVIL is also present
                         let hasAdravil = sampleNames.includes(requiredSample);
          
          
                         if (!hasAdravil) {
                             return {
                                 title: "If Immunexis 10mg is added to a visit, ADRAVIL Sample Pack 5mg must also be added. However, ADRAVIL Sample Pack 5mg can be added without Immunexis 10mg.",
                                 status: "error"
                             };
                         } else {
                             return {
                                 title: "Sample dependency validation passed - both Immunexis 10mg and ADRAVIL Sample Pack 5mg present",
                                 status: "success"
                             };
                         }
                     } else {
                         return {
                             title: "Sample dependency validation passed - no Immunexis 10mg found",
                             status: "success"
                         };
                     }
          
          
               } catch (error) {
                     // Error in specificSampleDependencyCheck
                     // In case of database error, we might want to pass validation or handle differently
                     // For now, we'll pass the validation to avoid blocking the user due to technical issues
                     return {
                         title: "Sample dependency validation passed - technical error occurred",
                         status: "success"
                     };
                 }
             }
          
          
             /**
              * The rule 'isAtLeastOneHCP' blocks the user from submitting a call.
              * Validation: Require at least one HCP (Person Account) for a HCO (Institution Account) call on Submit.
              * @returns result { title: string, status: "success" | "error" };
              */
             async function isAtLeastOneHCP() {
                 try {
                    
                     // Log current account details from record (from outer scope)
                     const currentAccountId = record.stringValue("AccountId");
          
          
                     // Use accountDao to check if current account is a Person Account
                     let isPersonAccount = await accountDao.getInstance().getIsPersonAccount();
                     let isInstitution = await accountDao.getInstance().getIsInstitution();
          
          
                     if (isPersonAccount) {
                         // This is already a Person Account (HCP), so requirement is met
                         return {
                             title: "HCP validation passed - current account is a Person Account (HCP)",
                             status: "success"
                         };
                     }
          
          
                     // Only apply HCP validation to Institution accounts
                     if (!isInstitution) {
                         return {
                             title: "HCP validation skipped - account is not an Institution Account",
                             status: "success"
                         };
                     }
          
          
                     // This is an Institution Account, check for HCP attendees
                     let childCallAccounts = await accountDao.getInstance().getChildCallAccounts();
                    
                    
                     let isRequirementValid = false;
                     let hcpAttendees = [];
                     let nonHcpAttendees = [];
          
          
                     if (Array.isArray(childCallAccounts) && childCallAccounts.length > 0) {
                         // Check if any attendee is a Person Account (HCP)
                         for (let i = 0; i < childCallAccounts.length; i++) {
                             let attendee = childCallAccounts[i];
                             let attendeeIsPersonAccount = attendee.boolValue("IsPersonAccount");
                             let attendeeName = attendee.stringValue("Name") || attendee.stringValue("Id");
                     
                             if (attendeeIsPersonAccount) {
                                 isRequirementValid = true;
                                 hcpAttendees.push(attendeeName);
                             } else {
                                 nonHcpAttendees.push(attendeeName);
                             }
                         }
                     }
          
          
          
          
                     // Add more descriptive message based on the scenario
                     if (!isRequirementValid) {
                         return {
                             title: "At least one HCP (Healthcare Professional) must be associated when creating a visit for an HCO (Healthcare Organization).",
                             status: "error"
                         };
                     } else {
                         return {
                             title: `HCP validation passed - Institution Account with ${hcpAttendees.length} HCP attendee(s): ${hcpAttendees.join(', ')}`,
                             status: "success"
                         };
                     }
          
          
               } catch (error) {
                     // Error in isAtLeastOneHCP
                     // In case of error, fail the validation to be safe
                     return {
                         title: "HCP validation failed - error occurred during validation",
                         status: "error",
                         error: error.message
                     };
                 }
             }
          
          
             /**
              * The rule 'isMoreThanOneHCO' blocks user from submitting a call.
              * Validation: Restrict to one HCO (Institution Account) attendee per Call.
              * @returns result { title: string, status: "success" | "error" };
              * Note: Expected only 1 HCO attendee per call)
              */
             async function isMoreThanOneHCO() {
                     try {
                        
                         let counter = 0;
                         let isPersonAccount = await accountDao.getInstance().getIsPersonAccount();
                         let accsRelatedToChildCall = await accountDao.getInstance().getChildCallAccounts();
          
          
                         let hcoAccounts = [];
                         let hcpAccounts = [];
          
          
                         if (isPersonAccount || accsRelatedToChildCall.length) {
                             for (let i = 0; i < accsRelatedToChildCall.length; i++) {
                                 let relatedAcc = accsRelatedToChildCall[i];
                                 let attendeeIsPersonAccount = relatedAcc.boolValue("IsPersonAccount");
                                 let attendeeName = relatedAcc.stringValue("Name") || relatedAcc.stringValue("Id");
                                
                                 if (!attendeeIsPersonAccount) {
                                     counter++;
                                     hcoAccounts.push(attendeeName);
                                 } else {
                                     hcpAccounts.push(attendeeName);
                                 }
                             }
                         } else {
                             counter++;
                         }
          
          
                         const isValid = counter <= 1;
          
          
                         if (!isValid) {
                             return {
                                 title: "Only 1 HCO (Healthcare Organization) attendee can be added per visit.",
                                 status: "error"
                             };
                         } else {
                             return {
                                 title: `HCO count validation passed - found ${counter} HCO account(s)`,
                                 status: "success"
                             };
                         }
          
          
                 } catch (error) {
                         // Error in isMoreThanOneHCO
                         return {
                             title: "HCO count validation failed - error occurred during validation",
                             status: "error",
                             error: error.message
                         };
                     }
                 }
          
          
          
          
             // Entry point: Check if proper parameters are provided
             if (record && user && env && db) {
                 // Check action name - only run validation for specific actions
                 const actionName = getActionName(env);
                 const allowedActions = ['Submit', 'Sign', 'runCustomScriptValidations'];
                
                 if (!allowedActions.includes(actionName)) {
                     // Skip validation for other actions
                     return [{
                         title: `Validation skipped - action is "${actionName}"`,
                         status: 'success'
                     }];
                 }
                
                 // Platform detection: Check which field structure exists to determine wrapping behavior
                 const contextData = parseContextData(record);
                
                 // Web uses nested field paths (e.g., "ProviderVisit")
                 // Mobile uses simple field names (e.g., "Visit")
                 hasWebField = contextData?.["ProviderVisit"] !== undefined;
                
                 // Wrap in array if using web platform, don't wrap for mobile
                 const wrapAsArray = hasWebField;
                
                 // Apply wrapping based on platform
                 if (wrapAsArray) {
                     // Web solution - wrap in array
                     return [validateVisit()];
                 } else {
                     // Mobile solution - no wrapping
                     return validateVisit();
                 }
             }
            
             // If no parameters provided yet, return validation function wrapped in array
             return [validateVisit];
           })();
           
          Ladataan
          Salesforce Help | Article