Groceries Split Marking Script
Introduction
This is a sample script that checks the transaction details on creation and when the text of transaction note matches is "groceries" and there's only one money transition (2 splits) it sets the category "groceries" for both of them.
Cargo configuration
First we need a Cargo.toml to build this script:
[package]
name = "groceries-markup"
version = "0.1.0"
edition = "2021"
[lib]
name = "groceries_markup"
crate-type = ["cdylib"]
[dependencies]
scripting-sdk = { path = "../../scripting/sdk", default-features = false }
scripting-sdk-macro = { path = "../../scripting/sdk-macro" }
[profile.release]
opt-level = "s"
lto = true
panic = "abort"
[profile.dev]
panic = "abort"
[workspace]
Header
First of all we include the required data and define our code a no_std.
#![no_std] use scripting_sdk::prelude::*; use scripting_sdk_macro::script;
Checking the applicability
Trigger function accepts the context:
fn is_groceries_transaction(ctx: &Context) -> bool {
Then we only want to apply it when we got our transaction for processing (scripts can be applied to something else as well):
if ctx.primary_entity_type().ok() != Some(EntityType::Transaction) {
return false;
}
Then let's validate we only have two splits which is usual case for the whole transaction (one positive and one negative). We can also check the values but let's ignore them for now
let primary_idx = ctx.primary_entity_idx();
if ctx.splits_for(primary_idx).flatten().count() > 2 {
return false;
}
And at last let's check that the note for transaction is "groceries":
for tag in ctx.tags_for(primary_idx).flatten() {
if tag.name().ok() == Some("note")
&& tag
.value()
.ok()
.is_some_and(|v| v.eq_ignore_ascii_case("groceries"))
{
return true;
}
}
Okay, the trigger function is complete, just return false if the note did not match.
false }
Updating the transaction
First of all we define our checker as a trigger function.
#[script(trigger_fn = is_groceries_transaction)]
The update function receives mutable context to update:
fn categorize_groceries(ctx: &mut Context) -> Result<()> {
And we get the primary entity (which is Transaction as we checked before) and
start iteration
let primary_idx = ctx.primary_entity_idx();
for idx in 0..ctx.entity_count() {
We should skit the index if we:
Can't extract entity:
let Ok(entity) = ctx.entity(idx) else {
continue;
};
The entity is not a split
if entity.entity_type().ok() != Some(EntityType::Split) {
continue;
}
If it's some additional split not related to the root transaction
if entity.parent_idx() != primary_idx as i32 {
continue;
}
If everything's fine we create a tag for the split
ctx.create_tag(idx as i32, "category", "groceries")?;
And just finish the function
}
Ok(())
}
Building
Just use org-babel-tangle on this file and then you can use
cargo build--release --target=wasm32-unknown-unknown
to build the resulting
target/wasm32-unknown-unknown/release/groceries_markup.wasm