Skip to content

The complete list · nothing held back

Everything it does. 162 of them.

This is not a highlights page. It is every capability in Mini PMS, grouped into nine areas, each with what it does and why that matters to somebody running buildings. It ends with the twelve things the product does not do yet, named plainly — because a gap you find three weeks after signing is a gap somebody sold you.

AvailableOccupiedReservedTurnover cleanOut of serviceMoney
162capabilities on this page
254named permissions behind them
46reports ready on the first day
518messages already written
12things it does not do yet, listed at the end

Six of the nine areas below carry a working instrument rather than a screenshot. Open the bed hierarchy, over-allocate a contract line for twenty beds and read the refusal, move a move-in date and watch the proration change, bounce a cheque, filter the 46 reports by role, and build a camp until it hits a package ceiling. They run on illustrative sample data and on the product’s real rules. The remaining three — operations, compliance and the portals — are drawn diagrams rather than live ones, and are labelled as such.

Area 1 of 9

Inventory and the bed map

Every bed on one screen, on any date you pick

A bed is the thing you sell, so a bed is the thing this system counts. Inventory nests four ways — unit, room, partition, bedspace — and the database itself refuses the two mistakes that cost real money: letting a room while a bed inside it is occupied, and letting the same bed to two people over overlapping dates. Build 480 beds from a pattern in one press, then run them from a bed map you can drag someone across and scrub forward to next Tuesday.

28API routes for properties, structure and spaces
8space statuses
29legal status moves between them
35permission keys across portfolios, properties, blocks, floors, units and spaces
179automated tests covering this area
9space types
6 of 9space types that count against your plan
4legal nesting shapes, at most 3 levels deep
3 triggers, 4 constraintsdatabase rules guarding the spaces table alone
12unit types, and 10 property types including labour camp and staff accommodation
7occupancy and inventory reports
13columns in the inventory workbook
60 minuteswindow to undo a bulk build
400 beds in under 500 msbed map render budget, measured in a real browser
150 daysspan of the bed map time scrubber
LiveProperty → unit → room → partition → bed — expand it yourself
  1. Unit
  2. L1 Room
  3. L2 Partition
  4. L3 Bedspace
  5. No L4

HINTRoom A01 is selected. Press “Let this room”, and watch it be refused.

None of that is screen logic. It is three lines of schema, and they hold whether the request arrives from this page, the API or a script somebody ran at midnight.

EXCLUDE
USING gist (space_id WITH =, period WITH &&)
one bed, one tenant, one night
TRIGGER
space_no_ancestor_or_descendant_overlap
and never the room above it as well
CHECK
nlevel(path) <= 3
room, partition, bedspace, and stop

All 18 capabilities · Inventory

Each one is built, tested and in the product today.

The whole estate, six levels deep

Group your properties (Dubai estate, Sharjah estate, one manager’s patch), then inside a property: blocks, floors, units, and inside a unit the rooms, partitions and beds. Every property carries the paperwork a Gulf operator actually holds — plot number, Makani number, title deed, DEWA premise number, emirate, handover date, timezone and currency.

You stop keeping the camp in one spreadsheet and the villas in another. A group can be created, renamed and deleted, and it refuses to be deleted while properties still sit in it — so nothing is ever orphaned by a stray click.

Beds nest three deep, and the database refuses a shape that makes no sense

A room can hold partitions or beds; a partition holds beds; a cabin holds beds. Nothing else is allowed, and nothing can go deeper than three. A bedspace that claims to hold two people is refused outright — if it holds two, it is a room. A space also cannot cross into another unit.

An import, a bulk build or somebody in a hurry cannot create a bed inside a bed, or a partition that belongs to a different flat. The shape of the camp on screen stays the shape of the camp on the ground.

A bed and the room around it can never both be let

If Room 101 is let whole to a contractor, its four beds are automatically marked blocked and cannot be sold. If any one bed is occupied, the room can no longer be let whole. Try it and you get a sentence naming exactly which space is in the way, not an error code.

This is the classic way an aggregator sells the same square metre twice and finds out when two crews turn up. It is checked in the application so you get a readable refusal, and again in the database so no import, no script and no emergency fix can get underneath it.

One bed, one person, one date range — and fifty simultaneous attempts still give one winner

Two allocations can never overlap on the same bed. A man moving out on the 31st and another moving in on the 31st is allowed; moving in on the 30th is refused. The rule extends up and down the room: a future booking on a bed blocks a future booking on the room above it, even though neither is occupied yet.

The check is made by the database index at the moment of writing, not by reading first and deciding after — so two agents allocating the last bed in the same second cannot both succeed. Your occupancy figure, your invoices and your bed roster all rest on this one rule holding.

Build a whole camp from a pattern

Describe the shape once — 2 blocks, 6 floors, 4 units a floor, each unit one room split into 2 partitions of 3 beds — and watch the resulting count update as you type. Press build and the blocks, floors, units, rooms, partitions and beds all appear in a single transaction. Nothing half-built is ever possible.

Laying out a 480-bed camp by hand is a week of typing and a hundred small mistakes. This is one screen and one press — and if the numbering would collide with codes already in the property it names the clash and asks you to change the pattern rather than failing halfway through.

Numbering that matches the building people actually walk into

Ground floor named your way, floors numbered from wherever you start, the 13th floor skipped when the building skips it, and unit codes from a pattern — {floor}{n:02} gives 101, 102, 103 on floor one. Rooms, partitions and beds are coded off the unit: 101-R1-A-B3. A per-floor multiplier prices the fifth floor at a premium in the same pass.

Door numbers are what people say out loud. Getting the 13th floor wrong once means every unit above it is mislabelled, and you find out when a resident cannot find his bed.

Your plan limit is checked before the build, and the build is undoable for an hour

Before anything is written the preview says it plainly: “Will create 480 spaces — your plan allows 750, 412 used → BLOCKED, need 142 more”, and names the add-on that fixes it. A build is stamped as one batch, and within 60 minutes you can undo the whole thing in one press. Pressing build twice returns the first batch rather than building 960 beds.

You learn you need a bigger plan before spending twenty minutes describing a camp. The undo is refused once any space in the batch has been reserved or occupied, so a camp that has been built and let cannot be deleted by accident. And because the count and the write happen together behind one lock, two people building at once cannot jointly slip past your limit.

The spreadsheet round trip, with a dry run that names every bad cell

Download a property’s inventory as an Excel workbook, edit it, upload it back. The download is the upload format, and a second sheet lists every legal value so nobody has to guess. Upload once to validate — it reports every problem with its row and column and writes nothing — then upload again to commit.

Real buildings you take on are irregular: the mezzanine studio, the room somebody converted to a store. This is the path for those. And because a partial import is worse than a rejected one, the write is all-or-nothing — you never have to work out which half went in.

The bed map — every bed in a property on one screen

One request draws the whole property: units, the rooms inside them, and the beds inside those, each tile coloured by status and carrying the occupant’s name. Hover or focus a bed and you get who is in it, who employs them, until when, and the gender policy on the space.

This is the screen a camp is run from. Rooms are drawn as frames around their beds rather than mixed in beside them, and a room with beds inside it reads “5 of 6 beds occupied” rather than showing a status of its own — because a room whose beds are let is not “Available”, whatever a status column says.

Scrub time and see what next Tuesday looks like

A slider runs from 30 days back to 120 days forward, with jump buttons for today, tomorrow, a week and a month. Drag it and the map redraws for that day, and a line beneath counts what changed against today: how many arriving, how many beds freeing up, how many changing over.

A bed map of this instant answers a question nobody asks. “How many beds are free on the 3rd, and which ones” is the question, and dragging forward until tiles start turning green answers it in five seconds. Dates you have already visited redraw instantly.

Move a man from one bed to another by dragging him — or by keyboard

Pick up an occupied bed and drop it on a free one. Before you let go, every bed that can take him is highlighted and every bed that cannot is greyed with its reason. The same move works without a mouse: select the bed, press the move button, choose a destination, press Enter. Escape cancels.

Re-bedding people is the most common thing that happens in a camp, and it used to mean opening the contract. The drop does not commit — it asks when the move takes effect and why, and keeps that reason on the record. The old allocation ends and the new one starts on that day, linked as one move.

The map refuses what it can see, and warns about what it cannot decide

Dropping onto a taken bed says who has it and until when. Onto a room rather than a bed, a bed out of service, a bed still under fit-out, a bed blocked by the room above it, or a bed in another property — each gets its own sentence. Separately, non-blocking warnings appear: the destination is a female-only bed and the source is not, or it is in turnover and has not been cleaned yet.

Watching a drag “work” for a moment and then snap back is how people stop trusting a screen. The refusals are decided from what is already on screen, so they are instant; the warnings deliberately do not block, because a camp boss filling the last free bed at midnight should not be stopped by a bed that has not been swept.

A tree explorer where the inventory is actually maintained

Expand a unit down to its rooms, partitions and beds. Add a space, rename one, change its rate, change its status, read its last 20 status changes with who made each and why, print its door code, and delete it. Each control appears only if you hold the permission that action needs.

A site supervisor can move a bed’s status but sees no edit button; a viewer is offered nothing to press. Deletion tells you which of the two refusals applies before you press — this space has spaces inside it, or somebody is in it. And a space’s type is fixed for life, because changing it would change what may nest inside it and what it costs against your plan.

Eight statuses, 29 legal moves, refusals written for a person

A space moves through under fit-out, available, reserved, occupied, notice period, turnover, out of service and blocked. The screen only ever offers moves the server will accept. Ask for an illegal one and you are told why in plain words — “an occupied space cannot be marked available directly; end the allocation, it goes to turnover, and becomes available once cleaned”.

One table of legal moves is shared by the screen and the server, so a button is never offered that then fails. Letting a room whole automatically blocks every bed inside it; releasing it brings them back to available — except the ones that were out of service, which stay out of service.

Taking a bed out of service needs a reason, and its own authority

A space cannot be marked out of service without saying why — the database rejects the row otherwise. And doing it needs a separate permission from ordinary status changes, one a leasing agent does not hold.

Out of service is the one status that removes a bed from your occupancy denominator, so a property at 70% becomes 100% by mothballing the empty beds. Splitting that permission means the person whose commission is measured on occupancy cannot quietly improve it. Three months later, “out of service” with no reason is a bed nobody dares re-let and nobody can explain.

Pricing a bed is a different permission from editing one — guarded at four doors

Correcting a bed’s label, its window, its floor: that is editing. Setting what it is worth is a separate authority, checked when creating a priced space, when repricing one, when a bulk build sets a rate across 400 beds, and when an uploaded workbook has its Base rate column filled in.

A control with a side entrance is not a control. Without the fourth check, the way to reprice a whole camp without the pricing permission would be to upload a spreadsheet. A workbook with the rate column left blank imports fine, so the ordinary round trip of fixing labels is unaffected.

A QR code on every door, and an A4 sheet to print them all

Each space gets a code that opens a pre-filled report-a-problem form for that exact bed or room — no sign-in needed. Print them for a whole property or a single unit, three to a row at 50mm, sized to scan from a metre away. Codes go on the beds and rooms people stand in front of, not on partition frames.

A resident with a broken air conditioner points a phone at the sticker and the fault arrives already tagged to the right bed, in the right unit, in the right property. The code carries the space code written on the door, so someone can type it when the camera will not focus.

Find every free bed for a date range, and one occupancy number everywhere

Ask for beds free between two dates, filtered by property, space type, gender policy and a maximum rate, returned grouped by unit so forty men from one contractor land in the same block. Separately, the tree, the map, the dashboard and the reports all compute occupancy from one rule and show their working: how many spaces are subdivided containers, how many are ancillary, how many are excluded, how many are genuinely lettable.

The search excludes beds blocked by something let above or below them and beds held against a live quotation — a bed the system would refuse at the end of the form never appears at the start of it. And a four-bed room is four sellable things, not five: counting the room too puts stock in your denominator that nobody could ever fill.

What this area does not do

  • No photographs of a bed or a room. The table exists with a storage key and a cover flag; no uploader and no gallery are wired to it.
  • The bed map is a grid, not a scaled floor plan. There are no coordinates on a space and no building plan to upload — rooms are laid out in reading order.
  • Blocks and floors can be created but not renamed or deleted. Units have the full set; blocks and floors do not.
  • No multi-select bulk actions, and no dragging a bed into a different partition. The tree is expand-and-edit.
Area 2 of 9

Leasing, contracts, allocations, move-in and move-out

A bed can only be sold once. The database says so.

A contract here says “20 beds in Camp A”, and an allocation says which twenty and who is in them — two different facts, kept apart, because the company signs the contract and its workers rotate through the beds. Every rule that could cost you money is enforced by PostgreSQL at the moment of writing, not by a check the software does beforehand: of fifty agents racing for the last bed, exactly one succeeds and the other forty-nine are told who has it and from when. From first enquiry to keys handed over there are 48 endpoints and 52 named refusals, each one a sentence an operator can act on rather than a database error.

48API operations covering contracts, allocations, moves and the CRM pipeline
52distinct named refusals, each a sentence an operator can act on
12contract states
40permission keys governing this area alone
6integrity rules inside PostgreSQL stopping a bed being sold twice
12database tables behind leasing and the pipeline
5conditions checked before a contract may go live — all reported at once
10checklist items gating a move-in and a move-out (5 each)
500placements accepted in a single all-or-nothing bulk assignment
1database query to draw a whole property’s bed map, whatever its size
90 / 30 / 7days before expiry an annual, monthly or short-term contract enters the renewal radar
14days a quotation stays valid before its held beds are released
7quotation states, including a superseded version that stays readable
3kinds of change a signed contract can take, each with its own approval
LiveA pooled line for twenty beds, and the allocations that fill it
CL-1042Pooled

20 beds

ASGC Group · SUB-2026-0117

Rate
AED 750/bed/mo
Term
1 Jan 2026 — 31 Dec 2026
Billed monthly
AED 15,000

That figure is the same at nought beds allocated and at twenty. A pooled line is billed on its quantity, so the invoice never waits for the site office to decide who sleeps where.

Allocated against this line0/20

20 still to place. Click a mint bed.

line_mode
POOLED
allocations moved
0
contract_lines rows changed
0
variations raised
0

Camp A · blocks A and B · 48 beds

FreeOn this lineHeld elsewhereOut of service
A-01
A-02
A-03
A-04
B-01
B-02
B-03
B-04

Point at any bed. Click a mint one to place a man against the line.

200GET /contract-lines/CL-1042/allocations → 0 rows
  1. Nothing has happened yet. Every allocation, refusal and swap below is written to the audit trail with the actor, the reason and the row it touched.

All 18 capabilities · Leasing

Each one is built, tested and in the product today.

Named beds or a headcount — both are real contract lines

A contract line is either a specific bed (P-004-U2-R1-B3) or a pooled promise — “20 beds in Camp A”, scoped to a property, block or unit. Pooled lines are filled in later, one arrival at a time, and the screen shows how many of the twenty are placed and how many are still to come.

Corporate camp deals are never written bed-by-bed. You sell a headcount in September and place the men in October, and the contract has to be able to say that without pretending to name beds it has not chosen yet.

The same bed cannot be let to two people

Overlapping tenancies on one bed are refused by the database itself, at the instant of writing. The refusal names who already has it and from what date — “Ravi Kumar has it from 2026-03-01 to 2026-08-31” — rather than reporting an error code.

This is the number every other figure rests on. If two men are booked into one bed, your occupancy, your invoices and your camp roll call are all wrong, and you find out when the second man arrives at the gate at 11pm.

A room and the beds inside it can never both be let

If a room has been partitioned into four beds, the room itself stops being sellable while any bed in it is taken — and the reverse. The check walks the whole tree, so a villa, its rooms, its partitions and its beds are all covered, over any overlapping dates including future bookings.

Subdividing is how you make the margin, and it is also how you accidentally sell the same square metre twice — once whole to a company and once bed-by-bed to individuals. Nobody notices until both parties turn up.

Twenty beds means twenty, not twenty-one

A pooled line counts its own placements. The twenty-first allocation against a line for twenty is refused, and the message says how many the line is for, how many are already placed, and that raising it needs a variation.

Over-allocating a pooled line puts a man in a bed nothing is paying for. It looks like occupancy on the board and like a gap in the revenue at month end.

A busload placed in one go — or none of them

Up to 500 placements in a single request. If assignment 27 of 40 fails, nothing is written and the answer says which row failed and why, so you fix that one and resend.

Forty men off one bus with thirty-eight placed is worse than none placed, because nobody can tell which two have nowhere to sleep. This is the daily reality of camp operations.

The bed map — and the bed map for next Tuesday

A whole property drawn as rooms and beds, colour-coded by status, showing occupant name, employer and tenancy end on each tile. A time scrubber moves the map to any date, and the panel below lists exactly what changed between today and that day — who arrives, who leaves, which beds free up.

“What does the camp look like on the 15th” is the question you actually run the operation on, and a status column can never answer it. A bed occupied until August is a bed you can sell for September.

Move a man to another bed by dragging him

Drag an occupant’s tile onto an empty bed — or select the bed and choose the destination with the keyboard. The old tenancy ends and the new one begins as one act, both tagged with the same swap reference so the history reads as one move. Obviously impossible moves (bed taken, out of service, it is a room not a bed, wrong property) are refused before the drop; gender-policy and uncleaned-bed cases warn but do not block.

Shuffling men between beds is the most common thing a camp boss does all week, and it must never leave somebody in two beds or in neither.

“Find me 18 beds, male, from 15 Sep, under AED 750”

A search over free inventory for a date range, filterable by property, space type, gender policy and maximum rate, returning the free beds grouped by unit so you can keep one employer’s men together. It excludes beds already held against somebody else’s live quotation.

Availability is a question about a period, not a status. And offering a bed that a colleague is holding against a quote produces a refusal at the end of a form the agent has already filled in.

Twelve contract states, and only the legal moves are offered

Draft, awaiting approval, approved, active, expiring, notice given, suspended, renewed, expired, terminated, cancelled, rejected. The server returns which moves are legal from where the contract stands now, so the screen shows those buttons and no others. Each move costs its own permission: a leasing agent can write and submit a contract and cannot approve, activate, cancel or terminate it. Terminating without a written reason is refused.

The person who negotiates the deal should not be the person who signs it off. That separation only means something if the software enforces it rather than printing it in a policy document.

A contract cannot go live until it is actually ready

Five conditions are checked before activation, and all failures are reported together, not one at a time: at least one line, somebody has signed it (or e-signature is enabled), the deposit has been invoiced in full, every named bed is in a lettable state, and the activation stays inside your plan allowance. Activating promotes the planned allocations and marks their beds reserved.

A contract that goes live without them bills a customer for a bed they were never given — and chasing a deposit after move-in is chasing a deposit that never arrives.

A signed contract is never edited — it is varied

Once live, commercial terms change through a variation: add a line, change a line’s quantity, rate or discount, or move the end date. The variation records what the terms are now and what they would become, plus the rent total before and after, and goes for approval. Applying it is a separate, deliberate click by somebody who holds the approver’s permission — and if the contract moved underneath it in the meantime, applying is refused rather than silently applying stale figures.

Editing a live contract in place makes the invoice history stop matching the agreement that produced it, which is the argument you cannot win with a customer six months later.

A renewal is a new contract, not an edit

Renewing writes a fresh draft pointing at the original, copies every live line with an escalation percentage applied to the rate, starts it the day after the old one ended, and marks the original renewed. The escalation is computed in whole fils, never a rounded decimal.

The old contract stays exactly as it was signed, so the year you charged AED 780 is still on record when you renew at AED 820. And nothing bills until the new draft is approved and activated — four clicks away, not one.

A renewal radar with a horizon per contract type

Everything ending inside a chosen window, ordered by end date, showing days remaining, whether it is inside its own renewal horizon and whether it renews automatically. The horizon is 90 days for an annual contract, 30 for monthly and 7 for short-term.

A 300-bed camp contract you notice 10 days before expiry is a contract you renew on the customer’s terms.

The move-in checklist is a gate, not a form

A move-in is scheduled against an allocation and completes only when all five items are true — keys issued, induction done, bedding issued, ID verified, meter reading taken. Anything outstanding is named in the refusal. Completing it is what makes the allocation active and the bed occupied.

An unanswered “keys issued” is a man in a room he cannot lock. An unanswered “ID verified” is an undocumented occupant in your camp on the morning of an inspection.

Move-out: notice, inspection, priced damages, and the clean that follows

Scheduling a move-out puts the bed into notice period so it shows up in “coming available” before it is empty. Completing it needs its own five-item checklist — keys returned, inspection done, bedding returned, meter read, access revoked — takes a list of damage items each with an estimated cost, totals them in whole fils, ends the tenancy, puts the bed into turnover, and automatically raises the turnover clean that brings it back to available.

A bed that goes empty with no clean scheduled is a bed nobody can let and nobody is looking at. The damage total is computed once so two people reading the same move-out agree what is owed.

The day’s move diary, with what is overdue

Arrivals and departures over a date range, each showing the bed, the occupant, the contract reference and the customer — plus a count of move-ins and move-outs that were scheduled in the past and never completed.

A move-out scheduled for last Tuesday that nobody closed is a bed the system thinks is taken and the camp boss knows is empty. That gap is pure lost revenue and it is invisible without this number.

Enquiry, viewing, quotation — the funnel before the contract

Leads with a source (walk-in, referral, portal, agent, WhatsApp, campaign, website, phone), a requirement — how many beds, what type, needed from, budget — and an owner; six stages that only ever move forwards and never reopen once won. Viewings booked against a lead, completed with an outcome, or closed as a no-show, cancelled or rescheduled. A lost lead must say why.

“How many enquiries became contracts” is only answerable if the stages are caused by the work rather than clicked afterwards — booking a viewing is what moves the lead, not somebody remembering to.

Quotations that hold real beds — and give them back on their own

A multi-line offer with a validity date, 14 days by default, which can take specific beds off the market while the customer decides. Send it, track that it was opened and how often, decline it with a reason, or revise it — a revision becomes version 2, supersedes version 1 (which stays readable) and carries the held beds across without ever dropping them back on the market. A nightly job expires lapsed quotes and releases their beds, returning each bed to the state it was in before, not blindly to available.

A hold another agent can allocate over is not a hold, it is a note. And a bed held against a quote nobody is chasing is a bed you are not selling — which is why the release runs on a clock rather than on somebody’s memory.

What this area does not do

  • A sublease never moves itself into the expiring state, and the auto-renew flag renews nothing. The renewal radar is a list you open; a renewal is always a deliberate act.
  • There is no upload of the signed contract PDF. A signature is recorded as a name, a date and a method.
  • Ejari and Tawtheeq registration are columns on the contract. The update endpoint does not accept them, so the field on the edit form is discarded.
  • Pooled beds are not auto-picked. The system caps the quantity and refuses the overflow; choosing which beds fill the line is a human act.
Area 3 of 9

Invoicing, proration, deposits, cheques, credit notes, collections

Rent invoiced, cheques banked, arrears chased

Every amount in Mini PMS is a whole number of fils, and every invoice line carries the sentence that explains it — which month, how many days of it, which tax rate and which rule chose that rate. Once an invoice is issued the database itself refuses to change it, so corrections are credit notes and the trail survives. And because a year’s rent in this market arrives as twelve pieces of paper, the post-dated cheque is a first-class record with its own eleven-state lifecycle, its own calendar and its own custody report rather than a payment with a date on it.

45finance endpoints across invoicing, payments, cheques, deposits and collections
43separate permission keys governing money
48rules the database itself enforces on money
11cheque states, from received to recovered
351unit tests on the money and billing engines alone
5bounce reasons, each driving a different consequence
6deposit movement types in the liability ledger
7invoice states
4ways a part month can be priced
7billing cycles, on either of 2 period anchors
5rungs on the chasing ladder, at days 1, 5, 10, 20 and 30
5aging buckets
8seeded UAE tax rules over 4 tax codes
7payment methods, 3 allocation strategies
5credit-note reason codes
18account mapping keys in the journal chart of accounts
14financial reports in the report catalogue
74end-to-end API tests over invoicing and payments
2nightly jobs that touch money, at 02:00 and 07:30 Gulf time
7collection case stages, and 4 promise outcomes
6customer ledger entry types, append-only
LiveAn invoice assembling itself — move the move-in date, change the proration

Step 1 of 8. An empty draft. Running total AED 0.00.

Tax invoice

INV-2026-1043

Gulf Falcon Contracting LLC

1–31 March 2026 · AED

Draft
Invoice lines, added one step at a time
DescriptionBasisAmount
No lines yet. Press Next.
LiveA post-dated cheque, from the safe to cleared — or bounced, and what follows

C-2026-0184 · Gulf Contracting LLC

Al Quoz Camp 1 · 240 of 384 beds · 01 Jan – 31 Dec 2026 · AED 2,160,000 for the year, handed over as twelve cheques on the day of signing.

Register as atDay one — 28 Dec 2025

Day one — 28 Dec 2025 12 in the safe, 0 at the bank, 0 cleared.

Click any cheque, or use the arrow keys.
Emirates NBD · Al Quoz004417PayGulf Space Management LLCThe sum ofOne hundred and eighty thousand dirhams onlyAED180,000Gulf Contracting LLC01 Jan 2026
ReceivedINV-2026-0301
  1. Received and logged against the contract28 Dec 2025
Where it physically is
Safe A · Box 3 · Al Quoz office
Drawn on
Emirates NBD · A/C Gulf Contracting LLC · 0198765432
Against
C-2026-0184 line 1 — 240 pooled beds, Al Quoz Camp 1
Scan
Both faces on file

What is physically in the safe

SAFE-A · BOX 3 · AL QUOZ
In the safe, presentable
12chequesAED 2,160,000
Lodged at the bank
0chequesAED 0
Cleared to date
0chequesAED 0
Returned or superseded, held on file
0instrumentsAED 0

12 instruments issued against this contract, 12 accounted for, counted as at 28 Dec 2025. A returned cheque is still a piece of paper in a drawer — it is worth nothing and it is still yours to hold, which is why it is counted and not deleted. A schedule of due dates cannot answer this question at all.

Against these sit four outbound cheques to the landlord — AED 1,440,000 for the whole property, all 384 beds, written the same week. This one contract of 240 beds covers every dirham of it and AED 720,000 besides. That is the two-sided lease: one asset, two chequebooks, and a spread rather than a rent roll.

All 18 capabilities · Finance

Each one is built, tested and in the product today.

Rent invoices raised automatically, and never twice for the same month

A nightly run reads every live contract, works out which periods are now due within the next few days, and raises the invoices — one per customer per period per property. You can run a preview first that shows exactly what would be billed, contract by contract, before a single customer sees a number.

A camp with 400 contracts is one button instead of a week of typing. And if the job crashes at 3am and you re-run it, nobody gets billed twice: the run reports how many lines it skipped because they were already there, which is the figure that proves it is safe to press again.

Part months priced four different ways, and the line says which

A resident moving in on the 18th does not owe a full month. The contract chooses how that is priced: by the real days in the real month, by a flat thirty-day month, as a whole period anyway, or free. Periods themselves run either on the calendar (1st to month end) or on the contract’s own anniversary, across seven cycles from daily to annual.

Every disputed invoice in this business is a part month. The line prints “11 of 28 days (2026-02-18 to 2026-02-28)” next to the amount, so the argument ends at the invoice instead of in a meeting.

Tax decided line by line, from a rulebook you edit

One invoice can mix exempt residential rent with standard-rated wifi and an out-of-scope deposit, so tax is resolved per line, against the date of supply, and each line stores the sentence explaining the answer. The rates and rules are editable rows in your own database, not code: a UAE starter set is seeded — deposits out of scope, first supply of new residential zero-rated, serviced accommodation standard, bare land exempt, ordinary residential exempt, services standard — with priorities you can insert between.

A single rate applied to a whole invoice is wrong for at least one line on it, and you only find out at an audit. When a rate moves, somebody edits a row rather than waiting for a release. And when a customer asks why there is 5% on one row and none on the next, the answer is already printed on the document.

A tax invoice a customer can pay from, with a scannable code and a per-person annexe

The printable document carries the full compliant field set — supplier and customer with tax registration numbers, invoice number, issue date, date of supply, per-line quantity, rate, taxable amount, tax rate and tax amount, a tax summary grouped by code, and totals. It renders in English or Arabic, right-to-left, with a real scannable QR drawn into the page. Corporate invoices get an annexe grouping the detail by employee and by bed.

A corporate client with 40 beds does not reconcile a single total — they reconcile employee by employee. The annexe is what gets the invoice approved rather than returned. The QR means a phone reads the reference and amount instead of somebody re-typing it. And exempt and zero-rated stay separate rows in the summary even though both read 0%, because a tax return distinguishes them absolutely.

Once an invoice is issued, nobody can change it — including us

A draft can be edited freely. The moment it is issued, the amounts, the customer, the number and the dates are frozen: the database rejects the change, not just the screen. Voiding is possible while nothing has been paid, needs a written reason, and keeps the invoice number while writing an equal and opposite ledger entry rather than deleting anything.

The customer has a copy. A gap in your invoice numbering is the first thing a tax authority asks about, and an invoice that quietly changed after it was sent is a document nobody can defend. This is what makes the ledger worth keeping.

Credit notes: the only way to correct an issued invoice

Corrections carry a reason code — correction, goodwill, cancellation, write-off or overbilling — and a written reason. The amount is capped at what is left uncredited, the tax inside it follows the invoice’s own proportion unless you state otherwise, and the invoice flips to fully credited when nothing is left. Two notes raised at the same instant cannot jointly exceed the invoice.

You keep an audit trail instead of an eraser. The cap is enforced with a row lock at the database, so a race between two clerks cannot over-credit an invoice — which is the failure that only shows up at year end. Every credit note gets its own number in its own series.

Every figure is a whole fils, and a split never loses one

Money is stored as whole minor units with a currency beside it — never a decimal that drifts. Anything divided (a shared cleaning bill across six rooms, a balance across three instalments) is split so the parts add back to exactly what went in, with the odd fils handed out largest-remainder. Adding dirhams to dollars throws rather than guessing a rate.

A payment plan for AED 10,000 over three months is 3,333.34 + 3,333.33 + 3,333.33 — not three times 3,333.33 with a fils nobody can explain. Rounding errors in money are not small, they are unexplainable, and an invoice line you cannot explain is a dispute.

Money in, spread across invoices the way you choose — with a receipt, and reversible

Record a receipt in cash, bank transfer, cheque, card, online, adjustment or deposit transfer, and say how to apply it: oldest due first, a named invoice, or your own split. Anything left over is held as customer credit against the next invoice rather than forced onto this one, and can be applied later from the row. The receipt prints which invoices it settled, how much went to each, and what each still owes today. A reversal unwinds every allocation, restores each invoice’s balance and status, and keeps the receipt number.

Which invoices a cheque settles decides whose chasing letter goes out on Monday. Over-applying makes an invoice look over-paid and corrupts your whole aging report. “I paid that” is settled by a receipt naming the invoice. And reversing rather than deleting leaves a trail instead of a hole in the receipt series.

Import your bank statement and match it to invoices

Upload the CSV your bank exports. The system reads the header, proposes which column is the date, the credit, the reference; you correct it. It then scores every incoming credit against your open invoices — the reference naming an invoice number is the strongest signal, an exact amount match next, proximity to the due date after that — and shows the top candidates with a reason for each. Nothing is created until you confirm, and rows it could not read come back with their line numbers rather than being skipped.

Different banks write different files: one says Transaction Date, another says Value Date, another splits credit and debit into two columns. Guessing gets it wrong on the second bank. An automatic match that is wrong moves money against the wrong customer and is discovered a month later by the customer you chased for it — so the operator always confirms. Re-importing the same statement does not pay anything twice, because the bank’s own reference is the identity.

A post-dated cheque register, with a calendar and a custody report

A cheque is its own record, not a payment with a date. It moves through received, in hand, deposited, then cleared or bounced — and from bounced to replaced, legal action or recovered; only the legal next moves are ever offered. Clearing an incoming cheque automatically becomes a payment and settles the customer’s invoices. A month calendar shows each day’s cheques due for presentation, inbound against outbound, with the net; a custody view groups every cheque the system believes you physically hold by where it says it is, and names the ones with no recorded location. Handing one to the bank runner is logged as a move of paper, not a change of status.

A year’s rent arriving as twelve pieces of paper is normal here, and knowing which of them clears next week is the cash-flow forecast. Before an audit somebody has to stand in front of the safe and count — the custody report is that list. And the register stops the fraud the separation exists to prevent: banking a cheque and marking it bounced are different permissions, so one person cannot do both.

What happens the moment a cheque bounces

You record why it bounced — insufficient funds, signature mismatch, stopped payment, closed account, or technical. That single answer drives everything: the customer’s risk band worsens (and never improves from a bounce), a bounce-fee invoice is raised and issued, a collection case opens, any payment the cheque had already produced is reversed, and finance, the leasing owner and the property manager are flagged. A technical bounce — a wrong date, a missing endorsement, usually your own error — carries no fee.

Four of those five things get forgotten when a bounce is handled by hand, and the one most often forgotten is reversing the payment, which leaves an invoice showing as paid when the money is not there. Charging a fee for your own clerical mistake is how a small error becomes a lost client.

A deposit ledger that is a liability, can never go negative, and closes at zero

Deposits sit in their own append-only ledger, entirely separate from revenue — held, deducted, refunded, forfeited, transferred in, transferred out, each with a reason. The balance can never fall below what is actually held, and the system tells you how much you may refund rather than just refusing. Carrying a deposit to a renewal contract is two movements netting to nothing, so no cash appears to move. At move-out, enter the rent to the leaving date, utilities, damages and anything else owed: the deposit covers what it can, the remainder is refunded, and the ledger closes at exactly zero — with anything still owed becoming an ordinary debt rather than a negative deposit.

A deposit is the customer’s money, held. It never belongs in a P&L, and a negative balance means the business has spent something it was only holding. Netting a settlement instead of exhausting it leaves a balance floating against a contract that ended, and those become disputes two years later. Taking money in, deducting, refunding and forfeiting are four different acts needing four different authorities.

Arrears aged into buckets, a ladder that fires once, and capped late fees

Open invoices fall into current, 1–30, 31–60, 61–90 and 90+ days, per customer and in total, with the oldest debt in days. The ladder then works: a friendly reminder at day 1, a formal reminder plus late fee at day 5, escalation to the corporate contact and a task for the property manager at day 10, a final notice with a service-restriction warning at day 20, a collection case at day 30. Each rung fires once per invoice however many times the job runs, and only ever moves forward. Late fees can be a fixed amount, a percentage of the overdue balance or an amount per day — each with a grace period and a hard ceiling, and each raised as its own invoice. Waiving one is a credit note with a reason. You can rehearse the whole run before anybody is chased.

An invoice left alone for a month should produce one escalation, not five emails in a minute after a weekend outage — and never a final notice followed by a friendly reminder. A per-day fee on an invoice forgotten for two years is a number nobody collects and any court strikes out, so the cap is what makes it enforceable. The fee on its own document is chaseable and creditable separately, because the overdue invoice itself can no longer be touched.

Collection cases, promises to pay, and plans that stop the chasing

One case per customer, not one per invoice. Record what they promised and by when; when the date passes the system judges it on money actually received since the promise was made, marks it kept, part kept or broken, and escalates the case on a broken one. An overdue balance can be turned into instalments that add back to the balance exactly — and while a plan is live, normal chasing is suspended.

A customer with four overdue invoices has one problem; four cases is four people ringing them. A promise nobody closes is a promise that was broken, so it is settled against the bank rather than against somebody’s memory. And chasing a customer who is keeping to an agreed plan costs more than the invoice.

A customer’s ledger, with a running balance and its own aging

Every invoice, credit note, payment and adjustment for one customer in date order, with the balance after each, plus that customer’s debt split across the aging buckets. The ledger cannot be edited or deleted — a correction is a new entry with the opposite sign — and an entry’s currency must match the document it belongs to.

This is what you send when a corporate client’s finance department queries the account. A ledger that can be edited is not a ledger; the reversing entry is the trail an auditor follows. The customer’s credit limit on that same screen is withheld from anyone without the specific permission to see it.

An accounting journal export that will not download unless it balances

Every invoice, credit note, payment, expense and deposit movement in a period, restated as debits and credits against account codes you control. Eighteen mapping keys — receivables, bank, deposits held, VAT payable, rental income, utility recharges, late fees, head lease rent and so on — each with a default code you change to match your accountant’s chart. The two sums and their difference are shown, and the file is only offered when they agree.

This is the one thing that leaves the product, and it has to import cleanly into whatever ledger you actually keep your books in. An unbalanced batch does not fail on import — it posts to suspense, and somebody finds it at year end.

Approvals on the decisions that give money away

A discount over your threshold, a write-off over your limit, and postponing a cheque all stop and ask somebody, with the context printed for the approver. The write-off threshold is measured against the running total already forgiven on that invoice, not against this note alone. Thresholds are rules you write in plain conditions like “amount_pct > 10”, against a named approving role.

AED 8,000 written off as two 4,000 halves passed both times before this was measured — a threshold you can step around by splitting the paperwork is a rounding instruction, not a control. Correcting a genuine billing error is deliberately not gated: refusing to fix a mistake until a director is free is how a customer gets chased for a charge everybody agrees was wrong.

Who is allowed to do what, split finely enough to matter

Preparing an invoice and issuing it are different permissions. Recording a receipt and deciding which invoices it settles are different. Taking a deposit, deducting from it, refunding it and forfeiting it are four. Banking a cheque, clearing it and marking it bounced are three. Rehearsing the chasing run costs less authority than actually running it. A site supervisor is sent no money figures at all — the server withholds them rather than the screen hiding them.

Separation of duties is the whole of financial control. A screen can hide a field; it cannot un-send it — so the figures a role must not see never leave the server, and the buttons a role cannot use are never offered. And the person deciding an approval must hold the authority, not merely sit at the desk named in the rule.

What this area does not do

  • Nothing is emailed. There is no endpoint that sends an invoice, a statement or a chasing notice — the ladder works out the step, records it, and reports that it was not dispatched.
  • Tenants cannot pay online. Card and online record that money arrived somewhere else; the card gateway that exists is for your own subscription to Mini PMS.
  • Documents are print-ready HTML, not stored PDF files, and there is no cash refund of an over-paid balance — only a deposit can be refunded.
  • Bank import reads CSV only, and clearing an outbound cheque does not yet settle a landlord or vendor payable.
Area 4 of 9

Work orders, PPM, assets, utilities, housekeeping, mess, stock, HSE

From the fire-exit check to the caterer’s invoice

This is the part of the job that happens on site: the AC that failed in C-204, the water tank due for cleaning, the 430 men who were meant to eat lunch, the mattress that left the store and never came back. Mini PMS runs all of it against one clock and one set of records, and it refuses the shortcuts — a job cannot be closed without evidence, the man who did the work cannot sign it off, and a bed a resident left does not become lettable again until somebody has passed a turnover checklist on it. The site supervisor gets the whole operation and none of the money, because the same record is sent two different ways.

80API endpoints in the operations module
23database tables behind this area
35permissions enforced across these endpoints
10states a work order can be in
66database rules that refuse bad operations data
4priorities, each with a response and a fix target
80%of the window at which a breach warning fires
14standard maintenance plans seeded onto a property in one click
12escalation situations, holding 32 rungs between them
9of those situations the hourly sweep actually looks for
7database triggers doing work no application code can skip
21uniqueness rules stopping duplicate references and repeated jobs
10reports covering this area
2inspection checklists shipped, holding 30 items between them
6items on the default turnover checklist that releases a bed
5%variance against the caterer’s invoice that raises an alert
5kinds of stock movement across 8 categories
60days of housekeeping rota one generation run will produce
14days ahead planned maintenance jobs are raised by default
4screens in the operator app covering this area
DiagramSeven ways a job arrives, and the two clocks it starts
Resident portalraised on his own phoneA staff memberanyone in the operator appPhone call at the desklogged by whoever answeredQR sticker on the doorno sign-in neededA failed inspectionone job per failed itemPlanned maintenanceraised 14 days aheadIncident follow-upthe corrective actionONE RECORDWO-2026-00418Category · priority · subjectNaming a bed fills in its roomand its building automatically.A job attached to nothing is refused.TWO CLOCKSFirst responseResolutionBoth stop on hold or awaiting parts.Warning at 80%. Breach past 100%.

All 18 capabilities · Operations

Each one is built, tested and in the product today.

Work orders from every direction a problem arrives

A job can start from the resident’s portal, a staff member, a phone call logged at the desk, a QR sticker on the room door, a failed inspection, a planned-maintenance schedule or an incident follow-up. Each gets a reference like WO-2026-00418, a category, a priority and a subject — a property, a unit, a bed or one named machine. Naming a bed fills in its room and its building automatically.

The complaints you never hear about are the ones nobody could be bothered to report. Seven ways in means the fault reaches the board instead of the corridor — and every job is attached to something findable when the technician gets to site, because a work order attached to nothing is refused outright.

An SLA clock that stops when the part is on order

Every job carries two clocks — one for first response, one for the fix — set from its priority when it is raised. Both stop while the job is on hold or awaiting parts, and pausing requires a written reason. A warning fires at 80% of the window; past 100% it is a breach. The targets are copied onto the job at the moment it is raised.

A supplier who takes four days is not your maintenance team failing. If the clock kept running, the lesson every technician would learn is to never record a hold — and you would lose the one signal telling you where the delays actually are. Copying the target at creation means changing your targets tomorrow does not silently re-judge last month’s jobs.

A job cannot be closed without evidence, or signed off by the man who did it

Marking a job done requires a written description of what was actually done and the labour hours; a P1 or P2 also needs at least one photo before and one after; and if the technician needed access to an occupied room, somebody signs that the room was left in order. Every missing item is listed at once. Verification is then a separate step with its own permission, refused if the verifier is the person the job was assigned to.

“Fixed” is not a maintenance history, and it is not a defence when the fault returns or a resident disputes the damage charge. Listing every gap in one go matters: a technician told one problem at a time on a phone in a corridor gives up and finds a way round the check. And verification is the second pair of eyes — so it is refused by the database as well as the application, and survives anybody rewriting the code.

Tenant damage becomes an invoice, with the evidence on the line

A completed job can be charged to a customer. The system raises an ad-hoc invoice with a damage line, and the line itself records the job reference, the completion date and how many before and after photos are on file. It cannot be charged twice, and it cannot be charged before the work was done.

An invoice for damage that does not say what the damage was gets disputed, and disputes are how recharges quietly stop being collected. Because it goes through the normal invoicing route, it picks up the same tax treatment and the same locked-once-issued rule as an invoice somebody typed by hand.

The camp boss runs the site and never sees the money

A site supervisor gets the full job list, the asset register, the room handover sheet and the repair history — and the cost figures are removed from the response before it leaves the server, not merely hidden by the screen. Where a figure is withheld the record says so, so the screen prints “hidden” rather than a dash that reads as “free”.

A screen can hide a column; it cannot un-send one. Anybody with the login and a browser console could read what a repair cost. And the argument survives without the number: “it has been worked on four times” and “repairs have now cost more than it did to buy” are both actionable, and neither needs a figure attached.

Planned maintenance on a recurrence rule, generated ahead of time

A plan says what to do, where — a property, a unit, a space type, an asset category or one named machine — how often as a recurrence rule such as “every third month”, who does it, what it should cost and how many days ahead to raise the job, fourteen by default. A nightly run raises what is coming due; running it twice raises nothing twice, and it reports how many it skipped. You can preview what a plan will produce between any two dates, and the product ships fourteen standard camp plans you can seed onto a property in one click.

Planned maintenance fails because it lives in somebody’s spreadsheet. Turning it into real jobs, fourteen days ahead, makes it compete for attention with the emergencies. The seeded plans are the ones this asset class actually needs — pest control monthly for a camp and quarterly elsewhere, a gas cylinder check a villa does not need.

A compliance grid you can hand to an inspector

Plans down the side, dates across the top, every cell reading done, overdue or upcoming with the job reference and completion date behind it, and totals at the bottom. A visit that was due and has not happened shows as overdue whether or not a job was ever raised for it.

An inspector asking when the fire extinguishers were last checked wants a grid, not a search. A cell that says overdue is more useful than one that is quietly empty — a job that was generated and never started is not evidence of anything.

An asset register that explains itself

Every AC unit, bed, mattress, wardrobe, water heater, fridge, extinguisher and router — tagged, placed in a space, with purchase cost, warranty expiry, expected life and condition. Every move is written down permanently: from where, to where, why, by whom. From it you get a printable handover list for a room at move-in, a per-asset history, and straight-line depreciation and book value in the asset report.

Three things stop working without the movement log: a maintenance history nobody can trust, a handover list that is somebody’s handwriting, and a missing mattress with nobody to charge it to. The history also makes the replacement case in words — past its expected life, worked on four or more times, or repaired for more than it cost to buy.

Contractors scored from their own work, not from opinion

Every vendor carries trade categories, a trade licence and insurance expiry, and agreed response and fix windows. The scorecard is computed from the jobs they did: how many, how many completed, average response time, average completion time, and reopen rate — a job sent back after being marked complete. A contractor with no completed work gets no score at all rather than a flattering zero.

A score somebody types is a score somebody negotiates. The reopen rate in particular is the most telling number about a contractor and the one nobody tracks by hand. Jobs you deleted stop counting toward their record — which sounds obvious and is exactly the thing that usually leaks.

Meters, tariffs, and electricity that reaches the P&L

Meters for electricity, water, gas, internet and chiller, at property, unit or bed level. Record a reading and the consumption since the last one is worked out by the database. Give the meter a tariff and a standing charge and that consumption becomes a real utilities expense against the property in the same moment. A reading lower than the last one is refused with an explanation — unless you have told the meter where it rolls over.

For a labour camp, electricity is often the largest cost after rent. Until a reading turns into money, the property’s profit figure is a fiction. And guessing between “the reading is wrong” and “the meter rolled over” is how somebody gets billed for another man’s month, so the product refuses to guess. Readings already costed keep the rate they were costed at when the tariff changes.

Housekeeping rounds that put a bed back on the market

Schedules per property and space type — daily, weekly, fortnightly or monthly — expand into dated task lists up to sixty days ahead, skipping beds that are out of service. A cleaner answers a checklist; a turnover clean that passes every item moves the bed from turnover back to available and records the status change. Failing one item leaves it unlettable. Tasks can also be raised by hand, and a second person can score any finished task from 0 to 100.

This is the quiet one that costs the most money. A bed a resident leaves goes into turnover, and if nothing ever takes it out again it is permanently unlettable — in a business whose central number is how many beds are lettable. Passing the checklist is what releases it. A room that is clean but whose light does not work is clean and still not lettable, which is why every item has to pass.

A store whose balance cannot be wrong

Linen, mattresses, cleaning supplies, gas cylinders, water, kitchen items and PPE, per property, each with a minimum level and a reorder quantity. Five kinds of movement — receipt, issue, return, adjustment, write-off — each recording the balance after it and what it cost, and each able to name the job or the clean it was issued against. A reorder list shows what has fallen below its minimum, the shortfall, and what it would cost to put right.

Two storekeepers working one store all morning is exactly where a stock system loses count. Here the running balance is maintained by the database under a row lock, and taking five from a store of three fails the whole transaction rather than leaving the store owing itself two mattresses. Movements can never be edited or deleted afterwards, which is what makes the stock card worth reading when the count does not reconcile.

Mess headcount against the caterer’s invoice

For breakfast, lunch and dinner, the expected headcount is derived from who actually had a bed that day, less anyone opted out of that meal or of all meals. The site enters what was really served. At month end you compare both against what the caterer billed — on meal count and on money, separately — and a gap above 5% on either raises an alert. The rate is frozen onto each day’s row, so a price rise in March does not rewrite February.

This is one of the most common leakage points in camp operations, and you cannot see it with a single headcount number. A caterer billing 480 covers at a camp whose beds support 430 is money leaving every day, and the gap only exists if you keep both figures. The two disagree independently, too — the right number of meals at the wrong rate, or the right rate for meals nobody ate.

Inspections: one form, two jobs, two signatures

A room condition report for move-in and move-out grades each item new through damaged; an HSE safety round grades each item pass, fail or not applicable. Two checklists ship — a fourteen-item camp safety round and a sixteen-item room condition sheet — copied onto the inspection so a template edited next year does not change what last year’s document says was checked. A damaged or failed item cannot be submitted without a photograph. The verdict is calculated from the answers, not typed. The person who walked the room may not approve it, and approving an HSE round with failures raises a work order per failed item.

What an inspection finds decides what comes off somebody’s deposit or what gets repaired, so the hand that walked the room must not be the hand that signs it off — enforced in the database, not just the screen. “The fire door was blocked” is a sentence, and a sentence is what gets argued with; a photograph is not. An unwalked checklist fails rather than passes, because nobody looked.

Incidents and complaints, kept as a record rather than a folder

Incidents by type — injury, fire, electrical, water, theft, fight, medical, absconding, death, other — with severity, people involved, immediate action, and an investigation that can be edited while open and never once closed. Closing requires writing down what was found. Complaints are separate, with their own clock, a resolution note, a 1-to-5 satisfaction rating and an optional link to the job that fixed it. Complaint volume is reported per property per hundred beds.

An incident closed with nothing written down is one that will happen again, and the corrective actions are the only part anybody reads a year later. A closed file whose investigation can still be edited is not evidence of anything. Per hundred beds rather than in total, because a camp of 400 and a villa of 8 are not comparable on a raw count — and the raw count is what makes a manager argue with the number instead of acting on it.

The gate: passes, a live visitor register, and a headcount check

Passes for visitors, occupant exits, materials and vehicles, with host, purpose, vehicle number and in and out timestamps. An overnight guest needs a separate approval. If the person is an occupant whose passport, visa or labour card has lapsed, the pass is refused outright with the document named. A register shows who is on site right now across every property, and a headcount check compares the people with beds allocated today against the people recorded inside.

An extra man sleeping in a bed nobody is paying for is money walking out of the door every night, and it is invisible in every other report. The document check bites at the gate, where it matters — and refusing rather than issuing-and-flagging is the only version that works, because a pass that exists is a pass the gate will honour. The headcount difference is deliberately reported as a prompt to go and look, never as a conclusion.

A disciplinary ladder that proves itself

Verbal warning, written warning, final warning, eviction notice — four rungs, each pointing at the one below it, one step at a time, against ten kinds of breach from noise to theft. An eviction notice must give a grace date. Warnings can be acknowledged by the occupant, closed with an outcome, or withdrawn — withdrawn records stay on file and stop counting. An occupant’s page shows the history and what it adds up to. If your organisation switches it on, an eviction notice needs an approval before it exists.

“This is his third warning” has to be provable from the records, not asserted by whoever is telling the story — a ladder with a missing rung is what a tribunal throws out. Approval sits only on the eviction notice, deliberately: a verbal warning that needed a director’s signature would simply never get written down, and the pattern that matters is only visible in the small entries.

An escalation ladder that will not let go until somebody answers

Twelve situations, each with up to three rungs saying who is told, on which channel, and how long after the clock started — a P1 nobody has touched at 15 minutes, 30 minutes and an hour; a visa expiring at 60, 30 and 7 days out; a bed vacant more than thirty days. An hourly sweep works out which rung is due. Reaching a high rung spends the lower ones, so a situation left alone for a week produces one escalation and not three. The top rung repeats every day until a human acknowledges it, and a situation that resolves itself — the invoice gets paid — stops the ladder.

An escalation nobody acknowledges is an escalation that did not work, and repeating it is the only thing separating a system from a filing cabinet. Equally, telling the owner about an emergency and then nudging the assignee about the same thing is how people learn to mute alerts. You can run the sweep as a preview to see what would fire before it does.

What this area does not do

  • Nothing is sent when an escalation fires. The sweep writes it and the screen shows it; no message leaves the building.
  • Photographs are counted and referenced but never uploaded here — no uploader is wired into these screens, so the keys have to arrive from elsewhere.
  • SLA targets are constants in code. There is no table, no route and no screen to change them per organisation or per category.
  • Metered utilities become a cost to the property and are never recharged to an occupant; the billable excess is reported, not invoiced.
Area 5 of 9

Occupant documents, the expiry ladder, gate passes, conduct

An expired visa refuses the gate pass, not a report

In a labour camp the paperwork is the operation: four hundred men, each with a passport, a visa, an Emirates ID and a labour card, each with its own date, and the consequence of missing one is a worker who cannot legally be on site. Mini PMS treats every expiry as something the product acts on — a ladder of warnings that escalates on its own from sixty days out, and a gate pass that is written REFUSED, with the document named, on the morning the date passes. The same walk-round that grades a room, the same log that records a warning, and the same register that says who is inside the fence are all held to database rules a later change cannot quietly drop.

9kinds of occupant document the product recognises
4rungs on the expiry escalation ladder
34API endpoints across occupants, documents, gate passes, inspections and conduct
21separate permission keys governing this area
77end-to-end tests running against a real database for this area alone
4documents every occupant is expected to hold, so a missing one shows grey
60 / 30 / 0days before expiry a document turns amber, red, then blocking
06:10when the nightly document sweep runs, in each organisation’s own day
28rules the database itself enforces on these records
30checklist lines shipped across two ready-made inspections
10kinds of rule breach a warning can cite
6ready-made reports on people and compliance
64tests on the expiry, verdict and ladder rules on their own
DiagramOne visa, counting down — 60, 30, 7, and the day it lapses
ONE VISA, COUNTING DOWN — FOUR RUNGS, EACH FIRING ONCET−6060 days outWho is toldOccupant · employerNo task — renewal is still routineT−3030 days outWho is toldOccupant · employer · HR coordinatorRaises a taskT−77 days outWho is toldHR coordinator · property managerRaises a taskT+0ExpiredWho is toldAll four, togetherNon-compliant · gate pass may be refusedTHE LADDER ESCALATES — IT DOES NOT ACCUMULATEA document five days from expiry is on T−7, not on all three.Separately, the colour: amber at 60 days, red at 30, blocking on the day it lapses.

All 18 capabilities · Compliance

Each one is built, tested and in the product today.

The compliance matrix

One grid: every occupant down the side, the four documents they are expected to hold across the top, and a coloured cell for each. Green is valid, amber is inside sixty days, red is inside thirty or already gone, grey means nothing has ever been uploaded. The worst people are always at the top of page one.

You find the man whose visa lapsed ninety-six days ago without reading a single row. The five counters above the grid are computed by the server over your whole organisation — not over the page in front of you — so pressing one narrows the list and never rewrites the counters. Nothing hides behind a page break.

Grey cells for documents you do not have

The grid draws a cell for every expected document whether or not it is on file. A missing labour card shows as grey with the words “Nothing on file — nothing will alert you about it either.”

A document you never uploaded has no expiry date, so no alert can ever fire on it. That is the most dangerous category and every other system renders it as an absent row.

The four-threshold expiry engine

Every document’s state is worked out from its expiry date: more than sixty days left is valid, sixty down to thirty is expiring, thirty down to zero is critical, and past zero is expired. It is computed the instant you save a document as well as every night, so a visa filed with three days left shows red immediately.

You never have to work out from a date what it means, and you never wait until tomorrow morning to find out.

The escalation ladder, and each rung fires once

Sixty days out the occupant and their employer contact are told. Thirty days out a task is raised for the HR coordinator. Seven days out the property manager hears about it. On the day it expires the person is marked not compliant and a gate pass can be refused. The rung reached is written on the document, so a visa sitting at forty-five days for a fortnight produces one warning rather than fourteen.

This is the difference between an alert people act on and one they filter into a folder. And a renewal that pushes the date back does not re-fire the earlier rungs on the way out.

A nightly sweep in each operator’s own day

At 06:10 the product re-reads every document in every organisation, moves the ones that crossed a threshold, fires the rung they newly reached, and rolls the answer up onto the person. It runs once per organisation per its own local calendar day, so a re-run or a missed hour cannot double up.

You read it with your morning coffee rather than discovering it the week it happens, and nobody gets the same warning twice because a job was retried.

The worst document decides the person

An occupant’s standing is the worst thing on file: one expired or rejected document makes them not compliant, one critical document puts them at risk, one expiring or unchecked one needs attention. Nothing on file at all is its own state rather than a pass.

A man with a perfect passport and a lapsed visa cannot work. Averaging his paperwork would say he was fine.

A gate pass that is refused, and says why

Issue a pass for an occupant whose papers have lapsed and the pass is created as REFUSED with the reason written on it — “Visa, Labour card have expired” — rather than quietly approved. The refusal appears in the register with its reason in its own column, and no movement can be recorded against it.

A guard turning somebody away at six in the morning needs a sentence they can read out. And a record that says a man was refused is what an auditor asks for; a record that says nothing happened is not.

Barring a person, and lifting the bar

Somebody can be barred across every property you run. Doing it demands a written reason, it refuses their gate pass whatever their paperwork says, and — this is the part usually missed — lifting the bar costs the same authority that set it, so a supervisor cannot undo a property manager’s decision.

Barring follows a person between employers and sites. It has to be answerable a year later, and a gate that only closes is not a gate.

Identity numbers encrypted, and reading one is an event

Passport, visa and ID numbers are encrypted where they are stored. Screens show the last four digits. Seeing the whole number takes a separate permission, a typed reason, and it is written into the audit log against your name and the time.

At the gate the number on the card has to match the number on the record — so the product lets you check it. Everywhere else, a spreadsheet export of four hundred passport numbers is not one click away.

Somebody checked it, or nobody did

A document can be verified — a person saying they have seen the passport and it is that man’s — or rejected with a mandatory reason the occupant can act on. The screen shows “checked 12/08/2026” or “nobody has checked it” beside the state. Verifying does not make an expired document valid; the dates still decide.

“Valid” otherwise only means somebody typed a date. These are two different facts and a labour inspector asks about the second one.

One live document of each type

Filing a new visa supersedes the old one rather than sitting beside it. The database refuses two current documents of the same type for one person.

Two visas on one man is how the grid shows green next to red for the same person and nobody can tell which one is true.

Import a contractor’s roster from a spreadsheet

Download a workbook in the product’s own format with a worked example row, fill it in, and upload it against an employer. Names, nationalities, employee numbers and three documents with their numbers and expiries all arrive together. There is a dry run that reports every problem — bad dates, duplicate employee numbers, a visa number with no expiry — without writing anything, and the real import is all-or-nothing.

A company signs for forty beds and emails a list of forty men. Typing that in one at a time is how compliance data stops being kept up to date. And a half-imported roster is worse than a rejected one, because nobody can tell which half arrived.

The visitor register and who is inside right now

Passes for visitors, occupants leaving, materials and vehicles. In and out are stamped as they happen, overnight guests need a named approver before the pass is approved, and one register answers “who is on site” across every property at once rather than making you ask each camp in turn.

A supervisor with four camps had to add up four screens. And the count on the tile is counted in the database over the whole filter, not summed from the rows that fit on the page.

Headcount against the beds you are paid for

Compare the number of people with a live bed allocation on a date against the number currently inside on a gate pass, per property. It flags more present than expected, fewer, or matched — and says in words that a gate log rarely captures everybody, so treat it as a prompt to go and look.

An extra man sleeping in a bed nobody is paying for is money walking out of the door every night, and it is invisible in every other report. The wording matters as much as the number: this is a signal, not a bill.

Inspections that grade in the right vocabulary

Five kinds of inspection — move-in, move-out, routine, HSE round, turnover. A room is graded new, good, fair, poor or damaged; a safety round is graded pass, fail or not applicable. Sending the wrong one is refused rather than stored. Two checklists ship with the product, thirty lines between them, and the lines are copied onto the inspection rather than referenced, so editing a checklist next year does not change what last year’s document says was looked at.

Your first inspection is not a blank page, and a signed sheet still means in two years what it meant on the day.

The verdict is computed, and damage needs a photograph

There is no “mark as passed” button. Hand the sheet in and the product works out pass or fail from the answers: one failed item or one damaged item fails the sheet, poor does not. A sheet nobody actually walked fails rather than passes. And a damaged or failed line with no photograph is refused at submission, naming the item.

The finding decides what comes off somebody’s deposit. “The fire door was blocked” is a sentence, and a sentence is what gets argued with; a photograph is not. Poor deliberately does not fail, because a mattress at the end of its life is not damage anybody caused and must not become a deduction by arithmetic.

The hand that walked the room cannot sign it off

Approving an inspection is a separate permission and a separate person. The service refuses the inspector’s own approval with an explanation, and the database refuses it with a constraint. An approved safety round with failures raises a work order for each failed item automatically — at approval, not at submission, so a mistaken fail corrected before sign-off leaves no job behind it.

Two signatures on the document that decides a deduction or a repair. The database half is the one that survives somebody rewriting the code.

A conduct ladder you can prove

Verbal warning, written warning, final warning, eviction notice — four rungs, one at a time, each pointing back at the one it followed. You cannot jump a resident from a first warning to an eviction. An eviction notice must carry a date the grace period ends, and can be sent for approval before it is issued. A warning issued in error is withdrawn rather than deleted: it stops counting immediately and the row stays.

“This is his third warning” has to be provable from the records, not asserted by whoever is telling the story. A ladder with a missing rung is what a tribunal throws out.

What this area does not do

  • There is no bulk reminder and no one-click “request document” from the matrix. Chasing a renewal happens outside the product today.
  • Barring somebody stops their gate pass and nothing else — nothing in the allocation path checks it, so a barred person can still be given a bed.
  • No scan is attached from the compliance screen: the form captures type, number, country and dates. The separate document vault does store and virus-scan a passport scan, and the two records are not joined.
  • Gate passes never expire on their own, and no printed pass or QR code is produced.
Area 6 of 9

Dashboards, KPIs, the 46-report catalogue and exports

46 reports, 33 KPIs, five dashboards, one engine

Every number an operator needs is already built: 46 named reports over 29 published data views, 33 KPI formulas each carrying the sentence that defines it, five dashboards written for five different jobs, and Excel that an accountant can actually use. What makes it different is what the system refuses to do — a report you are not allowed to run is not on your list, a figure your role may not see comes back blank and labelled rather than deleted, and an export that hit its row cap says so on its own cover sheet. A number that cannot be computed is blank, never zero, so an empty camp never drags a portfolio average down.

46reports in the catalogue
33KPIs returned by one call
29published data views a report may read
26API endpoints in the analytics module
160automated tests over this area alone
6report categories
330report columns defined across the catalogue
113filters offered across the catalogue, 8 of them required
87columns that carry an automatic total in the export
30reporting views in the database, every one running as the caller
27KPI formulas written as separately tested functions
21distinct permissions gating the report catalogue
5dashboards, plus an 8-tile executive ribbon
50,000row cap on an export, stated on the cover when hit
13months of history behind every sparkline and trend line
90 back / 180 forwarddays covered by the Cash Ribbon
7sections in the generated board pack, with 5 commentary boxes
18account codes in the starting chart of accounts
LiveAll 46 reports — filter the catalogue, then switch role and watch it shrink

46 reports in the catalogue — 46 available to a Property Manager.

Full operational and commercial control within scope. Every report declares one permission, and that declaration is the gate — there is no /reports/rent-roll endpoint to go round.

Occupancy & inventory7 of 7 available

Tenancy & contracts7 of 7 available

Financial14 of 14 available

People & compliance6 of 6 available

Operations9 of 9 available

Executive3 of 3 available

Point at any report. 46 of them, and each one is a row in a table you can schedule, filter and export.

All 18 capabilities · Reports & KPIs

Each one is built, tested and in the product today.

Forty-six reports, ready on the first day

The full report catalogue ships built: occupancy and inventory (7), tenancy and contracts (7), financial (14), people and compliance (6), operations (9) and executive (3). Each one carries the plain question it answers — the rent roll asks “what is contracted, at what rate, until when?” — plus its columns, its filters and its default sort.

Nobody has to specify, commission or wait for the report that answers the question they have this morning. The bed roster, the AR aging, the PDC register and the property P&L are already there with the right columns.

A report is data, not a bespoke page

There is no separate program per report. One engine reads one of 29 published data views, projects only the columns that view declares, and filters only on the columns marked filterable. Nothing a browser sends can name a table or a column that is not on that list.

New reports arrive without new code, and the report side door can never reach data the rest of the product protects — which is how reporting normally leaks in property systems.

A report you may not run is not on your list

The catalogue endpoint returns only the reports the person asking is permitted to run. It does not grey them out — it omits them.

A greyed row named “Head-lease obligations” tells a camp supervisor that head-lease cost exists and roughly what it is called. The list of questions a company asks is itself information.

A report can never be looser than the data behind it

Running any report demands the stricter of two things: the permission the report declares and the permission its underlying data view declares. That applies to reports your own people build as much as to the 46 we shipped.

The usual way a report builder leaks is that somebody saves a view over sensitive data and gives it a mild-sounding permission. Here that is arithmetically impossible — the data’s own gate always applies.

Excel an accountant can use, and a cover sheet that proves it

Every report exports to Excel, CSV or a print-ready page. The Excel file has a frozen header row, sized columns, the right number format per column, a bold totals row, and a cover sheet naming the report, the question it answers, the organisation, who ran it, when, the currency, and every filter that was applied. Money exports as a number, not as text with a currency stuck on it.

The first thing anyone does with an exported register is sum a column — text cells cannot be summed. And a disputed figure that cannot be reproduced is a figure that gets believed; the cover sheet is what lets someone re-run the exact same question next month.

An export that was cut short says so on its own face

Exports are capped at 50,000 rows. When the cap is hit, the cover sheet carries a red warning — “This export was truncated at the row limit. Narrow the filters and run it again.” — and the run log records it as truncated.

A manager handed 50,000 of 62,000 rows who is not told will reconcile against a total that was never complete, and will trust it because it came out of the system.

Exports that cannot be weaponised or mangled

Any text a customer typed that starts with =, +, - or @ is neutralised before it reaches a CSV cell, and every file carries the marker that stops Excel on Windows turning Arabic names into gibberish.

A report is exactly the path by which text someone typed into your system reaches a spreadsheet on your finance manager’s laptop. And a roster of names that opens as mojibake is a roster nobody can use at the gate.

Thirty-three KPIs, each carrying the sentence that defines it

Occupancy physical and economic, revenue per available bed, ADR equivalent, vacancy loss, gross spread, opex, capex amortisation, net margin, margin %, cost per bed, collection rate, DSO, arrears and arrears ratio, cheque bounce rate, deposit coverage, churn, retention, renewal, tenure, turnover and re-let days, SLA compliance, mean time to repair, reopen rate, PPM compliance, compliance health and incidents per 100 beds. Every one arrives with its value, last period’s value, the change, the target and a 13-month sparkline — and a one-line definition that shows on hover.

Two people arguing about “occupancy” usually mean two different numbers. Here the tile, the report and the board pack all read the same formula, and the tile tells you which one it is before you act on it.

A figure that cannot be computed is blank, never zero

A property with no beds has no occupancy rate. A month with no invoices has no collection rate. Those come back empty rather than as 0%.

A zero sorts worst in a league table and drags the portfolio average down. A new camp still in fit-out would make the whole estate look like it was failing.

Last night’s numbers stay last night’s numbers

Every night the system records that day’s KPIs — for the portfolio and for each property separately. Trend lines and sparklines are drawn from those recordings, not recomputed from today’s data.

Occupancy on 3 March is not recoverable once somebody corrects an allocation, and corrections are routine. Without this, the March figure you reported to your board silently changes in June. Re-running the job the same night updates rather than duplicates.

Targets, and a green / amber / red verdict against them

A target can be set on any KPI, for the whole portfolio or one property, standing or for a single month. Tiles then colour themselves: at or above target is green, within 10% is amber, below is red — and it inverts automatically for the measures where lower is better, such as arrears ratio and days-to-repair.

A number without a target is trivia. And “up is good” is wrong for half the list — a rising arrears ratio is not an improvement, and nobody should have to invert it in their head at 7am.

Five dashboards, one for each job in the building

Executive (the estate this month), Finance (receivables, cheques, tax, DSO), Leasing (approvals, renewals, available stock, average days to let), Property (one building in detail) and Site (one camp today: headcount, today’s moves, open jobs, visitors on site, incidents, expiring papers).

A camp supervisor and a finance controller do not want the same screen with different rows hidden. They want different screens, and the one they get is the one written for their job.

The camp supervisor’s dashboard contains no money at all

The site dashboard does not hide financial figures — it never asks for them. Nothing in it queries an invoice, a payment, a rate or a cost, so there is no money field in the response for anything to leak.

A screen can hide a field; it cannot un-send it. Anything that arrives at the browser is in the network tab, in any log, and in anything holding that login. Absence is a stronger promise than concealment.

Withheld, not deleted

On the dashboards that do carry money, a role without financial access gets the figure blanked and flagged — the tile keeps its label, its unit and its definition, and the screen says “this is a withheld figure” with the permission that would show it. Contract values are governed separately from profit-and-loss figures, so a leasing agent sees what a deal is worth without seeing what the building costs to run.

Deleting the field renders as an empty cell, which reads as “there is no revenue”. This product has shipped that mistake once and will not again. Two questions, not one, because a leasing desk legitimately needs the first and not the second.

The Cash Ribbon — 90 days back, 180 forward

One picture of cash: receipts and cleared cheques above the line, head-lease instalments and supplier bills below it, with a running balance threaded through and a danger band wherever the projection dips under a floor you set. Click any day to see the individual instruments that make up that bar.

This is the screen an operator opens every morning. The running balance starts from cash actually received minus cash actually paid — not from zero — so a healthy business does not appear to go negative on day one and teach everyone to ignore the warning band.

The expiry radar — six kinds of expiry in one list

Contracts, head leases, tenancy registrations, occupant documents, vendor trade licences and vendor insurances, in a single list sorted by days remaining, with severity colouring: already expired, within 7 days, within 30, within 60.

The whole point is that nobody should have to remember to look in six places for the same class of problem. One list, one sort, 120 days of horizon.

The monthly board pack, with the manager’s own commentary

One command produces a print-ready seven-section pack for the month: the ribbon, occupancy and margin, where the revenue went, receivables, the portfolio, an eight-row KPI scorecard against target with bullet charts, and the risks and expiries. Five commentary boxes are typed into before sending. Every chart is drawn as vector, not a screenshot.

A board pack is printed and projected — the two places a screenshotted chart falls apart. And the numbers in it are the same numbers the dashboard showed, from the same formulas, so nobody has to reconcile the pack against the system.

An accounting journal that refuses to export unbalanced

Every invoice, credit note, payment, expense and deposit movement in a period, restated as debits and credits against account codes you can change, with an 18-account chart provided as a starting point. The file will not download unless debits equal credits — the screen names the difference and lists the documents that could not be posted.

An unbalanced batch does not fail on import into your accounting package either. It posts to suspense, and somebody finds it at year end.

What this area does not do

  • Scheduled reports are stored and nothing delivers them. The screen says so in a banner rather than implying a delivery, and a schedule cannot be paused or deleted.
  • A saved view cannot be edited or deleted once created — views are made from the catalogue and thereafter run, not changed.
  • The personal drag-and-drop dashboard is API-only. Fifty-two widgets, per-user layouts and role defaults all exist and are tested, and no screen calls them yet.
  • Receivables aging is as at today only. Point-in-time aging — what the book looked like on 30 June — is not offered rather than offered wrongly.
Area 7 of 9

Multi-tenancy, roles, permissions, approvals, audit, second factors

254 permissions. 16 roles. Every row walled off twice.

Mini PMS keeps each operator’s data separate inside the database itself, not only in the application code — so a forgotten filter on one screen returns nothing rather than somebody else’s camp. On top of that sits a catalogue of 254 named permissions and 16 ready-made roles, built so a site supervisor holds literally zero permissions matching invoice, payment, cheque, deposit, expense, head-lease cost or rate — a rule asserted by a test, not written in a manual. Authority is checked twice on the way in (may you do this, and is this property yours?), and every irreversible act carries a typed confirmation, a written reason, a mandatory second factor and an audit row the software has no permission to edit.

254named permissions in the catalogue, across 15 groups
16roles shipped configured — 12 staff, 4 external
138 of 142database tables carry an organisation column, and every one is protected
13actions marked dangerous: typed confirmation, written reason, second factor
238integration tests covering isolation, access and approvals alone
254 / 227 / 94 / 67 / 57permissions held by Owner, Property Manager, Finance, Leasing Agent, Site Supervisor
10readings gated individually rather than by a read-only role
201 of 254permissions a route or handler actually demands
384route handlers carrying a declared permission
2row-level policies written on every organisation table
5separate database logins, each with the narrowest access it needs
9tables the sign-in path can reach at all
17 of 33lifecycle steps demanding their own permission
8kinds of thing an approval rule can govern
14checks the database audit runs on every build
187unit tests on the permission engine itself
12.634 ms → 0.042 msone screen’s query, after the platform-access rule was told which logins it applies to
15 min / 7 days / 60 minaccess token life, sign-in renewal window, support-session ceiling
5 → 15 min, doubling to 24 hthe sign-in lockout ladder
10single-use recovery codes issued once, stored only as hashes
LiveOne screen, seven roles — switch and watch the money stop arriving

Everything in the organisation, including billing and the danger zone.

Al Quoz Camp 1384 beds · 6 blocks · head lease to 202812 of 12 shown

OwnerThe owner sees the whole board. Nothing here is withheld from them — which is the only reason the other six views are worth looking at.

Pick any figure to see the permission key that governs it, and which of the seven roles hold it.

Withheld, not deleted — the tile keeps its label, its unit and its definition, so nobody mistakes you may not read this for this is empty. And the redaction happens before the JSON is written, not in the markup: a screen can hide a field, it cannot un-send one.

All 18 capabilities · Access & isolation

Each one is built, tested and in the product today.

Two locks between your data and everyone else’s

Your organisation’s rows are separated by the database itself, in addition to the application filtering every query. Each request opens a transaction that names your organisation, and the database refuses to hand over a row belonging to anyone else — even to a query that forgot to ask. The protection is not a hand-written list of tables: it walks the database catalogue, finds every table carrying an organisation column, and covers it.

The way a multi-customer system leaks is a missing filter on one screen out of four hundred, or a new table nobody remembered to add to a list. Here the first mistake returns an empty list instead of another operator’s roster, and the second cannot happen — a table added tomorrow is protected by tomorrow’s migration run, and a separate audit re-checks that nothing was missed.

254 permissions, each written in plain English

Every action in the product has its own named permission — 254 of them across 15 groups: Organisation, Access, Property, Landlord, Pricing, CRM, Contracts, People, Billing, Payments, Expenses, Operations, Comms, Automation, Reporting. Each carries the sentence shown in the role editor.

When you need somebody who can record a cheque but not mark one bounced, that is one checkbox rather than a support ticket. And when a request is refused, the message names the exact permission missing, so the person can ask for the right thing instead of “access”.

Sixteen roles that arrive already configured

Twelve staff roles — Owner, Administrator, Property Manager, Leasing Agent, Finance, Finance Viewer, Site Supervisor, Maintenance, Housekeeping, HR Coordinator, Read-only, Auditor — and four external ones for corporate clients, residents and landlords. Each states its intent and is a fixed bundle of permissions.

You can hire a camp boss on Sunday and have them working on Monday without designing a permission scheme first. The bundles are the ones this industry actually runs, not a generic admin/user/guest split.

The camp boss sees no money, and it is a test rather than a promise

The Site Supervisor role holds zero permissions matching invoice, payment, cheque, deposit, expense, head-lease cost, financial reports or rates. Two dashboards that had been quietly returning contract values and running costs now withhold them before the data leaves the server.

A screen can hide a field; it cannot un-send it. Your supervisor’s phone holds a token, and anything holding that token would have seen what you pay the landlord. Now the figure never travels — the tile keeps its label and definition and says withheld, rather than showing an empty cell that reads as “there is no revenue”.

A role that reaches only one property — and can expire on a date

Every grant carries a scope: the whole organisation, one portfolio, or one property. A supervisor scoped to DIP Camp 2 cannot list, open or read the occupants of Jebel Ali — lists are filtered, and a named record is refused in words. A grant can also carry an end date, after which it simply stops counting.

You run several sites with several contractors on them. A supervisor at one camp reading who sleeps at another is a personal-data leak, not a tidiness problem. And a three-month secondment ends by itself instead of by somebody remembering.

Nobody can hand out authority they do not hold themselves

You may only grant a role whose permissions you already have, and you may only edit, reset the password of, or deactivate a person whose roles you could yourself grant. The same applies to reach: somebody confined to two properties cannot grant anybody access to the whole organisation.

Without the second half the first is decoration — an administrator who cannot grant Owner but can reset the Owner’s password simply resets it and signs in as them. The refusal names exactly which permissions would have been escalated, so it is fixable rather than mysterious.

The organisation can never lose its last owner

Deactivating the last active Owner, or stripping their Owner role, is refused. So is deactivating your own account.

An organisation locked out of itself cannot repair itself — only the platform can restore an owner. This is the mistake somebody makes at four in the afternoon, and the one nobody inside the company can undo.

Ten readings that are their own permission, never a side effect

Ten specific reads are granted individually rather than swept up by a read-only role: the audit trail, what you pay for a property, a landlord’s full bank account, what a repair cost, occupant passport and visa numbers, a customer’s credit file, your own subscription billing, financial reports, executive reports and the executive dashboard.

A read-only role that reveals the account you pay a landlord’s rent into is not a read-only role. These are granted deliberately or not at all — and because the exclusion sits inside the helper that assembles every bundle, the next sensitive field added is withheld by default rather than by somebody remembering.

Thirteen actions you cannot take casually

Thirteen permissions are marked dangerous — voiding an invoice, approving a write-off, backdating a contract, blacklisting an occupant, forfeiting a deposit, deleting a space, deactivating a user, assigning roles, managing API keys, writing or running an automation, exporting all data, closing the organisation. Holding any of them makes a second factor mandatory on that account, and each act is recorded with a written reason.

These are the things that cannot be undone from a screen. The person doing them proves who they are, says why, and leaves a permanent record with their name on it — which is what makes the record worth anything when a landlord’s auditor asks six months later.

Every status change asks for its own permission, including backdating

Contracts, cheques and work orders each move through a lifecycle behind a single route, and seventeen of those steps demand their own permission: submitting a contract is not approving one, banking a cheque is not marking it bounced, finishing a job is not signing it off. Separately, writing a contract that starts before today needs its own dangerous permission, judged against your organisation’s local date rather than the server’s.

In a market that runs on post-dated cheques, whoever can present a cheque must not also be able to record it bounced — that is exactly the fraud the separation exists to stop. Backdating moves revenue into a closed period and rewrites what a tenant owed; it used to need no more authority than fixing a typo.

Approval rules you write yourself, in a language that cannot be turned against you

You set the rules: which kind of thing needs agreement, under what condition, and from which role. Eight kinds are modelled — discounts, write-offs, contracts, variations, refunds, expenses, cheque postponements and disciplinary notices. Conditions read like “amount_pct > 10”, “amount_minor > 500000” or simply “always”.

Your escalation ladder is yours, not ours. The condition is parsed by a small closed grammar rather than executed, so nothing typed into that settings field can reach anything else — and a rule naming a field the system does not know is refused when you save it, instead of silently never firing until an auditor notices a year of unapproved write-offs.

An approval is one decision, by somebody else, for those exact numbers

You cannot approve your own request whatever role you hold. The approver must both sit at the named desk and hold the matching authority — a rule naming Read-only or Leasing Agent as the discount approver is refused rather than obeyed. An approval is spent when it is used, and it is matched against the numbers it was granted for. Your “awaiting me” queue is filtered on the same permission the Approve button demands, and a request you raised is marked as yours, offered a Withdraw rather than an Approve.

A workflow one person can complete alone is not an approval workflow. An approval that becomes a standing exemption is worse than none — a 15% discount agreed by a manager must not authorise a 90% one on the next line, and a postponement agreed in March must not silently re-postpone the same cheque in July. And a badge counting three items whose buttons both fail is worse than no badge.

A second factor that a copied code cannot pass

Time-based codes from any authenticator app, with the secret encrypted in the database rather than merely hidden. A code already used inside its 30-second window is refused. Ten single-use recovery codes are issued once, stored only as hashes, and consumed on use.

A code glanced over a shoulder stays valid for the rest of its window unless the system remembers it was already spent. And if a database were ever disclosed, neither the authenticator secrets nor the recovery codes in it would sign anybody in.

A sign-in form that is not a staff directory

Five wrong passwords lock the account for 15 minutes; the sixth for 30, the seventh for 60, doubling to a ceiling of 24 hours. Unknown address, wrong password, deactivated account and locked account all return the same answer after the same amount of work.

Otherwise the login page tells anyone who asks which of your addresses are real accounts, because a wrong guess at a real address takes fifty milliseconds longer than a guess at a fictional one. Every attempt — including guesses at addresses belonging to nobody — is written to a log the application has no permission to edit or delete.

Sessions you can see, end, and that end themselves when stolen

Every device you are signed in on is listed with its address, browser and last-seen time, and any one can be revoked. Access lasts 15 minutes and renews silently for up to 7 days; each renewal invalidates the one before it. If a spent renewal is ever presented again, the whole line is revoked and everybody signs in fresh. Deactivating somebody, or changing a password, ends their other sessions immediately — while keeping the one you are holding.

This turns a stolen session from indefinite access into access that ends the moment the real person comes back. Revocation is checked on every request rather than at the next renewal, so ending a session takes effect now, not in fifteen minutes. And changing a password because you fear somebody has it is worthless if their session stays live.

Support access you can see, bounded to an hour and a mode

Platform support can only reach your organisation through a recorded session: a written reason, read-only by default, and write access requiring a ticket reference plus typing your organisation’s name exactly. It expires after 60 minutes, can be ended at any moment, and can never void an invoice, delete a contract, blacklist a person, read a passport number or close the organisation.

Somebody helping you should be able to reproduce your problem, not become your Owner. The record of who looked, when and why is written into your own organisation’s audit trail — so a support visit is visible to you, not only to us.

An audit trail the software has no permission to edit

Every change writes who did it, in which organisation, to which record, what action, the before and after with sensitive values masked, plus IP address, browser and request id. The application’s database login holds only INSERT and SELECT on that table.

Append-only by grant rather than by convention means it stays true through a bug, a compromised endpoint, or somebody deciding to tidy up. And the masking means an audit row proving that somebody edited a passport number does not itself contain the number.

Portal logins are bound to one party, and sign-in itself touches nine tables

A resident, corporate client or landlord login is attached to exactly one customer, occupant or landlord — read from the database rather than from the browser’s token — and the request is refused outright if that link is missing or points at a deleted party. Separately, the sign-in process runs as a database login that can reach only nine identity tables and never a contract, invoice, occupant or property.

An account that cannot be tied to a party has no rows it may see, and “none” must never be spelt as “unfiltered” — in a query builder an unset filter means all. And even a total compromise of the login path yields no operational data, because that path was never granted any.

What this area does not do

  • Single sign-on is not implemented, and custom roles cannot be created — you assign, scope and expire the sixteen that ship.
  • There is no operator-facing audit trail screen. Rows are written and protected; they are read through the platform console’s activity feed.
  • There is no rate limiting on sign-in, export or notification endpoints, and no organisation-wide “require a second factor for everyone” switch.
  • Approval chains and 48-hour approval escalation are not built: the first rule that catches a request wins, and an unanswered request waits.
Area 8 of 9

Packages, quotas, entitlements, trials, subscription billing

Five packages, eleven limits, counted at the database

Mini PMS is sold on what you actually manage — properties, users and beds — with eleven separate allowances behind those three headline numbers, and every one of them counted by a rule the product publishes on screen rather than keeps to itself. The limit is checked in the same breath as the record is created and locked while it checks, so twenty people adding beds at once cannot jointly overshoot a package; when it does refuse, the refusal names the cheapest add-on or package that fixes it. Behind the customer’s side sits a fourteen-screen platform console where the owner edits packages without a code change, negotiates a custom deal as a document with an expiry date, and can enter a customer’s account read-only — leaving a record the customer can see in their own activity trail.

5packages — four published, one negotiated
11metered allowances per package
69platform API endpoints, 77 operations
14screens in the platform console
9places a limit is consumed inside the creating transaction
36capability switches in the feature matrix
7purchasable add-ons
7subscription lifecycle states
5customer-facing subscription endpoints
6scheduled jobs behind subscriptions and billing
3, 7, 14days past due at which an unpaid platform invoice is chased
14days of grace before an account goes read-only
7days to pay a platform invoice
60minutes a support session lasts
×2the Base limits a trial is capped at, whichever package is chosen
80% and 95%the usage thresholds that flag an account as growing
5kinds of subscription change kept in history
AED 199 / 599 / 1,799 / 4,999seeded monthly prices for Base, Starter, Pro and Premium
1 / 3 / 10 / 40properties allowed on Base, Starter, Pro and Premium
3 / 8 / 25 / 100users allowed on Base, Starter, Pro and Premium
200 / 750 / 3,000 / 15,000spaces allowed on Base, Starter, Pro and Premium
1live custom quote allowed per organisation at a time
LiveBuild a camp against a package ceiling — the refusal names the cheapest way out
6
14
1,800
Or start from
BaseStarterProPremiumCustom
This estate lands onProAED 1,799/month

Spaces is what puts it there: 1,800 needs Pro’s 3,000.

Metered on Pro

Property P&L, preventive maintenance and advanced reporting.

Properties6 of 10

Room for 4 more.

Counted as Properties that are not deleted and not marked exited.

Users14 of 25

Room for 11 more.

Counted as Active internal users. Portal users for tenants and clients are not counted.

Spaces1,800 of 3,000

Room for 1,200 more.

Counted as Rooms, partitions, bedspaces, whole units, studios and cabins — every status, including under fit-out and out of service, because you are storing them. Parking, storage and amenity spaces are not counted.

Everything fits on Pro — AED 1,799 a month, or AED 17,990 a year, which is two months free. 14-day trial, no card. Pick a smaller package above to see what it would refuse.

Occupants
4,000
Corporate customers
250
Portal seats
1,500
Document storage
100 GB
API calls a month
250,000
Data retention
60 months

Capabilities

30 of 36 on Pro. Features are cumulative, so the matrix is one line rather than a grid of ticks.

From Base7/7

  • Properties and spaces — included on Pro
  • Individual contracts — included on Pro
  • Recurring invoicing — included on Pro
  • Post-dated cheques — included on Pro
  • Deposits and refunds — included on Pro
  • Maintenance work orders — included on Pro
  • Tenant portal — included on Pro

From Starter14/14

  • Corporate contracts — included on Pro
  • Pooled contract lines — included on Pro
  • Short-term daily billing — included on Pro
  • Head lease — the cost side — included on Pro
  • Asset register — included on Pro
  • Utility metering — included on Pro
  • Housekeeping and mess — included on Pro
  • Visitors and gate passes — included on Pro
  • Corporate portal — included on Pro
  • WhatsApp notifications — included on Pro
  • Scheduled report email — included on Pro
  • Bulk import from Excel — included on Pro
  • Public API — included on Pro
  • Webhooks — included on Pro

From Pro9/9

  • Property P&L and margin — included on Pro
  • Preventive maintenance — included on Pro
  • HSE and incidents — included on Pro
  • Landlord portal — included on Pro
  • Advanced reports — included on Pro
  • Custom roles — included on Pro
  • Approval workflows — included on Pro
  • Audit export — included on Pro
  • Multi-currency — included on Pro

From Premium0/5

  • Custom report builder — needs Premium
  • White-label branding — needs Premium
  • Custom domain — needs Premium
  • SSO — SAML and OIDC — needs Premium
  • Priority support SLA — needs Premium

Custom only0/1

  • Dedicated database — needs Custom

All 18 capabilities · Plans & quotas

Each one is built, tested and in the product today.

Five packages, eleven separate allowances, thirty-six capability switches

Base, Starter, Pro and Premium are published; Custom is built per customer and never listed. Each carries limits for properties, users, spaces, occupants, corporate customers, portal seats, document storage, API calls a month, WhatsApp and SMS credits, webhook endpoints and how long records are kept. Prices are seeded — AED 199, 599, 1,799 and 4,999 a month — and then editable in the console. Nothing is fixed in the code.

You are priced on the estate you run, not on transactions or on how many invoices you raise. And the limit that bites you first is visible before you sign, rather than being discovered on the night you are onboarding a camp.

Limits enforced at the moment of creation, counted by a rule you can read

The count happens inside the same database transaction as the record being created, and the pair — your organisation, that one metric — is locked while it happens. Every metric also carries the sentence describing exactly what it counts: spaces counts rooms, partitions, beds, studios and cabins in every state including out of service, but never parking, storage or amenity space.

Twenty staff building out a camp at the same moment cannot each pass the check and jointly land you 15 beds over your package with nothing reporting an error. And when a number looks wrong, the counting rule is on the screen beside it, so it is a conversation rather than a dispute.

A refusal that names the cheapest way out

When a limit stops you, the answer carries the metric, what you have, what the package allows, how many over you would be, and the cheapest add-on and the smallest package that would resolve it — worked out by cost of covering the shortfall, not by sticker price.

Hitting a ceiling at 2am while a contractor’s forty men are at the gate is a growth moment, not a support ticket. You are told what to buy and how many of it, in the same response.

Downgrade guard — everything that must be reduced, shown before you commit

Moving to a smaller package is checked against what you actually hold. You are shown every blocker at once — “Users: 14 in use, Starter allows 8” and “Properties: 5 in use, Starter allows 3” together — and the check runs on a preview button before anything changes. Add-ons and any negotiated limit are taken into account.

Being told to deactivate four users, doing it, and then discovering you also have too many properties is a bad afternoon. One list, before you press anything.

Change your package and buy add-ons yourself

A Subscription screen in the operator app shows your package, its lifecycle state, three usage bars for properties, users and beds, the other eight allowances in a table, a comparison against every published package, and buttons to move up or down and to set add-on quantities. Only the owner sees the buttons; finance staff see the whole table without them.

Growing from three properties to eleven does not need a phone call or a support ticket. The person who needs the numbers to justify the upgrade can pull them without holding the permission to make it.

Add-ons stack on top of whatever you negotiated

Seven add-ons: an extra property at AED 79 a month, five extra users at 99, 250 extra spaces at 149, 25 GB more storage at 49, 1,000 WhatsApp or SMS credits at 99, ten more webhooks at 49, and a quoted onboarding and migration service. A negotiated limit replaces the package’s; an add-on then adds to whatever survived that.

An operator who negotiated 500 beds and then buys a 250-pack has 750, not 250 — the deal you struck is not quietly overwritten by a purchase. And one extra camp does not force a jump to the next tier.

Per-customer limits, with a reason and a permanent history

The platform owner can raise or lower any one allowance for one organisation without moving them off their package, and every such change writes a row saying what it was, what it became, who did it and why. The same history records package changes, status changes, add-ons and trial extensions.

Six months later, “why is this account on Pro with 250 properties?” has an answer with a name and a date on it, instead of being folklore.

Trials give you the Pro feature set at twice the Base limits

Fourteen days on Base, Starter and Pro; thirty on Premium. Whichever package you pick, the trial carries the full Pro capability list, held to double the Base allowances — two properties, six users, 400 beds. The platform owner can extend a trial by up to 180 days, and can convert it to a paying subscription on monthly or annual terms.

You get to see what corporate contracts and the head-lease cost side actually look like before you pay for them, and you cannot accidentally build a whole 3,000-bed portfolio inside a free trial and then be stuck.

A missed payment throttles you; it never deletes anything

A subscription moves active → past due → grace, for 14 days → suspended. In grace you keep reading and keep working your existing contracts, but cannot create new properties, beds or contracts. Suspended is read-only: you can still sign in and still export everything. Only a fully expired account loses sign-in. A trial that ends unconverted goes to grace, not straight to suspension, with a warning three days out.

A late transfer from your accounts department must not take a labour camp’s occupancy records offline. The account slows down in a way you can see and reverse, and your data is never the hostage.

The chase ladder is recorded on the invoice, not recalculated from a date

An unpaid platform invoice is chased at 3, 7 and 14 days past due, and the rung it reached is written onto the invoice itself. Past the last rung the subscription moves to grace. Paying is the only thing that moves an account back up: settling the last outstanding invoice returns it to active in the same transaction as the payment.

A job that runs late chases you once, not three times, and a customer who pays on day eight is never chased on day fourteen. The screen shows what actually happened to that customer, not what the calendar says should have.

Subscription invoicing that cannot bill the same period twice

The nightly run raises the next platform invoice with the package line and one line per add-on, numbered sequentially per year as PINV-2026-000123, due in seven days, then rolls the subscription’s period forward. It is guarded twice — by the run’s own key for the day, and by a lookup for an existing invoice covering the same period. The lines are frozen onto the invoice at the moment of billing.

Running the billing by hand after fixing something is safe: it does nothing the second time and says so, rather than double-charging every customer. And an invoice still explains itself after the package it was priced from has changed.

Card payments where the card number never reaches the system

Payment goes through the gateway’s own page. What comes back and is stored is a token, the brand, the last four digits and the expiry — there is no column for a card number anywhere, and the last-four field carries a database rule that it is exactly four digits. The result the gateway posts back to the server is what settles the invoice; the customer’s browser returning is never trusted on its own. Gateway credentials are encrypted and can be written into the console but never read back out of it.

A stolen copy of the database is worthless to a card thief, and the deployment stays in the light-touch PCI band — a questionnaire rather than an audit. Two callbacks for one order still produce one payment, so a refresh on the return page cannot charge twice.

Stored cards are charged before anyone is chased

Each night the run bills subscriptions at 02:15, charges cards with a live mandate at 02:25, then walks the chase ladder at 02:30 and ends expired grace periods at 02:45. A charge is skipped where a payment attempt is already in flight, and refused outright while the gateway is holding placeholder credentials.

Nobody gets a dunning email for an invoice their own card was about to settle four minutes later. And a demo installation cannot fill your ledger with declines that look exactly like real refusals.

Editing a live package never rewrites what current customers agreed

Changing the price or limits of a package that has subscribers creates version n+1 and takes the old version off sale; everyone on it stays exactly where they were until they are moved. A package with no subscribers is corrected in place. The console tells you which of the two just happened, and how many customers stayed behind. A package with subscribers cannot be deleted — only archived once it is empty.

Raising Pro from 1,799 to 1,999 must not silently change what existing operators signed up to. The first anybody would know is a disputed invoice.

Custom quotes: a negotiation with a document, a price and an expiry date

Build a bespoke package for one named operator starting from a published tier — pick Pro and every field fills with Pro’s real figures, then change the three being negotiated. It produces a numbered proposal, PQ-2026-000007, showing two columns: what the standard package gives and what this one does. Draft → sent → accepted, declined or withdrawn. Accepting writes a private package for that customer alone, sets the negotiated price and the limits on their subscription, in one transaction. Only one live quote per organisation, enforced by the database.

“What did we actually sell them?” has an answer a year later, in the customer’s own words and numbers. A quote can only be accepted after it has been sent — a price nobody was shown can never become somebody’s bill — and never after it has lapsed.

Feature flags with a rollout that only ever adds

Every flag has a master switch, a rollout percentage, a named owner and a compulsory expiry date. The percentage picks organisations by a stable calculation on their identity, so raising 10% to 20% only ever adds customers and never shuffles the set. One organisation can be forced in or held out regardless, and that override needs a written reason which stays on the screen permanently. A flag past its expiry turns itself off and is flagged as overdue.

New capability reaches a handful of camps before it reaches four hundred, and nobody who already had it loses it because somebody nudged a slider. When a customer asks why they cannot see something, the console gives the actual reason — override, expired, switched off, or outside the rollout.

Support sessions the customer can see in their own records

Platform staff can enter a customer’s account to reproduce a problem. Read-only by default and it needs a written reason. Write access additionally needs a ticket reference and the organisation’s exact name typed out. Either way the session lasts sixty minutes, shows a permanent red banner, and can be ended from the console at any time. Write mode still cannot void an invoice, delete a contract or close the organisation, and neither mode can read passport numbers, credit files, billing history or the audit trail.

You can see who came in, when, why, and on which ticket — in your own activity feed, not ours. The actions nobody should take on your behalf stay impossible even for us.

Signup that either fully works or does not happen, and a price list that cannot go stale

Registering creates the organisation, the owner’s account, the owner role, the trial subscription and six sensible approval rules in a single transaction, then sends a verification email — after the transaction commits, so a mail relay being down cannot undo a correctly created company. The public pricing page reads the same rows the console edits, and if it cannot reach them it says so instead of showing figures from a build.

Nobody ends up with an account they cannot use and a support conversation nobody can resolve. And the price on the website is the price in the product, always — no customer ever arrives holding a screenshot of a number that no longer exists.

What this area does not do

  • The 36 capability switches shape the comparison table and the pricing page and gate no route. The limits are enforced; the feature switches are not. Never read a lower package as “locked”.
  • Two of the eleven allowances — API calls a month and WhatsApp credits — are never counted, and an overrun on a metered metric is allowed rather than billed.
  • There is no cancel-subscription endpoint and signup takes no card. Converting a trial is a console action or a card payment against an issued invoice.
  • There is no onboarding wizard, no support inbox, and no self-serve route from the pricing page to a custom quote — the Custom column sends you to the contact form.
Area 9 of 9

Resident, corporate and landlord portals; notices and messages

Three portals. A wrong ID answers “no such thing.”

Three different outsiders — a resident, the company that houses him, and the landlord the building is leased from — arrive at one sign-in page and get three different products. Which one you get is decided by the record the account is attached to, not by anything the browser asks for, and every query the server runs carries that party inside it. Ask for another company’s invoice by its ID and the answer is “no such invoice”, because “you may not see that” would already have told you it exists.

21portal endpoints behind the party gate
16portal sections across the three audiences
27field names a portal answer may never contain
75notification events in the catalogue
518message templates shipped ready to send
44automated tests proving one party cannot reach another’s data
8portal journeys driven in a real browser
16events urgent enough to ignore quiet hours
6delivery channels
9kinds of recipient a rule can address
5ways one notice can be targeted, combined
5 minuteslife of a document download link
5 in 60 minutesbefore repeat alerts become one digest
4portal roles, holding 22, 12 and 8 permissions
32staff-side routes for notices, messages, alerts and webhooks
20consecutive failures before a webhook endpoint is paused
DiagramOne door, three portals, and the fields that never travel
One sign-in pagethe server decides, not the browserResident5 sectionsbound to one occupant· Which bed is his· His documents, last four digits· Raise and follow a repair· The notice board· A thread with the site officeCorporate client9 sectionsbound to one customer· Beds contracted, filled, paid for· Spend and what is outstanding· The staff roster· Contracts, invoices, receipts· “We need more beds” → a real leadLandlord2 sectionsbound to one landlord· Head-lease term and annual rent· Instalments, and which cheques cleared27 FIELD NAMES A PORTAL ANSWER MAY NEVER CONTAIN — THE QUERIES NEVER ASK FOR THEMwhat a repair cost youwhat you pay the landlordyour margincredit limitrisk bandcollection notesfull passport numbersbank details

All 18 capabilities · Portals & comms

Each one is built, tested and in the product today.

One sign-in page, three different portals

A resident, a corporate client’s staff and a landlord all sign in at the same address. The screens they get are decided by the record the account is attached to — an occupant, a customer or a landlord — and the browser is told which one only after the server has looked it up. A resident gets five sections, a corporate client nine, a landlord two.

You do not run three products or three websites. You create the login, attach it to the right person or company, and the right screens appear. Nobody has to be told which URL to use.

Every question carries who is asking

Before any portal request runs, the server looks up which single customer, occupant or landlord the account belongs to, and puts that party into the database query itself. An account that cannot be matched to a party is refused outright rather than treated as “no filter”. Asking for a record belonging to someone else answers “no such thing” — not “you are not allowed” — because the second sentence would confirm the record exists.

This is what lets you put two competing contractors on the same system without either of them ever seeing a trace of the other. It is not a screen hiding a column; the other company’s rows never leave the database.

The things a portal can never be sent

Twenty-seven field names are written down as never permitted in a portal answer — what a repair cost you, what you pay the landlord, your margin, a customer’s credit limit, risk band and payment-behaviour score, collection notes, full passport numbers, bank details. They are not stripped out afterwards; the queries never ask for them. A test sweeps every portal response against the list, so a new screen that leaks one fails before it ships.

Your client is looking at the same building you are, from the other side of a margin. One careless field on one screen tells them what you pay for the property. This makes that a build failure rather than a discovery.

Staff notes on a conversation stay staff-only

Any message thread can carry internal notes. The portal is served by a completely separate query that never asks for internal messages, does not even return the field that says one exists, and the database itself refuses to record an internal note written by anyone outside your team.

“Chase their finance team, 40 days late” is a note your collections clerk needs and your customer must never read. Three independent layers stop it, so no one has to remember.

The resident’s portal

Which bed is his, in which unit and property, from and until when, and who his employer is. His documents with the expiry date and only the last four digits of the number, with rows shaded amber inside thirty days and red once passed. Raise a maintenance request, and follow every one he has raised. The notice board, and a conversation with the site office.

The two questions a labourer actually asks are “which bed am I in” and “when does my visa run out”. Answering them on his own phone stops both from arriving at the accommodation office as a queue.

The corporate client’s portal

Beds contracted, beds actually filled, beds paid for and empty; spend this month; what is outstanding; how many of their staff have a document expiring within thirty days. Then the roster — every employee, which property and which bed, from when — their signed contracts, every invoice with the lines that make it up, and every receipt against their account.

The HR or admin manager at a contracting firm currently gets this as a spreadsheet by email, out of date the day it is sent. This is the same information, live, and it removes the monthly “can you send me the list” call.

The landlord’s portal

The properties you hold from them, the head-lease term and annual rent, the instalment schedule with what has been paid and when — and the cheques you have written to them, with bank, date, amount and whether each has cleared. Cheques written by your tenants are filtered out at the query, so they can never appear here.

In this market the landlord’s standing question is “has my cheque cleared”. Answering it without a phone call is worth a portal on its own — and the filter means giving them that answer never risks showing them your rent roll.

“We need more beds” becomes a real lead

A corporate client fills in how many beds, from when, and any notes. That does not send an email — it creates a lead in your pipeline, marked as coming from the portal, with their company already attached, and they can see what stage it has reached.

The request lands where your leasing team already works instead of in somebody’s inbox, and the client can see it was received without chasing.

A document cabinet with a five-minute door

Files you have released to a party — contracts, invoices, their staff’s documents, lease papers — listed for them to download. Each download link lives five minutes. A file still being virus-scanned is shown and says so rather than disappearing. Every download is recorded, and so is every refused one. Which files a party may see is decided in one place, by document type, and the list and the download read from that same rule so they can never disagree.

A vault of passports where nobody can say who read what is not access control. And a resident whose visa scan is still being checked needs to be told that, not left wondering where it went.

Giving and taking away portal access

Your staff create a portal login, choose one of four portal roles, and attach it to a customer, occupant or landlord. The role and the party must agree — a landlord role cannot be attached to a company — the party must still exist, one email address means one account, and a temporary password is shown once. Withdrawing access deactivates the account rather than deleting it.

An account that could be pointed at the wrong party is a data breach with a form in front of it. And deleting a user makes a year of messages and requests anonymous, so revocation switches them off instead.

Notices, aimed at exactly the right people

Write a notice once and target it five ways at the same time — by property, by block, by floor, by employer, or by naming individual residents. Filters within one dimension are “any of these”; across dimensions they narrow each other, so “Camp A and Camp B, but only one employer’s men” is one notice. Set a severity, pin it to the top, give it an expiry, or schedule it. Who it went to is worked out and frozen at the moment you send, and sending the same notice twice is refused.

A water shutdown in one block should not go to three thousand people. And a notice whose audience was recalculated every time somebody opened it would have a read count that moved on its own.

Conversations that keep their context

A thread belongs to exactly one outside party and can be tied to the thing it is about — a contract, an invoice, a work order — so the history stays attached to the record. A portal reply moves it to awaiting staff; a staff reply moves it to awaiting customer; an internal note moves it nowhere, because a note to yourself is not an answer.

Email threads about a specific invoice end up in one person’s mailbox. These sit against the invoice, and anyone covering that desk can pick them up.

Seventy-five things worth telling someone about

A written-down catalogue of 75 events — contracts approved and expiring, invoices issued and one, five, ten and twenty days overdue, cheques deposited, cleared and bounced, move-ins and move-outs, passports and visas at sixty, thirty and seven days and expired, work orders breaching their response time, stock below minimum, a bed empty too long, plan limits, sign-ins that look wrong. Each carries which channels suit it, which variables a message may use, and whether it is urgent enough to wake somebody. You write rules over them: this event, on these channels, to these people, when these conditions hold.

Rules name a role, not a person, so the alerts keep working when the property manager changes. And because the catalogue is a fixed list rather than typed-in text, a misspelt event name is caught rather than becoming a notice that silently never arrives.

518 messages already written, and yours on top

Every event ships with wording for every channel it supports, in both languages — 518 messages in all, with the ones customers and residents actually receive written properly rather than generated. Edit any of them and your version quietly takes over; “reset to default” deletes your version rather than copying the original back, so improved wording still reaches you. A message referring to a value the event does not carry is refused when you save it, instead of arriving blank.

A new operator’s first overdue notice should not go out empty. And a template that quietly renders a blank where the amount should be is noticed by nobody, ever.

Nobody woken at 3am for a rent reminder

Each person sets quiet hours in your organisation’s own timezone, mutes channels they do not want, and mutes individual events. A window that runs past midnight works properly. Sixteen events are marked urgent enough to ignore all of it — a bounced cheque, a critical incident, an expired visa, an invoice twenty days late — and those go through.

The fastest way to make people ignore your alerts is to send them a routine one at midnight. The fastest way to lose money is to hold a bounced cheque until morning. This separates the two by event, once, rather than per rule.

No duplicates, and a digest after the fifth

The same notice to the same person on the same channel about the same record inside one hour is sent once — enforced by a uniqueness rule in the database, not by looking first, so two events landing in the same instant still produce one message. And once someone has had five of the same kind within the hour, the sixth and everything after joins a single digest instead of arriving on its own.

Two overdue notices for one invoice makes you look disorganised to the customer. Twelve visa-expiry emails in ten minutes makes your own staff filter you into a folder.

A log of what was not sent, and why

Every attempt writes a row: which event, which channel, who it was for, which rule produced it, and the outcome. The useful half is the failures and the withholdings — “the recipient has muted email”, “no phone number on record”, “held by quiet hours, 22:00–07:00”, “folded into a digest of nine”. The database refuses to record a suppression or a failure without a reason.

“Why didn’t the tenant get the reminder?” is the question a notification system has to answer, and most answer it with silence. Yours answers it in one screen.

Real gateways, and a dry run that proves it

Email goes through your own mail relay, SMS through Twilio and WhatsApp through Meta’s official business API, all configured from the platform console with the passwords stored encrypted. A number typed as 050 123 4567 is turned into +971501234567 before it goes anywhere; SMS is costed in real segments, so a template that grew past 160 characters — or gained one Arabic character and dropped to 70 — is visible before the bill is. Both gateways have a dry run that builds the exact request against your real credentials and shows it to you, with the token and the recipient’s number masked, without sending anything. An unconfigured channel says which piece is missing; it never reports success for a message that does not exist.

You can prove the integration is wired before you dial a single resident, and when something does not send you get the missing piece named rather than a green tick that lied.

What this area does not do

  • Six of the 75 events are actually raised by the product today. Invoice overdue, contract expiry, document expiry, cheque bounced and move-in notices do not fire on their own yet.
  • Nothing sends at all until a mail relay, Twilio and Meta credentials are configured. Until then the log records the attempt and the reason.
  • No payment, no upload and no profile screen in the portal — a resident cannot upload a renewed visa, and a corporate client cannot create its own sub-users.
  • No business event reaches a webhook. Endpoints, secrets, signed test deliveries and the retry ladder all work; only test events flow.

The honest part

What it does not do yet

Every area above ends with its own limits. These are the twelve that would change whether Mini PMS is the right product for you, gathered in one place. They are written the same way as the capabilities — what the position actually is, and what it costs you — because a limit written vaguely is a limit written to be missed.

Nothing is delivered until you configure a relay

Email, SMS and WhatsApp are wired to real gateways — your own mail server, Twilio, Meta’s business API — and each has a dry run that builds the exact request against your credentials. Until those credentials are entered, the adapters write to a log instead of sending.

The console shows every attempt and the reason it did not go. On a fresh installation they all read “connection refused”, which is a true answer rather than a green tick.

Most notification events do not fire yet

The catalogue holds 75 events and 518 written messages, and you can build rules over any of them. Six are actually emitted by the product today. Invoice overdue, contract expiry, document expiry, cheque bounced and the move-in and move-out notices are not among them.

The chasing ladder is the clearest case: it works out which step each overdue invoice is due, records it, and reports plainly that it was not dispatched.

Documents are print-ready pages, not PDF files

Invoices, receipts, quotations, custom quotes and the monthly board pack open in a browser and print properly, with the vector charts and the full compliant field set. No PDF file is generated, stored or attached.

It is recorded as a decision rather than an oversight. What it costs you today is that an invoice cannot be attached to an email — and nothing is emailed yet either.

A tenant cannot pay you through the product

Rent cannot be paid from the portal or by card anywhere in the system. Card and online are ways of recording that money arrived somewhere else. The card gateway that does exist is for your own subscription to Mini PMS.

In a market that runs on post-dated cheques and bank transfers, the cheque register and the bank-statement matcher carry the weight instead — but if your plan was to collect card payments from residents, this is not that product yet.

Nothing updates by itself

There are no push updates anywhere in the product. The bed map, the dashboards, the registers and the three portals fetch when you open them or when you act; live tiles poll on a timer.

If a colleague fills the last bed while you are looking at the map, your screen does not change until you reload it or move the time scrubber.

No photograph is uploaded anywhere

Work orders count before and after photos, an inspection refuses a damaged item without one, and spaces, tasks and meter readings all hold photo keys. No uploader is wired into any of those screens, so the keys have to arrive from elsewhere.

The rules that depend on evidence are real and enforced. Satisfying them today means pasting a storage key, which is honest about where the build stopped.

No single sign-on, and no roles of your own design

There is no SAML or OIDC, no domain-based provisioning and no role mapping. Roles cannot be created either: you assign, scope to a property and expire the sixteen that ship.

The sixteen cover the jobs this industry runs and can be scoped to one camp and dated to end by themselves — but an unusual internal split has no home in them.

Plan capability switches are not enforced

The three headline limits and eight more are counted inside the database, in the same transaction as the record, behind a lock. The 36 capability switches shape the comparison table and the pricing page, and no route reads them.

Never read a smaller package as locking a feature. The allowances bite; the switches describe.

No seasonal or date-ranged pricing

A rate rule can vary by space type, gender policy, minimum term and a named bed, and the calculator shows exactly which rule produced a figure. It carries no date range, so a summer rate and a winter rate cannot both live on one tariff.

For an annual camp contract this rarely bites. For short-stay or seasonal accommodation it is the first thing you would ask for.

Nothing renews itself

A sublease does not move into the expiring state on its own, and the auto-renew flag is stored, shown, and renews nothing. The renewal radar is a list you open, with a horizon of 90, 30 or 7 days according to the contract type.

A renewal is always a deliberate act — a new contract pointing at the old one, approved and activated. Which is safe, and which means the radar has to be opened by somebody.

Webhooks carry only test deliveries

Endpoints can be registered, secrets rotated, a signed test delivery sent, and the retry ladder and delivery log watched. No business event is published to them, and retries run when somebody presses the button rather than on a worker.

If your plan is to drive an external system from Mini PMS events, the plumbing is there and the events are not flowing through it yet.

Arabic is switched off, deliberately

The message catalogue, the right-to-left stylesheets, the bidirectional isolation and the Arabic half of every template are all built, tested and kept. The product renders in English only, behind a single switch.

A language button that yields a right-to-left page of English advertises a capability and disproves it in one click. The tax invoice keeps its Arabic, because a document issued to a counterparty is not the interface.

How this list is kept

  • Every capability above was checked against the running code, and every figure on this page was counted rather than estimated — routes counted in the controllers, permissions counted in the catalogue, tests counted in the suites.
  • Where a capability and a limit disagreed, the limit won. Several entries here exist because a permission, a column or a comment claimed something the code did not do.
  • Nothing on this page is a customer count, a rating or a quotation from anybody. The only evidence offered is what the product itself does.

Next

See it against your own estate

Fourteen days, no card. The trial carries the full Pro capability list at twice the Base allowances — two properties, six users, 400 beds — which is enough to lay out one real camp, sign one real contract and raise one real invoice against it.