feat: add partial shipment support (Phase 3)

- Add shipped_quantity field to OrderItem for tracking partial fulfillment
- Add partially_shipped order status for orders with partial shipments
- Add fulfill_item method for shipping individual items with quantities
- Add get_shipment_status method for detailed shipment tracking
- Add vendor API endpoints for partial shipment operations:
  - GET /orders/{id}/shipment-status - Get item-level shipment status
  - POST /orders/{id}/items/{item_id}/ship - Ship specific item quantity
- Automatic status updates: partially_shipped when some items shipped,
  shipped when all items fully shipped
- Migration to add shipped_quantity column with upgrade for existing data
- Update documentation with partial shipment usage examples

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-01-01 18:28:54 +01:00
parent 55c1a43f56
commit 5a3f2bce57
6 changed files with 597 additions and 5 deletions

View File

@@ -242,9 +242,130 @@ recent = db.query(InventoryTransaction).filter(
).order_by(InventoryTransaction.created_at.desc()).limit(10).all()
```
## Partial Shipments (Phase 3)
Orders can be partially shipped, allowing vendors to ship items as they become available.
### Status Flow
```
pending → processing → partially_shipped → shipped → delivered
↘ ↗
→ shipped (if all items shipped at once)
```
### OrderItem Tracking
Each order item has a `shipped_quantity` field:
```python
class OrderItem:
quantity: int # Total ordered
shipped_quantity: int # Units shipped so far
@property
def remaining_quantity(self):
return self.quantity - self.shipped_quantity
@property
def is_fully_shipped(self):
return self.shipped_quantity >= self.quantity
```
### API Endpoints
#### Get Shipment Status
```http
GET /api/v1/vendor/orders/{order_id}/shipment-status
```
Returns item-level shipment status:
```json
{
"order_id": 123,
"order_number": "ORD-1-20260101-ABC123",
"order_status": "partially_shipped",
"is_fully_shipped": false,
"is_partially_shipped": true,
"shipped_item_count": 1,
"total_item_count": 3,
"total_shipped_units": 2,
"total_ordered_units": 5,
"items": [
{
"item_id": 1,
"product_name": "Widget A",
"quantity": 2,
"shipped_quantity": 2,
"remaining_quantity": 0,
"is_fully_shipped": true
},
{
"item_id": 2,
"product_name": "Widget B",
"quantity": 3,
"shipped_quantity": 0,
"remaining_quantity": 3,
"is_fully_shipped": false
}
]
}
```
#### Ship Individual Item
```http
POST /api/v1/vendor/orders/{order_id}/items/{item_id}/ship
Content-Type: application/json
{
"quantity": 2 // Optional - defaults to remaining quantity
}
```
Response:
```json
{
"order_id": 123,
"item_id": 1,
"fulfilled_quantity": 2,
"shipped_quantity": 2,
"remaining_quantity": 0,
"is_fully_shipped": true
}
```
### Automatic Status Updates
When shipping items:
1. If some items are shipped → status becomes `partially_shipped`
2. If all items are fully shipped → status becomes `shipped`
### Service Usage
```python
from app.services.order_inventory_service import order_inventory_service
# Ship partial quantity of an item
result = order_inventory_service.fulfill_item(
db=db,
vendor_id=vendor_id,
order_id=order_id,
item_id=item_id,
quantity=2, # Ship 2 units
)
# Get shipment status
status = order_inventory_service.get_shipment_status(
db=db,
vendor_id=vendor_id,
order_id=order_id,
)
```
## Future Enhancements
1. **Multi-Location Selection** - Choose which location to draw from
2. **Backorder Support** - Handle orders when stock is insufficient
3. **Return Processing** - Increase stock when orders are returned
4. **Transaction API** - Endpoint to view inventory history