Monopoly app bouwen in Salesforce – deel 8: Huur betalen

Als je de vorige aflevering hebt gelezen, dan zou je denken dat we nu een heel simpele flow gaan bouwen. We weten wie de eigenaar van de Asset is en welke huur er nu voor gevraagd mag worden, dus de betaling kan helemaal automatisch worden geregeld.

We gaan het echter een klein beetje interessanter maken. Als je in het echt Monopoly met elkaar speelt, moet je ook goed opletten.

Als we een app voor een echt bedrijfsproces aan het inrichten zouden zijn, dan is het juist wenselijk omdat soort menselijke stappen, die een kans op fouten of vertraging vormen helemaal te automatiseren. Maar we zijn bezig met een gezelschapsspel en een creatieve uitdaging waarvoor we zo veel mogelijk Salesforce technieken willen gebruiken.

Wat ik wil is dat de verhuurder moet opletten om ook daadwerkelijk geld te ontvangen. Slapend rijk worden? Mooi niet!

Maar hoe pakken we dat dan aan? In het echte spel geef je de verhuurder even de tijd om te zeggen dat er betaald moet worden, maar zit die te slapen en gooit de volgende speler de dobbelstenen, dan is de kans om huur te innen voorbij gegaan. Nu zit je echter in een Screen Flow en daarbinnen heb je geen beschikking over een Wait element. Als je als actieve speler snel wilt doorklikken, dan kan dat gewoon. Bovendien zitten de andere spelers niet met jou mee op jouw scherm te kijken, dus in real time zien wat er bij een andere speler gebeurt, is lastiger.

De oplossing die we hiervoor gaan maken is een ‘voorwaardelijke betaling’. De actieve speler betaalt gewoon, maar als de verhuurder niet snel genoeg reageert, gaat de transactie niet door en krijgt de ‘huurder’ het voorwaardelijk betaalde bedrag weer terug.

Stap 1: een nieuw object: Transaction

Hiervoor introduceren we een nieuw object waarin we de transactie vastleggen. Daar moeten het bedrag, de betalende en de ontvangende Player in zitten en we willen kunnen zien of de betaling voorlopig of definitief is.

  • Amount
    • Currency 12,2
  • Transaction Source Player
    • Lookup Player
  • Transaction Destination Player
    • Lookup Player
  • Status
    • Picklist met de volgende waarden
      • Tentative
      • Confirmed
      • Canceled

Het proces in het kort

Wanneer de speler op een veld komt waarvan de Asset in het bezit is van een andere speler, zal het volgende gebeuren:

  • De juiste huurprijs wordt berekend
  • Er wordt een Transaction gemaakt met de Status Tentative
  • Er wordt een Custom Notifitation gestuurd naar de Player die de Asset bezit
  • De actieve speler krijgt een scherm te zien waarop staat wie de Asset in bezit heeft en wat de te betalen huurprijs is

Voor de actieve speler is de interactie hier afgelopen. Door op de Next/Finish button te klikken, eindigt de beurt. Nu moet de verhuurder aan de slag.

  • De notificatie moet deze speler op de Transactie attenderen
  • De verhuurder opent de record pagina van de transactie
  • Voor deze speler is de pagina grotendeels Read Only
  • De enige interactiemogelijkheid is Confirm quick action in het Highlights Panel
  • Die button is voorwaardelijk zichtbaar:
    • Alleen wanneer de speler die de pagina bekijkt de Transaction Destination Player is
    • En alleen wanneer de Status van de Transaction Tentative is

De ontvangende speler krijgt beperkt de tijd om de Transaction te bevestigen. Dus gaan we ook een record triggered flow inrichten die een Transaction bijwerkt naar Canceled als die na een bepaald aantal minuten nog niet Confirmed is.

Tot slot moet de betalende speler uiteindelijk geïnformeerd worden of de transactie definitief is geworden of niet. Dat posten we op de chatter feed van die Player.

Stap 2: Voorbereidingen

Voor we verder kunnen bouwen aan de screenflow voor de actieve speler, moeten we een aantal dingen regelen.

  • Een Custom Notification Type maken
  • Een flow maken die de juiste huurprijs voor een Asset berekent
  • Een User lookup maken op het Player object
  • De Confirm action maken voor de Transaction
  • Goed inrichten van de Transaction Lightning Page
  • De record triggered flow inrichten die een Transaction op Canceled zet als die na 2 minuten nog niet op Confirmed staat
  • De record triggered flow bouwen die op de chatter feed van de betalende speler post of de betaling definitief is geworden of niet

Custom Notification Type

Custom notifications zijn meldingen die Salesforce gebruikers kunnen ontvangen. Of je ongelezen meldingen hebt zie je rechtsboven in je scherm.

Je kunt deze sturen vanuit een flow, maar daarvoor moet je eerst een Custom Notification Type maken.

Hiervoor zoek je in de de linkerbalk van de setup naar Notification, klik op Custom Notifications en klik op de New button.

We geven hem een naam kiezen waar deze beschikbaar moet zijn en klikken op Save.

Huurprijs berekenen

De huurprijs hoeft in de meeste gevallen slechts opgezocht te worden in de juiste Asset Rent Rule van de Asset. De huur voor de nutsbedrijven is afhankelijk van hoeveel stappen je hebt gezet om daar te belanden, dus daar moeten we wel een berekening maken.

We maken een nieuwe Autolaunched Flow genaamd ALF – Return Asset Rent Price.

Hier beginnen we met het ophalen van de andere Assets uit dezelfde Asset Group die ook in bezit zijn van dezelfde speler. Dit aantal Assets in bezit uit de groep is ook een van de eigenschappen waarmee we de juiste Asset Rent Rule willen gaan zoeken.

In de hoofdflow hebben we niet rechtstreeks de Asset als een record variabele beschikbaar. We kunnen een Get Records element gebruiken om de Asset op te halen op basis van het Id, maar als we de inputPlayer variabele van de hoofdflow doorgeven aan deze subflow, dan hebben we via de Position lookup en de Asset lookup daarvan wel toegang tot alle veldwaarden van de Asset zoals Id, Buildings, Owned by Player en Asset Group.

Hierna tellen we met een Assignment hoeveel Assets er gevonden zijn die aan deze criteria voldoen. Tip: als je een number variabele maakt om dingen te tellen, zowel binnen een flow als voor een Number veld op een object, zorg dan dat de default waarde ingesteld is op 0. Daardoor kun je er zekerder van zijn dat je geen onverwachte uitkomsten krijgt bij bijvoorbeeld het gebruik van groter dan / kleiner dan in beslissingen.

Nu weten we alles om precies de juiste Asset Rent Rule te kunnen zoeken.

Hoewel het niet de bedoeling is dat deze filters meer dan één resultaat opleveren, is dat meestal wel mogelijk. De beheerder van de app of iemand die rechten heeft om Asset Rent Rules te maken of bewerken, kan altijd een fout maken. Om toch te kunnen voorspellen en controleren welke record van de verschillende passende resultaten er gebruikt wordt, sorteer ik bij een Get Records voor één resultaat vaak wel zo dat de laatste gewijzigde record wordt gebruikt.

De Asset Rent Rules die we gemaakt hebben, omvatten niet de basis huurprijzen. Dus eigenlijk zou onze flow ook een back up plan moeten bevatten, waarbij de Base Rent Price van de Asset wordt gehanteerd als er geen Asset Rent Rule gevonden wordt, die aan de criteria voldoet. De eenvoudigere route is dat we alsnog Asset Rent Rules aanmaken voor de basis huur van elke Asset. Let op: bij de steden met 3 straten hebben we steeds 2 Asset Rent Rules nodig die op de Base Rent Price uitkomen.

Met deze excel en Salesforce Inspector Reloaded, kun je de extra Asset Rent Rules importeren.

De laatste stap is dat we vanuit deze flow de gevonden huurprijs terug gaan geven aan de Parent flow. Dit doen we door de waarde uit de gevonden Asset Rent Rule in een variabele toe te wijzen die beschikbaar is voor output.

Huurprijs maal aantal stappen

Speciaal voor de nutsbedrijven, maken we wel nog een extra aftakking in de flow. De uitkomst van de beslissing bepaalt of we de opgehaalde huurprijs nog met het aantal stappen moeten vermenigvuldigen. De waarde die voor deze beslissing gecheckt moet worden, moet niet hard-coded in de flow zitten, dus we moeten de informatie die nodig is om deze beslissing te maken in data of custom metadata stoppen.

De simpelste methode lijkt mij een nieuwe checkbox op het Asset object te introduceren: Multiply_Rent_Price_by_Number_of_Steps__c. Dit hoeven we alleen voor het Elektriciteitsbedrijf en het Waterleidingbedrijf aan te vinken.

Verder moeten we het aantal stappen dat de actieve speler gezet heeft kunnen meegeven aan deze subflow. Om er geen extra variabele voor te hoeven aanmaken, maken we een speciaal veld aan voor de waarde van de laatste worp van de speler op het Player object. Ook moeten we in de SCR – Roll and Move flow de waarde van de worp op de Player vastleggen.

Nu kunnen we de extra Decision in ALF – Return Asset Rent Price toevoegen.

Voor het toewijzen van de prijs voor een van de nutsbedrijven hebben we een formule nodig die we vervolgens in de Assignment kunnen gebruiken.

Terwijl we aan de kant van de beslissing waar we niet hoeven te vermenigvuldigen, simpelweg de Rent Price van de gevonden Asset Rent Rule toewijzen, gebruiken we aan de andere tak van deze Decision deze formule.

Confirm Action

Deze Action is een van de meer eenvoudige features, maar des te belangrijker voor een admin om te beheersen.

Een Action kun je zien als een soort mini page layout, bedoeld om een specifieke record update heel eenvoudig te maken voor gebruikers.

  • Je laat alleen de velden zien die van belang zijn voor de update die je gebruiker moet uitvoeren
  • Je kunt de nieuwe waarden van de te updaten velden alvast voor de gebruiker prefillen
  • Je kunt ook velden die je niet aan de gebruiker laat zien prefillen, bijvoorbeeld met een bepaalde status waarde of de datum van vandaag

Zo stel je de eindgebruiker in staat sneller te werken en verklein je de kans op fouten.

Een Action is veel sneller te maken dan bijvoorbeeld een Screen Flow en ook veel eenvoudiger.

We gaan naar Object Manager in Setup en zoeken het object Transaction op.

De layout ziet er als volgt uit.

De enige waarde die we hoeven te prefillen is Status = Confirmed.

User lookup op Player

Voor de zichtbaarheid van deze button op de Lightning Page, is een van de voorwaarden dat de gebruiker die de pagina bekijkt, de ontvangende speler is.

Om dat te kunnen bepalen, hebben we een veld op de Player record nodig, dat aangeeft welke User Id bij die gebruiker hoort.

Transaction Lightning Page

In deel 2 heb je al gezien hoe we op een Lightning Page Field Sections voorwaardelijk zichtbaar kunnen maken. Op precies dezelfde manier kun je dat ook doen met Actions in het Highlights Panel.

Dat je elke Component op een Lightning Page eigen voorwaarden voor zichtbaarheid kunt geven, maakt dit veel gebruiksvriendelijker dan verschillende recordtypen hebben en voor elk een volledige Page Layout te moeten managen.

Om te beginnen, gaan we naar de Lightning App Builder voor het object Transaction en maken we een nieuwer Lightning Page aan.

We beginnen nu ‘from scratch’. Allereerst slepen we een Dynamic Highlights Panel op de pagina.

Daarin slepen we de velden Transaction Source Player en Amount. Door ze op het Highlights Panel te zetten, zijn ze automatisch Read-Only.

In het Highlights Panel zetten we ook de Action Buttons neer die we nodig hebben.

De custom Confirm action kunnen we op dezelfde manier als andere componenten op de pagina voorwaardelijk zichtbaar maken. Aan de volgende twee voorwaarden moet voldaan zijn om deze button te tonen:

  • Status = Tentative
  • $User.Id= $Record.Transaction_Destination_Player__r.User__c

Dat laatste blijkt niet te kunnen. Je kunt voor voorwaardelijke zichtbaarheid niet een veldwaarde met een andere veldwaarde vergelijken, alleen met een ‘hard coded’ verwachte uitkomst. Dus moeten we een formule bouwen en die in het filter gebruiken.

We voegen nog een Field Section toe die alleen editable mag zijn voor de Admin, zodat we test records kunnen aanmaken om de voorwaardelijk zichtbare button te testen. Ook moeten we een custom tabblad aanmaken voor dit object. Dat gebeurt namelijk niet vanzelf als je het nieuwe object aanmaak met Schema Builder zoals wij gedaan hebben.

Om de zichtbaarheid te testen, moeten we eerst onze eigen user aan de Player waarmee we gaan testen koppelen.

Nu we de bewerkbare sectie op de pagina hebben en een tabblad, kunnen we via de App Launcher naar het tabblad Transactions gaan en er een paar aanmaken. Hieronder zie je hoe ik getest heb of de voorwaardelijke zichtbaarheid werkt zoals ik die bedoeld had.

Cancel Transaction flow

Om de ontvangende speler slechts 2 minuten de tijd te geven om te bevestigen, maken we een flow die de Transaction bijwerkt naar Canceled als die 2 minuten na het ontstaan nog steeds Tentative is.

Hiervoor laten we de flow alleen activeren als de record nieuw wordt aangemaakt en de Status Tentative heeft.

We plaatsen niets onder het ‘run immediately’ pad, maar voegen een pad met 2 minuten vertraging toe.

Na 2 minuten gaan we controleren of de Transaction waarvoor het flow interview gestart is, nog steeds de status Tentative heeft. Hiervoor is het nodig dat we dezelfde record opnieuw ophalen met een Get Records element. Doe je dit niet, dan kijk je naar de veldwaarden zoals die waren op het moment dat het flow interview werd gestart en weet de flow niet van eventuele wijzigingen die sinds dat moment op het record zijn gemaakt.

Hierna volgt een Decision waarin we checken of de actuele status van de Transaction nog Tentative is. Alleen wanneer dat inderdaad zo is, updaten we de status naar Canceled. Let op dat we hier dus niet de $Record variabele gebruiken in deze check.

De test

Om de werking van de flow te testen, maken we drie Transactions aan.

  1. Een maken we aan met de status al op Confirmed bij het aanmaken. Hierbij moet de flow niet geactiveerd worden.
  2. Een maken we aan met Status Tentative, maar die updaten we meteen daarna naar Confirmed. Hierbij wordt de flow wel geactiveerd, maar vindt er geen update plaats.
  3. De derde maken we als Tentative aan en daar doen we niets meer mee. Hier zou de status wel automatisch moeten worden geüdatet.

Voor deze test moeten we daarnaast ook het volgende voorbereiden:

  • Aangeven voor op het Transaction object dat de historie van het Status veld gevolgd moet worden
  • De Related List Transaction History op de Page Layout en de Lightning Page zetten.

In de onderstaande screenshots zie je dat ik Transaction 6 om 9:46 heb geüpdatet om te laten zien dat er geen automatische update na 2 minuten heeft plaatsgevonden. Bij Transaction 7 zie je dit ook en je kunt daar ook in de Transaction History zien dat ik de status van deze Transaction meteen na het aanmaken zelf heb aangepast naar Confirmed. Bij Transaction 8 zien we in de Transaction History dat de Status om 9:45 is aangepast. Dat is door de flow gedaan.

Dus ook dit stuk functionaliteit ziet er goed uit.

Post to chatter flow

Ook hiervoor maken we een Record Triggered Flow. Deze moet geactiveerd worden zodra de status van een Transaction naar ofwel Confirmed ofwel Canceled wordt bijgewerkt.

Vervolgens maken we twee verschillende Text Templates die gebruikt kunnen worden in de chatter posts.

We gebruiken een Descision om afzonderlijke paden voor beide opties te krijgen en plaatsen dan in elk pad een Post to Chatter action.

De test

Om dit te kunnen testen, moeten we de Feed Tracking inschakelen voor het Player object en de Chatter component op de Player Lightning Page zetten.

We maken van de gelegenheid gebruik om ook Dynamic Related List componenten voor de gerelateerde lijsten Transactions Received en Transactions Paid op de pagina toe te voegen.

Nu kan ik met een Transaction record gaan testen. Zoals we zien in de onderstaande screenshot werkt het wel, maar krijgen we een post in platte tekst met alle HTML tags voor de vetgedrukte tekst erin.

UnofficialSF to the rescue

Ik zal er in een toekomstig blog nog eens uitgebreid op in gaan, maar nu houd ik het kort. UnofficialSF is een website waar je zeer nuttige custom Screen Components en Flow Actions kunt vinden die je in je Salesforce org kunt installeren. Ik weet dat ze een handige Action hebben waarmee je een chatter post met werkende rich text opmaak kunt plaatsen vanuit een flow.

Om die te installeren moeten we ook de Screen Components en Flow Actions basepacks installeren. De base packs en installatielinks daarvan vind je hier.

Het handigste is om de download link te kopiëren. Het https://login.salesforce.com deel hiervan heb je niet nodig. Je pakt een nieuw tabblad van de org waar je in aan het bouwen bent, wist daar alles achter je hoofddomein in de adresbalk en plak de gekopieerde link daar achter, waar je juist alleen de tekst vanaf /packaging nodig hebt.

Dit herhalen we voor het Screen Components Base Pack en nadat die beide succesvol geïnstalleerd zijn voor Post Rich Chatter.

Nu vervangen we in de flow de ‘gewone’ Post to Chatter actions door de Post Rich Chatter actions.

En bij de nieuwe test zien de feed posts er zo uit. Hiervoor heb ik wel de html tags in de Text Templates moeten opschonen.

O ja, niet geheel onbelangrijk: als de Transaction naar Confirmed gaat, moet het geld definitief van de betalende naar de ontvangende speler worden verplaatst.

Hiervoor maken we een Autolaunched Flow: ALF – Execute Confirmed Transaction.

Eerst maken we twee variabelen aan:

  • inputTransaction
    • Type: Record
    • Object: Transaction
  • updatePlayers
    • Type: Record
    • Object: Player
    • Multiple record: true

Eerst halen we de beide Player records op. We kunnen het zo doen dat we slechts één get Records element nodig hebben, maar om eerlijk te zijn, voeg je daarvoor zoveel extra complexiteit toe, dat het hier de moeite niet waard is. We weten immers dat we nooit met meer dan twee Player records te maken zullen hebben in deze flow.

Hierna trekken we bij de betalende speler het Transaction Amount van hun Cach Balance af, en datzelfde bedrag tellen we op bij de Cash Balance van de ontvangende speler.

Vervolgens voegen we de beide enkelvoudige Player variabelen toe aan de updatePlayers collectie, waardoor we wel de beide updates in één interactie met de database kunnen verwerken.

Na deze update gebruiken we de Lock Record action om te zorgen dat niemand nu nog iets aan deze afgehandelde transactie kan bewerken.

Tot slot moeten we deze flow nog invoegen in de flow die een Transaction Confirmed maakt.

En vergeet bij het opslaan van een nieuwe Flow versie nooit te noteren wat er veranderd is ten opzichte van de vorige versie.

De screenflow voor de actieve speler

Nu komen we terug bij de flow SCR – Buy Asset or Pay Rent die we in het vorige deel hebben gemaakt. Daar gaan we verder bij de derde, nu nog lege tak van de beslissing waar wordt bekeken of de Asset in eigendom van de Bank of de actieve speler is.

Vóór we de speler een scherm kunnen laten zien, moeten we de juiste huurprijs vinden. Daarvoor voegen we hier allereerst de ALF – Return Asset Rent Price subflow in.

Vervolgens maken we eerst de Transaction record aan en tonen daarna het scherm aan de gebruiker.

Verder bouwen we nu nog de controle in of de Position waar de Player nu op staat wel een Asset heeft. Bij de subflow waar we eventueel aan een Position gekoppelde Rules gaan uitvoeren, doen we dat immers ook binnen de subflow. De decision die we hiervoor moeten toevoegen ziet er zo uit.

Je ziet dat we nu bij elke volgende stap van de keten testen of het betreffende relatieveld niet leeg is. De gedachte is dat het geen zin heeft om te vragen of de Position een Asset heeft, als we überhaupt geen Position weten. Omdat het derde pad van de Who owns the Asset? decision het default path is, zou dat ook gevolgd worden als de Owned_by_Player__c waarde van de Asset null is en dat gaat niet werken, want dan krijg je al niet het juiste aantal Assets in bezit in de groep en dus ook geen of een onjuiste Asset Rent Rule.

Het theoretische scenario dat helemaal niemand de Asset bezit, vangen we dus af in dat eerste Decision element.

Documenteer je aanpassingen

Nu sla ik de flow met mijn wijzigingen op als een nieuwe versie. Bij het opslaan van een nieuwe flow versie is het belangrijk om in de Description aan te geven wat er anders is ten opzichte van de vorige versie. Dat maakt analyse bij eventuele toekomstige problemen eenvoudiger.

Ik noem daarin de datum van de aanpassing, mijn volledige naam, want als admin of consultant kan het zijn dat meerdere mensen in de loop der tijd via hetzelfde gebruikersaccount aanpassingen maken, en een verwijzing naar een case- of ticketnummer waarin de gevraagde functionaliteit of wijziging is aangevraagd en opgeleverd.

Opnemen in de hoofdflow

De volgende stap is om de SCR – Buy Asset or Pay Rent flow op te nemen in SCR – Roll and Move. Deze komt direct na de SCR – Check for and Execute Position Rule subflow op beide plaatsen.

Ook hier slaan we de wijzigingen op in een nieuwe versie met een goede Description.

Inmiddels zitten we op in totaal 11 flows die we in deze serie hebben gebouwd sinds aflevering 4. Daarom werk ik nu graag het schematische overzicht bij van hoe alle flows met elkaar samenwerken en met name welke subflows waar worden gebruikt.

Volgende stappen

Nu hebben we de belangrijkste handelingen voor in een beurt van een speler gemaakt. Volgende keer gaan we bepalen hoe de beurt overgaat naar een andere speler en hoe de actieve speler de Flow waar we in de afgelopen paar afleveringen aan gewerkt hebben eigenlijk kan openen en uitvoeren.

Geef een reactie

Het e-mailadres wordt niet gepubliceerd. Vereiste velden zijn gemarkeerd met *