42 applications, each for one need, all attached to the same contacts and the same projects. Click a tile to see what it does. The pill shows the licence: LGPL-3 is free with no conditions, BUSL-1.1 is source-available and free to run for your own operations.
The applications below add to the 35 that ship with Odoo 18 Community, they do not replace them: CRM, Sales, Invoicing, Inventory, Purchase, Projects, Employees, Point of Sale, Website and the rest stay right there, on the same contacts and the same projects.
A dated period on the contact record, with the stand-in: who to write to in the meantime.
The banner shows where the work happens, on the chatter of a task, an opportunity or a record, not only when you hit send. One click schedules the message for the morning after they are back, and a reminder returns that day carrying the subject of the last exchange, so there is something to say beyond “just checking in”.
A shutdown recorded on a company warns for every one of its people, and the collective holidays of a trade, published a year ahead, are seeded in one pass.
The module also reads what already exists: the out-of-office replies arriving in your mail, and the calendar where you noted those holidays by hand before there was a module. It takes the period out of them, proposes it, and never writes anything on its own.
⚠️ Nothing is ever blocked: writing to someone on holiday is often exactly what you want, so that it is waiting for them.
Comparable to The out-of-office detection of Salesloft or Outreach, taken out of the outbound cadence


A meeting gets prepared, gets held, then gets lost. The agenda sleeps in an email, the decisions in somebody's notes, and six months later nobody knows who settled what.
An agenda prepares the meeting to come: its objectives, its topics with their duration and their speaker, the action items to discuss. A report tells the meeting held: the topics covered, the decisions with the person who made them, attendance, and the verbatim. Both attach to the same calendar event and the same project, with no re-entry.
⚠️ Follow-up works by exception: a meeting is presumed to deserve an agenda and a report, and the list shows step by step where each one stands. That is the opposite of the usual reflex, which shows only what was done and leaves the rest invisible.
Open tasks on the project can invite themselves to the next meeting. Decisions are filed into the knowledge matrices, where people will look for them. The report comes out as a PDF in your image, ready to send.
Audio capture and transcription go through a processor you host, not through a third-party service.
Comparable to Otter.ai + Fellow.app, integrated

An email that matters ends up pasted into a task, or nowhere at all. Between the inbox and the file there is a copy-paste nobody enjoys and everybody forgets.
The mailbox brings two sources into one list. Your IMAP account, polled every five minutes, and Symbifox itself: every email sent from a message thread or received by the organization's gateway is projected there too. The two meet on the message's unique identifier, so one exchange never appears twice.
Filing to a record attaches an email to the thread of whatever it is about: the task, the ticket, the contact, the opportunity, the invoice. Two clicks, and the exchange lives where someone will look for it in six months.
⚠️ Accounts are per person, never under the admin account. A mailbox polled by a shared account makes everyone's mail readable by everyone, which is neither intended nor fixable afterwards.
Works with Migadu, Microsoft 365, Gmail and any standard IMAP server.
Comparable to Outlook or HubSpot Inbox, inside Odoo



The links already exist in your database: booking, secure drop, meeting room, email, phone. The page resolves them at display time rather than copying them.
That is the whole difference. When someone's booking link changes, their page follows, and the QR already printed in their signature keeps pointing to the right place, since it encodes the page's address and not the link's. A hosted link-page service cannot do that.
Three kinds of page. One per employee, created and refreshed without intervention. One per client organization: the entry point you send when a engagement opens, with who to talk to, where to drop a document and where to find their things on the portal; its address is drawn at random rather than derived from the client's name, and it carries an expiry announced to the advisor before it falls. And a one-off page, with no owner, armed with an expiry from the moment it is created.
⚠️ A portal link only shows if someone at the organization actually has an account: a button that opens nothing but a login screen is the worst link in a welcome email.
Five looks, light or dark theme according to the device, FR/EN switch, and a branded QR whose legibility is measured rather than assumed.
Comparable to Linktree or Carrd, wired to your real records

Texting stays the fastest channel and the least documented. The exchange that settles an appointment lives on a mobile, and the file keeps none of it.
A line is a number attached to a person in the organization. A conversation groups everything exchanged with one number, messages on one side, calls on the other. An attachment places the trace of a message or a call in a record's thread, a task or a ticket for instance.
SMS and MMS go out and come in live through VOIP.ms, from a conversation space built into Symbifox, with an unread counter in the top bar. Your Android backups and your call history import, which brings what already exists in rather than starting over.
⚠️ Everything that enters here belongs to someone. A line has an owner, and the conversations held on it are their data. A thread marked confidential leaves the lists and the searches and stays with the person who holds it.

Writing the same note on thirty records happens one record at a time, or not at all. It is usually “not at all” that wins, and the trace of the decision is missing where people will look for it.
Select several records in any list view, then post the same note on each one's message thread, in a single operation. The action appears on its own on every model with a thread, with no configuration: a module installed tomorrow will have it too.
⚠️ It is an internal note by default. Sending it as a message to followers exists, but behind a confirmation: thirty notes disturb nobody, thirty emails sent in one gesture are hard to take back.
Comparable to Bulk actions in Notion or Asana, inside the Odoo chatter
An official letter still comes out of a word processor too often, with the address retyped by hand from Odoo and the letterhead caught up from one version to the next.
Here the letter brings together the sender, a recipient drawn from your contacts, the date, the salutation, the body and the closing. It takes a reference on finalisation, and at that moment the recipient's name and address freeze as they stood that day: an address corrected next year does not rewrite the letter that went out.
Three objects prepared in advance do most of the work. A template carries a standard subject and body with merge fields replaced by the recipient's values. A text block is a reusable paragraph, filed by category and recalled by a shortcut. The letterhead decides the dressing of the page, from a dark banner to paper already printed.
⚠️ The dressing is locked. The author picks the layout, never the colours or the logo, and each company on the instance issues its letters in its own image. That is the only way correspondence stays recognisable when ten people write it.
Comparable to Word + mail merge, plugged into your Odoo

Offering a time by email costs three round trips and often lands on a slot already taken elsewhere.
An appointment type carries everything that repeats: the duration, the place, the calendar consulted, the emails sent, the form put to the requester. A resource is whatever has to be free for the meeting to happen, a person, a room, a vehicle; a combination groups what has to be free at the same time.
A slot offered to a visitor is therefore a computation, never a list written in advance: the availability calendar opens the range, the combination confirms it, and the diary withdraws it the moment it is taken. Availability comes from your real calendar, not from a copy someone has to keep current.
The page is published on your domain, in your colours. Every confirmed booking places the event on the calendar, sends its invitation, creates the contact record with its language, and can open a project task. The intake form asks its questions before the meeting rather than during it.
The consents collected along the way are the Law 25 ones, kept with the booking: taking an appointment gathers personal information, and the tool treats it as such.
Comparable to Calendly or Cal.com (your data, your domain)
Setting a meeting for six people costs a dozen emails, and the slot chosen often turns out to be no longer free in the organizer's diary.
The organizer proposes slots already free in their calendar. Each invitee answers slot by slot, yes, if need be, no, from a page that reads on a phone and in their own time zone.
A slot stops being viable as soon as a required person answers no: that is what separates a useful poll from a grid of boxes to count by hand.
⚠️ Proposed slots do not block the calendar. Public bookings keep coming through while the poll runs, because holding six slots to keep one would make the calendar unusable every other week.
On closing, the meeting goes out the usual way: calendar event, .ics invitation, video room, reminders.
An outreach campaign rarely fails for lack of effort. It fails because a target slips between two follow-ups and nobody notices.
A campaign brings together a list of targets, a cadence and the tracking of each file through to its conclusion. A target is a file: an organization or a person, their contact, their details and their history. An interaction is a logged contact, call, email, letter, text or meeting, with its direction, its outcome and a summary.
⚠️ The cadence belongs to the campaign, not to each person's discipline. It sets, per channel, how many contacts to make and at what interval; the module derives each file's next contact date and shows at all times who is overdue, who has never been reached, and who has replied.
A request not to be contacted propagates to every campaign, present and future. An opt-out that only held for the current campaign is not an opt-out.
Replies received, calls actually made and appointments booked record themselves.
Comparable to Lemlist or Salesloft, inside your Odoo

Odoo's blog knows whether a post is published or not, and nothing else: not its pillar, not the cadence of the stream, not what blocks what, not whether the translation has kept up.
An editorial entry carries its angle, its promise, its target keyword, its verified sources and the list of what is left to do. A calendar is a stream with its rules: target cadence, word floor, required languages, pillars with their intended share.
⚠️ The design principle fits in one sentence: store only the decisions, compute everything else. The date of the last publication, the ratio per pillar, the word count, language completeness are never typed in, so nothing drifts.
A button proposes the next piece to ship and explains every point of its ranking, rather than handing down a verdict. A pre-flight guard refuses publication until the list is green, and it holds for a scheduled publication too: that is precisely the one nobody thinks to check.
The style and accessibility checks are plain rules, with no AI model: they always run, including when the network is down.
Comparable to A Notion or Airtable editorial calendar, wired to your blog

Paper travels badly. The supplier receipt stays in a coat and then in a pile, the business card picked up at a trade show gets retyped three weeks later, and the signed document waits until you are back at the office to join its file.
This page opens the camera and files instead. The card becomes a contact, with duplicates flagged before anything is written. The bill becomes a draft supplier invoice, never a posted entry, with its photo in the thread. The document joins the notepad with a reminder that becomes an activity, or goes straight to the thread of a record you searched for on the phone.
⚠️ A scanned bill stays a draft. Automatic reading that posted itself would turn a misreading into a decided entry.
It installs on the home screen like an app, on Android as on iPhone, with no store and no download, and it also carries its tile in the app grid for whoever works at a desk.
Every tile is guarded by the right of the gesture it performs: it only appears to someone who can already create a contact, an invoice or a note, and attaching a file to a record requires the right to edit it, not merely to read it.
Comparable to A scanning app, without a seventh package to install and maintain
Two questions nobody knows where to write down, which end up in a diagram file that is out of date the day it is finished.
The first is the reporting line: a “manager” field on the record, distinct from the company the person belongs to, because working for someone and reporting to someone are two different facts. A subsidiary head who answers to the group director is a real structure: entry flags it, it does not refuse it, and the drawing shows the link with the employer's name under theirs.
The second is ownership: who holds which company, for what share, in which share class, voting or not, since when and where the information came from.
⚠️ The percentage lives on the link, not in the box, which an ordinary tree cannot carry: a company has several holders, each for a share. One hundred percent is never required, because a half-known structure is the normal case for a prospect. What is refused is the closed circle, almost always a holder and a holding swapped round.
Both drawings come out in the same place: a screen you expand and collapse, and a vector PDF in your organization's colours. A structure too deep to fit the page says so, instead of truncating in silence.
Comparable to A diagram tool, without the file that ages the day it is finished


A chat channel scrolls; a noticeboard holds. They are not the same thing, and that is why an important announcement gets lost in the first one.
Every post is addressed to all staff, to departments or to groups. ⚠️ Anyone outside it sees it nowhere, not through an export and not through a direct link: the audience is an access rule, not a display filter.
Required reading writes a named, dated line, goes out by email with the link to confirm, and the editors see who is missing. A policy “sent to everyone” proves nothing; a policy confirmed by twenty-eight people out of thirty-two says exactly where the file stands.
A report goes to the designated person, and to them alone: they receive an activity and an email that says nothing of the content, and the file keeps a copy of what was reported.
Five bridges fill the feed on their own: a delivered celebration card, a badge awarded by a colleague, the results of a pulse wave, an upcoming event, and the tile of what you still have to read.
Comparable to Microsoft Viva Engage, without the open feed or the communities



The card the whole office signs is usually made out of a spreadsheet of birthdays and an email chain. Both create the same problem: the person being celebrated ends up finding out.
A link and a QR code are enough: everyone adds their word, their photo or their GIF, and the card goes out on the chosen date. What sets the module apart lies in two rules rather than two screens.
⚠️ The first: the date comes from the person, never from the HR file. They are asked for the day and the month, without the year, and they answer among four choices including “no thanks”, which takes them out of everything. Their refusal stays invisible to colleagues and to HR alike, or it would give them away.
The second: the surprise sits in the access rule, so a list, an export or a report cannot get around it.
The calendar also covers work anniversaries and their milestones, welcomes, departures, retirements, congratulations and condolences. The organiser gets a reminder ten days ahead, with the button that creates the card, because a calendar nobody looks at makes nothing. A card left empty is never delivered.
People also write by hand, with a finger or a stylus. On delivery, the card goes out with its keepsakes: a PDF and a page to keep outside the system, and to a personal address if the person gave one, because a card in the database disappears one day with the account.
Comparable to Kudoboard, without the subscription or the spreadsheet of birthdays

Odoo knows who works for you. It does not know which collective agreement covers that person, how much seniority they hold in their bargaining unit, or which clock is running on the grievance filed last week.
This module adds that half. The bargaining unit, carried by a company: a group where each site is a separate company has as many agreements as it has sites, each with its own expiry. The agreement and its clauses filed by subject, found without being hunted for. Seniority, carried by unit membership rather than by the employment contract: they are not the same, one grants a benefit and the other decides a layoff.
The posted seniority list is a dated photograph, not a view that recomputes. A correction made the following month does not rewrite the posting, and that is the whole point: a rank is challenged against what was posted, not against today's state.
The grievance lives in one place for both sides, and every step carries the agreement's delay and the deadline that follows from it. A file is rarely lost on the merits; it is lost on the calendar.
Dues are described by a rule holding the percentage and the flat amount together, declared by period, and reconciled against what the union received. ⚠️ Dues follow the unit, not the card: section 47 of the Quebec Labour Code requires the deduction from every employee in the unit, member or not. So coverage and membership stay apart, one deciding the deduction and the other the vote.
A workplace with no union sees nothing change: the absence of certification is a readable state, not a hole.


What the agreement requires of the employer, and when. An obligation carries its deadline, its recurrence and its reminder: the person answering for it gets the task before the date, because an obligation nobody is warned about is an obligation missed. Once done, an annual duty creates the next one and the old one stays on file with its date, since that compliance history is exactly what a union brings back out.
The posted seniority list is composed at one click as of the date you choose, then freezes. An error is fixed by posting a corrected, dated list, which leaves the trail of the correction instead of erasing it.
Job postings keep three doors to the same job apart: the ordinary posting, bumping and recall. Every bid carries its rank as of the posting, copied. ⚠️ Awarding to a less senior bidder is not forbidden, but the reason has to be written down: that is the text read back in a grievance.
The remittance is prepared one line per covered employee, and the agreement's rule applies to the base you enter. The gap between declared and computed stays visible: it gets explained or corrected, never erased.
The counterpart to the employer side. Membership keeps its history: a person leaves and comes back, and it is the history that answers "since when has she been a member". Signing a card never changes coverage, which comes from the certification.
Meetings count quorum on members present, not on the covered headcount: in a unit where many pay without having joined, those two numbers differ sharply. The share in favour is computed on votes cast, abstentions left out, otherwise votes the meeting carried would be counted as lost.
Stewards carry a dated bank of union leave, because what gets challenged is not the principle but the balance on the day of a refusal. A refused leave counts for zero hours and stays on file, with its written reason.
Reconciliation is the point of the rest: what the union received, what the employer declares having remitted, and the unit's covered headcount, side by side. A statement with no remittance linked is never called reconciled: the absence of a comparison must not read as a successful one.
Odoo follows the pipeline very well: the position, the person, the application, the stages, the refusal reasons. It stops just short of the assessment itself, which lives in a free notes field and a questionnaire per position, with no weighting, no round, no panel and no comparison between candidates.
This module picks up there. You write a scorecard per role and per round, with weighted criteria and a scale described in observable behaviours. ⚠️ Each panel member files their scoring before seeing anyone else's, which stops the first opinion expressed from pulling all the others.
A refusal issued after an interview has been held requires a written reason, and the name of whoever decided stays on file. A published scorecard is frozen: last year's session reads back exactly as it was scored.
No automatic ranking is produced. The score helps a person decide, and a knock-out criterion flags without eliminating anyone: a tool that ranks candidates decides for you and leaves you the responsibility.
Eight modules add themselves as needs arise: the four candidate emails rewritten, a portal where the person follows their file, the job offer on your letterhead and its sending for signature, Law 25 retention and destruction with an anonymised measure that survives the destroyed scorings, expenses and cost per hire, and each job board's performance measured through a tracked link.
Comparable to Workable or Recruitee, without your candidates leaving your house


A working day rarely happens in a single screen. You jump from activities to tasks, from tasks to email, from email to the calendar, and every jump costs the time it takes to find your place again.
A desk is a named layout that puts those screens side by side. Each pane points at an existing screen and keeps its own search bar, view switcher and favourites: this is not a summarised tile, it is the full screen, usable where it sits.
You create as many as you have contexts. Six layouts, one keyboard shortcut per desk, and optionally a time-of-day slot: the morning desk opens in the morning, the portfolio review one in the afternoon.
⚠️ Desks are personal. A colleague's does not appear in your list, and that is deliberate: a layout imposed on everyone goes back to being a dashboard nobody looks at.
Comparable to Salesforce Lightning App Builder, open-source flavor

Something worth remembering rarely arrives while the right screen is open. It arrives during a call, in the middle of a quotation, and it ends up on a scrap of paper or in an email you send yourself.
A note is taken from any screen, through the icon in the top bar or a keyboard shortcut, without changing tab. It is private by default, formatted, and attaches to one or several records: a contact, a quotation, a project, a ticket. Any record with a message thread can carry one.
You find it again by search, by its tags, by the pin, or from the linked record itself through a quick-access button. The day the note becomes something to do, it converts into a dated activity or a project task without re-entering the context.
That last point is what separates it from a notebook: a note that does not know what it refers to makes you tell the same story twice.
Comparable to Apple Notes, but wired into your records

A process map gets delivered as a PDF, filed in a shared folder, and stops being true at the first change. Six months later, it gets redone.
Here the pools, lanes, nodes and flows live as records, attached to a client and a project, versioned. The model governs: the PDF, the .bpmn and the .drawio are only renderings of it, and a fresh one can always be produced.
A map is cut into levels: the overview at level 1, collapsed sub-processes at 2 and 3, each its own page. That is what lets you show the whole to a management team and the detail to the crew without keeping two documents.
Every step carries its own message thread, its workshop photos and its documents. That is where the question that kills process maps gets settled: where did so-and-so's remark about step 7 go.
An as-is map stops being a one-off deliverable and becomes a reference worked on continuously. Export to .bpmn and .drawio to carry on in a third-party editor.
Comparable to Lucidchart or Visio, wired to your records


Gamification fails when it rewards what is easy to count rather than what counts. A click counter ends up producing clicks.
Fox Quest wires its experience points to real activity: tasks completed, timesheets filed, tickets settled, meetings held. The level is crossed in steps, ten fox-themed levels from Renardeau to Kitsune. The badge is earned at a milestone or granted by hand by a manager, across six categories and five rarities.
Rewards are configurable and concrete: a gift card, a flex day, team kit, a bonus tier. A point that leads nowhere stops being looked at within a month.
⚠️ Dosage is the real subject. The application exists to put a little play where it helps, not to turn a team into a language-app cohort. Rules come out as easily as they go in, and nothing depends on them.

A ticket carries a number, a subject, someone asking and a priority. Odoo can keep that list. What it cannot tell you is why a file has been asleep for six days.
This module adds a waiting state independent of the stage: a ticket can be “in progress” on the team's side and blocked at the client's or a third party's. Running the two together makes the team carry a delay that is not theirs.
Every ticket attaches to a project and to the team's hour bank: time logged on the ticket draws down the agreed balance, with no double entry and no month-end reconciliation. Macros keep the replies you rewrite ten times a month, and service levels are measured on the stage, not on the age of the ticket.
The public form is per team, on your domain and in your colours. ⚠️ It acknowledges receipt immediately: a request with no automatic reply is a request that gets rewritten by email, and then you handle two.
On closing, a satisfaction survey goes out on its own. It is a lean fork of the OCA helpdesk_mgmt module, not a separate product: what comes from Odoo stays Odoo.
Comparable to Zendesk or Freshdesk, small-business flavor

Searching an ERP assumes you already know where the thing lives. You open the application, then the list, then you type. Three gestures before the first useful character.
Cmd+K opens one bar that crosses nearly twenty-five data types: contacts, opportunities, projects, tasks, meetings, emails, SMS, calls, invoices, signature requests, secure transfers, matrices, resolutions, articles, products, documents.
Type a number to jump straight to a task or a ticket. Active records come first and closed files are greyed: a search that puts a closed file at the top wastes more time than it saves.
Arrow-key navigation, opening in one click or in a new tab with Ctrl+Enter. Nothing to configure: the palette follows what is installed.
Comparable to Linear or Notion's Cmd+K, inside Odoo
A due date says which day, never which moment. Yet a great many things only make sense at a particular hour: the call happens in the morning, the follow-up over lunch, the backup outside office hours.
This module adds an indicative time slot to the task alongside its due date: morning, midday, end of day, out of hours. It shows on the kanban card and works as a filter, which lets you open your day on what is done now rather than on everything due today.
⚠️ It is an indication, not a constraint. Nothing fires at the stated hour and nothing is blocked outside it: a slot that triggered an action would become a schedule, and a schedule has to be defended, justified and maintained.
The same slot is used elsewhere in Symbifox, notably to decide which desk opens at which point in the day.

A task waiting on the client is not “in progress”, and it is not “blocked” either. For want of a word for it, it stays in progress, and the team carries a delay that is not theirs.
This module adds two distinct states, Waiting on client and Waiting on third party, alongside Odoo's own. They read differently on the kanban and stay fully compatible with existing filters and reports: an added state that breaks the reports only serves for a week.
⚠️ The state is distinct from the stage. A task can sit at the “In progress” stage and be waiting on a third party, and that combination is exactly what you want to see: the stage says where the work stands, the state says why it is not moving.
The same mechanism serves helpdesk tickets, for the same reason: measuring a response time without separating out who is keeping whom waiting gives a figure nobody can defend.
Comparable to The “waiting status” pattern found in most project management tools
Two people open the same task two days apart. One gets archived, and everything it carried goes with it: the conversation, the attachments, the hours logged on it.
This module moves the content before archiving. The message thread, the activities, the followers, the attachments, the hours and the dependencies are reassigned to the task you keep, and the others are archived, empty.
The hours matter most: they are billable, they feed the hour bank and the project's profitability. An hour trapped in an archived task is neither lost nor visible, which is the worst of the two.
⚠️ Source tasks are archived, never deleted. Their number stays valid and an old reference still leads somewhere, because a dead link in last year's email cannot be explained away.
Comparable to Zendesk ticket merge, for your Odoo tasks
A file lives on Nextcloud, the file it belongs to lives in Symbifox, and the person spends the day between two tabs copying links.
Three doors lead to the same files: a tab on the record of a project or a task, bounded to that record's folder; a full application in the main menu; and a launcher in the top bar that slides the browser over whatever screen you are on.
Folder tree on the left, built-in preview, drag-and-drop upload, moving, configurable share links, office documents opened straight in Nextcloud.
⚠️ Each person browses with their own Nextcloud account, connected once through your organization's authentication. Nextcloud therefore applies its own permissions, and every share carries the name of whoever created it. A browser going through a service account would show everyone everyone else's folders.
Symbifox does not replace Nextcloud: your files stay with you, on your server.
Comparable to SharePoint's file explorer, on your own Nextcloud
An NFC tag triggers nothing by itself. It holds a few dozen bytes, and the only format both phone platforms read without an app is a link. The phone opens it, and the server acts: that is what lets you add a gesture without anyone having to update their phone.
⚠️ What is written is a public code, never a secret: a tag reads at four centimetres without your consent and copies for pennies. Identity comes from elsewhere, and that is what separates the three doors: the Android app, with the paired device's token; the browser, with the session already open; and the signed NTAG 424 DNA chip, which signs every read and works for a person with no account. Every gesture runs with the bearer's rights, never more.
The gestures shipped: open the record, take out and return equipment, book a room, record attendance, start a timer, walk a round of points in order, report a problem that opens a pre-filled ticket. A tag can ask a question before acting, or offer a menu.
It can also ask for a checklist: compliant or not, a measured value with its range, a choice, a note. This month's fire extinguisher, the fire door, the padlock. The record is immutable once written, the anomaly is followed through to its correction, and the register comes out as a PDF with the regulation's section at the top.
With no network, the tap is kept with its time and goes out when the signal returns, checklist included. A QR code printed beside it does the same work for a phone without NFC. Signed chips are prepared from the app, but the company key never goes down into a phone.
Comparable to A patrol-tour reader and its software, replaced by a one-dollar tag and the phone you already have


An assistant that cannot see your data answers into thin air, and an assistant that sees it from a third-party service takes your files out of your house. Gen answers both problems at once.
It opens from two places: a side panel that follows the record you are reading, and a full-width page with all your conversations. A conversation opened from the panel remembers the record it started on, and the panel then shows only what concerns it.
An instruction is a short directive the assistant respects everywhere, or only on one record type: that is where the house tone is written down rather than restated every time. Every turn is counted, in tokens, in duration and in equivalent cost, and the steps it took stay readable: read a task, search the mail.
⚠️ The assistant writes under your identity. A task it opens carries your name in the history, not an anonymous robot's, and it does nothing your account would not be allowed to do.
The connection to the model is made from your server: no data travels through a service that is not yours.
Comparable to Microsoft Copilot, self-hosted flavor

A supplier bill arrives as a PDF and leaves as manual entry: the supplier, the date, the amount, the tax breakdown, the lines. Forty a month is a day's work that produces nothing.
Drop the PDF in. Extraction returns the supplier, the dates, the amounts, the GST and QST breakdown and the detail lines, then prepares the accounting draft.
⚠️ Human validation stays mandatory, and the screen is built for it: the extracted values are re-read at a glance, beside the original image. An extraction that posted itself would turn a reading error into a decided entry.
The analytic account is set along the way, which spreads the expense by project or by client without changing the books, and feeds the profitability shown elsewhere.
Processing happens on your server. A supplier bill carries your prices, your volumes and your partners: it has no business leaving your house to be read.
Comparable to Dext (formerly Receipt Bank), open-source and self-hosted
A wiki ages badly because nothing in it says which version was distributed, to whom, or when it is due for review.
A document is a record: a name, a reference, a type, an owner, a next review date. Its content lives in versions, each with its number, its change log, its approver and its effective date. Publishing a version freezes its content: that is the one that was distributed, and it will not move again.
A distribution links a version to someone, client or employee, and records the sending, the method and the acknowledgement. That is what answers “who had received the password policy in February” without reopening a mailbox.
Matrices track an engagement's deliverables: status, owner, dates, attachments, linked tasks. Decisions taken in meetings are filed into them, where people will look.
More than a wiki, less than an ERP: a document's body can be written here, section by section, or stay a pointer to an external file.
Comparable to Notion or Confluence, structured by project


Writing to a contact means remembering things that are written down nowhere: whether to address them formally, that their assistant must be copied, and that their partner asked to be left off.
Every contact carries their register, their salutation, their closing, and what you should know before writing. As you compose, the banner reads the recipients themselves, in To and in Cc, and recalls who should be copied, who usually is, and who asked not to be. The button adds the copies to the right field.
⚠️ Copy rules are proposed, never applied: they are inferred from emails actually sent, with the observation that motivates them, and nothing moves before you confirm. A copy added automatically eventually sends bad news to the wrong person.
The relationship is measured daily on the messages themselves: your emails left unanswered, the length of what you write, the last exchange in each direction. Those are facts, not an invented score.
Paired with the client experience module, the persona also shows the contact's latest rating and their comment, right where you are writing to them.
Comparable to Salesforce Einstein Insights, small-business and open-source flavor



A contact book degrades on its own. Titles change, numbers change, and nobody opens a record to correct an extension they never dial.
Scan a business card, enrich a contact from the signatures in their emails, one at a time or in bulk, create a contact from an email received, import vCards, spot duplicates.
At a trade show or on a visit, scanning lives on an installable mobile page: an address to open, no app to download, and it is the phone's camera that takes the card.
⚠️ Enrichment never replaces a value already entered, and every addition is logged. That is the one rule that makes the automation acceptable: a tool that overwrites a hand-corrected number gets switched off within a week.
A completeness score highlights the records worth finishing, rather than leaving you to hunt. Model calls run on your own infrastructure.
Comparable to Clearbit or Dropcontact, open-source and self-hosted
A team's one-time codes live on somebody's phone. Nobody knows what is held for whom, and a departure is discovered on the day someone needs to get in. This module puts the vault in Odoo, every code tied to its client and its mandate, and the client record says how many you hold for them.
The property it buys is rarer than the feature: the server holds no readable seed. Encryption happens in your browser, the key is derived from a passphrase that never leaves it, and the codes are computed in the page. We cannot produce your codes, and that is not a promise: it is checkable in the published source.
Open it with a fingerprint or a hardware key, and keep a recovery code offline for the day the passphrase is lost. The archive takes what you use twice a year out of the everyday list without destroying anything: the code still lives there, and search finds it. An assistant reads the domain in your accounts and proposes a filing for a whole vault, which you correct before it writes.
⚠️ And you are locked in nowhere: the encrypted export is the default, a plain export sits behind your passphrase, with one otpauth:// address per code that any other manager can read back.
An e-learning module plays content. This one keeps the register, which is not the same work.
A requirement says who owes what, and it takes two forms that do not reduce to one another: a specific activity each covered person must have taken and kept valid, or a number of hours in certain categories, with the categories that do not count. From that rule comes one line per person, with its due date and its state: that list is what you show the inspector.
Facing it, the completion carries the proof: the real date, the hours, the provider, the number, the certificate attached, and the expiry date.
⚠️ Nothing is overwritten: three first-aid courses over nine years leave three lines, and the sequence shows. Expiry moves on its own, carried by a daily task rather than a field frozen the day it was written.
And what is missing is not worth zero: a line with no hours is marked incomplete and leaves the totals, instead of entering them at a null value that looks like a measurement.
Comparable to A corporate LMS, cut back to what can be proven

Odoo's content player keeps no completion date, and the line it adds to an employee file carries the date of the recomputation.
This bridge writes the record the moment completion happens, with the course hours. Anyone without a portal account is given one, without which a finished course reports nothing at all.
It also watches the content: when a course gains or loses chapters afterwards, the register flags it and decides nothing. Raising the version makes everyone who took the old one sit it again; acknowledging the change freezes the count and asks for nothing.
⚠️ Fixing a typo should not send anyone back to class, and that is why the decision stays with a person rather than with a rule.
Odoo 18 knows no standard course format: its player takes an image, an article, a document, a video or a quiz, and that is all. A provider delivering training as SCORM, which is nearly all of them, has no way to play it or to get the result back.
This module adds the missing type, with both APIs the standard mandates, the 1.2 one and the 2004 one, because any real catalogue holds some of each.
Completion reaches the register through the player's own hook: there is a single write path.
⚠️ The module says what it does not do: SCORM 2004 sequencing is not applied, and it announces that on the package record. A player that claims to sequence and gets it wrong is worse than one that says it does not sequence.
Odoo Events already runs a session: the date, the room, the seats, the registrations, the check-in. This bridge does not redo any of it.
It adds the two things a training session needs and an event does not carry: the signed attendance sheet, produced as a PDF with the signatures on it, and the write to the register.
⚠️ It also fixes two false friends in the native module. Odoo's “Attended Date” holds the moment someone clicked: a March session marked off in September would be dated September, and the register would date the training from the day somebody remembered it. The bridge takes the date of the session.
And a cancelled registration keeps its attended date, because the computation never clears it: reading that field would count people who never showed up.
An unsigned attendance is recorded and flagged incomplete, never lost: the person was there.
Some training is not a course: it is a document to read, and the expected proof is a dated, sometimes signed acknowledgement filed in the person's record. A prevention policy, a multi-module onboarding process, a procedure that changes.
Distributing a version assigns the training. The acknowledgement writes the completion record, dated the moment it arrives and tied to the version read, not to the document in general. A new version reopens the obligation for anyone who only acknowledged the old one, because a policy that changes has to be read again.
⚠️ A required signature is genuinely required: until it is there, nothing is recorded. A ticked box does not replace a dated, signed document.
When an onboarding process has several modules, each gets its own, which is the only shape that can say which one is missing.
A process map says what gets done and by which role. The register says who must know how. Between the two sat a fact nobody held: who holds the role.
The bridge adds it to the lane, and a training requirement can then target that lane instead of a hand-copied list of names that goes stale on the first departure. Someone joining the role inherits the requirement; someone leaving it stops being covered.
⚠️ A lane with nobody in it targets nobody, and certainly not everyone by default.
From a step on the map you see what it takes to hold it, and from there who is current. On-the-job training is recorded with what stands as its proof: the trainer, the learner, the step worked on, and both confirmations. Training that only the trainer attests to is not training received.
The register knows what a training course cost: the hours, the hourly rate, the employer contributions, the fees. The budget knows what was planned. This bridge puts the two side by side.
A budget line names training categories and its actuals pick up the cost of training taken in the period, with no accounting entry. What is owed and not yet taken shows up as committed, not as a year-end surprise.
⚠️ The refusal is shared by both modules: incomplete training does not enter the actuals, its hours join the unvalued hours where the budget already flags them, and an obligation that cannot be priced is counted rather than silently skipped.
An under-stated commitment is worse than a missing one, because it looks filled in.
The mobile surface of the register: my obligations with their due dates, my completed training with its expiry, and what has been assigned to me.
⚠️ Three deliberate refusals.
No route takes an employee id: the person comes from the token, never from what the client sends, because a parameter you do not accept is a parameter nobody can forge. An organisation's training register holds precisely what people have no business knowing about each other.
Nothing writes to the register from the phone: proof of training is filed, not declared.
And an account with no employee record returns an empty list rather than an error: not being in the register is not a fault.
Three duties a generic register does not carry.
The certificate first: an employer must be able to issue one to anyone who attended training it delivered itself, when the outside trainer issues none. The PDF carries the subject, the hours, the dates, the trainer, and the right legal entity: the one on the record, not the one printing it.
Retention next: six years after the last year the document relates to, computed, and deleting a line still covered is refused.
The statement last: payroll, the minimum contribution, eligible spending computed as hours times hourly rate, the carried-over surplus and, where applicable, the levy.
⚠️ Incomplete records are kept out and listed separately with their hours, because a statement that swallows gaps produces a wrong figure that looks right. The document says of itself that it supports the filing rather than replacing it.
Eleven regulatory references ship with it, quoted in full.
An allergy is health information, and therefore sensitive under Quebec's Law 25. A field visible to the whole company and a field reserved to whoever organises meals are not the same feature.
Here, the person concerned and the administration read the record, never the direct manager nor the rest of the staff. Everyone declares their own, because a consent its holder cannot exercise is not one.
The catalogue loads Canada's recognised priority allergens on install, plus latex, insect stings and fragranced products.
⚠️ And above all: the catering list, which gives a group's dietary constraints without the names, with the headcount and the highest severity. That is what you hand a caterer, instead of circulating a medical record to order sandwiches.
The privacy bridge adds express consent, retention tied to the employment relationship, and a purge shipped disabled.
Comparable to The allergy spreadsheet in the HR drawer, made compliant

Getting a document signed costs a subscription per user, or a printout, a signature and a scanner. Both routes take the document out of your system.
A signature request brings together a PDF, one or more signers and fields placed on the pages: signature, initials, date, name, email, text, number, checkbox, dropdown. Each signer receives a link of their own and needs no account.
When the last person has signed, the document is sealed: a PAdES digital seal that PDF readers show as “signed and not altered”, a completion certificate, and an immutable audit trail with timestamp, address, SHA-256 fingerprints and optional RFC 3161 timestamping.
⚠️ The public verification page is what makes that trail hold up. The third party who receives the PDF drops their copy on it and sees whether it matches, computed in their own browser, with nothing uploaded: they do not have to trust you in order to check your document.
An optional QR code stamped on the document leads there. Send a quotation, a purchase order or a corporate resolution in one click.
Comparable to DocuSign or Odoo Enterprise Sign, inside your Odoo

A file of several gigabytes goes out as an attachment that bounces, or through a free service that hosts the client's data elsewhere and keeps no trace of it.
A transfer brings together a sender, recipients, a subject, a message, files and an expiry. The share link carries a token that cannot be guessed and stops working at expiry. The recipient code is single-use, sent by email or SMS: until it is entered, neither the message nor the files appear.
Upload goes from the browser to object storage directly: large files never pass through the server, which is the only way to send several gigabytes without bringing it to its knees.
⚠️ The access log records every event, from creation to purge, with timestamp, address and fingerprints. That is what answers the question that matters after an incident: who opened what, and when.
Automatic purge at expiry, data hosted in Canada, public page in your brand according to the domain the visitor arrives through.
Comparable to WeTransfer or SwissTransfer, with Law 25 compliance built in

An annual security course gets skimmed and forgotten in two weeks. What changes behaviour is getting caught with no consequence.
A lure template brings together the fake email, the landing page, the educational page shown afterwards and the course assigned on failure. A campaign sends that template to a group, on a chosen date, spread out or not. A result is one line per person per campaign: sent, opened, link followed, form submitted, and above all lure reported.
Reporting counts as much as failing. A “Report as malicious” button lives in everyone's mailbox, and an organization where people report quickly defends itself better than one where nobody clicks.
⚠️ In a real incident, email clawback pulls a message out of Microsoft 365 or IMAP mailboxes, with a reversible quarantine: a permanent deletion on a false positive costs more than the message did.
Risk profiles are per person, and exist to target training, not to rank people.
Comparable to KnowBe4 or Terranova, open-source inside your Odoo


An IT audit is easy to narrate and hard to prove. The question that comes back is not “do you have backups” but “which of your four providers holds them, and since when”.
Five notions are enough. An audit element is a theme to check: fourteen ship with the module, from policies to backups. An IT provider is a third party that touches the audited organization's data. An audited client is the engagement file. An assessment is the crossing of a client, a provider and an element: it is the cell of the grid, and it carries a status. A watchpoint is a risk raised during the engagement, with its priority and the person responsible.
The crossing is what changes everything. Assessing an element without naming the provider that carries it produces a report you cannot hand to anyone to act on.
The dashboard sums up each engagement in three numbers, the green, yellow and red elements, without opening the file. Coverage sits next to the adequacy rate: an engagement at 100% adequate on 30% assessed is not a good engagement.
Co-developed with PME Conforme for Law 25 audits of Quebec small businesses and non-profits.
Comparable to Vanta or Drata, tailored to Law 25



Playing music on your premises creates royalties, and the amount is not final. Background-music tariffs are before the Copyright Board, which can certify them years later, retroactively.
An establishment is where the music plays: its floor area, its uses and its operating days form the base. A licence period is a paid slice, with its renewal date, its settlement date and its payment reference. A royalty line applies a tariff to a period and carries, beside it, what was actually paid.
⚠️ The reference table is dated: every period carries both the proposed rate and the certified rate. That is what no spreadsheet does, because a spreadsheet cannot say “not certified yet”, and an empty cell reads as a zero.
One button quantifies exposure since 2020: what you paid under a tariff that nothing makes final, and what you could still owe.
No music passes through: the module tracks your compliance, it broadcasts nothing.
Comparable to The tracking no spreadsheet does, because a spreadsheet cannot say "not certified yet"

A consent you cannot reconstruct is not a consent. The question asked in an audit is not “did you ask” but “what exact text did the person accept, and when”.
A purpose says why you collect. A notice is the text presented to the person: it is versioned and each version carries a fingerprint, which lets you state later what was accepted. A consent links a person, a purpose, a notice version and evidence: signed file, capture, portal log.
Contact preferences say through which channel a person agrees to be reached, with a Do not contact switch that overrides everything else.
The documentary side follows the same logic: retention schedule, destruction register, incident register, handling of access requests.
⚠️ The engine is multi-framework. Quebec's Law 25 is built in; companion modules cover GDPR, UK GDPR, PIPEDA and New Zealand's Privacy Act 2020. Emails and certificates adapt to the applicable framework, because a response deadline and a definition of incident are not the same from one regime to the next.
Comparable to OneTrust or Didomi, small-business flavor


Odoo Community has no Gantt view, and its tasks have no start date at all. So the schedule gets drawn elsewhere, and it diverges from the project in the first week.
The module brings both, with no Gantt library: a single geometry is computed server-side, and the screen, the PDF, the PNG, the SVG, the workbook and the Microsoft Project file all draw it identically. Two renderings that diverge are two truths presented to the same client.
A schedule is drawn on a project, or off-project when the point is to illustrate a proposal without creating forty tasks. The swimlane groups bars by stage, milestone, person, company, or nothing.
⚠️ A missing start is not hidden: the bar carries a dotted rule and says where its date came from. A schedule that guesses a start date without saying so gives a plan nobody can check.
MSPDI and Excel import and export, both ways. Shareable on the portal without buying a seat per person.
Comparable to A per-user Gantt subscription, or Enterprise

Working with a client on their tasks usually means opening an account for them on your side, or asking them to open one on theirs. Both cost, and both leak.
Two instances that trust each other pair by invitation, administrator to administrator: one issues a single-use code, the other accepts it. A shared secret is born of the two halves, neither instance picks it alone, and it then signs every message.
A federated task appears on the other side in a closed project, assigned to the person they chose. State, due date, messages and small attachments travel both ways.
⚠️ What does not cross matters just as much. Internal notes stay with their author, and so does a message that begins with 🔒. Nothing is ever deleted: withdrawing a share stops the exchange, it does not erase what was said.
Only projects that name a peer offer sharing, which keeps a task from landing at a partner's through one distracted click.
Comparable to Nextcloud federated sharing, Asana guests, Jira external collaborators


In a guided engagement, the question that comes back is not “is this moving” but “what are you waiting on from me”. It comes back because the answer is written nowhere the client can read it.
Every step of the engagement is documented: what happens in it, what has to be supplied, and by whom. The team works from an internal dashboard; the client opens a self-service portal and sees where things stand, what is coming, and what is still owed from their side.
⚠️ The part that changes things is the last one: telling the client what they owe, with its date. A tracker that only shows the team's progress moves the chasing from the client to you without reducing how much chasing happens.
It is built for linear engagements, the ones that go through the same steps every time. An engagement that forks at every client is better followed as an ordinary project.
Comparable to Asana Goals + a homegrown client portal
Selling a block of hours is simple. Knowing what is left of it, mid-month, without opening a spreadsheet, is less so.
An hour bank is one record per client, not an accounting account. The balance fits in a sentence: credits and adjustments, less debits. A debit is every timesheet line entered on one of the included projects. A credit is every validated invoice line, filtered by company, by invoicing partner and by product. An adjustment is a manual entry for whatever escapes the other two: a write-back, a correction, a discount.
⚠️ The application reads timesheets and invoices, it never edits them. A bank that rewrote a logged hour would make the billing diverge from what was actually done.
The balance appears on the client portal in real time, and an alert goes out at the agreed threshold. A client warned at ten hours left tops up; a client warned at zero negotiates.
Comparable to The “hour bank” model from consulting firms, automated

Time gets written down afterwards, from memory, at the end of the week. It is the best-documented source of error in hourly billing.
A timer starts from any task, from a kanban or from the list of recent tasks. Several can run at once and be paused, because a real day is not a queue.
On stopping, the duration is proposed according to your rounding and the timesheet is written in one gesture. Rounding is set once: billing to the minute produces invoices nobody reads.
The Android app does the same outside the browser, with a persistent notification, a home-screen widget, and sharing a task link to start its timer from an email.
The timer lives in the top bar: it stays visible whatever screen is open, which is the only way not to forget it is running.
Comparable to Toggl or Harvest, natively integrated into Odoo
Measuring satisfaction is easy. Not wearing the same people out while doing it, and doing something with the result, is a great deal harder.
A feedback is a register entry: a score, a comment or both, with its date, its channel and the client concerned. A programme links a survey to a measurement intent and sets the invitation template, the reminder delay and the minimum cadence between two solicitations.
⚠️ The guardrails are global, not per programme: a cooldown per contact across all channels, a Do-not-contact list, and the exclusion of files in collections. Three programmes each respecting their own rules can, together, write to the same client four times in a month.
The NPS score is computed over an honest window and stays hidden below ten responses: a score on three responses is not a score, it is an anecdote with a decimal point.
The closed loop on detractors carries a deadline. The complaints register follows ISO 10002: a real acknowledgement, root cause, satisfaction follow-up. Testimonials stay locked until a consent has been recorded.
Comparable to Qualtrics or Delighted (the essentials, in your Odoo)

Odoo Community ships no budget module.
This one puts your budget positions on groups of ledger accounts, not on analytic accounts: a budget therefore works on day one, even if no vendor bill carries an analytic key yet.
Four amounts, in Odoo's own vocabulary: planned, actual, committed and theoretical. The actual is never copied, it is re-read from your books every time it is displayed.
⚠️ The theoretical follows the calendar, not the clock. A yearly expense does not spread over twelve months: the monthly split is editable, and when your subscriptions provide a dated schedule, the theoretical is built on it and says so. A flat time-elapsed prorata would report an overrun every renewal month, and an alert nobody trusts protects nothing.
An open budget is not edited: it is revised, and the original stays readable. A coverage check reports the accounts no position covers and the ones two positions count twice. Alerts fire when a drift passes a percentage and a currency floor: a percentage alone screams about small positions, an amount alone stays silent about large ones.
And the rolling forecast: a twelve to eighteen month horizon that crosses fiscal years, re-made every month with one button, where each pass keeps its figures forever. It is the comparison between two passes that teaches you something: “we forecast 28,896, we now forecast 27,666, and the difference is what August actually did”.
Comparable to Odoo Enterprise's Budgets module, in open source, following the calendar rather than the clock

Odoo knows what a campaign returned: invoiced amount, leads, quotations, all computed on utm.campaign. None of its thirty-three fields is a cost. The campaign therefore measures a return whose denominator it does not have.
This module does not build a parallel spend register, which would eventually diverge from the accounting. It attaches one analytic account per campaign, with a uniqueness constraint so an account never serves two campaigns, and splits the spend between what went through accounting and what is internal time.
⚠️ Time is valued by Odoo from the hourly cost. When that rate is missing, the module counts the unvalued hours and says so, rather than displaying “nothing spent”: a zero cost reads as a profitable campaign.
An Enterprise to Community migration drops part of your data by design: a Community database has no table for helpdesk tickets, knowledge articles, signature templates or subscription plans. The import tool counts them in its summary, then they leave with the source copy.
This module is the other end. It keeps first: everything set aside becomes browsable and searchable inside your instance, whether a destination exists or not. It re-homes next, onto the open-source counterpart, and every mapping states what it cannot carry before anything is written. A preview shows the first ten records as they would be created, with the list of what was dropped and why.
Nothing is taken on trust: a contact that no longer exists is dropped with its reason rather than written at random, and each record is created on its own, so one bad row does not cost the others.
⚠️ And ten checks go over your instance afterwards, where the import tool only prints a list that lives as long as a terminal window: language or timezone codes that do not exist, a site address left on a test value, scheduled work still stopped, counters running behind, attachments whose file did not come along. A check that could not look says so, instead of blending in with a green light.

A hosted estate lives in somebody's head until the day that person is on holiday and a certificate expires.
The application opens on a dashboard of counters rather than a list: availability over the last 24 hours, open alerts, services expiring at 30, 60 and 90 days, maintenance, backups, domains and security. Three buttons re-run the checks on demand.
A service is something you host for a client. Around it come the containers, the certificates, the restic backups, the available updates and the maintenance due dates.
⚠️ A probe that answers is not a service that works. The health check distinguishes the kind of what it measures: a service can be up, answer 200, and no longer do anything.
The application also covers the client estate (endpoints, encryption keys, groups), software licences with their seats and expiry alerts, VoIP.ms telephony and DMARC email authentication reports.
Comparable to Plesk or cPanel, open-source


There is always a field missing. A file number used at the client's, a renewal date, a checkbox only your team understands. The usual answer is a development project, for three fields.
Forge stays deliberately narrow: it adds fields to existing forms, and nothing else. It creates no new record type, draws no report, and replaces no bespoke development.
Five objects are enough. The custom field, from text to currency, from date to a link to another record. The view injection, which decides where it shows: form, list, card, search filters. The stat button, which shows a count at the top of a form and leads to the records counted. The default value, per person or per company. The shared filter, offered to the team rather than to its author alone.
⚠️ Forge keeps the description of every addition, then rebuilds what it needs to if an update wipes the underlying wiring. Data entered in a custom field is never touched: that is the difference between a customisation and a change you redo at every version.
The module is named bf_studio_light in the code and on GitHub.
Comparable to An open-source alternative to proprietary customization tools

A management instance you look at every day ends up looking like everyone else's, and so do the PDFs that come out of it. The document sent to the client suffers most.
This module applies the Lexend typeface, drawn for legibility, everywhere: interface and PDF reports. It sets the brand colours, company logos and favicon from the settings, without touching code.
⚠️ The setting is per company. On an instance carrying several, each issues its documents in its own image, and an invoice never goes out in the neighbouring company's colours.
The consistency runs from the web client through to the PDF: a brand that stops at the edge of the screen is not a brand, it is a theme.
Comparable to No direct equivalent on the open-source side
A management interface is looked at eight hours a day, and often in the evening. Odoo's pure white was never designed for that.
This module switches the interface to dark tones, on a restrained grey palette drawn from the brand colours. It is not a mechanical colour inversion: backgrounds, borders, hover states and warning colours are reworked one by one, so that the red of a passed deadline stays readable instead of turning brown.
⚠️ The brand blue is never used as text on a dark background. It stays a fill, a rule, an accent. A light blue set in letters on dark grey passes contrast checks on paper and tires the eyes in practice.
The choice is personal and is made from the account preferences. It is not imposed on the team and changes nothing in the documents produced: a PDF exported in dark mode is still a PDF on white.
Comparable to The Slack or Notion dark mode, inside Odoo
A gift is easy to count. Knowing which of your two appeals produced it, and which of last year's donors gave nothing this year, takes a structure spreadsheets do not have.
Fundraising is built on four levels. The fund says where the money goes and carries the analytic account that allocates the gift. The campaign is a period's overall effort, with its goal. The appeal is one precise effort inside the campaign. The package is a variant of that appeal, the printed envelope against the email.
⚠️ It is the package that answers the real question: which of the two worked. Without it, you measure a campaign and credit its result to the channel you prefer.
People are called constituents, donors, members and prospects together, with their giving summary, their rating, their household and their appeal codes. The lapsed donor report names those who gave last year and not this one.
Public donation form and donor portal included. Compliant Canadian receipts come from the companion module “Donation receipts”.
Comparable to Raiser's Edge / Blackbaud, in your Odoo

A non-compliant donation receipt is not a layout detail: it exposes the charity to revocation of its registration, and the donor to a refused credit.
Receipts meet CRA and Revenu Quebec requirements, in French: registration number, gapless annual numbering, eligible amount, gifts in kind with fair market value and appraiser, cumulative annual receipts, cancellation and reissue.
⚠️ The eligible amount is the gift less the advantage received, and that is where most tools go wrong: a $200 fundraising dinner whose meal is worth $60 produces a $140 receipt, not a $200 one.
Gapless numbering is the other requirement people discover during an audit. A cancelled receipt keeps its number and is replaced by a replacement receipt that names it, rather than disappearing.
The receipt is a standalone layout in your brand, not a disguised invoice, sent by email with its PDF attached.
Comparable to Raiser's Edge's tax receipting, adapted for Canada
Single-purpose modules that support the applications: fixes, bridges, settings, themes.
What gets added, what we learn deploying it, and nothing else. You leave whenever you want.
One confirmation email, and nothing before you follow its link. No tracker, no resale.