legal.disbursements-vat Unreviewed
Whether a cost passed to a client is a VAT disbursement (outside scope) or a recharge (VAT due), per HMRC Notice 700.
1.0.1 · published 2026-10-03 by charlie · Anterra
Pinned by 14 tests, run in TypeScript, Python and Rust.
Unreviewed. This capability’s implementations agree in every language and pass its published test vectors, which were worked out from the official sources cited. But no qualified solicitor has yet checked those vectors, or confirmed that the capability covers the cases it claims. Treat it as a draft. Do not use it for real people, money or decisions without your own expert review. Once a qualified reviewer signs off, this notice is replaced with their name, qualification and the date. Each new version needs fresh sign-off.
Not professional advice. This capability calculates legal figures from published rules. It is a software component for developers, not legal advice. Rules change and every rate here has an effective date. Check that the dates cover your case. Verify results against the official sources listed in its README, and have a solicitor review how you use it, before anyone relies on the output. Provided “as is” under its licence, without warranty.
What it does
Solicitors, accountants and other professionals pass costs on to clients all the time, and the VAT question is always the same: is it a **disbursement for VAT purposes** (paid as the client's agent, left out of the value of your supply, no VAT added by you) or a **recharge** (a cost of your own supply, included in its value, so VAT is charged on it at the rate of your main supply, usually the standard rate, even if the original cost had no VAT)?
HMRC's answer is a checklist: a payment to a third party may be treated as a disbursement only if **all** of these are met (VAT Notice 700, section 25.1.1):
For example
disbursement_vat(acted as agent true, client received and used true, client responsible to pay true, client authorised payment true, client knew third party true, separately itemised true, exact a…)→ treatment disbursement, failed conditions , reason all eight conditions of VAT Notice 700 section 25.1.1 are met: pass the cost on at the exact amount, outside the value of your s… a court fee paid on the client's behalf with their authority, itemised at cost: a disbursementdisbursement_vat(acted as agent false, client received and used true, client responsible to pay true, client authorised payment true, client knew third party true, separately itemised true, exact …)→ treatment recharge, failed conditions actedAsAgent, reason not a disbursement for VAT purposes (actedAsAgent not met): include the cost in the value of your supply and charge VAT … actedAsAgent alone failing makes it a rechargedisbursement_vat(acted as agent true, client received and used false, client responsible to pay true, client authorised payment true, client knew third party true, separately itemised true, exact …)→ treatment recharge, failed conditions clientReceivedAndUsed, reason not a disbursement for VAT purposes (clientReceivedAndUsed not met): include the cost in the value of your supp… clientReceivedAndUsed alone failing makes it a recharge
The function
The same function in TypeScript, Python and Rust, pinned by the same tests. Pick your language; the choice follows you around the registry.
pub fn disbursement_vat(facts: &DisbursementFacts) -> DisbursementTreatment
| facts | DisbursementFacts | |
| returns | DisbursementTreatment |
The types it declares, generated into your project
/// The eight conditions of HMRC VAT Notice 700 section 25.1.1, each true when met.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct DisbursementFacts {
/// you acted as your client's agent when you paid the third party
pub acted_as_agent: bool,
/// your client actually received and used the goods or services
pub client_received_and_used: bool,
/// your client was responsible for paying the third party
pub client_responsible_to_pay: bool,
/// your client authorised you to make the payment on their behalf
pub client_authorised_payment: bool,
/// your client knew a third party would provide the goods or services
pub client_knew_third_party: bool,
/// the outlay is separately itemised on your invoice
pub separately_itemised: bool,
/// you recover only the exact amount you paid
pub exact_amount_recovered: bool,
/// the goods or services are clearly additional to your own supply
pub additional_to_own_supply: bool,
}
// VatCostTreatment is a string in Rust, one of: "disbursement", "recharge".
// Parameters take it as &str and results hold it as String.
/// How to invoice the cost, and why.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct DisbursementTreatment {
/// disbursement: outside the scope of your VAT; recharge: part of your supply, VAT at its rate
pub treatment: String,
/// the names of the conditions not met, in HMRC's order; empty for a disbursement
pub failed_conditions: Vec<String>,
pub reason: String,
}
Your code names it in one line, in the file that uses it
fune!(legal.disbursements-vat@^1); // then call disbursement_vat(…)
Imports name this capability’s declared dependencies, which fune builds next to it in your project; each one links to its page.
use super::funejson::Value; ← the fune runtime: the JSON value the test vectors use; fune build keeps it only where a signature takes one
/// Disbursement (outside the scope of your VAT) or recharge (VAT due), and why.
///
/// The conditions are HMRC VAT Notice 700 section 25.1.1, in HMRC's order; all must be met.
pub fn disbursement_vat(facts: &DisbursementFacts) -> DisbursementTreatment {
let conditions = [
("actedAsAgent", facts.acted_as_agent),
("clientReceivedAndUsed", facts.client_received_and_used),
("clientResponsibleToPay", facts.client_responsible_to_pay),
("clientAuthorisedPayment", facts.client_authorised_payment),
("clientKnewThirdParty", facts.client_knew_third_party),
("separatelyItemised", facts.separately_itemised),
("exactAmountRecovered", facts.exact_amount_recovered),
("additionalToOwnSupply", facts.additional_to_own_supply),
];
let failed: Vec<String> = conditions.iter().filter(|(_, met)| !*met).map(|(name, _)| name.to_string()).collect();
if failed.is_empty() {
return DisbursementTreatment {
treatment: "disbursement".to_string(),
failed_conditions: Vec::new(),
reason: "all eight conditions of VAT Notice 700 section 25.1.1 are met: pass the cost on at the exact amount, outside the value of your supply, with no VAT added".to_string(),
};
}
let reason = format!(
"not a disbursement for VAT purposes ({} not met): include the cost in the value of your supply and charge VAT on it at the rate of your main supply",
failed.join(", ")
);
DisbursementTreatment { treatment: "recharge".to_string(), failed_conditions: failed, reason }
}
fn flag(v: &Value, name: &str) -> bool {
match v.get(name) {
Value::Bool(b) => *b,
other => panic!("{} must be true or false, received {:?}", name, other),
}
}
pub fn disbursement_facts_from_value(v: &Value) -> DisbursementFacts {
DisbursementFacts {
acted_as_agent: flag(v, "actedAsAgent"),
client_received_and_used: flag(v, "clientReceivedAndUsed"),
client_responsible_to_pay: flag(v, "clientResponsibleToPay"),
client_authorised_payment: flag(v, "clientAuthorisedPayment"),
client_knew_third_party: flag(v, "clientKnewThirdParty"),
separately_itemised: flag(v, "separatelyItemised"),
exact_amount_recovered: flag(v, "exactAmountRecovered"),
additional_to_own_supply: flag(v, "additionalToOwnSupply"),
}
}
pub fn disbursement_treatment_to_value(t: &DisbursementTreatment) -> Value {
Value::obj(vec![
("treatment", Value::str(&t.treatment)),
("failedConditions", Value::Arr(t.failed_conditions.iter().map(|s| Value::str(s)).collect())),
("reason", Value::str(&t.reason)),
])
}
pub fn fune_vector(args: &[Value]) -> Value {
disbursement_treatment_to_value(&disbursement_vat(&disbursement_facts_from_value(&args[0])))
}Install
fune build
With that line in your source, in a Rust project (language rust in fune.project), fune build resolves it and nothing else, pins them in fune.lock, downloads only the Rust package of each, and builds the code above into your project’s .fune/build, one readable file per capability with a header linking back here. A crate’s build.rs runs it before every compile. Or pin a range in fune.project and build in one step:
fune add legal.disbursements-vat
The manifest, vectors and README with only the Rust implementation. Install it without the registry with fune add ./legal.disbursements-vat-1.0.1-rust.fune, or fetch it from a terminal with fune pull legal.disbursements-vat@1.0.1:rust.
The whole function, every language, is one file too: legal.disbursements-vat-1.0.1.fune, 24,265 bytes, sha256 f395722568fd97d13528843cefafa35d4e2f217ccc31ee0d5ac5bdbe53b13f21. It installs into a project of any language.
Customise it in your app
The seams this capability offers. Put a marker directly above a function of your own and fune build wires it into the built code; the package on the registry is not changed, the built file’s header lists it under CUSTOMISED, and fune hooks lists every hook in the project. How hooks work.
before — your function gets the arguments and returns them, changed or not, or throws to refuse the call.
// fune: before legal.disbursements-vat
after — your function gets the result and the arguments, and returns the final result.
// fune: after legal.disbursements-vat
replace — it requires no other capability, so there is no dependency to replace.
step — your function runs at a numbered point inside the function’s body, receives the in-scope values it names as parameters, and may return replacements. List the points with fune show legal.disbursements-vat --steps.
// fune: step legal.disbursements-vat after <n|label>
Tests
A version published now needs at least 8 tests for every function, and one that expects the error for each function that throws; the registry refuses it otherwise. fune verify --all runs each case in TypeScript, Python and Rust, and a project runs them again with fune verify. This page lists the cases; it does not run them. The exact JSON is vectors.json.
| Case | Arguments | Expected | |
|---|---|---|---|
| a court fee paid on the client's behalf with their authority, itemised at cost: a disbursement | acted as agent true, client received and used true, client responsible to pay true, client authorised payment true, client knew third party true, separately itemised true, exact a… | → | treatment disbursement, failed conditions , reason all eight conditions of VAT Notice 700 section 25.1.1 are met: pass the cost on at the exact amount, outside the value of your s… |
| actedAsAgent alone failing makes it a recharge | acted as agent false, client received and used true, client responsible to pay true, client authorised payment true, client knew third party true, separately itemised true, exact … | → | treatment recharge, failed conditions actedAsAgent, reason not a disbursement for VAT purposes (actedAsAgent not met): include the cost in the value of your supply and charge VAT … |
| clientReceivedAndUsed alone failing makes it a recharge | acted as agent true, client received and used false, client responsible to pay true, client authorised payment true, client knew third party true, separately itemised true, exact … | → | treatment recharge, failed conditions clientReceivedAndUsed, reason not a disbursement for VAT purposes (clientReceivedAndUsed not met): include the cost in the value of your supp… |
| clientResponsibleToPay alone failing makes it a recharge | acted as agent true, client received and used true, client responsible to pay false, client authorised payment true, client knew third party true, separately itemised true, exact … | → | treatment recharge, failed conditions clientResponsibleToPay, reason not a disbursement for VAT purposes (clientResponsibleToPay not met): include the cost in the value of your su… |
| clientAuthorisedPayment alone failing makes it a recharge | acted as agent true, client received and used true, client responsible to pay true, client authorised payment false, client knew third party true, separately itemised true, exact … | → | treatment recharge, failed conditions clientAuthorisedPayment, reason not a disbursement for VAT purposes (clientAuthorisedPayment not met): include the cost in the value of your … |
| clientKnewThirdParty alone failing makes it a recharge | acted as agent true, client received and used true, client responsible to pay true, client authorised payment true, client knew third party false, separately itemised true, exact … | → | treatment recharge, failed conditions clientKnewThirdParty, reason not a disbursement for VAT purposes (clientKnewThirdParty not met): include the cost in the value of your supply… |
| separatelyItemised alone failing makes it a recharge | acted as agent true, client received and used true, client responsible to pay true, client authorised payment true, client knew third party true, separately itemised false, exact … | → | treatment recharge, failed conditions separatelyItemised, reason not a disbursement for VAT purposes (separatelyItemised not met): include the cost in the value of your supply and… |
| exactAmountRecovered alone failing makes it a recharge | acted as agent true, client received and used true, client responsible to pay true, client authorised payment true, client knew third party true, separately itemised true, exact a… | → | treatment recharge, failed conditions exactAmountRecovered, reason not a disbursement for VAT purposes (exactAmountRecovered not met): include the cost in the value of your supply… |
| additionalToOwnSupply alone failing makes it a recharge | acted as agent true, client received and used true, client responsible to pay true, client authorised payment true, client knew third party true, separately itemised true, exact a… | → | treatment recharge, failed conditions additionalToOwnSupply, reason not a disbursement for VAT purposes (additionalToOwnSupply not met): include the cost in the value of your supp… |
| the solicitor's own train fare: not received by the client, not additional to the solicitor's supply | acted as agent true, client received and used false, client responsible to pay true, client authorised payment true, client knew third party true, separately itemised true, exact … | → | treatment recharge, failed conditions clientReceivedAndUsed, additionalToOwnSupply, reason not a disbursement for VAT purposes (clientReceivedAndUsed, additionalToOwnSupply not me… |
Show the other 4 tests
| Case | Arguments | Expected | |
|---|---|---|---|
| a bank's telegraphic transfer fee (Notice 700 25.1.3 example 1): supplied to the solicitor, not as agent | acted as agent false, client received and used false, client responsible to pay false, client authorised payment true, client knew third party true, separately itemised true, exac… | → | treatment recharge, failed conditions actedAsAgent, clientReceivedAndUsed, clientResponsibleToPay, reason not a disbursement for VAT purposes (actedAsAgent, clientReceivedAndUsed,… |
| a search fee passed on with a handling mark-up | acted as agent true, client received and used true, client responsible to pay true, client authorised payment true, client knew third party true, separately itemised true, exact a… | → | treatment recharge, failed conditions exactAmountRecovered, reason not a disbursement for VAT purposes (exactAmountRecovered not met): include the cost in the value of your supply… |
| nothing met: every condition is listed, in HMRC's order | acted as agent false, client received and used false, client responsible to pay false, client authorised payment false, client knew third party false, separately itemised false, e… | → | treatment recharge, failed conditions actedAsAgent, clientReceivedAndUsed, clientResponsibleToPay, clientAuthorisedPayment, clientKnewThirdParty, separatelyItemised, exactAmountRe… |
| a condition left unanswered is an error, not a quiet recharge | acted as agent true, client received and used true, client responsible to pay true, client authorised payment true, client knew third party true, separately itemised —, exact amou… | → | error: separatelyItemised must be true or false |
Lint allowances
Before anything is published, Functional Weave reads every implementation and refuses code that reaches outside the function. A few of its rules can be waived on one line, with a reason. This version is published despite one such finding. A registry reviewer read each one its publisher annotated (fune-allow) before it went out; one marked reviewed was accepted by the registry’s maintainers. Read the lines before you build it in.
| Where | Rule | Reason | Allowed by |
|---|---|---|---|
| impl/python.py:22 | dynamic-attribute | reads the eight dataclass fields named in the constant _CONDITIONS; no name comes from input getattr() with a computed attribute name can reach any attribute (__class__, __globals__, ...); name the attribute (obj.field), or annotate it with fune-allow dynamic-attribute and the reason |
reviewed by the registry |
More from the author
1. `actedAsAgent`: you acted as the agent of your client when you paid the third party; 2. `clientReceivedAndUsed`: your client actually received and used the goods or services provided by the third party (this usually rules out your own travel and subsistence, phone bills and postage); 3. `clientResponsibleToPay`: your client was responsible for paying the third party (for example Stamp Duty on the client's contract); 4. `clientAuthorisedPayment`: your client authorised you to make the payment on their behalf; 5. `clientKnewThirdParty`: your client knew that the goods or services would be provided by a third party; 6. `separatelyItemised`: your outlay is separately itemised when you invoice your client; 7. `exactAmountRecovered`: you recover only the exact amount you paid to the third party; 8. `additionalToOwnSupply`: the goods or services are clearly additional to the supplies you make to your client on your own account.
This capability applies that checklist and nothing more. It returns the treatment, the names of any conditions that failed (in the order above) and a one-sentence reason. Deciding each fact is the professional judgement: HMRC's own examples in section 25.1.3 include a bank's telegraphic transfer fee, which is supplied to the solicitor, not the client, and so is a recharge even at cost.
On the invoice, a recharge goes into "value for VAT" with your fees, and a disbursement goes below the VAT line (Notice 700 section 25.1.2 has the worked example: services 80.00 + expenses 20.00 = value for VAT 100.00, VAT 20.00, disbursements 50.00, total 170.00). If you treat a standard-rated cost as a disbursement you cannot reclaim its input tax, because the supply was made to your client, not to you. Compute the VAT itself with finance.tax.add-vat.
Source: HMRC, "VAT guide (VAT Notice 700)", section 25 "Supplies made by or through agents: other situations", 25.1 "Disbursements for VAT purposes", https://www.gov.uk/guidance/vat-guide-notice-700, read 23 September 2026. See also "VAT: costs or disbursements passed to customers", https://www.gov.uk/guidance/vat-costs-or-disbursements-passed-to-customers. UK VAT only.
## Before you rely on this
**Not professional advice.** This capability calculates legal figures from published rules. It is a software component for developers, not legal advice. Rules change and every rate here has an effective date. Check that the dates cover your case. Verify results against the official sources listed above, and have a solicitor review how you use it, before anyone relies on the output. Provided "as is" under its licence, without warranty.
**Unreviewed.** This capability's implementations agree in every language and pass its published test vectors, which were worked out from the official sources cited. But no qualified solicitor has yet checked those vectors, or confirmed that the capability covers the cases it claims. Treat it as a draft. Do not use it for real people, money or decisions without your own expert review. Once a qualified reviewer signs off, this notice is replaced with their name, qualification and the date. Each new version needs fresh sign-off.
1.0.1 marks it unreviewed. The code and the tests are unchanged.
Files
| Path | Bytes |
|---|---|
| README.md | 3,865 |
| impl/python.py | 1,719 |
| impl/rust.rs | 2,881 |
| impl/typescript.ts | 1,417 |
| vectors.json | 9,544 |