logistics.sscc
Build an 18-digit SSCC pallet label number from extension digit, GS1 company prefix and serial, with its check digit.
1.1.0 · published 2026-10-03 by charlie · Anterra
Pinned by 16 tests, run in TypeScript, Python and Rust.
What it does
Builds a Serial Shipping Container Code, the 18-digit number on a GS1 logistics label that identifies one pallet, cage or parcel through the supply chain. It is:
| digits | part | |---|---| | 1 | extension digit, 0-9, the company's own choice | | 4-12 | GS1 company prefix, as allocated by GS1 | | the rest, to make 17 | serial reference, zero-padded | | 1 | GS1 mod-10 check digit |
For example
build_sscc(1, 0614141, 123,456,789)→ 106141411234567897 GS1's own example: (00) 1 0614141 123456789 7build_sscc(0, 5012345, 0)→ 050123450000000008 serial zero is padded to fill the nine digits a 7-digit prefix leavesbuild_sscc(3, 5012345678, 42)→ 350123456780000420 a 10-digit prefix leaves six serial digits; check digit 0
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 build_sscc(extension_digit: i64, company_prefix: &str, serial_reference: i64) -> String
| extension_digit | int | 0 to 9, chosen by the company to widen its number range |
| company_prefix | string | the GS1 company prefix GS1 allocated, 4 to 12 digits, leading zeros kept |
| serial_reference | int | 0 or more; left-padded with zeros to fill the 16 digits after the extension digit |
| returns | string | 18 digits, check digit last, no "(00)" application identifier |
Your code names it in one line, in the file that uses it
fune!(logistics.sscc@^1); // then call build_sscc(…)
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
use super::validation_gs1_check_digit::gs1_check_digit; ← from validation.gs1-check-digit ^1.0.0 · built alongside by fune
/// Build an 18-digit SSCC: extension digit, GS1 company prefix, zero-padded
/// serial reference and GS1 mod-10 check digit.
///
/// # Panics
/// Panics on an extension digit outside 0-9, a prefix that is not 4 to 12
/// ASCII digits, or a serial reference that is negative or too long.
pub fn build_sscc(extension_digit: i64, company_prefix: &str, serial_reference: i64) -> String {
if !(0..=9).contains(&extension_digit) {
panic!("extensionDigit must be 0 to 9, received {}", extension_digit);
}
let len = company_prefix.len();
if !(4..=12).contains(&len) || !company_prefix.bytes().all(|b| b.is_ascii_digit()) {
panic!("companyPrefix must be 4 to 12 digits, received \"{}\"", company_prefix);
}
if serial_reference < 0 {
panic!("serialReference must not be negative, received {}", serial_reference);
}
// Prefix and serial share 16 digits, so a longer prefix leaves less room.
// Too large is an error: truncating would reuse another unit's number.
let room = 16 - len;
let serial = serial_reference.to_string();
if serial.len() > room {
panic!(
"serialReference must fit in {} digits after a {}-digit company prefix, received {}",
room, len, serial_reference
);
}
let data = format!("{}{}{:0>width$}", extension_digit, company_prefix, serial, width = room);
format!("{}{}", data, gs1_check_digit(&data))
}
pub fn fune_vector(args: &[Value]) -> Value {
Value::str(&build_sscc(args[0].as_i64(), args[1].as_str(), args[2].as_i64()))
}Install
fune build
With that line in your source, in a Rust project (language rust in fune.project), fune build resolves it and its 1 dependency, 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 logistics.sscc
The manifest, vectors and README with only the Rust implementation. Install it without the registry with fune add ./logistics.sscc-1.1.0-rust.fune, or fetch it from a terminal with fune pull logistics.sscc@1.1.0:rust.
The whole function, every language, is one file too: logistics.sscc-1.1.0.fune, 10,910 bytes, sha256 aea2a0319e89e237e8778fea97ac8cd01600de11bd63409a4546fd35791a5e55. 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 logistics.sscc
after — your function gets the result and the arguments, and returns the final result.
// fune: after logistics.sscc
replace — inside this capability’s code only, calls to a dependency go to your function, with the same signature. Other capabilities that use it are unaffected; write in * to replace it everywhere.
// fune: replace validation.gs1-check-digit in logistics.sscc
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 logistics.sscc --steps.
// fune: step logistics.sscc 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 | |
|---|---|---|---|
| GS1's own example: (00) 1 0614141 123456789 7 | 1, 0614141, 123,456,789 | → | 106141411234567897 |
| serial zero is padded to fill the nine digits a 7-digit prefix leaves | 0, 5012345, 0 | → | 050123450000000008 |
| a 10-digit prefix leaves six serial digits; check digit 0 | 3, 5012345678, 42 | → | 350123456780000420 |
| leading zeros in the prefix are kept | 9, 0000123, 5 | → | 900001230000000058 |
| largest serial a 12-digit prefix allows | 0, 123456789012, 9,999 | → | 012345678901299996 |
| a 4-digit prefix leaves twelve serial digits | 1, 1234, 999,999,999,999 | → | 112349999999999999 |
| serial one too large for a 12-digit prefix | 0, 123456789012, 10,000 | → | error: serialReference must fit in 4 digits |
| serial too large for a 7-digit prefix | 1, 0614141, 1,000,000,000 | → | error: serialReference must fit in 9 digits |
| negative serial | 1, 0614141, -1 | → | error: serialReference must not be negative |
| extension digit above 9 | 10, 0614141, 1 | → | error: extensionDigit must be 0 to 9 |
Show the other 6 tests
| Case | Arguments | Expected | |
|---|---|---|---|
| negative extension digit | -1, 0614141, 1 | → | error: extensionDigit must be 0 to 9 |
| prefix too short | 1, 123, 1 | → | error: companyPrefix must be 4 to 12 digits |
| prefix too long | 1, 1234567890123, 1 | → | error: companyPrefix must be 4 to 12 digits |
| prefix with a letter | 1, 06141A1, 1 | → | error: companyPrefix must be 4 to 12 digits |
| prefix with a space is not tidied | 1, 0614 141, 1 | → | error: companyPrefix must be 4 to 12 digits |
| GS1's check digit worked example: SSCC 37610425002123456 takes 9 | 3, 7610425, 2,123,456 | → | 376104250021234569 |
More from the author
So a 7-digit prefix leaves 9 digits of serial reference and a 10-digit prefix leaves 6. A serial reference too large for the room left is an error rather than being truncated, because a truncated serial silently re-uses another pallet's number.
## Check digit
The standard GS1 mod-10 over the 17 data digits, from `validation.gs1-check-digit` since 1.1.0 (1.0.0 repeated the arithmetic here, because `validation.gtin` only accepted GTIN lengths). The answers are the same. GS1's own example, `(00) 1 0614141 123456789 7`, is a vector, and so is the SSCC from GS1's check digit worked example, `376104250021234569`.
## What it does not do
- It returns the bare 18 digits. The label's human-readable line shows them after the application identifier as `(00) 106141411234567897`; the barcode (GS1-128) encodes `00` followed by the 18 digits. - It does not allocate serial references. GS1 asks that an SSCC is not re-used for at least a year after the unit was shipped; keeping the counter is the caller's job. - It cannot tell whether a prefix was really allocated to you.
## Source
GS1 General Specifications, the SSCC (logistic units) and check digit calculation sections, https://www.gs1.org/standards/barcodes-epcrfid-id-keys/gs1-general-specifications ; GS1 UK, "How do I create SSCCs and logistics labels?", https://www.gs1uk.org/knowledge-hub/barcodes/how-do-i-create-ssccs-and-logistics-labels
Files
| Path | Bytes |
|---|---|
| README.md | 1,817 |
| impl/python.py | 1,694 |
| impl/rust.rs | 1,651 |
| impl/typescript.ts | 1,501 |
| vectors.json | 2,065 |