Huizen bouwen is een belangrijk element van het spel, dus dat moet uiteraard ook aanwezig zijn in onze versie in Salesforce. En Flow Builder heeft mooie mogelijkheden om een hier een aantrekkelijke User Experience voor te maken. Laten we niet teveel woorden vuil maken aan de introductie en meteen de handen uit e mouwen steken.
Om te bepalen of een speler huizen kan kopen, moeten we eerst weten of die speler alle Assets in een Asset Group bezit en of er in deze Asset Group nog wel meer huizen gebouwd kunnen worden.
Dus gaan we op Asset group vastleggen:
- wat het totale aantal Assets in die Asset Group is
- wat de aanschafprijs van huizen in die Asset Group is
- wat het maximaal aantal huizen is dat er op de gezamenlijke Assets van de Group mag worden gebouwd
Hiervoor maken we de velden Number of Assets in Group (getal), Property Purchase Price (valuta), Maximum number of houses (getal) aan. Toegang tot die velden regelen we niet via de profielen, maar via de Permission Sets.


Om de waarden van deze nieuwe velden in te voeren op de Asset Group Records, gaan we naar het tabblad Asset Groups. Daar kiezen we voor de lijstweergave all en daar voegen we deze nieuwe velden aan toe via Fields to Display.


Nu kunnen we deze lijstweergave gebruiken om op alle Asset Groups in te vullen hoeveel Assets er in de Group zitten en wat een huis op de Assets van die Group kost.

Bij de groepen Stations en Utilities laten we de Property Purchase Price leeg omdat je daar geen huizen kunt kopen.
Een object dat alleen voor deze flow bestaat
Tijdens het uitvoeren van de flow willen we voor elke Asset Group ook weten hoeveel gebouwen er al gebouwd zijn en hoeveel er dus nog bij gekocht kunnen worden. Dit en nog wat andere gegevens hebben we nodig om te kunnen bepalen:
- of de speler in die stad (Asset Group) gebouwen mag kopen
- hoeveel gebouwen er al gebouwd zijn in die stad
- hoeveel gebouwen de speler maximaal nog mag kopen
- wat de totale kosten van het kopen van het voorgenomen aantal gebouwen zijn
- of de speler wel voldoende geld heeft om die aankoop te doen
Het gaat om behoorlijk wat gegevens die we hiervoor graag samen in één object willen hebben. Nu wil ik dat liever niet allemaal op de Asset Group doen, om dit object clean en compact te houden. Bovendien hebben we de meeste van deze gegevens ook nergens anders nodig dan tijdens de flow waarin we de bovengenoemde zaken checken.
Hierom maken we een speciaal object om deze gegevens samen in op te slaan: Building Transaction.
De velden die we in elk geval nodig hebben zijn:
- Asset Group
- Number of Assets in Group
- Property Purchase Price
- Asset Group Max number of Buildings
- 5 x het aantal Assets in de Asset Group
- Asset Group Number of Buildings Built before
- Transaction Quantity
- Asset Group Number of Buildings Built after
- Transaction Amount
Hoe moet de screen flow eruit komen te zien

Bij het ontwerp voor deze flow laten we ons leiden door de user interface die we willen hebben.
De speler moet in een Repeater Component een blokje op het scherm zien voor elke stad waarvan ze alles straten bezitten en waar nog niet op alle straten een hotel staat.
In elk blokje kan de speler met een slider aangeven hoeveel gebouwen ze willen kopen. Het maximum van de slider wordt binnen elk blok bepaald door hoeveel gebouwen er maximaal gebouwd mogen worden (een hotel is eigenlijk gewoon 5 huizen) min het aantal huizen dat er al gebouwd is op de straten van die stad.
Nadat de speler heeft aangegeven waar ze hoeveel huizen willen kopen, moet het nieuwe aantal Buildings op de Assets van die Asset Group worden verwerkt en moet het aankoopbedrag van de Cash Balance van de speler worden afgetrokken.
Uiteraard bouwen we ook een controle in of de speler daar wel genoeg Cash voor heeft, en zo niet, dan kan de speler dit aantal huizen dus niet kopen.
Nu we in grote lijnen hebben bepaald wat we uiteindelijk willen opleveren, kunnen we analyseren wat we nodig hebben om dit te realiseren.
Bepalen in welke steden de speler huizen mag kopen
Dit proces kunnen we als een op zichzelf staand proces zien waar geen scherm input voor nodig is. Via de inputPlayer hebben we toegang tot alle gegevens die we nodig hebben. Daarom bouwen we hiervoor een Autolaunched Flow.
Door te beginnen met het ophalen van alle Assets die in bezit zijn van de inputPlayer, kunnen we de Asset Groups ophalen waarbinnen de speler Assets heeft.
Dat doen we in drie stappen:
Get Assets waarbij Owned_by_Player__c gelijk is aan inputPlayer.Id. En uiteraard moeten de Assets niet belast zijn met hypotheek, want dan mag je er volgens de regels geen huizen op bouwen.

Dan gebruiken we een Transform element om een Text Collection Variable te maken met daarin de Ids van de Asset Groups waar de Assets van de speler toe behoren. Deze verzamelingsvariabele gebruiken we hierna voor het ophalen van de Asset Groups.

Aan de linkerkant nemen we de collectie uit Get Assets owned by Player. Aan de rechterkant, bij de output kiezen we voor Type Text en vinken we Multiple values aan.
Nu trekken we de Connector van Asset Group naar de output. Dankzij deze functionaliteit van het Transform element, hebben we hier geen Loop voor nodig.
De derde stap is de Asset Groups ophalen. Bij de criteria van dit Get Records element kiezen we voor Record Id, de Operator In, en de gewenste waarde AssetGroupIds, de uitkomst van het Transform element dus. Door de Asset Groups waarbij de Property Purchase Price leeg (null) is weg te filteren, halen we die voor de stations en nutsbedrijven niet op. Daar kunnen immers geen huizen op worden gebouwd.

Nu weten we alleen nog niet bij welke van deze Asset Groups de speler alle Assets bezit.
Dat kunnen we snel testen met een dubbele ofwel ‘nested’ loop. In de eerste, buitenste loop bekijken we een voor een alle Asset Groups die we gevonden hebben.

De eerste stap binnen deze loop is om uit alle Assets van de speler alleen diegenen te filteren die tot de huidige groep behoren.

Op de Asset Group, de stad, staat al vastgelegd hoeveel Assets, straten er tot de groep behoren. Als het aantal Assets dat we nu gefilterd hebben daaraan gelijk is, weten we dat de speler de straten van deze stad allemaal bezit en in deze stad huizen mag kopen.
We hebben een Assignment element nodig om te tellen hoeveel Assets er uit ons filter komen.

We maken een nieuwe number variabele met standaard waarde 0 en 0 decimalen. De naam wordt numberOfAssetsOwnedInCurrentGroup. Als Operator van deze Assignment kiezen we Equals Count. Als waarde kiezen we voor de gefilterde Assets van deze Asset Group.
Hierna plaatsen we een Decision element. Wat we testen is of het aantal Assets dat de speler uit deze Asset Group bezit gelijk is aan het aantal zoals genoemd op de Asset Group record. Hiermee controleren we of de speler alle straten van die stad bezit.

Alleen als de speler alle Assets van de groep, alle straten van de stad bezit, zijn de volgende elementen van belang. We gaan dus verder onder het Yes pad van de beslissing.
We zitten nog steeds binnen de loop over de gevonden Asset Groups. Onder het Yes pad van de beslissing beginnen we aan de binnenste loop. Daarvoor gebruiken we de straten (Assets) van de stad (Asset Group) die we al uitgefilterd hadden.

Binnen deze loop over de straten van de stad die we nu bekijken tellen we het aantal huizen dat nu al gebouwd is. Dat aantal gaan we opslaan in een Building Transaction record. Hiervoor gaan we even één stapje terug. We bereiden voor we deze Asset loop in gaan alvast de Building Transaction voor.

Dit is wat we allemaal al kunnen bepalen voor de Building Transaction vóór we de al gebouwde huizen gaan tellen.

Binnen de Assets loop tellen we het aantal huizen dat er op elke straat al staat op bij:
- Number of Buildings built op de huidige Asset Group (van de buitenste loop)
- Asset Group Buildings Built in de Building Transaction
- Asset Group Buildings Built after in de Building Transaction
En het aantal al gebouwde huizen trekken we juist af van
- Number of Buildings Saleable in de Building Transaction
- Asset Group Buildings available in de Building Transaction

Nu sluiten we de Asset loop af en voegen we daarna, wel binnen de Asset Groups loop de Building Transaction toe aan de verzameling van Building Transaction records.

Nu gaan we deze Building Transactions wel echt opslaan in de database. In de screenflow waar de subflow die we nu gemaakt hebben, zullen we die ophalen door te filteren op de Flow interview Id.

Deze flow slaan we op en activeren we.

De Screenflow
Bij het ontwerpen van deze flow liep ik lang tegen de beperkingen van de Repeater Component aan waardoor wat ik oorspronkelijk bedacht had voor deze flow niet haalbaar bleek te zijn. Ik wilde eigenlijk één flow voor kopen en verkopen van huizen maken en had de Building Transactions eigenlijk nooit willen opslaan in de database.
Wanneer dat niet werkt ga je kleine aanpassingen uitproberen tot er een resultaat ontstaat dat goed genoeg werkt en dat gaan we nu samen bouwen. Aan het begin van deze aflevering zag je al een voorproefje van de User Interface die we willen bereiken.
De eerste stap in de screenflow is de subflow die we aan het begin van deze aflevering hebben gebouwd.

Deze krijgt de welbekende inputPlayer variabele mee als input. De subflow slaat de Building Transactions echt op. Die halen we op met een Get Records element en helemaal aan het einde van de flow zullen we de Building Transactions, als ze hun doel gediend hebben, weer verwijderen.

Je ziet nu waar de Flow Interview Id op het Building Transaction object voor dient. Hoewel de subflow een andere flow is, maakt de interactie van de gebruiker ermee wel gewoon deel uit van hetzelfde flow interview als de screenflow van waaruit de subflow wordt opgestart. Daarom kunnen we de Flow Interview Id gebruiken om er zeker van te zijn dat we alleen Building Transactions ophalen voor de huidige speler op dit moment.
Als uit de subflow blijkt dat de speler geen huizen kan kopen, dan moet de rest van de flow natuurlijk worden overgeslagen, dus voegen we nu een beslissing in. Als er Building Transactions zijn gevonden, dan betekent dat dat de speler huizen kan kopen en vervolgen we onze weg.

Als er ten minste één Building Transaction is, dan krijgt de speler het volgende scherm te zien.

Allereerst zorgen we dat de speler zelf steeds kan zien hoeveel geld ze hebben. Zo kun je als speler zelf mee tellen of je niet teveel geld van plan bent uit te geven.
De rode tekst is bij de eerste keer dat de speler het scherm bezoekt niet te zien. Deze kan zichtbaar worden als uit de controle ná dit scherm blijkt dat de speler van plan is meer geld aan nieuwe huizen uit te geven dan ze in cash beschikbaar hebben. Daar kijken we verderop nog uitgebreider naar.
Dan zie je de Repeater Component. De speler zal voor elke stad waarin ze huizen kunnen kopen zo’n blokje zien.
Bij het configureren van een Repeater, kiezen we als eerste de Collection Variable die de brondata bevat die we erin gaat tonen. In dit geval zijn dat de verschillende gevonden Building Transactions.

Onder Configure Display Options zorgen we dat de speler er niet zelf elementen aan toe mag voegen en geen elementen mag verwijderen. De slider die we in elk blok gaan zetten dient om aan te geven hoeveel huizen de speler wil kopen voor die stad.
Het maximum van de slider, bepalen we aan de hand van het aantal huizen dat voor die stad nog gekocht mag worden volgens de gegevens in de Building Transaction.

Zo kan de schaal van de slider voor elke stad anders zijn.

Dat er 2 Repeater blokken naast elkaar kunnen staan, kunnen we inrichten onder het Style tabblad van de Repeater Component zelf.

Voordat we de aankoop van nieuwe huizen gaan uitvoeren, moeten we eerst controleren of de speler wel genoeg geld heeft voor alle huizen die ze willen kopen. Ik had dat heel graag met een Screen Action gedaan die op basis van de aanpassingen van de sliders door de speler meteen het totale bedrag zou berekenen, maar een Screen Action kan op dit moment nog niet werken met gegevens uit de Repeater component. Dus voeren we deze controle uit nadat de speler op de Volgende knop klikt.
In grote lijnen doen we het volgende:
- We vermenigvuldigen voor elke stad het aantal huizen dat de speler wil kopen met de aankooprijs van een huis in die stad
- We tellen die bedragen bij elkaar op
- We controleren of het totaalbedrag hoger is dan de Cash Balance van de speler
- Blijft de speler binnen hun budget, dan kunnen we verder met de rest van de flow
- Wil de speler meer geld uitgeven dan ze hebben, dan leiden we de speler meteen terug naar het Buy Building scherm, waar ze nu de rode waarschuwingstekst ook te zien krijgen.

Omdat ook bij een volgende keer de speler weer teveel geld kan proberen uit te geven, is het belangrijk om voor we gaan tellen de TotalTransactionAmount weer op 0 te zetten.
Daarna evalueren we elk element van de Repeater.

Binnen de loop kijken we eerst met een Decision element of de speler voor die stad gebouwen wil kopen. Zo niet, dan kunnen we meteen door naar de volgende stad.

Dan halen we de hele Building Transaction Record erbij die bij dat element in de Repeater hoort, want de output van de Repeater bevat niet alle gegevens van de oorspronkelijke input record.

Deze gefilterde verzameling die uit één element zou moeten bestaan, gebruiken we voor de binnenste loop. We blijven hiervoor een Loop nodig hebben, want ook al bevat ze gefilterde verzameling maar één element, het blijft een verzameling. Dus hebben we een loop nodig om naar de elementen daarin te kunnen kijken.

Daarbinnen berekenen we dat de speler aan huizen wil uitgeven in deze stad.
De formule transactionAmount berekent het aantal dat in de slider in de Repeater gekozen is maal het Property Purchase Price die we in de Building Transaction hebben vastgelegd.

Nu voeren we wat updates door op de Building Transaction, tellen we het berekende aankoopbedrag op bij het totaal en stellen we een verzameling samen van de Id’s van de Asset Groups waar huizen gebouwd moeten worden.

Na het einde van de Loop over de Repeater elementen vergelijken we het uit te geven bedrag met de Cash Balance van de Player. Wil de speler teveel geld uitgeven, dan leiden we hen terug naar het scherm.

De voorwaardelijke zichtbaarheid van de rode waarschuwingstekst is gebaseerd op deze beslissing.

De speler heeft wel genoeg geld
Als de Cash Balance van de speler wel volstaat om de hoeveelheid huizen te kopen die de speler heeft gekozen in het scherm met de sliders, dan kunnen we transactie gaan verwerken.
Omdat we uiteindelijk op de verschillende Assets het aantal huizen gaan updaten, moeten we die nu ophalen. Welke Assets we nodig hebben en gaan ophalen, bepalen we met de AssetGroupIds collection variable. Deze hebben we gevuld met alleen de ID’s van de Asset Groups waar de speler huizen voor mag en wil kopen.

Daarna maken we gebruik van een ‘nested loop’. Dit deden we ook al met de Repeater output en daarbinnen een loop over de Building Transactions die daarbij horen.
We moeten voor elke Building Transaction het juiste aantal gekochte huizen gaan toevoegen aan de juiste straten. Dus de buitenste loop behandelt elke Building Transaction.

Daarbinnen filteren we eerst alleen de Assets uit die bij deze Transaction horen. Dat kunnen we doen met het Filter element, via een kenmerk dat de Building Transactions en de Assets beide hebben: de Asset Group Id. Praktisch gezegd: als we nu kijken naar de huizen die we voor Arnhem hebben gekocht (één Building Transaction), hoeven we voor het bouwen van die huizen alleen de straten (Assets) van Arnhem te hebben.

Meer technisch gezien: door te filteren welke records er worden gebruikt in die binnenste Loop, voorkomen we onnodige iteraties en verspilling van tijd.
| Limieten Een aantal jaren geleden hanteerde Salesforce een limiet dat er binnen een Flow Transactie maximaal 2.000 elementen geraakt mogen worden. Bij loops, worden dezelfde elementen meerdere keren bezocht, dus dan gaat het snel. Deze limiet bestaat niet meer, maar is nog steeds een indicatie voor hoeveel tijd de flow transactie in beslag gaat nemen. Duurt een transactie langer dan 10 seconden, dan kapt het platform hem af en is het flow interview mislukt. Door tijdens het bouwen te zorgen dat elke transactie ruim onder de 2.000 geraakte elementen blijft, voorkom je in de praktijk dat soort ‘Time Outs’. Een manier om dit te voorkomen is filteren voor je een loop in gaat, om onnodige rondjes door die loop te voorkomen. |
Met deze gefilterde collectie van alleen de Assets van één stad gaan we dus de binnenste loop in, als dat nodig is…
Want er is een mogelijkheid dat we niet eens een Loop nodig hebben. Dankzij het Transform element.
Wat we per Asset moeten doen, is het juiste aantal huizen toewijzen. Met het Transform element, kun je zonder Loop in één element een hele record collection variable maken/bewerken.
Stapje terug: even de rekensom die we hier moeten uitvoeren. En dat doen we even met een voorbeeld. Stel we zijn nu bezig de gekochte huizen aan de straten van Arnhem toe te voegen. Dan moeten we voor de som drie getallen weten:
- Hoeveel huizen staan er nu al op de straten van Arnhem
- Hoeveel huizen moeten we er nog blij plaatsen
- Hoeveel straten heeft Arnhem
Het aantal huizen dat op elke straat moet staan na de bouw, is (bestaande huizen + nieuwe huizen) / aantal straten.
Stel dat we al 6 huizen hadden staan, er komen er 3 bij en Arnhem heeft 3 straten, dan is de som dus: (6 + 3 = 9) / 3 = 3.
Als er een heel getal uit deze som komt, kunnen we het juiste aantal huizen met het Transform element alleen toewijzen aan de straten van Arnhem.

Om te zorgen dat we daar ook echt alleen maar hele aantallen toewijzen, gebruiken we afronding naar beneden in de formule. Stel dat we na de bouw 10 huizen moeten hebben, dan is het aantal huizen per straat dat we met behulp van het Transform element kunnen toewijzen 10/3 = 3,33 afgerond naar beneden = 3.

Hierna moeten we checken of er nog meer huizen te bouwen zijn. Als we 10 huizen te verdelen hebben over 3 straten, dan hebben we nu met het Transform element 3 huizen aan elke straat toegewezen. Dan moeten we nog 1 huis extra op één van de straten toevoegen. Om te zien of er nog te bouwen huizen over zijn, hebben we deze som nodig:
Totaal aantal huizen dat er na de bouw moet zijn in de hele stad – aantal huizen per stad die we al gebouwd hebben * aantal straten.
In dit voorbeeld is dat dus 10 – 3 * 3 = 10 – 9 = 1.

Asset Group Buildings Built after is in het voorbeeld 10 en buildingsBuiltInTransform is 3 * 3 = 9. Dus de waarde van numberOfBuildingsYetToBuild wordt 1.Met een Decision element, checken we of er nog huizen te bouwen zijn.

Zo ja, dan moeten nog aan één of twee van de drie Assets of één van de twee Assets een extra huis toewijzen. In dit voorbeeld gaat het om één van de drie. Voor gevallen waar we nog twee huizen te verdelen hebben, moeten we wel gebruik maken van een Loop.

Binnen deze Loop moeten we dus ook na het eerste gebouwde huis checken of er ook nog een tweede huis op de volgende straat gezet moet worden.
Daarom moeten we ook binnen de Loop checken of er nog te bouwen huizen over zijn. Hierom verplaatsen we het Decision element dat we zojuist vóór de Loop hadden gezet naar binnen de Loop.

In het voorbeeld van 10 huizen in Arnhem is de uitkomst van deze beslissing voor de eerste straat Y en voor de tweede en de derde N.
Hierdoor zal alleen aan die eerste straat een vierde huis worden toegewezen. Dan staat er dus 4 + 3 + 3 = 10 huizen in de stad.
Elke stad heeft één straat waar je net iets meer huur mag vragen. Dat is dan natuurlijk de straat waar je het liefst het grootste aantal huizen hebt staan. Dit bereiken we door in het Get Assets element van een paar stappen terug aflopend te sorteren op Base_Rent_Price__c.


In het Assignment element wijzen we niet alleen het juiste aantal huizen aan de straat toe. Ook halen we één van het aantal nog te bouwen huizen af. Dat is het aantal dat we controleren in de beslissing aan het begin van elke keer dat we door de Loop gaan. Hierdoor voorkomen we dat we teveel huizen bouwen.
Of we nu binnen de loop nog een huis aan de straat hebben toegevoegd of niet, we voegen de straat altijd toe aan de updateAssets collectie. Hierna kunnen we de binnenste én de buitende Loop verlaten.

Hierna moeten we de boel gaan opslaan. De volgende updates voeren we uit:
- We trekken het totale aankoopbedrag voor de huizen af van de Cash Balance van de Player en updaten de Player record
- We updaten de Assets om voor elke straat het nieuwe aantal huizen op te slaan.
- We verwijderen de Building Transactions omdat deze buiten deze flow nergens voor nodig zijn.

Dit was een aardig complexe flow om te bouwen en vooral om te bedenken. Wat we nu nog moeten doen, is deze invoegen op de juiste plekken in de SCR – Roll and Move flow. Dat pakken we in de volgende aflevering aan.