Fishery law in Austria is country law. The Tyrolean Fisheries Association (TFA) executes the law for Tyrol. Anyone who likes to fish in Tyrolean rivers and lakes needs to get a license from the TFA. As of January 1st a new fishery law enters into force with a certain degree of digitization. Every fisher needs to (re-)register at the district office to get a license card for 2021 and so becomes member of the TFA. The data is entered to the governmental hunting and fishing database (JAFAT).
The TFA was in need to get an own administration and member portal system with very custom features, such as getting new licensed fishers and the current list of fishing districts from the JAFAT, get the district managers, enable them to buy two-week licenses for guests. This includes registration for the members and managers to become users of the portal. Members can renew their membership/license using the portal each year by paying a fee. All licenses needs validation outdoors in the field. A supervisory fisherman scans a QR code on the license card with a smartphone or tablet and gets online feedback if the license is valid for the district. Overall all has to be simple, for simple members, for district manager and for administration.
We implemented the so called iFisch system using Plone 6 Classic - which is not released, not even alpha, but very stable already. I used Bootstrap 5 CSS-classes (also beta) a lot - I saved writing custom CSS while contributing to Plone core instead with this project (not only with CSS). Since I am core developer since Plone 1.0 I can handle this, will keep it in sync with current coredev: Do not do this at home!
We used the Dexterity type system behaviors to model the member, yearly payment, districts and guest licenses. For self management of members basic data such as address, phone and email contact we used field permissions and collective.fieldedit
. Further type level permission were implemented introducing new roles and a bunch of one or two state workflows. The portal self registration with double opt-in was implemented from scratch. Listings of members, districts and guest licenses for managers are collections enhanced with collective.collectionfilter
for easy filtering with custom tables as output. Plone toolbar is disabled for all all roles and buttons are in place with actions where needed. All payment related is delegated to Stripe using it's invoice API. Plone stores only customer ids and invoice ids. Stripe callbacks are mapped to Zope events and handled where needed. For QR Code scanning we implemented a light weight main template just with all needed features on the fingertips.
The synchronization script is standalone, outside of Plone and runs hourly by Cron. It utilizes the requests library to connect to JAFAT and fetches data of new members and all districts. Again requests is used to fetch and send data to plone.restapi to create new members and create or update districts. plone.restapi is closed down to one role only, so only the special sync user is able to use it.
We host the whole at the Tyrolean provider CIBEX. Currently it runs in a tiny Docker-Swarm using PostgreSQL (RelStorage 3), Traefik (Webserver, Loadbalancer, Letsencrypt), Sync/Cron and Plone. Overall we have 16GB RAM and 6 CPUs, which may need to scale up (simple with this setup) - we will see.
While in summer we meet in person, all meetings since October went virtual (Jitsi). We used Gitlab (custom hosted) with its issue tracker for communication and also Gitlab is used as build pipeline for testing, container security screening and live containers. Overall this worked very good, those years of (Plone) community communication skills helped. Also our customer got really fast into it and even edited in Gitlab small text changes directly in the templates and provided merge requests for those.
Our customer TFA and also we am very happy with this project. It was delivered in time, feature complete, secure, almost in budget and all despite to COVID19.