milk_manager/milk_app/consumption.py
2025-04-09 20:34:32 +08:00

96 lines
4.2 KiB
Python

import datetime
from flask import Blueprint, request, jsonify
from flask_jwt_extended import jwt_required, get_jwt_identity
from .database import db, ConsumptionRecord, MilkBatch, User
from .utils import convert_volume
consumption_bp = Blueprint('consumption', __name__, url_prefix='/api/consumption')
@consumption_bp.route('', methods=['POST'])
@jwt_required()
def record_consumption():
current_user_id = get_jwt_identity()
data = request.get_json()
if not data: return jsonify({"error": "Request must be JSON"}), 400
batch_id = data.get('milkBatchId')
amount_str = data.get('amountConsumed')
unit = data.get('unitConsumed') # 'ml', 'L', 'items'
consumed_at_str = data.get('dateConsumed', datetime.datetime.utcnow().isoformat())
# --- Validation ---
if not batch_id or amount_str is None or not unit:
return jsonify({"error": "Missing fields: milkBatchId, amountConsumed, unitConsumed"}), 400
if unit not in ['ml', 'L', 'items']:
return jsonify({"error": "Invalid unitConsumed."}), 400
try:
amount = float(amount_str)
if amount <= 0: raise ValueError("Amount must be positive.")
except (ValueError, TypeError):
return jsonify({"error": "Invalid number format for amountConsumed."}), 400
try:
# Attempt to parse date, fallback to now if invalid format
consumed_time = datetime.datetime.fromisoformat(consumed_at_str.replace('Z', '+00:00'))
except ValueError:
consumed_time = datetime.datetime.utcnow()
# --- Find Batch & Check Status ---
batch = MilkBatch.query.filter_by(id=batch_id, user_id=current_user_id, is_deleted=False).first()
if not batch: return jsonify({"error": "Milk batch not found or invalid."}), 404
if batch.remaining_volume < 0.001: return jsonify({"error": "Selected milk batch is empty."}), 400
if batch.expiry_date < datetime.date.today():
# Optionally warn or prevent consuming expired? For now, allow but maybe log.
print(f"Warning: Consuming from expired batch {batch_id}")
# --- Calculate Consumed Volume in Batch's Unit ---
try:
consumed_volume_in_batch_unit = 0
if unit == 'items':
consumed_volume_in_batch_unit = amount * batch.volume_per_item
elif unit == batch.volume_unit:
consumed_volume_in_batch_unit = amount
else: # Convert (e.g., L consumed from ml batch)
consumed_volume_in_batch_unit = convert_volume(amount, unit, batch.volume_unit)
# --- Check Availability & Update ---
# Use tolerance for float comparison
if consumed_volume_in_batch_unit > (batch.remaining_volume + 0.001):
return jsonify({"error": f"Not enough milk. Only {batch.remaining_volume:.2f} {batch.volume_unit} left."}), 400
batch.remaining_volume -= consumed_volume_in_batch_unit
batch.remaining_volume = max(0, batch.remaining_volume) # Prevent going negative
# --- Record Consumption ---
new_record = ConsumptionRecord(
user_id=current_user_id,
milk_batch_id=batch_id,
amount_consumed=amount, # Log the originally entered amount/unit
unit_consumed=unit,
consumed_at=consumed_time
)
db.session.add(new_record)
db.session.commit()
# Return updated batch state
return jsonify(batch.to_dict()), 201
except (ValueError, TypeError) as e:
db.session.rollback()
return jsonify({"error": f"Invalid input or calculation error: {e}"}), 400
except Exception as e:
db.session.rollback()
print(f"Error recording consumption: {e}")
return jsonify({"error": "Database error occurred."}), 500
@consumption_bp.route('', methods=['GET'])
@jwt_required()
def get_consumption_history():
current_user_id = get_jwt_identity()
# Add pagination later if needed
limit = request.args.get('limit', 50, type=int)
offset = request.args.get('offset', 0, type=int)
records = ConsumptionRecord.query.filter_by(user_id=current_user_id)\
.order_by(ConsumptionRecord.consumed_at.desc())\
.limit(limit).offset(offset).all()
return jsonify([record.to_dict() for record in records]), 200