AlbertaOS
.txt
README
.pdf
CV.pdf
CRM-FAMA.app

CRM FAMA in production · v96

A CRM for a university guidance centre. Before it, the details of one person lived in different systems that did not talk to each other, so nobody had the full picture of a case. The CRM keeps them in one place, from the form filled in on the website to the enrolment confirmed by the institution, and it holds several education providers together, each with its own catalogue and its own rules.

reworking · click a card to move it along the pipeline
Leads
In discussion
Enrolled

Where a request comes in from

From seven directions: the contact form on the site, the instalment calculator, the guidance tests, manual entry, a Telegram bot, a social conversation turned into a request, and the automations. All seven go through the same duplicate check, because duplicates are always created at the door, and fixing them later costs ten times as much.

The check works on two levels. Tax code, email and phone say it is the same person. A matching name only warns and never blocks: people do share names, and the same person asking again months later about a different course is not a mistake to be corrected.

Recognising that two things are the same thing

The hardest part of this project is not the CRM, it is working out when two records are the same thing. The same person arrives from the website with one email address and from the institution's portal with another. The same course changes its code every academic year. The same case has different identifiers depending on where you look at it. A good share of the releases of these months is exactly that work: one real case at a time, deciding when two things should be merged and when it is right that they stay apart.

The browser extension

Each institution that runs the courses has its own portal, and none of them offers an automatic way to find out how a case ended: the only way is to open the record and look. I built a Chrome extension that collects that information while the record is on screen in the portal, so the data reaches the CRM without anyone keying it in.

The automatic sync is deliberately limited: it only updates what already exists and never creates anything. Opening a record out of curiosity must not produce new ones.

When a case stalls

The system notices on its own and warns whoever is handling it, then widens the warning to whoever is accountable. The thresholds are configurable from the admin panel, except the one on the last step: the person working a case must not be able to extend, on their own, the time before somebody else finds out. The clock does not restart from when the case was created, but from the last sign of life recorded in the system.

How I work on this project

Every change starts from a real case reported by the client. Before writing it I measure it against real data, and I release it only if it does not make what already works any worse.

The first example is about measuring. Every year the institutions republish the same courses under a new code, so the system has to work out on its own that the course just loaded is the new edition of one already there. I wrote the function that pairs them, comparing name, institution and course type, and before wiring it to anything I ran it against the real catalogue: of seven proposed pairs, four were wrong. The system was pairing courses whose names looked alike, which turned out to be far too fragile a rule. So I added three more checks, and on the next run every proposed pair was right.

The second is about not making things worse. Another report had led me to a rule that was wrong, and fixing it took one line. That line, though, would have created a bigger problem than the one it solved: the system would have lost the link to cases already archived and recorded them a second time. I corrected the rule only after adding the guard that keeps that link.

What is underneath

Five roles and twenty-two permissions that switch on one by one, with a per-user override that always beats the role. An automation engine configurable from the panel, twenty-three events, with a dry run that shows recipients and text without sending anything. Automations that must fire exactly once are protected by an atomic lock, not by a check that can arrive too late. Mail and documents go through Microsoft Graph, chat through Messenger and Instagram Direct. Commission is calculated according to the rules the client configures, with a guard that prevents paying twice.

In production, with more than 90 documented releases since April 2026. It was not delivered and closed: it is under continuous maintenance and development.

90+documented releases
564automated tests
51domain models
php 8.3laravel 11alpine.jsmysql 8microsoft graphmeta apichrome extension