Transporter mobiele applicatie
TST Terschelling
Kiewiet Rijwielverhuur BV
Op het autovrije Nederlandse eiland Ameland komt bijna elke toerist met de veerboot en heeft onmiddellijk een fiets nodig. Kiewiet Cyclisme is de meest gevestigde verhuurwinkel van het eiland en achter hun website zit een speciaal gebouwd platform dat elke laag van het bedrijf behandelt: online reserveringen, bagagevervoer vanaf de veerbootdock, detailhandel, agentenpartnerships, betalingsverwerking, factuurgeneratie en een vloot desktopterminals op de werkvloer. Dit is het verhaal van dat systeem.
De meeste reserveringsplatforms lossen één probleem op. Kiewiet had vier tegelijkertijd opgelost en in één samenhangende ervaring gebonden.
Alle vier stromen leiden naar dezelfde koets, dezelfde betalingspoort, hetzelfde bestelnummeringssysteem en dezelfde factuurmachine.
Het platform is gebouwd op PHP 8.4 zonder externe framework geen Laravel, geen Symfony.
Elke publieke URL wordt ingevoerd via een enkele index.php entrypoint. Apache's .htaccess schrijft alle niet-bestandverzoeken naar het. Vanaf daar analyseert een aangepaste router (NSSwitchBoard) tot zes URI-padniveaus, koppelt het eerste segment aan een routingtabel en laadt de juiste paginabeheerder.
Elke pagina controller is een klasse die een gedeelde basis (CyclismeWebPage) uitbreidt en wordt opgeslagen als een singleton binnen de PHP sessie. Dit betekent dat de hele pagina staat de winkelwagen, geselecteerde data, actieve productfilters, agent login status blijft bestaan via verzoeken zonder een enkele database te lezen voor de UI-staat. Een klant kan tien minuten bladeren, fietsen van meerdere categorieën toevoegen, vervolgens doorgaan met de betaling, en de server reconstrueert de exacte staat van hun sessie vanuit het geheugen op elk verzoek.
Database modellen volgen een twee-bestandspatroon. Een _base.php-bestand wordt automatisch gegenereerd uit het database schema en bevat niets anders dan getypeerde eigenschappen en getter/setter boilerplate het wordt nooit handmatig bewerkt. Een begeleiderbestand voegt businesslogic aan de bovenkant toe. Deze scheiding betekent dat schema veranderingen schoon regenereren zonder aanpassingscode te raken.
De basis ORM-klasse (NSPersistentObject) implementeert dirty-state tracking door middel van een dubbele mechanisme: een expliciete dirty flag die de code handmatig instelt, en een achtergrond MD5 checksum van alle serialiseerde eigenschappen die tijdens de laadtijd zijn genomen. Als de vlag is ingesteld of de checksum is veranderd, blijft een store() oproep bestaan. Dit voorkomt toevallige dubbelschrijven en maakt gedeeltelijke updates veilig.
De opslaglogic bepaalt INSERT versus UPDATE in runtime door te controleren of de UUID van het record al bestaat in de database. Er zijn geen afzonderlijke creatie- en updatemethoden het ORM beslist. Linked modelcollecties (bijv. de lijnelementen binnen een reservering) worden gehecht aan ouderobjecten en worden opgeslagen in een enkele gecoördineerde oproep.
Een lichtgewicht query builder (DataStore) wikkelt de ORM met positioneel parameteriseren. Queries lijken op query (("Groep_uuid=:1 EN Verhuur>0", $groupUUID). De bouwer oplost typen automatisch integers passeren door onbevrijde, stringen gaan door de database escape functie, arrays uitbreiden naar SQL IN (...) lijsten, en aangepaste datum / tijdtypen seriealiseren naar MySQL-formaat. Het resultaat is een EntitySelection een lui of enthousiaste iterator over volledig geladen modelobjecten.
EntitySelection ondersteunt ook in-memory set-operaties: kruising, vereniging en verschil van resultaatsets met behulp van ID-lijsten. Dit maakt het mogelijk om meerdere queries te combineren zonder extra rondreizen.
Een fiets die van dinsdag tot vrijdag is gereserveerd is niet beschikbaar voor een overlappende periode maar een fiets die maandagmorgen is teruggebracht kan maandagmiddag beschikbaar zijn indien de omlooptijd het toestaat. Producten::buildAvailabilityMatrix() berekent een per-product, per-dag beschikbaarheidsgraad over het gehele gevraagde datumbereik, rekening houdend met alle bestaande reserveringen. Deze matrix stuurt de real-time kalender-interface op de boekingspagina.
De prijs is afhankelijk van de productgroep, het aantal huurdagen en het tijdstip van het jaar. Een apart prijsmodel (Prijzen) bewaart regels gespecificeerd per groep, duurbereik en datumbereik. De motor oplost de toepasselijke prijs voor elk wagen item bij de kassa. Agentenrekeningen ontvangen een aparte prijsgroep, zodat dezelfde fiets een ander tarief kan dragen, afhankelijk van of het direct of via een partnerbureau wordt geboekt.
De promotiekodes worden in realtime gevalideerd tijdens de boekingstroom, met regels voor verval, minimale bestelwaarde en toepasselijke productcategorieën.
Het bagagevervoer wordt beperkt door het tijdschema van de Wagenborg ferry. Klanten selecteren een ferry overschrijding, en het systeem valideert dat de gevraagde transporttijd compatibel is met die zeil. Het ferry schema wordt opgehaald en opgehaald vanaf een externe API door een speciale fabrieksklasse (FerryScheduleFactory) en lokaal opgeslagen voor snelle zoekopdrachten.
Het platform integreert twee betalingspoorten: OGone (Ingenico) en EMS. Klanten kiezen hun voorkeursmethode bij de kassa. Na de gateway callback bevestigt Reservering::ValidateReservation() de betalingsstatus, genereert het bestelnummer, schrijft de reservering naar de database, genereert een PDF-bevestiging en verzendt een bevestigings-e-mail allemaal in een enkele atoom na-betalingsequentie.
Bestelnummers worden gegenereerd door een fabriek die tellers bijhoudt per besteltype (reserveringen, winkelverkopen, bagagebestellingen, facturen) met automatische resets op jaar- en maandgrenzen.
Het platform genereert PDF-documenten nationeel voor drie soorten documenten:
Een PostOffice-klasse wikkelt PHPMailer om deze te verzenden als e-mail bijlagen naast sjabloon HTML-bevestigingsberichten.
Een aparte REST API-eindpunt (ApiEntrypoint.php) dient een mobiele scannerapplicatie die wordt gebruikt door transportpersoneel op het eiland. Geautenticeerd via een X-Auth-Token-header, laat het eindpunten zien voor het laden van pakketgegevens, het indienen van leveringsscans en het bijwerken van de locatie van het pakket.
Het webplatform werkt niet afzonderlijk. Het loopt over ongeveer twintig macOS werkstations in de winkellocaties van Kiewiet op Ameland is een parallelle desktop-applicatie gebouwd in 4D v20R8 een volledig relationeel applicatieplatform met zijn eigen gebruikersinterface, gegevenslaag en zakelijke logica.
De desktop-app beheert de volledige operationele kant van het bedrijf: het personeel checkt de klanten in en geeft fietsen over met behulp van huurvouchers (VerhuurBonnen), doorlopende reparaties verrichten, de inventaris van werkplaatsen beheren, de kassa van het verkooppunt beheren en bagagevervoersroutes plannen.
Beide systemen delen een MySQL-database, maar ze delen niet alleen een verbinding. Veranderingen stromen door een speciale synchronisatielaag die is gebouwd bovenop een RecordSync-tabel die fungeert als een change journal.
Wanneer de PHP webshop een record creëert of wijzigt een nieuwe reservering, een betalingsbevestiging, een nieuwe klant schrijft ze een vermelding in RecordSync met een volgordenummer en de applicatie-identificatie "Online". Een achtergrondproces in de 4D aanvraagopnames voor deze wijzigingen via bridge.PHP, haalt de gewijzigde records en plaatst ze in de 4D datastore met behulp van ORDA (Object Relational Data Access). Het sequentienummer gaat pas vooruit na een succesvolle verwerking, waardoor geen enkele wijziging wordt gemist.
Het omgekeerde pad werkt via het trigger systeem van 4D. Alle 62 databasetabellen hebben triggers die op elke save worden afgevuurd en naar een centrale DataBridge-orkestrator worden gedelegeerd.
De triggerarchitectuur maakt gebruik van SharedStorage voor interprocescommunicatie binnen de 4D runtime, en een deduplicatie-mechanisme voorkomt dat dezelfde recordwijziging meerdere keren in de rij wordt geplaatst tijdens snelle opeenvolgende opslagen.
In de desktop-app wordt een routeoptimalisatie-motor gebruikt. Een klas LuggageRouting geeft pakketten toe aan transportroutes, en een OpenRouteService-integratie vraagt de ORS API om wegafstanden te berekenen.tussenstops).
Het ontwerpsysteem is gebouwd op 32 SCSS-gedeeltelijke delen die zijn georganiseerd van design tokens via componentenlagen tot pagina-specifieke layouts.
Interactief gedrag wordt behandeld door 26 JavaScript-modules die de boekingskalender, de mandjebak, productfiltering, bagage tijdselectie, agentenwerkstromen en betalingsstromen bestrijken. De kalender integreert FullCalendar.js met aangepaste beschikbaarheid rendering gedreven door gegevens uit de PHP beschikbaarheidmatrix. Moment.js behandelt alle data-arithmetische gegevens aan de klantzijde, in sync gehouden met de serverzijde datumvalidatie.
AJAX-communicatie tussen de frontend en de backend maakt gebruik van een gestructureerd responsformaat: elke reactie draagt een status, een optionele redirect URL, een array van HTML-fragmenten met CSS-selectordoelen voor jQuery-gedreven DOM-injectie en een field-data-array voor formulierpopulatie. Dit maakt de cliëntcode uniform.
Een aparte admin backend (office.php) biedt een volledige beheerinterface voor het bedrijf: bladeren en bewerken van alle gegevensmodellen, het beheren van prijzen en inventaris, het bekijken van reserveringen en betalingen, en het genereren van bedrijfsverslagen. Het kantoor gebruikt 65+ JSON schema bestanden één per tabel die veld weergave namen, gegevenstypen, sorteren gedrag en lijst zichtbaarheid definiëren. Deze rijden een dynamische lijst- en formulier renderingsmachine, zodat het toevoegen van een nieuw veld aan de database automatisch wordt verspreid naar de admin-interface zonder veranderingen in de sjabloon.
Kiewiet is geen website met een boekingswidget. Het is een compleet operationeel platform dat is gebouwd om te specificeren voor een echt, seizoensintensief bedrijf waar de foutmarge laag is een dubbel geboekte fiets of een gemiste bagage aflevering beïnvloedt de vakantie van een gezin.
De technische beslissingen een aangepaste ORM met vuile staat checksums, een sessie-persistent pagina object grafiek, een tweerichtingswisselingstijdschrift tussen twee heterogene runtimes werden niet gemaakt voor elegantie. ze werden gemaakt omdat het bedrijf exact dit gedrag vereiste en geen off-the-shelf oplossing bood het.
Meer werk
TST Terschelling
TransportMaster
TransportMaster