Situația
Un grup medical cu cincisprezece clinici funcționa pe opt lucruri care nu știau unele de altele: un site, un widget separat de programări, un fișier Excel pentru stocuri, un al doilea fișier pentru laborator, un program de contabilitate, o aplicație de pontaj, un dashboard de publicitate și un inbox comun. Fiecare cifră care conta exista în cel puțin două locuri, iar acolo unde nu se potriveau nimeni nu putea spune care e cea corectă.
Asta nu e o problemă de tehnologie. E o problemă de business care apare ca o problemă de tehnologie, și e exact genul de muncă la care mă pricep cel mai bine.
Ce am construit
O singură platformă Django, care servește toate cele cincisprezece locații: site-ul public, programările, fișele pacienților, CRM-ul, logistica de laborator, stocurile din mai multe depozite, contabilitatea fiscală, HR-ul și raportarea cheltuielilor de publicitate. Totul în spatele unui singur admin, cu izolare la nivel de clinică și de regiune pentru nouă roluri de personal, așa încât o recepționeră dintr-un oraș nu poate vedea pacienții din alt oraș și nici nu trebuie să se gândească de ce.
Funcționalitățile sunt partea banală. Trei dintre sistemele de dedesubt merită descrise, pentru că ele fac diferența între software care merge în ziua lansării și software în care mai ai încredere doi ani mai târziu.
Un flux de consimțământ care ar rezista în instanță
Consimțământul medical sub GDPR nu e un checkbox cu un timestamp. Ce trebuie să poți produce, mai târziu, e exact documentul cu care o anumită persoană a fost de acord într-un anumit moment - nu versiunea ta curentă.
Așa că fluxul generează documentul, îl arată pe ecran, confirmă identitatea cu un cod trimis prin SMS și stochează PDF-ul randat împreună cu textul, limba în care a fost afișat, adresa de la care a fost semnat și un lanț de hash-uri care îl leagă de fișă. Dacă modifici șablonul după aceea, se schimbă ce vede următorul pacient și nimic din ce au acceptat cei dinainte.
Regula de la bază: consimțământul se dă unei fraze, nu unui boolean.
Dacă sistemul tău stochează true, a stocat lucrul greșit.
Stocul ca registru, nu ca număr
Majoritatea sistemelor de stocuri țin o coloană quantity în care
scrie codul. Coloana aia ajunge greșită la un moment dat - un race, un retry, un
click greșit, un transfer aplicat pe jumătate - și odată ce e greșită nu mai ai
cum să afli de ce, pentru că dovada a fost suprascrisă de răspuns.
Aici cantitatea nu se stochează. Fiecare recepție, transfer, consum și corecție e o înregistrare imutabilă cu semn, iar soldul oricărui depozit e suma înregistrărilor care îl ating. Corecțiile adaugă inversul în loc să editeze istoricul. Asta face din reconciliere o proprietate a design-ului, nu o sarcină lunară, și înseamnă că răspunsul la „de ce avem unsprezece bucăți din astea?” e mereu disponibil și mereu complet.
Mai jos pe pagină găsești o versiune funcțională a ideii.
Un calendar care nu poate suprapune două programări
Programările din cincisprezece clinici se suprapun constant, iar recepția trebuie să vadă o zi întreagă dintr-o privire, fără ca două programări să ocupe vreodată același loc. Asta e problema de interval scheduling: sortezi după ora de început, apoi pui fiecare programare pe prima coloană a cărei programare anterioară s-a terminat deja.
Ce o face să merite implementată corect și nu aproximativ e că varianta greedy e demonstrabil optimă - numărul de coloane pe care le produce e exact numărul cel mai mare de programări care se suprapun într-un singur moment, adică o limită inferioară strictă pe care niciun algoritm n-o poate bate. Poți vedea cum se confirmă asta în demo-ul de mai jos.
Realitatea românească
Facturare, jurnalul de casă și drivere pentru casele de marcat fiscale cerute de lege. Lucrări protetice urmărite prin curier între clinică și laborator. Pontaj cu un mic client desktop care trimite un heartbeat. Nimic din toate astea nu e inginerie interesantă și toate la un loc fac diferența între un sistem pe care clinicile îl folosesc și unul pe care îl ocolesc.
Decizia pe care aș apăra-o cel mai tare
Fiecare dependență externă din platformă face fail open. Dacă furnizorul de SMS e picat, dacă API-ul de publicitate limitează cererile sau dacă serviciul de facturare dă timeout, programarea se finalizează oricum, iar munca rămasă intră în coadă și e marcată pentru un om.
Alternativa - să refuzi o programare pentru că un terț nu e disponibil - înseamnă să-ți externalizezi veniturile către uptime-ul altcuiva. Protecțiile interne merg invers: rate limit-urile, validarea și honeypot-ul fac fail closed, pentru că acolo nu ai pe cine să dai vina.
Ce aș face altfel
Aș construi stratul de raportare mai devreme. Am proiectat registrul și programarea corect de la început și am lăsat raportarea să crească de la sine, ceea ce a însemnat că primele trei luni de întrebări de la management au primit răspuns prin interogări scrise de mână de mine. Corect, dar lent, și ar fi trebuit să prevăd asta.