"""Logistics Blueprint: shipments, couriers, tracking. Prefix: /pos/api/logistics """ from flask import Blueprint, request, jsonify, g from middleware import require_auth from tenant_db import get_tenant_conn from services.logistics_engine import ( create_shipment, get_shipment, list_shipments, update_shipment_status, get_couriers, add_courier, ) logistics_bp = Blueprint('logistics', __name__, url_prefix='/pos/api/logistics') @logistics_bp.route('/shipments', methods=['GET']) @require_auth() def list_all_shipments(): status = request.args.get('status') courier_id = request.args.get('courier_id', type=int) related_type = request.args.get('related_type') related_id = request.args.get('related_id', type=int) page = int(request.args.get('page', 1)) per_page = min(int(request.args.get('per_page', 50)), 200) conn = get_tenant_conn(g.tenant_id) try: result = list_shipments( conn, g.tenant_id, status=status, courier_id=courier_id, related_type=related_type, related_id=related_id, page=page, per_page=per_page, ) return jsonify(result) finally: conn.close() @logistics_bp.route('/shipments', methods=['POST']) @require_auth() def create_new_shipment(): data = request.get_json() or {} conn = get_tenant_conn(g.tenant_id) try: result = create_shipment(conn, { 'tenant_id': g.tenant_id, 'branch_id': data.get('branch_id', g.branch_id), 'shipment_type': data.get('shipment_type', 'outbound'), 'related_type': data.get('related_type'), 'related_id': data.get('related_id'), 'courier_id': data.get('courier_id'), 'tracking_number': data.get('tracking_number'), 'origin_address': data.get('origin_address'), 'destination_address': data.get('destination_address'), 'recipient_name': data.get('recipient_name'), 'recipient_phone': data.get('recipient_phone'), 'estimated_delivery': data.get('estimated_delivery'), 'shipping_cost': data.get('shipping_cost'), 'weight_kg': data.get('weight_kg'), 'dimensions_cm': data.get('dimensions_cm'), 'notes': data.get('notes'), 'created_by': getattr(g, 'employee_id', None), }) return jsonify(result), 201 finally: conn.close() @logistics_bp.route('/shipments/', methods=['GET']) @require_auth() def get_shipment_detail(shipment_id): conn = get_tenant_conn(g.tenant_id) try: shipment = get_shipment(conn, shipment_id) if not shipment: return jsonify({'error': 'Shipment not found'}), 404 return jsonify(shipment) finally: conn.close() @logistics_bp.route('/shipments//status', methods=['PUT']) @require_auth() def update_status_endpoint(shipment_id): data = request.get_json() or {} new_status = data.get('status') if not new_status: return jsonify({'error': 'status is required'}), 400 conn = get_tenant_conn(g.tenant_id) try: result = update_shipment_status( conn, shipment_id, new_status, location=data.get('location'), description=data.get('description'), raw_response=data.get('raw_response'), ) return jsonify(result) except ValueError as e: return jsonify({'error': str(e)}), 400 finally: conn.close() @logistics_bp.route('/couriers', methods=['GET']) @require_auth() def list_couriers(): conn = get_tenant_conn(g.tenant_id) try: couriers = get_couriers(conn, g.tenant_id) return jsonify({'couriers': couriers}) finally: conn.close() @logistics_bp.route('/couriers', methods=['POST']) @require_auth() def create_courier(): data = request.get_json() or {} if not data.get('name') or not data.get('code'): return jsonify({'error': 'name and code are required'}), 400 conn = get_tenant_conn(g.tenant_id) try: cid = add_courier( conn, g.tenant_id, data['name'], data['code'], tracking_url_template=data.get('tracking_url_template'), api_endpoint=data.get('api_endpoint'), is_active=data.get('is_active', True), ) return jsonify({'id': cid, 'message': 'Courier created'}), 201 finally: conn.close()