Metadata-Version: 2.4
Name: logisticspy
Version: 0.3.0
Summary: A Python toolkit for logistics and supply chain calculations
Author: krishnanz550i-cmyk
License: MIT
Project-URL: Homepage, https://github.com/krishnanz550i-cmyk/logisticspy
Project-URL: Issues, https://github.com/krishnanz550i-cmyk/logisticspy/issues
Keywords: logistics,supply chain,freight,chargeable weight,uom,unit of measure,packaging,warehouse,inventory,grn
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Topic :: Office/Business
Classifier: Topic :: Scientific/Engineering
Requires-Python: >=3.8
Description-Content-Type: text/markdown
License-File: LICENSE
Provides-Extra: dev
Requires-Dist: pytest; extra == "dev"
Dynamic: license-file

# 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.

```bash
pip install logisticspy
```

---

## What's inside

| Module | What it does |
|---|---|
| [`logisticspy.weight`](#the-weight-module-chargeable--volumetric-weight) | Calculate chargeable weight & volumetric weight for air, courier, road, rail, and sea freight |
| [`logisticspy.packwise`](#the-packwise-module-uom-conversion--packaging-consolidation) | 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)

```python
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)

```python
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 |

```python
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`)

```python
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)

```python
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

```python
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:

```python
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_sale` for 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](CONTRIBUTING.md) for guidelines.

## License

MIT
