Fiscal Management System: PFE Capstone
My bachelor’s capstone in Management Information Systems: a Laravel, Inertia and React platform that takes tax files from receipt to a signed committee decision, with role-based access, a full audit trail, committee scheduling, minutes generated as signed PDFs and reclamations to tax centres. Built entirely by hand in 2024, before AI coding agents.
View on LinkedIn · Source code
- Started
- February 2024
- Timeline
- 4 months
- Complexity
- Medium to Advanced
- Type
- Web App
Screenshots show the system running locally on fictional demo data: taxpayers, amounts, documents and committee members are invented. The clock was set back to June 2024, the project’s own timeframe.
This fiscal management system was my capstone project (PFE, projet de fin d’études) for the Licence en Informatique de Gestion, the Tunisian bachelor’s degree in Management Information Systems. It digitizes the life of a tax file inside a fiscal administration: the file arrives from a tax centre with its reports, is registered, scheduled for a review committee, examined in session and decided, and, when something is missing or doubtful, sent back to the centre for clarification.
I designed and built it alone, from the database schema to the last React component, between February and June 2024.
At a glance
- A fiscal file register with drag-and-drop reports, PDFs and images readable in place, archiving and restore.
- A complete audit trail: every edit, archive and restore keeps a snapshot of the previous version and its documents.
- Committee scheduling on a calendar, with files assigned to each session.
- A three-step session wizard (attendance, minutes, decisions) that produces the official minutes, the procès-verbal, as a PDF bearing each present member’s signature.
- Reclamations: requests for missing or corrected data, e-mailed to a tax centre and answered through a token-protected form.
- Role-based access with five roles and eight permissions, enforced on the server and reflected in the interface.
- Dashboards, a light and a dark theme, and a French interface throughout.
Built by hand, before AI coding
I wrote this system in the first half of 2024, before autonomous coding agents, Claude Code or “vibe coding” were part of how software got made. Every feature was designed, written and debugged by hand.
The work looked the way software was built then:
- Official documentation answered the architecture questions: Laravel, Inertia.js, React, Material UI and Spatie Laravel Permission, read until the patterns made sense.
- YouTube tutorials taught me what I had never built before, such as multi-step forms, server-driven React pages and PDF generation.
- Stack Overflow solved the errors no documentation mentioned.
- Free, reusable components were found and adapted rather than generated: a Flowbite sidebar, Material React Table, the React Suite calendar, react-dropzone, a lightbox and a carousel, each reshaped to fit the domain.
- Manual testing, for long hours: with no agent running checks for me, every workflow was exercised by hand again and again. Register a file, schedule a committee, run the session, open the PDF, find what broke, start over.
The commit history keeps the traces of that process: 38 commits between February and June 2024, from “step 1 completed” and “added services clean architecture” in February to “Multi Step Form”, “pdf pv”, “roles and permissions” and “reclamation form” in the spring.
The workflow it standardizes
A tax file passes through many hands, and each step needs a record of who did what, when, and on what evidence. The system turns that path into five stages, each owned by a role:
- Registration: a dossier manager registers the file (taxpayer, CIN or tax ID, tax centre, amount, subject, issuing body and key dates) and uploads the centre’s reports.
- Scheduling: the secretary schedules a committee session, chooses its members and assigns pending files to it.
- Session: committee members record attendance, write the minutes and decide each file: accepted, postponed or rejected. Postponed files go back to the pool for a later session.
- Clarification: when data is missing, wrong or doubtful, the dossier manager sends a reclamation to the tax centre, which answers online with corrected values and documents.
- Archiving: decided files are archived, and every change along the way stays in the journal.
Features
Dashboard
The home screen sums up the activity: how many files and committees there are, how they split by status, and how many committees met on each day of the past week, drawn with Chart.js with percentages in the tooltips.
Fiscal file register
- A data table (Material React Table) with sorting, global search, per-column filters, column visibility, density, full-screen mode, pagination and colour-coded statuses. Each file’s reports appear as icons that open the document.
- Active and archived tabs, with bulk archive and restore of the selected rows behind a confirmation dialog.
- A registration form validated on the server (required fields, no dates in the future) with drag-and-drop upload of several reports at once: PDF, Word or images.
- Private storage: reports are kept outside the public folder, filed by taxpayer, and served only through an authenticated route.
The detail page shows a file’s data and reads its reports in place: PDFs are embedded and images open in a zoomable lightbox. Editing lets the dossier manager correct the data and add or detach reports.
Audit journal and version history
Every edit, archive and restore writes a journal entry before the change is applied. Each entry is a full snapshot of the file as it was, linked to the reports attached at that moment, so an earlier version can be reopened and compared. Archive and restore run inside database transactions, so a file and its journal never disagree.
Committee agenda
Sessions appear on a monthly calendar (React Suite), coloured by status: not confirmed, confirmed or completed. A completed session links straight to its minutes as a PDF; an upcoming one opens the session itself. Members only see the sessions they sit on.
The secretary schedules a session with its title, date, time and members, and is taken straight to the pending files to build its agenda. Assigning files confirms the session and marks them as selected; files that are neither pending nor postponed cannot be assigned.
Running a session: the minutes wizard
On the day, a committee member opens the session and is guided through three steps (an MUI Stepper), each validated before moving on. Only members of the session can open it, only once it is confirmed, and a completed session cannot be run twice.
- Attendance: tick who is present; absences are listed automatically.
- Minutes: a live preview of the procès-verbal, the opening time captured automatically, the president chosen among those present, opening and closing statements, and as many free sections as the meeting needs.
- Decisions: one decision and one note per file, with the whole file one click away.
Finishing the session is a single database transaction: attendance is stored, a decision record is written for each file, file statuses change, the minutes and their sections are saved, the PDF is generated and the session is closed. If any step fails, nothing is kept.
The PDF is rendered on the server by DomPDF from a Blade template and embeds the signature each user uploads with their account. All minutes are archived in a list that can be searched by number and filtered by day, month and year.
Reclamations to tax centres
When a file’s data looks wrong or incomplete, the dossier manager builds a reclamation from the file itself: tick the fields to contest, give each a reason (missing, wrong or doubtful), list the documents to request and choose the tax centre’s contact. The system e-mails the centre a link that carries a random 60-character token.
The link opens a form generated from that specific request: one input per contested value and one drag-and-drop zone per requested document. Its validation rules are built at runtime from the request, so a centre cannot skip a field or a document.
Responses land in the Notifications space, filterable by file number and date, where each one shows the corrected values next to the documents received.
Administration
- Accounts are created by an administrator, never by self sign-up: name, CIN, e-mail, password, role, an optional photo and a mandatory signature image, the one printed on the minutes.
- Roles and permissions (Spatie Laravel Permission): super admin, admin, dossier manager, secretary and member, over eight permissions such as create a file, create comite, show pv and infra. Every protected route checks a permission, and the React pages receive the user’s roles and permissions, so menus, buttons and row actions appear only to those allowed to use them.
- Contacts: the directory of tax centre correspondents used by reclamations, with search, editing and deletion behind a confirmation.
- Profile: each user updates their details, password, photo and signature.
Interface
The interface is entirely in French, with a light and a dark theme that switch instantly and persist between visits: a Material UI theme kept in step with Tailwind’s dark mode. Flash messages confirm each action, loading states use skeletons, and a public landing page introduces the platform.
Architecture
The application is a Laravel monolith that serves React pages through Inertia.js. Routes, controllers, validation and authorization stay on the server; every screen is a React component that receives its data as props. There is no separate API to maintain, and navigation still feels like a single-page app.
- A service layer: the business logic for files, the journal, storage and minutes lives in services behind interfaces bound in Laravel’s service container, so controllers depend on contracts, not implementations.
- Sixteen Eloquent models: files, reports and journal snapshots; committees, their members, minutes, sections and per-file decisions; reclamations, their contested fields, requested documents and responses; contacts and users.
- Transactions around every multi-step write: archive, restore and the end of a session.
- Middleware for permissions, for pages that must never be cached (login and password screens) and for profile ownership.
- Form Requests, including rules generated at runtime for reclamation responses.
- Private file storage served through an authenticated controller, and Blade templates for the minutes PDF and the reclamation e-mail.
- Migrations that record the schema as it grew, from the first fiscal-files table on 13 February 2024 to the reclamation responses in June.
Tech stack
- Backend: PHP, Laravel (Eloquent, Form Requests, Mail, Blade, filesystem disks), Inertia.js, Laravel Breeze for authentication, Spatie Laravel Permission, Laravel DomPDF, Ziggy.
- Database: MySQL.
- Frontend: React, Inertia.js, Vite, Tailwind CSS (forms, typography and aspect-ratio plugins), Material UI and MUI Icons, Material React Table, Chart.js with react-chartjs-2, React Suite (calendar), Headless UI, Heroicons, React Icons, Font Awesome, react-dropzone, Yet Another React Lightbox, Swiper, React-Toastify, Day.js, Axios.
- Tooling: Composer, npm, Git and GitHub.
Outcome
The system covers the whole life of a tax file, from its arrival to a signed, archived decision, with every action traceable to a person and a moment. It was completed in June 2024 as my bachelor’s capstone. It still installs, migrates and runs from its repository today: every screenshot on this page comes from a fresh local install, filled with demo data and driven through its real workflows.