Skip to content
Back to skills

Inventree Parts

ASecurity

Sync parts into InvenTree with stable IPNs and full supplier/cost data. Use when adding or updating a part in InvenTree, assigning an IPN, importing an LCSC part number (C####) with inventree-part-import, importing an Alibaba/AliExpress order (toggles/rotaries/encoders/buttons), linking a backorder-alternate SKU, filling per-panel cost, or reconciling KiCad LCSC labels against InvenTree. Owns the IPN scheme, the import workflow, and the non-LCSC supplier handling.

  • 3 stars
  • 0 votes
  • 0 copies
  • 2 views
  • Added September 12, 2026
developmentpythonrustgoc#expressgitapi

Works with

  • cli
  • api
  • mcp

Security analysis

A100/100

Scanned September 12, 2026

npx -y skills add OpenSkyHawk/OpenSkyhawk --skill inventree-parts --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Inventree Parts?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Inventree Parts
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/openskyhawk-inventree-parts/badge)](https://www.skillsdirectory.com/skills/openskyhawk-inventree-parts)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: inventree-parts
description: Sync parts into InvenTree with stable IPNs and full supplier/cost data. Use when adding or updating a part in InvenTree, assigning an IPN, importing an LCSC part number (C####) with inventree-part-import, importing an Alibaba/AliExpress order (toggles/rotaries/encoders/buttons), linking a backorder-alternate SKU, filling per-panel cost, or reconciling KiCad LCSC labels against InvenTree. Owns the IPN scheme, the import workflow, and the non-LCSC supplier handling.
---

# InvenTree Parts — IPN + import + cost

InvenTree (`http://192.168.85.85`, token at `~/.config/inventree/token`) is the parts + costing
service. Goal: every part has a **stable descriptive IPN** (identity) plus complete supplier
data (MPN, datasheet, parameters, **price**) so per-panel cost rolls up and BOMs stop churning
when an LCSC SKU backorders. Full history/spec: GitHub issue **#238**.

## Two identities, never conflate
- **IPN** = the canonical part identity (our descriptive scheme). What a BOM/design references.
- **LCSC C# / MPN** = the *buyable* SupplierPart SKU. Resolved at order time; can change with
  availability without touching the IPN.

## IPN scheme (descriptive)
- **R:** `RES-<val>-<tol>-<pkg>` — val in R/K/M (`10K`,`4K7`,`100R`), milliohm shunt `0R010`;
  tol `1PCT`/`0P1PCT`. e.g. `RES-10K-1PCT-0805`.
- **C:** `CAP-<CER|C0G|ELEC>-<val>-<volt>-<pkg>` — val `100NF`/`2P2UF`; pkg `0805`/`D4`/`D5`
  (elec cans; **D4=Ø4×5.4 for 16–25V, D5=Ø5×5.4 for 35V** — size scales with voltage).
- **IC:** `IC-<part>-<pkg>` (`IC-STM32F103C8-LQFP`, `IC-MCP23017-SSOP`). **Q/D/LED/TVS/XTAL/
  FB/NTC/FUSE/SW/MOD/CONN** similar; `CONN-<XH|MF|HDR>-<pins>P`; module `MOD-RP2040-ZERO`;
  on-board tact `SW-TACT-6MM`; fuse **holder** `FUSE-HOLDER-BLADE-DIP4`.
- Off-board controls (encoders/switches/pots/OLED/stepper) get **no IPN in KiCad** (no
  footprint) but **do** get an IPN in InvenTree for cost (`SW-TOGGLE-*`, `ENC-EC11`, …).

## The import tool (third-party — do NOT fork)
`inventree-part-import` (https://github.com/30350n/inventree-part-import), run as
`~/ipi-venv/bin/python -m inventree_part_import -o lcsc <C#…>`. Config dir:
`~/Library/Application Support/inventree_part_import/` (`inventree.yaml` host/token,
`categories.yaml` LCSC→our-category aliases, `config.yaml`, `suppliers.yaml` LCSC=pk1). **The
config is version-controlled at `tools/inventree-part-import/`** (minus the token) — copy those
into the config dir on a new machine. When the tool errors "failed to match category", add the
LCSC leaf category to `categories.yaml` `_aliases` (we alias into our flat tree, not
create-on-the-fly).
It imports supplier/MPN/datasheet/parameters/**price**. **It has NO IPN capability**
(`ApiPart` has no IPN field; `get_part_data()` emits only name(=MPN)/description/link/
active/component/purchaseable; `hooks.py` can't inject IPN). So **IPN is our job**, set on a
placeholder the tool then enriches.

## Adding/syncing a single LCSC part — the workflow (PROVEN)
1. **Create placeholder** InvenTree part: `name` = the **MPN**, `IPN` = our scheme, `category`
   set. (MPN is on the KiCad symbol's `MP` field.) One `POST /api/part/`.
2. **RUN THE TOOL** (required — placeholder alone gives IPN+category only, NO cost/params/
   datasheet): `~/ipi-venv/bin/python -m inventree_part_import -o lcsc <C#>`. It finds the part
   by **name == MPN** (`get_part`), **updates in place** (`update_object_data` PATCHes only its
   keys → **IPN preserved**), and **skips category matching** (only runs when creating new).
3. **Verify** the part now has a supplier part + price + params (not just IPN).

## Duplicate-avoidance (critical)
The tool matches an existing part by: (a) an existing SupplierPart-with-a-**manufacturer_part**
by SKU, else (b) a ManufacturerPart by MPN, else (c) `get_part(MPN)` by **name**, else it
**CREATES NEW**. So a part that has a SupplierPart but **no manufacturer_part AND a
non-MPN (descriptive) name** will **DUPLICATE** on a tool run. Before re-syncing such a part,
**rename it to its MPN** (or add a manufacturer part) so it matches. **Dry mode (`--dry`) does
NOT reliably predict match-vs-create** — don't trust it; check the part's mfr-part/name instead.
Side effect: a successful sync **normalizes the part `name` to the MPN** (IPN stays the identity).

## Alternates = churn-proofing (one IPN, many SupplierParts)
The tool auto-enriches only the **name-matched primary**, BUT it will also enrich an
**alternate in place** if the alternate's SupplierPart already exists **with a manufacturer_part
linked** — the tool matches by SKU → its manufacturer_part → the part, and updates. So the clean
alternate procedure is a **stub + tool run** (don't hand-copy fields):
1. Create the stub on the **target IPN'd part** (only the identity bits — MPN + SKU):
   - company (mfr): find or `POST /api/company/` `{name, is_manufacturer:true}`
   - `POST /api/company/part/manufacturer/` `{part, manufacturer, MPN}`
   - `POST /api/company/part/` `{part, supplier:1, SKU:<C#>, manufacturer_part:<mfr_pk>}`
     — the **manufacturer_part link is REQUIRED** (a bare SupplierPart with no mfr link makes the
     tool create a duplicate instead of matching).
2. `~/ipi-venv/bin/python -m inventree_part_import -o lcsc <alt C#>` → matches the stub by SKU →
   **updates in place**, filling price/packaging/link/description/params/datasheet.
Do this **just-in-time** when a backorder/mismatch appears — not a bulk upfront pass.

**Don't fuss over the part `name`.** Nothing keys off it — BOMs use the **IPN**, ordering uses
the **SKU**, cost rolls up by part. The tool forces `name=MPN` on every import, so an alternate
run flips the name to the alternate's MPN + overwrites the description (IPN + supplier links are
unaffected). That's fine — the name is a cosmetic UI label; the **IPN is the stable identity**.
Only reset the name (`PATCH /api/part/<pk>/ {name, description}`) if a part labelled by an
obscure alternate MPN bothers you visually — it's optional, not required.

**Flag an out-of-stock supplier part** (keep the IPN, steer ordering to the alternate):
`PATCH /api/company/part/<sp>/` `{active:false, note:"OUT OF STOCK (date) — use <C#>"}`.

## Non-LCSC suppliers (Alibaba/AliExpress)
Toggles/rotaries/EC11/buttons come from **Alibaba** (e.g. Wenzhou Wintai), never LCSC. Model:
create the vendor as a Company (supplier+manufacturer), then Parts (Controls category) with our
IPN + a SupplierPart + price. **Fold shipping into unit cost** (order shipping ÷ total units,
added to each $/ea → landed price break). LCSC field stays empty for these.

## InvenTree API cheat-sheet (urllib + Token; retry/backoff on ≥500; **report every write**)
- Part: `POST /api/part/` `{name, category, IPN, description, component:true, purchaseable:true, active:true}`
- SupplierPart: `POST /api/company/part/` `{part, supplier, SKU}` (LCSC supplier = **pk1**)
- ManufacturerPart: `POST /api/company/part/manufacturer/` `{part, manufacturer, MPN}`
- Price break: `POST /api/company/price-break/` `{part:<supplierpart_pk>, quantity, price, price_currency}`
- Company: `POST /api/company/` `{name, is_supplier, is_manufacturer, is_customer, website, description}`
- Set IPN on existing: `PATCH /api/part/<pk>/` `{IPN}`
- Categories: Passives=7, Semiconductors=8, Connectors=9, Crystals=11, Modules=10, Controls=2.

## Pricing gotcha — add a qty=1 price break (REQUIRED for cost rollup)
LCSC price breaks start at **qty≥20** (no qty=1), and `inventree-part-import` imports them
verbatim. But InvenTree computes **single-unit** part pricing from a break at/below qty=1 — with
none, `supplier_price`/`overall` = **None** for any part without stock/purchase history, so it
**drops out of the BOM cost rollup** (assembly total reads ~10× low). **Fix: after import, add a
qty=1 break (= the lowest existing break price) to each supplier part** that lacks one:
`POST /api/company/price-break/` `{part:<sp_pk>, quantity:1, price:<lowest>, price_currency}`.
Then the rollup sums correctly. (Parts purchased via a PO get stock pricing and mask this — it
bites the non-purchased ones: TVS, dividers, etc.)

Note: assembly BOM rollups recompute via InvenTree's **scheduled** pricing task
(`scheduled_for_update=True`); the REST `PUT /api/part/<pk>/pricing/` only *flags* it — a full
recalc needs the server-side scheduled task / admin "Recalculate pricing" to run.

## Cost-refresh routine (manual, not scheduled)
`inventree_part_import --update-recursive Electronics -i false` re-pulls current LCSC
price/stock/datasheet/params for all electronic parts (IPN-safe; names flip to last-imported
MPN — ignore). Run **on demand** (before an order / ~monthly) via
`tools/inventree-part-import/refresh-costs.sh`. **No OS automation (no cron/launchd)** — it's a
manual routine; run it, or ask Claude to.

## Assembly tiers (BOM lives on the assembled PCB)
- **Bare PCB** = `<BOARD>-PCB` (fabricated, no parts).
- **Assembled PCB (PCBA)** = `<BOARD>-PCBA` — populated board; **the component BOM (from KiCad,
  joined by IPN) lives here**. Panel boards use this suffix (ASN-41-PCBA, ARC-51A-PCBA,
  APN-153-PCBA). Infrastructure boards (PDU / Gateway / PanelGroup) keep the plain IPN as their
  top level (no cockpit panel above them).
- **Panel assembly** = plain `<BOARD>` (e.g. `ASN-41`) — reserved for the future panel level
  (PCBA + off-board controls/toggles + faceplate + hardware). Off-board controls belong here,
  not on the PCBA BOM.
- **Revisions:** fabbed board → lock rev 0.1.0 (as-fabbed) + open rev 0.2.0 for changes; unfabbed
  → single open 0.1.0, edit in place until fab. **Value change on same footprint = same rev;
  footprint/routing change = new rev.** Base-board 0.1.0 parts may be pre-**locked** → unlock
  (`PATCH {locked:false}`) → add BOM items → relock. Build BOM from `kicad-cli sch export bom
  --fields '${IPN},Reference' --group-by '${IPN}'` (qty = ref count), `sub_part` by IPN.

## Config options available (not all used — reference)
The tool's config supports more than we use, if a need arises:
- **`auto_detect_columns`** (config.yaml) — import from a **BOM/CSV file** (add our `LCSC` column
  header): `inventree_part_import <bom.csv>` instead of listing C#s. Nicer for new-board bulk.
- **`parameters.yaml` + category `_parameters`** — import structured specs (Capacitance, Tolerance,
  Voltage…). We skip these — the **IPN already encodes value/tol/voltage**, so params are optional.
- **`_ignore: true`** on own-product categories (Assemblies/Bare PCB/Products) — hygiene.
- **`hooks.py`** — pre-creation fixups of `category_path`/`parameters` (CANNOT set IPN — no IPN
  field on ApiPart).
- Reference config (comprehensive deep tree + 106 params + hooks):
  https://github.com/30350n/inventree_part_import_config — we deliberately keep a **flat** tree
  instead (curated, no bloat).

## Rules
- **Never source new LCSC SKUs** — use only parts already selected (in a schematic/InvenTree);
  gap-list the rest for the user. **Report every InvenTree write for review.**
- Missing an LCSC **category alias** → the tool errors on *create* ("failed to match category").
  Add the alias to `categories.yaml` (or use the placeholder path, which skips category match).
- KiCad symbols carry **IPN + LCSC co-equal** (see `pcb-design`); this skill keeps InvenTree in
  sync with those.

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.