A Python toolkit for logistics and supply chain calculations
Project description
logisticspy
A growing Python toolkit for logistics and supply chain calculations.
logisticspy gives developers clean, well-tested building blocks for common logistics problems — from freight weight calculations to warehouse inventory management. Each module is self-contained, production-ready, and designed to slot into existing systems without friction.
pip install logisticspy
What's inside
| Module | What it does |
|---|---|
logisticspy.weight |
Calculate chargeable weight & volumetric weight for air, courier, road, rail, and sea freight |
logisticspy.packwise |
Convert between units of measure (UOM) and consolidate packaging hierarchies — pallets → cartons → each |
More modules are planned: volume calculations, freight cost estimation, transit time tools, and more.
Quick start
Chargeable weight (air freight)
import logisticspy
result = logisticspy.calculate(
length=60, width=40, height=40, unit="cm",
actual_weight=18, weight_unit="kg",
mode="air",
)
print(result.volumetric_weight_kg) # 16.0
print(result.chargeable_weight_kg) # 18.0 ← actual weight wins
print(result.basis) # "actual"
UOM conversion & stock tracking (warehouse)
from decimal import Decimal
from logisticspy.packwise import (
discrete_standard, PALLET, EACH,
ProductUOMConfig, GRNBehavior, StockLedger,
)
hierarchy = discrete_standard() # PLT → CTN → BOX → EA (10 / 12 / 6)
cfg = ProductUOMConfig(
sku="OIL-500ML",
hierarchy=hierarchy,
purchase_uom=PALLET,
sale_uom=EACH,
grn_behavior=GRNBehavior.CONSOLIDATE_UP,
)
ledger = StockLedger()
ledger.register(cfg)
ledger.receive_grn("OIL-500ML", [(PALLET, Decimal("1"))], reference="GRN-001")
ledger.fulfil_sale("OIL-500ML", Decimal("100"), reference="SO-001")
print(ledger.stock_level("OIL-500ML").base_qty) # 620
print(ledger.stock_level("OIL-500ML").breakdown()) # [(CTN, 8), (BOX, 7), (EA, 2)]
The weight module: chargeable & volumetric weight
Carriers bill shipments based on whichever is greater: the actual weight or the volumetric weight (calculated from package dimensions). This module handles that calculation cleanly, with support for multiple units, transport modes, named divisor presets, and multi-package consignments.
Supported modes and default divisors
| Mode | Default divisor (cm³/kg) |
|---|---|
air |
6000 |
courier |
5000 |
road |
3000 |
rail |
3000 |
sea |
N/A — uses CBM × 1000 |
Note: These are sensible defaults based on common industry conventions. They are not tied to any specific carrier. Always confirm the applicable divisor with your carrier or contract for billing-critical calculations.
Divisor presets
Two divisor values — 5000 and 6000 — are both widely used, sometimes even by the same carrier depending on the service or region. Use named presets to switch between them easily:
| Preset | Divisor |
|---|---|
"a" |
5000 |
"b" |
6000 |
import logisticspy
pkg = dict(length=60, width=40, height=40, actual_weight=10, mode="air")
result_a = logisticspy.calculate(**pkg, divisor_preset="a") # divisor 5000
result_b = logisticspy.calculate(**pkg, divisor_preset="b") # divisor 6000
print(result_a.volumetric_weight_kg) # 19.2
print(result_b.volumetric_weight_kg) # 16.0
# Or pass your carrier's exact divisor directly:
result = logisticspy.calculate(**pkg, divisor=4500)
Units
Input dimensions: cm, m, mm, in, ft (default: cm)
Input weights: kg, g, lb, oz (default: kg)
result = logisticspy.calculate(
length=20, width=15, height=10, unit="in",
actual_weight=5, weight_unit="lb",
mode="courier",
)
Output is always normalized:
| Field | Unit |
|---|---|
actual_weight_kg, volumetric_weight_kg, chargeable_weight_kg |
kilograms (kg) |
volume_m3 |
cubic metres (m³) |
Sea freight (CBM)
result = logisticspy.calculate(
length=1, width=1, height=1, unit="m",
actual_weight=500, mode="sea",
)
print(result.volumetric_weight_kg) # 1000.0 (1 CBM = 1000 kg equivalent)
Multi-package consignments
packages = [
{"length": 50, "width": 40, "height": 40, "actual_weight": 10},
{"length": 60, "width": 40, "height": 40, "actual_weight": 25, "quantity": 2},
]
result = logisticspy.calculate_consignment(packages, mode="air")
print(result.total_actual_weight_kg)
print(result.total_volumetric_weight_kg)
print(result.total_chargeable_weight_kg)
Use per_piece=True if your carrier calculates chargeable weight per package and then sums them:
result = logisticspy.calculate_consignment(packages, mode="air", per_piece=True)
The packwise module: UOM conversion & packaging consolidation
In warehouses and distribution centres, goods are often purchased in one unit of measure (pallets, drums, bulk bags) but sold in another (loose units, kilograms, packs). packwise bridges that gap.
It always stores stock internally in base units — the smallest unit in the hierarchy — so all conversions are computed from a single number with no rounding drift across levels.
Preset hierarchies
Five ready-to-use presets cover the most common industries. All conversion factors are overridable.
| Preset | Hierarchy | Default factors | Base unit |
|---|---|---|---|
discrete_standard() |
PLT → CTN → BOX → EA | 10 / 12 / 6 | Each |
dry_bulk() |
PLT → BAG → KG → G | 40 / 25 / 1000 | Gram (decimal) |
liquid_bulk() |
PLT → DRUM → L → ML | 4 / 200 / 1000 | mL (decimal) |
apparel() |
PLT → CTN → PACK → PC | 20 / 10 / 5 | Piece |
pharma() |
PLT → SHIP → INNER → STRIP → TAB | 20 / 12 / 10 / 10 | Tablet |
What's included
UOMHierarchy— model any parent→child packaging chain with conversion factors; convert between any two levels.ProductUOMConfig/GRNBehavior— per-SKU purchase/sale UOMs and goods-receipt handling (NORMALIZE_TO_BASE,CONSOLIDATE_UP,KEEP_AS_IS,CUSTOM), with per-SKU factor overrides.- Stateless converters —
convert,consolidate_loose,process_grn_line,split_for_salefor one-off calculations without maintaining state. StockLedger— stateful inventory tracking in base units with a full audit trail (GRN, sale, transfer, adjustment).PackwisePlugin— hooks that turn logisticspy GRN / PO / SO / stock transfer documents into ledger movements.
Stock is tracked with decimal.Decimal throughout for exact arithmetic — no floating-point drift.
Roadmap
logisticspy is designed to grow into a comprehensive logistics toolkit. Current modules:
- ✅
weight— chargeable & volumetric weight - ✅
packwise— UOM conversion & packaging consolidation
Planned:
- 🔜 Volume and freight calculations
- 🔜 Freight cost estimation helpers
- 🔜 Transit time tools
Disclaimer
This library implements widely-used industry conventions for illustrative and estimation purposes. Divisors and CBM ratios vary by carrier, service level, region, and contract terms. Always confirm exact billing methodology with your carrier or freight forwarder for invoicing-critical calculations.
Contributing
Contributions, bug reports, and feature requests are welcome. See CONTRIBUTING.md for guidelines.
License
MIT
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file logisticspy-0.3.0.tar.gz.
File metadata
- Download URL: logisticspy-0.3.0.tar.gz
- Upload date:
- Size: 32.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
39ec740de7c69118f3a930466d56d168b472484f4239cf531b4f574703e8408a
|
|
| MD5 |
f49d25284403d9eb634995695f227c80
|
|
| BLAKE2b-256 |
110da92ecd35c68a81037e35c949c27e5c781745b33a459a3a0d891a29a09685
|
Provenance
The following attestation bundles were made for logisticspy-0.3.0.tar.gz:
Publisher:
publish.yml on krishnanz550i-cmyk/logisticspy
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
logisticspy-0.3.0.tar.gz -
Subject digest:
39ec740de7c69118f3a930466d56d168b472484f4239cf531b4f574703e8408a - Sigstore transparency entry: 1840923828
- Sigstore integration time:
-
Permalink:
krishnanz550i-cmyk/logisticspy@63a447c5388af613b3d1ede5f13a858c9829741c -
Branch / Tag:
refs/tags/v0.3.0 - Owner: https://github.com/krishnanz550i-cmyk
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@63a447c5388af613b3d1ede5f13a858c9829741c -
Trigger Event:
release
-
Statement type:
File details
Details for the file logisticspy-0.3.0-py3-none-any.whl.
File metadata
- Download URL: logisticspy-0.3.0-py3-none-any.whl
- Upload date:
- Size: 30.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c3d25159dcc356ac14f2cbfe60a4a7322afeb8bd71115334f59ec2c5dfb54782
|
|
| MD5 |
5a2933f20633bfd3d422a75df01a6daa
|
|
| BLAKE2b-256 |
007a90ae2f8969480f4c50968fbc6085aa870950e47fd8712a749a6519c168f2
|
Provenance
The following attestation bundles were made for logisticspy-0.3.0-py3-none-any.whl:
Publisher:
publish.yml on krishnanz550i-cmyk/logisticspy
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
logisticspy-0.3.0-py3-none-any.whl -
Subject digest:
c3d25159dcc356ac14f2cbfe60a4a7322afeb8bd71115334f59ec2c5dfb54782 - Sigstore transparency entry: 1840923893
- Sigstore integration time:
-
Permalink:
krishnanz550i-cmyk/logisticspy@63a447c5388af613b3d1ede5f13a858c9829741c -
Branch / Tag:
refs/tags/v0.3.0 - Owner: https://github.com/krishnanz550i-cmyk
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@63a447c5388af613b3d1ede5f13a858c9829741c -
Trigger Event:
release
-
Statement type: