Overview
Receive inventory into the system by creating a new batch. This endpoint creates both a batch record and an entry movement for tracking purposes.
Authentication
Required Roles: admin or gestor
# backend/Product/Adapters/batch_controller.py:14
@router.route('/receive', methods=['POST'])
@require_role('admin', 'gestor')
def receive_purchase():
This endpoint requires Bearer token authentication. The consultor role cannot create batches.
Request Body
The UUID of the product to receive inventory for. Product must exist in the system.
Number of units received. Must be greater than 0.
Cost per unit paid to the supplier. Used for COGS calculation.
Optional UUID of the supplier who provided this inventory.
Optional ISO 8601 datetime string. Batches with expiration dates are prioritized for sale (FEFO - First Expired, First Out).Format: YYYY-MM-DDTHH:MM:SSZ or YYYY-MM-DDTHH:MM:SS+00:00
Request Example
{
"product_id": "a8b12c3d-4e5f-6789-0abc-def123456789",
"quantity": 100,
"unit_cost": 12.50,
"supplier_id": "supplier-uuid-here",
"expiration_date": "2026-12-31T23:59:59Z"
}
Response
Unique batch identifier (UUID)
Product UUID this batch belongs to
Total quantity received in this batch
Current quantity available (equals initial_quantity at creation)
Cost per unit for this batch
ISO 8601 timestamp when the batch was created
ISO 8601 expiration date or null if not applicable
Supplier UUID or null if not provided
Reference to the associated entry movement
Response Example
{
"id": "batch-uuid-abc123",
"product_id": "a8b12c3d-4e5f-6789-0abc-def123456789",
"initial_quantity": 100,
"available_quantity": 100,
"unit_cost": 12.50,
"purchase_date": "2026-03-04T10:30:00+00:00",
"expiration_date": "2026-12-31T23:59:59+00:00",
"supplier_id": "supplier-uuid-here",
"entry_transaction_ref": "movement-uuid-xyz789"
}
Error Responses
400 Bad Request - Missing Fields
{
"error": "Missing required fields"
}
Returned when product_id, quantity, or unit_cost are not provided.
400 Bad Request - Invalid Quantity
{
"detail": "Quantity must be greater than 0"
}
404 Not Found - Product Not Found
{
"detail": "Product not found"
}
401 Unauthorized
Returned when authentication token is missing or invalid.
403 Forbidden
Returned when user role is consultor (insufficient permissions).
Implementation Details
The endpoint performs the following operations:
- Validates required fields and product existence
- Creates batch with generated UUID and current timestamp
- Creates entry movement for audit trail
- Returns batch data with 201 status
# backend/Product/Domain/stock_service.py:18-55
def register_entry(self, product_id: str, quantity: int, unit_cost: float,
supplier_id: Optional[str] = None, expiration_date: Optional[datetime] = None) -> Tuple[Batch, Movement]:
if quantity <= 0:
raise HTTPException(status_code=400, detail="Quantity must be greater than 0")
product = self.product_repo.get_by_id(product_id)
if not product:
raise HTTPException(status_code=404, detail="Product not found")
# Create Batch
batch_id = str(uuid.uuid4())
new_batch = Batch(
id=batch_id,
product_id=product_id,
initial_quantity=quantity,
available_quantity=quantity,
unit_cost=unit_cost,
purchase_date=datetime.now(timezone.utc),
expiration_date=expiration_date,
supplier_id=supplier_id
)
self.repo.create_batch(new_batch)
# Create Entry Movement
mov_id = str(uuid.uuid4())
new_movement = Movement(
id=mov_id,
product_id=product_id,
type=MovementType.ENTRY,
quantity=quantity,
unit_price=unit_cost,
total_price=unit_cost * quantity,
reference_id=batch_id,
notes="Purchase Entry"
)
self.repo.create_movement(new_movement)
return new_batch, new_movement
Best Practices
Always provide expiration dates for perishable products. The system uses FEFO (First Expired, First Out) logic to prioritize batches nearing expiration.
Track supplier_id to enable supplier performance analysis and quality audits.