Invoice rules
An invoice rule is a check that a lab builds on a job before Flow drafts its
invoice. Each rule is When, If and Then. A lab owner or admin builds it from
menus under Settings, Rules, Invoices. No lab's checks are fixed in Flow's
code. The rules read only Flow's own records, so they work with or without
an accounting integration. The code is in server/core/invoice-rules.js.
flowchart LR
ready[Job ready to invoice, no invoice] --> each{For each rule that is on}
each -- every condition matches --> then{Then}
then -- wait --> wait[Waits for a person]
then -- skip --> skip[Never drafted by rule]
then -- pending --> flagged[Draft, on Pending with the note]
then -- non_chargeable --> kept[Draft, on Non-chargeable, never sent]
each -- no rule matches --> draft[Draft invoice in Flow]
draft --> check{Pending, skip and non-chargeable rules check the draft}
check -- a pending rule matches --> flagged
check -- a skip rule matches --> held[Stays Pending: not invoiced]
check -- a non-chargeable rule matches --> kept
check -- none match --> readyTab[Ready]
Parts of a rule
| Part | Values |
|---|---|
| When | job_ready: the job is ready to invoice and has no invoice |
| If | 1 to 10 conditions. All must match |
| Icon | Optional. Any Lucide icon name, picked in New rule or Edit rule. The card on Rules shows it. With none, the card shows the mark of Then: flag pending, clock wait, ban skip |
| Then | wait: the job waits for a person. skip: rules never draft it, and Send holds a draft that already matches. pending: Flow drafts it, and the draft is on the Pending tab with the rule's note. non_chargeable: Flow drafts it, and the draft is on the Non-chargeable tab |
A person can still draft a waiting job from the Jobs board. The job lists
each rule that stops it, for example rule "Old job": waits for a person.
Pending rules
A pending rule never stops a draft. Flow checks each draft from a job
against it, as it checks the draft's lines (server/core/sending.js). When
every condition matches, the draft is on the Pending tab. The list shows the
rule's name. The invoice page shows the note: 1 to 200 characters that tell
a person what to fix. Send all ready never sends it.
Send is a gate, like a CI check. At each press of Send, Send all ready or Approve, Flow runs every check again, pending rules included. A draft that someone edited, or whose job changed, and that now fails a check is not sent. It stays on the Pending tab with its problem. The other ticked drafts go, and the message counts the ones held. Approve on one invoice refuses a draft that fails a pending rule.
Send also holds a draft whose job matches an enabled skip rule. That includes
a draft made by hand, and a draft made before the rule existed. The draft
stays on Pending. The list shows the rule's name. The note is
Rule "<name>": not invoiced. Void this draft or change the rule.
The draft goes to Ready when the cause is fixed:
- A rule on
practitionerreads the job. Fix the practitioner in the work system. The next sync changes the job, and Flow writes the Reference again. - A rule on
referencereads the draft. Edit the Reference on the invoice page, or fix the job. - Turn off or change the rule.
Example: the work system writes "Not Listed" when a practice gives no
practitioner, so a Reference reads Not Listed - Ann - 13909.
| Name | If | Then |
|---|---|---|
| Practitioner not known | Practitioner is one of Not Listed, Unknown |
Pending: "Put the practitioner in the job." |
| Practitioner not on file | Customer is Example Practice, and Practitioner is not one of its practitioners | Pending: "Check the practitioner with the practice." |
| Bare name | Practitioner is one word | Pending: "Use the practitioner's full name." |
| Number in Reference | Reference has a number beside words | Pending: "Keep the patient's name or the client's reference number, not both." |
A client may send its own reference number in place of the patient's name,
so Dr Ann Lee - 21764 is a good Reference. "Has a number beside words"
reads each part between - and finds a number only next to words, as in
Coral Smith (OIQ-9902).
Non-chargeable rules
Non-chargeable work is work the lab does but never bills, for example work for its own clinics. The word is Xero's: Xero Projects calls such work non-chargeable. Xero has no invoice for it.
A non-chargeable rule never stops a draft. Flow drafts the job with its
lines and prices, so the lab keeps the value of its work. The draft is on
the Non-chargeable tab, never on Pending or Ready. Send, Send all ready and
Approve never send it: Not sent: Rule "<name>": non-chargeable. Flow keeps it and never sends it. A draft made before the rule moves to the tab by
itself. Turn the rule off, and its drafts are checked as usual again.
| Name | If | Then |
|---|---|---|
| Own clinics | Customer contains Example Group |
Non-chargeable |
Fields
| Field | Kind | Reads | Tests |
|---|---|---|---|
job_age_days |
number | Days from the job date to now | more than, at most, is, is not, is empty |
job_total |
money (dollars) | The job's total | more than, at most, is, is not |
job_status |
menu of the work system's statuses | The job's status there | is, is not, is one of, is empty |
practitioner |
text, with a menu of the practitioners on jobs | The job's practitioner, else the customer's default | is, is not, is one of, is not one of, contains, does not contain, starts with, does not start with, has a number, has a number beside words, is one word, is empty, is not empty |
reference |
text | The draft's Reference. Pending rules only | the same tests as practitioner |
customer |
menu of customers | The job's customer. Contains and does not contain read the customer's name | is, is not, is one of, contains, does not contain, is empty |
customer_billable |
yes or no | The customer's billable setting |
is |
line_item |
menu of price items | The item codes on the job's lines | has, does not have |
line_share |
price item and percent | That item's lines as a share of the other lines, to within 1 cent | is, is not |
Text and menu values match in any case and with spaces trimmed. A value that
Flow knows is picked from a menu, never typed: GET /api/invoicing/rule-fields
answers the fields, their tests, and the menus (statuses, customers, price
items, and the practitioners on jobs with their counts). Contains and starts
with take typed text. On Customer, contains and does not contain take typed
text and read the customer's name. An empty text matches only is empty, is
not and is not empty.
Starter rules
On the first read, Flow makes starter rules from the earlier fixed settings,
once for each organisation (invoicing_settings.rules_seeded). The list is in
docs/INVOICING_RULES.md. A lab can change, turn off or delete each one. A
deleted starter rule does not come back.
API
| Route | Who | Does |
|---|---|---|
GET /api/invoicing/rules |
everyone | The rules, in order |
GET /api/invoicing/rule-fields |
everyone | Fields, tests and menus |
POST /api/invoicing/rules |
owner, admin | A new rule { name, icon, enabled, conditions, action }. A pending rule's action is { kind: 'pending', note } |
PUT /api/invoicing/rules/:id |
owner, admin | Change some of a rule; the result must be a whole valid rule |
DELETE /api/invoicing/rules/:id |
owner, admin | Delete a rule |
An organisation has 100 rules at most. The table is invoice_rules
(migration 0011_invoice_rules.sql, add only; icon from 0024_rule_icon.sql).
Next
- Formula lines: a rule that adds a line to the draft, such as a rush charge of 20% of the other lines. It shows as an option in the invoice editor.
- More triggers than
job_ready.