Afgelopen tijd ben ik bezig geweest met het ontwikkelen van een web framework. Dit framework is gebaseerd op de core van de NichePlayer. De laatste maand ben ik met dit framework een app te ontwikkelen voor mijn studenten scoutinggroep de U.F.O.-stam. De functionaliteit is ten opzichte van de NichePlayer significant anders. In tegenstelling tot de NichePlayer hoeft de U.F.O.-app geen muziek af te spelen.

De U.F.O.-App is ontwikkeld ter vervanging van het betaalsysteem voor drankjes en andere consumpties. Voor de invoering van de app werd gewerkt met fysieke muntjes. Deze konden leden per 10 stuks voor €9,- kopen bij iemand van de PilCie (de bar commissie van de vereniging). Wanneer een lid een consumptie ging kopen stopte je een muntje in een doosje, en haalde het lid de consumptie uit de koelkast.

Het muntjes systeem had een aantal nadelen. Zo konden consumpties alleen veelvouden van €0,90 kosten. Dit is prima voor een biertje (inkoopprijs €0,80) of een glas wijn (€6 voor een fles, 7 glazen per fles, €0,85 per glas). Het probleem komt echter bij producten die bijvoorbeeld een inkoopprijs hebben van €1,40 maar worden verkocht voor twee muntjes (€1,80). Het muntjes systeem was voor sommige producten te goedkoop, en anderen juist te duur.

Daarnaast was het moeilijk om een goede administratie bij te houden, zowel op gebied van betalingen, consumpties en huidig inventaris. Het was daarbij ook onbekend hoeveel muntjes er precies bij de leden thuis lagen, wat problematisch is voor de begroting van de vereniging.

Digitaal systeem

Ik heb geprobeerd de gedachte achter het oude systeem om te vormen naar het digitale domein. Een belangrijk aspect is de sociale controle bij betalingen. Bij de muntjes moest iemand van de bar commissie fysiek aanwezig zijn, het betaalverzoek controleren, en daarna de muntjes overhandigen. Dit principe heb ik overgenomen in de U.F.O.-App. Geld binnen de app werkt op basis van saldo. Dit saldo is gekoppeld aan user accounts. Het opwaarderen van het saldo werkt nog steeds door het betalen van een betaalverzoek. Dit omdat het implementeren van een betaal API complex is, en niet goed gecombineerd kon worden met mijn master opleiding. Wanneer een lid wil opwaarderen komt deze naar een PilCie lid. Het lid laat vervolgens zien dat de betaling succesvol was. De sociale controle wordt uitgevoerd doordat het lid een (unieke) QR code moet scannen op de telefoon van het PilCie lid. Deze QR code is uniek omdat er gebruiker en tijd informatie wordt meegestuurd, en kan niet later opnieuw worden gebruikt. Als de code correct is gescand rondt het lid de opwaardering af, en wordt het bedrag bijgeschreven op het saldo.

Consumpties kunnen worden gekocht uit een grote lijst met producten. Omdat er wordt gewerkt met saldo kosten producten niet langer een veelvoud van €0,80. Wel wordt er een percentage genomen op de inkoopprijs om kapotte en vergeten consumpties op te kunnen vangen. Hierdoor is het ook mogelijk om een groter aanbod te hebben. In plaats van biertjes, wijn, of frisdrank kunnen nu ook speciaalbiertjes en ciders worden verkocht.

Een mooie bijkomst aan het nieuwe digitale systeem is dat de betalingen, consumpties en inventaris automatisch beter worden bijgehouden. Transacties zijn te herleiden naar een persoon, uitvoerder (voor opwaarderingen), product en tijd. Hiermee kan precies worden achterhaald hoeveel geld er in omloop is in het systeem, en hoeveel er precies van een bepaald product in voorraad is. Bestellingen kunnen hiermee accurater worden gedaan.

Naast het digitaliseren van het oude systeem is er ook ruimte voor nieuwe ontwikkelen. Een nieuwe functionaliteit is bijvoorbeeld Buckets. Een Bucket is een lijst waar andere gebruikers producten kunnen toevoegen. Hierdoor hoef iemand die naar de koelkast loopt niet te onthouden wat er gehaald moet worden, en wie precies iets wilde! Consumpties die aan de bucket worden toegevoegd worden betaald door het lid die ze toevoegt. Wat opmerkelijk is, is dat nieuwe functies als Buckets nu al normale taal binnen de groep beginnen te worden.

Ontwikkeling

Met het ontwikkelen van de app werk ik aan het opzetten van een (digitale) community. Hierbinnen kan ik functionaliteiten en aannames testen bij een grote groep gebruikers. Omdat de U.F.O.-stam een voor mij bekende groep is, is het toegestaan dat er dingen misgaan (wat ook een aantal keer is gebeurd). Het is een veilige plek om te kunnen ontwikkelen ten opzichte van het direct uitbrengen van andere projecten.

Tijdens het ontwikkelproces heb ik constant gebruikerstests uitgevoerd en veel feedback ontvangen. Hierbij zijn een aantal nieuwe functies uitgekomen en heb ik de User Interface (UI) en User Experience (UX) aangepast. Leden blijven constant naar mij toe komen met ideeën voor de app, en ik ben gelijk te vinden wanneer iemand een bug tegen komt.

Ik heb gemerkt dat een live versie van een product significant anders is binnen de ontwikkelomgeving. Het bleek dat het systeem in productie erg traag was vanwege een aantal ontwerp- en denkfouten. Op langer termijn zou de app onbruikbaar zijn geworden, maar door het korte lijntje (ik ben zelf als lid van de U.F.O.-Stam ook een gebruiker) konden de bugs snel opgelost worden. De core die ooit uit de NichePlayer kwam is met dit project sneller en stabieler, en de wijzigingen zijn eenvoudig te (her)implementeren in het oude framework.

Technische details

Dit systeem heeft twee onderdelen: een backend en een frontend. De backend is geschreven met PHP en maakt gebruik van het Symfony Framework. Dit framework biedt tools waarmee snel een API kan worden opgezet met user management en een relational database.

De frontend heb ik, net als een hoop andere projecten sinds mijn stage geschreven met het Quasar Framework. Dit op VueJS gebaseerde framework biedt een snelle manier om user interfaces te schrijven. Het heeft een groot aantal kant-en-klare interface componenten en is eenvoudig uit te breiden met plugins en andere scripts.

De koppeling tussen deze twee systemen wordt gedaan met een Object-Relational Mapping library voor Vuex. Deze library regelt het ophalen van de data van de server, maar biedt ook manieren om te zoeken in de data op de client. Door de relational mapping functies is het eenvoudig om gekoppelde types op te halen (bijvoorbeeld: een transactie heeft een user en een product id).