Cargora Manual, as shipped in the download
Cargora — User Manual
Purchase orders matched against what actually turned up. Version 3.1.4 [src: app/controllers/api.php:101].
About this manual
Every statement here was written by reading Cargora's own source, and each one carries the file it came from in a bracketed src marker. Paths are relative to the folder this docs directory sits in. If a sentence disagrees with the application, the application is right and this manual has a bug — the markers exist so you can check rather than take our word for it.
Nothing is described that the code does not do. Where something is deliberately limited, that is said plainly rather than left out.
What Cargora is
Purchase orders, packing slips and delivery notes go in as documents. A vision model reads them. Then the product does the thing a receiving desk actually needs: it matches a purchase order against the slips delivered for it and tells you what is short, what is over, and what never arrived [src: app/controllers/v3.php:25].
The matching engine is pure and offline — feed it two sets of line items and it returns a report the screen and the CSV exporter both render, with no database and no network involved [src: app/src/Reconcile.php:13].
How lines are matched
By SKU, compared with every space removed and upper-cased, so ab 12 matches AB12; punctuation still counts [src: app/src/Reconcile.php:31].
Duplicate SKUs within one document are aggregated — quantities summed, and the first non-null unit price kept [src: app/src/Reconcile.php:10]. Three lines of the same part on one slip become one line of three times the quantity.
A line with no SKU cannot be matched and is skipped [src: app/src/Reconcile.php:39]. The agent instructions say the same thing outright [src: app/controllers/v3.php:26], because it is the single most common reason a line does not appear in a report.
What a reconciliation tells you
For every SKU on either document: what was ordered, what was shipped, the difference, whether the unit prices agree, and which SKUs appear on one document but not the other [src: app/src/Reconcile.php:6].
The quantity delta is slip minus purchase order, so a negative number is short [src: app/src/Reconcile.php:102] and a positive one is over [src: app/src/Reconcile.php:108].
Price agreement has three answers, not two
A price is reported as match, mismatch, or unknown — and unknown is what you get when either side did not state a price [src: app/src/Reconcile.php:106].
That third answer matters. A product that reported a missing price as agreement would quietly pass exactly the lines you most need to look at.
Prices must agree exactly; there is no cents of slack [src: app/src/Reconcile.php:21]. Quantities carry only enough tolerance to absorb floating-point noise [src: app/src/Reconcile.php:23].
Deliveries that arrive in pieces
A purchase order rarely arrives in one van, and the completeness view is built for that [src: app/src/Reconcile.php:198].
Cumulative receipt is computed by reconciling the purchase order against every slip's lines concatenated — because the aggregation step already sums quantities per SKU, the same code that matches one slip matches ten [src: app/src/Reconcile.php:185].
Each line lands in one of four states [src: app/src/Reconcile.php:270]:
| State | Meaning |
|---|---|
not_received | Nothing has arrived against this line |
outstanding | Some arrived; some is still owed |
complete | The ordered quantity has arrived |
over | More arrived than was ordered |
Quantities are cumulative; prices are not
This is the design decision worth understanding, and the source explains it well [src: app/src/Reconcile.php:189].
Quantities add up across deliveries, so a cumulative figure is meaningful. A cumulative unit price would not be — the aggregation keeps the first non-null price it meets, so it would silently be whichever slip happened to land first.
So price agreement is reported per slip, against the slip that actually charged it [src: app/src/Reconcile.php:191], and each line carries its own breakdown by slip [src: app/src/Reconcile.php:282].
The reason given is the practical one: that is the only form in which a price gap is actionable, because you dispute it with one delivery, not with an average [src: app/src/Reconcile.php:192].
Users and roles
Three roles. The source names two assets it is guarding, and the second is specific to this product [src: app/src/Perms.php:5]:
A quantity on a matched line is a claim against a supplier. Correcting "received 90" to "received 120" silently changes what the business says it is owed, and deleting a purchase order destroys the evidence a delivery dispute rests on. So correcting lines is the receiving desk's daily job, and deleting the record is not [src: app/src/Perms.php:8].
The first asset is the LLM API key, because extraction spends the buyer's own AI credits [src: app/src/Perms.php:7].
This table was produced by executing the product's own permission function against every permission the routes ask for.
| Permission | viewer | member | admin |
|---|---|---|---|
documents.view | yes | yes | yes |
reconcile.view | yes | yes | yes |
exports.run | yes | yes | yes |
documents.upload | — | yes | yes |
documents.edit | — | yes | yes |
extraction.run | — | yes | yes |
reconcile.run | — | yes | yes |
documents.delete | — | — | yes |
settings.view / settings.manage | — | — | yes |
team.view / team.manage | — | — | yes |
audit.view | — | — | yes |
Note that extraction is a member permission here. Reading a delivery note is part of the receiving desk's daily work [src: app/src/Perms.php:15], so the spend sits with the desk rather than with an administrator. Deleting a document does not [src: app/src/Perms.php:16].
A viewer reads documents, reconciliations and the completeness view, and can export [src: app/src/Perms.php:14].
⚠️ Backups follow the model here
Worth stating because several products in this family do not: Cargora's database backups are permission-gated, requiring the settings permission rather than a bare sign-in [src: app/controllers/api.php:324]. A viewer cannot download the database.
The API and agent access
Six tools are exposed to an agent, of which one writes [src: app/controllers/v3.php:30]: listing documents, document detail, match status, discrepancies and export rows on the read side; updating a line item on the write side.
Extraction is not available to an agent — it spends the account owner's AI credits and stays a deliberate human action [src: app/controllers/v3.php:28].
The instructions also tell an agent the matching rule and its main failure mode up front: lines match by SKU, ignoring case and spaces, and a line with no SKU cannot be matched [src: app/controllers/v3.php:26].
What Cargora does not do
It does not let an agent spend your money [src: app/controllers/v3.php:28].
It does not match on description or price. SKU is the key, and a line without one is skipped rather than guessed at [src: app/src/Reconcile.php:39].
It does not report a price as agreeing when it does not know. Missing prices produce unknown [src: app/src/Reconcile.php:106].
It does not average prices across deliveries [src: app/src/Reconcile.php:189], because an averaged price is not something you can take to a supplier.
It does not raise a claim, hold stock or pay an invoice. It tells you what was ordered, what arrived, and where the two disagree. What you do about it is yours.