The credit note, the only correction path for a finalized invoice
B3: Kamo owns everything before finalize() and the provider owns everything after, and every provider on the roadmap treats a finalized invoice as immutable. So...
The document counter, and a number that cannot fork
Every existing document number on this platform is a check-then-insert race with no unique constraint behind it: quote numbers are COUNT(...)+1, subscription in...
The invoice entity, with every tax field and no tax engine
Kamo's own invoice, written only by InvoicingService. Nothing on it is supplied by a caller: every figure is derived from the lines on every write, which is the...
Choose the KamoDesktop resolution, and stop hiding why a launch failed
The remote desktop tracks the browser window and always has, so there was no way to have MORE desktop than window. A picker now sets a fixed size, stored per br...
Let the caller choose a fixed KamoDesktop resolution
The remote desktop was never locked to a resolution — it tracks the browser window, and always has. guacd honours the client's size and xorgxrdp resizes the X s...
The REVOKEs three entity javadocs already claimed existed
BillingWormGuard, BillingDispatch and BillingSyncIssue each cite "a REVOKE UPDATE, DELETE in the KamoInitializer migration". No such migration existed — the sta...
The billing proxy route, and MANAGE_BILLING_PROVIDER's four TS mirrors
Task 8, the kamo-internal half. Six mirrors of the rights architecture; four of them live here. 3. app/lib/rightsHierarchy.ts MANAGE_BILLING_PROVIDER ...
The relay to InvoicingService, and the right that gates it
Task 8. SecurityService is the authorization boundary; InvoicingService is ClusterIP with no ingress, so this relay is the only way in. InvoicingServiceClient ...
MANAGE_BILLING_PROVIDER (277), a child of commerce settings
Task 8, mirror 1 of 6 (the Java enum) and mirror 2 (the parent(...) line). Id 277 was re-derived from origin/main immediately before allocating it, NOT taken f...
`?bug=<id>` opens that report's drawer
Where the new status-change email lands. It has to name the report rather than the tab: the status filter defaults to New, and that email is by definition about...
Glimmering unread cards, a live badge, and per-status counts
THE BADGE IS ABOUT YOU NOW, NOT ABOUT THE QUEUE. It counted every NEW report platform-wide, so it only ever fell when somebody moved a status — reading the queu...
Tell the reporter when Send To AI moves their report
MediaService now emails a reporter whenever their bug or enhancement changes status, but a dispatch applies a status of its own — usually In Progress — through ...
Preview variables for the bug-status-changed template
The admin preview renders a template against a sample variable map, and this one has no arm — so an org administrator opening SYSTEM_BUG_STATUS_CHANGED would se...
Email the reporter when their bug or enhancement changes status
Triage was silent. Somebody filed a report and then had to keep opening the tab to find out whether anyone had looked at it — which is the same as not being tol...
Per-viewer read state, a live badge, and per-status counts
THE BADGE WAS ABOUT THE QUEUE, NOT ABOUT YOU. /new-count counted every NEW report platform-wide, so it could only fall when somebody moved a status — reading th...
Announce the queue change when a report is dispatched to AI
A dispatch is the only thing that takes a report out of NEW from OUTSIDE MediaService, so without this it was the one transition the console's badge learned abo...
Per-viewer read state for system bug reports
SystemBugView is per VIEWER, not per bug — a firstViewedAt column on SystemBug would let whoever opened a report first clear it off every other manager's badge....
The canonical letter a bug reporter gets when their report moves
A member who files a bug or an enhancement had no way to learn what happened to it short of opening the tab and looking. This adds the template that tells them,...
System_bug_view — who has read which report
Per VIEWER, not per bug, and that is the whole reason it is a table rather than a first_viewed_at column on system_bug: triage is worked by several people at on...
Per-server lines on the extensions summary
The phone server's extensions tab showed the organization-wide figure and nothing else, so an empty KamoPBX server displayed "21 billable extensions — $63.00 / ...
Keyword interception at step 0 on the RingCentral rail (MS1 Task 4)
Today a STOP to a number no MEMBER_VOIP_CONFIG row owns hits the "No member found for to-number" branch and vanishes — and that branch is EXACTLY where an opted...
SmsKeywordService — the one writer for inbound keywords (MS1 Task 3)
Closes the TODO Task 2 left behind. Until this commit a verified inbound STOP reached BulkTextInboundController, was recognised, was logged, and was then DROPPE...
Detach the chat navigator from the window it maximizes
The conversation navigator was a child prop of ToolWindowShell, rendered inside the maximized window's content box. Clicking a row in it opens that conversation...
Company phone (main/toll free) on a member's signature, with their extension
The Content tab of **************** gains two switches: Company Phone (Main) and Company Phone (Toll Free). Enabled, they print the organization's published lin...
Persist the two company phone toggles
Reads and writes userCompanyPhoneMain / userCompanyPhoneTollFree on the signature field config, so the Content tab's new switches survive a save. saveFieldConf...
Attribute free extensions to the server they are on
An org running a busy FreePBX server beside a new, empty KamoPBX server saw "21 billable extensions — $63.00 / month" while looking at the KamoPBX one. Both num...
Land the company phone toggle columns before EmailService reads them
ADD COLUMN IF NOT EXISTS on **************** for user_company_phone_main and user_company_phone_toll_free. This service's ddl-auto: update would add both from ...
The company phone toggles an individual signature can carry
Two org-wide flags on email_signature_field_config: user_company_phone_main and user_company_phone_toll_free. They put the organization's published line on a me...
The inbound-keyword receipt, and re-key consent by campaign (MS1 Task 3)
Two things the consent ledger needs before an inbound STOP can be recorded tenant-scoped and exactly once. `SmsInboundReceipt` (comms_sms_inbound_receipt) is t...
The sync journal, and a terminal run that really is frozen
BL1 Task 5, shared-library half. Five journal enums, three entities and three repositories under **************** No stereotypes: BillingWormGuard is a plain fi...
Carrier-facing inbound SMS webhook (MS1 Task 2)
POST **************** -- form-encoded or JSON, guarded by the CARRIER'S OWN SIGNATURE. Why a new endpoint. /api/voip/webhook cannot receive a STOP and fails si...
E.164 normalizer and SMS keyword interpreter (MS1 Task 1)
Two pure classes with no I/O and no Spring stereotype, so every service can load them and SharedLibBeanSafetyTest stays green. E164.normalize(raw, defaultRegio...
The provider connection row, one per org
BL1 Task 1. A near-verbatim transliteration of the payroll provider enums and PayrollConnection into **************** Enums, one entity and one repository — no ...
Give every kamocrm extension WebRTC, and gate outbound calling
All 21 kamocrm team members now hold an 8XXX extension on the k1m1 PBX (Chris renumbered off 7255 to 8134). Two changes here back that. WebRTC endpoint blocks ...
ServiceJobApi.createJob, and the back-link builder
Restores the client half of SW6c Task 2, which landed as 157fa714 and was dropped by a history rewrite in a concurrent session; the create screen itself survive...
The service-request grid and client read the real endpoints
Restores the rest of SW6c Task 4, which landed as d58a6aba and was dropped by a history rewrite in a concurrent session; the detail page survived inside bddd361...
Screenshots AND videos on a bug report
Reporters were describing screen recordings in prose and then posting the file into a chat, because the report form only took stills. It now takes MP4, MOV and ...
Attach a video to a bug report, not only a screenshot
A still cannot show the three clicks before the thing broke, so reporters were describing recordings in prose and posting the file into a chat instead. The atta...
Restore the pipeline Quotes tab and wire quote conversion
`POST /quotes/{uid}/convert` shipped in SW4 and nothing has ever called it: `quotesApi` had ten methods and `convert` was not among them, so the endpoint was li...
Our price comes from the catalog, and can't be typed again
The comparison pages hardcoded KamoCRM at $29 Starter and $64 Business while the catalog sold them at $34 and $72. Nine files, forty-two mentions, and the build...
Recorded-call notice on extension-to-extension calls
Completes the third call path. Staff-to-staff calls never touch the attendant — they go from-internal -> ext-local, where the Dial belongs to FreePBX — so [from...
POST /service-jobs, gated on MANAGE_SERVICE_WORK
The create verb the work-order surface never had. app/commerce/service/new is a seventeen-line placeholder because there was nothing to call. Gated on MANAGE_S...
CreateJob, so a work order can be raised without an order
The one verb the service-job surface was missing. Until now the only thing on the platform that could produce a ServiceJob row was **************** off a paid o...
Mirror VIEW/MANAGE_ORDERS and VIEW/MANAGE_PRICING
The client half of closing the order-mutation hole. SecurityService now gates 39 order, pricing and tax endpoints on four new rights; these are the two mirrors ...
VIEW/MANAGE_ORDERS and VIEW/MANAGE_PRICING, ids 273-276
No order-mutation right existed anywhere. POSController's order endpoints and every pricing and tax handler in CommerceMarketController read the org off the ses...
Recorded-call notice on direct-dialled extensions too
A caller who dials an extension over the greeting now hears the notice as well. That path went through from-did-direct, where the Dial belongs to FreePBX's ext-...
Recorded-call notice to both parties at once, then a bridge tone
When an agent answers a queued call, the caller hears the client notice and the agent hears the agent notice at the same time, then a shared 1000Hz tone marks t...
4 GB per seat on every plan, and seats rather than users
Storage stops being a tier differentiator. The 2/4/5 ladder put one gigabyte between Starter and Business on a page that then had to explain what "pooled" meant...
The six work-order tabs persist
SW6b Task 4. Labour, parts, time, vendors, sign-off and notes were React `useState` on `commerce/service/[id]` and lost everything on reload. They now write to ...
Like what you see shipping?
All of it arrives in your workspace on its own. Start on the free plan and read this page again in a month.
