Files
eamco_office_api/app/delivery/views.py
Edwin Eames 6d5f44db55 feat: 5-tier pricing, market ticker integration, and delivery stats
Major update spanning pricing, market data, and analytics:

- Pricing: Replace single-price service fees with 5-tier pricing for
  same-day, prime, and emergency deliveries across create/edit/finalize
- Market: Add Ticker_Price and CompanyPrice models with endpoints for
  live commodity prices (HO, CL, RB) and competitor price tracking
- Stats: Add daily/weekly/monthly gallons endpoints with multi-year
  comparison and YoY totals for the stats dashboard
- Delivery: Add map and history endpoints, fix finalize null-driver crash
- Schema: Change fill_location from INTEGER to VARCHAR(250), add
  pre_load normalization for customer updates, fix admin auth check

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 17:54:30 -05:00

1125 lines
39 KiB
Python
Executable File

import logging
from decimal import Decimal
from flask import request
from flask_login import current_user
from datetime import date, datetime, timedelta
from app.delivery import delivery
from app import db
from app.common.decorators import login_required as common_login_required
from app.common.responses import error_response, success_response
logger = logging.getLogger(__name__)
from sqlalchemy import or_
from app.classes.customer import (Customer_Customer)
from app.classes.delivery import (Delivery_Delivery,
Delivery_Delivery_schema,
Delivery_Notes_Driver,
)
from app.classes.employee import Employee_Employee
from app.classes.cards import Card_Card
from app.classes.pricing import Pricing_Oil_Oil
from app.classes.auth import Auth_User
from app.classes.promo import Promo_Promo
from app.classes.stats_customer import Stats_Customer
from app.classes.auto import Tickets_Auto_Delivery, Tickets_Auto_Delivery_schema
# This endpoint is fine, but I've added some comments for clarity.
@delivery.route("/map", methods=["GET"])
@common_login_required
def get_deliveries_for_map():
"""Get deliveries for map view by date."""
date_str = request.args.get('date')
if date_str:
try:
target_date = datetime.strptime(date_str, '%Y-%m-%d').date()
except ValueError:
return error_response("Invalid date format. Use YYYY-MM-DD", 400)
else:
target_date = date.today()
deliveries = (db.session
.query(Delivery_Delivery, Customer_Customer)
.join(Customer_Customer, Delivery_Delivery.customer_id == Customer_Customer.id)
.filter(Delivery_Delivery.expected_delivery_date == target_date)
.filter(Delivery_Delivery.delivery_status.notin_([1, 10]))
.order_by(Delivery_Delivery.customer_town.asc())
.all())
result = []
for delivery_item, customer in deliveries:
result.append({
'id': delivery_item.id,
'street': delivery_item.customer_address,
'town': delivery_item.customer_town,
'state': delivery_item.customer_state,
'zipcode': delivery_item.customer_zip,
'customerName': delivery_item.customer_name,
'notes': delivery_item.dispatcher_notes or '',
'latitude': customer.customer_latitude,
'longitude': customer.customer_longitude,
'gallonsOrdered': delivery_item.gallons_ordered,
'isFill': delivery_item.customer_asked_for_fill == 1,
'deliveryStatus': delivery_item.delivery_status,
'customerId': delivery_item.customer_id,
})
return success_response({'deliveries': result})
@delivery.route("/history", methods=["GET"])
@common_login_required
def get_deliveries_history():
"""Get completed deliveries (delivered or finalized) for a date range."""
start_date_str = request.args.get('start_date')
end_date_str = request.args.get('end_date')
try:
start_date = datetime.strptime(start_date_str, '%Y-%m-%d').date() if start_date_str else date.today()
end_date = datetime.strptime(end_date_str, '%Y-%m-%d').date() if end_date_str else date.today()
except ValueError:
return error_response("Invalid date format. Use YYYY-MM-DD", 400)
# Status 10 = finalized, 11 = delivered
deliveries = (db.session
.query(Delivery_Delivery)
.filter(Delivery_Delivery.expected_delivery_date >= start_date)
.filter(Delivery_Delivery.expected_delivery_date <= end_date)
.filter(Delivery_Delivery.delivery_status.in_([10, 11]))
.order_by(Delivery_Delivery.expected_delivery_date.desc(), Delivery_Delivery.customer_town.asc(), Delivery_Delivery.customer_name.asc())
.all())
result = []
total_gallons = 0
prime_count = 0
emergency_count = 0
same_day_count = 0
for d in deliveries:
gallons = float(d.gallons_delivered) if d.gallons_delivered and d.gallons_delivered > 0 else float(d.gallons_ordered or 0)
total_gallons += gallons
if d.prime == 1:
prime_count += 1
if d.emergency == 1:
emergency_count += 1
if d.same_day == 1:
same_day_count += 1
result.append({
'id': d.id,
'customerId': d.customer_id,
'customerName': d.customer_name,
'town': d.customer_town,
'address': f"{d.customer_address}, {d.customer_town}, {d.customer_state} {d.customer_zip}",
'gallonsDelivered': gallons,
'prime': d.prime == 1,
'emergency': d.emergency == 1,
'sameDay': d.same_day == 1,
'deliveryStatus': d.delivery_status,
})
return success_response({
'deliveries': result,
'totalGallons': total_gallons,
'totalDeliveries': len(result),
'primeCount': prime_count,
'emergencyCount': emergency_count,
'sameDayCount': same_day_count,
})
@delivery.route("/updatestatus", methods=["GET"])
@common_login_required
def move_deliveries():
"""
Batch updates delivery statuses based on their expected delivery date relative to today.
- Today's deliveries -> "Out for Delivery" (2)
- Tomorrow's deliveries -> "Scheduled for Tomorrow" (3)
- Future deliveries -> "Waiting" (0)
- Past-due deliveries -> "Pending" (9)
"""
logger.info("GET /delivery/updatestatus - Batch updating delivery statuses")
counter = 0
today = date.today()
tomorrow = today + timedelta(days=1)
# Update statuses for deliveries scheduled for today
deliveries_today = db.session.query(Delivery_Delivery).filter(
Delivery_Delivery.delivery_status.notin_([1, 5, 10]), # Not cancelled, issue, or finalized
Delivery_Delivery.expected_delivery_date == today,
Delivery_Delivery.delivery_status != 2 # Avoid redundant updates
).all()
for delivery_item in deliveries_today:
delivery_item.delivery_status = 2
db.session.add(delivery_item)
counter += 1
# Update statuses for deliveries scheduled for tomorrow
deliveries_tomorrow = db.session.query(Delivery_Delivery).filter(
Delivery_Delivery.delivery_status.notin_([1, 5, 10]),
Delivery_Delivery.expected_delivery_date == tomorrow,
Delivery_Delivery.delivery_status != 3
).all()
for delivery_item in deliveries_tomorrow:
delivery_item.delivery_status = 3
db.session.add(delivery_item)
counter += 1
# Update statuses for future deliveries (after tomorrow)
deliveries_waiting = db.session.query(Delivery_Delivery).filter(
Delivery_Delivery.delivery_status.notin_([1, 5, 10]),
Delivery_Delivery.expected_delivery_date > tomorrow,
Delivery_Delivery.delivery_status != 0
).all()
for delivery_item in deliveries_waiting:
delivery_item.delivery_status = 0
db.session.add(delivery_item)
counter += 1
# Update statuses for deliveries that are past their expected date
deliveries_pending = db.session.query(Delivery_Delivery).filter(
Delivery_Delivery.delivery_status.notin_([1, 5, 10]),
Delivery_Delivery.expected_delivery_date < today,
Delivery_Delivery.delivery_status != 9
).all()
for delivery_item in deliveries_pending:
delivery_item.delivery_status = 9
db.session.add(delivery_item)
counter += 1
if counter > 0:
db.session.commit()
return success_response({'update': True})
return success_response({'update': False})
@delivery.route("/<int:delivery_id>", methods=["GET"])
@common_login_required
def get_a_delivery(delivery_id):
"""
Get a single delivery's details.
"""
logger.info(f"GET /delivery/{delivery_id} - Fetching delivery")
get_delivery = db.session.query(Delivery_Delivery).filter(Delivery_Delivery.id == delivery_id).first()
if not get_delivery:
return error_response("Delivery not found", 404)
# Using the schema is cleaner and less error-prone than building a manual dictionary.
delivery_schema = Delivery_Delivery_schema(many=False)
return success_response({"delivery": delivery_schema.dump(get_delivery)})
@delivery.route("/past1/<int:customer_id>", methods=["GET"])
@common_login_required
def get_customer_past_delivery1(customer_id):
logger.info(f"GET /delivery/past1/{customer_id} - Fetching customer past deliveries (first 5)")
get_customer_past_delivery = (db.session
.query(Delivery_Delivery)
.filter(Delivery_Delivery.customer_id == customer_id)
.order_by(Delivery_Delivery.id.desc())
.limit(5))
delivery_schema = Delivery_Delivery_schema(many=True)
return success_response({"deliveries": delivery_schema.dump(get_customer_past_delivery)})
@delivery.route("/past2/<int:customer_id>", methods=["GET"])
@common_login_required
def get_customer_past_delivery2(customer_id):
logger.info(f"GET /delivery/past2/{customer_id} - Fetching customer past deliveries (next 5)")
get_customer_past_delivery = (db.session
.query(Delivery_Delivery)
.filter(Delivery_Delivery.customer_id == customer_id)
.order_by(Delivery_Delivery.id.desc())
.offset(5)
.limit(5))
delivery_schema = Delivery_Delivery_schema(many=True)
return success_response({"deliveries": delivery_schema.dump(get_customer_past_delivery)})
@delivery.route("/auto/<int:customer_id>", methods=["GET"])
@common_login_required
def get_customer_auto_delivery(customer_id):
logger.info(f"GET /delivery/auto/{customer_id} - Fetching customer auto deliveries")
get_customer_past_delivery = (db.session
.query(Tickets_Auto_Delivery)
.filter(Tickets_Auto_Delivery.customer_id == customer_id)
.order_by(Tickets_Auto_Delivery.id.desc())
.offset(5)
.limit(5))
delivery_schema = Tickets_Auto_Delivery_schema(many=True)
return success_response({"deliveries": delivery_schema.dump(get_customer_past_delivery)})
@delivery.route("/order/<int:delivery_id>", methods=["GET"])
@common_login_required
def get_a_specific_delivery(delivery_id):
"""
Get a single delivery by its ID.
"""
get_delivery = db.session.query(Delivery_Delivery).filter(Delivery_Delivery.id == delivery_id).first()
if not get_delivery:
return error_response("Delivery not found", 404)
delivery_schema = Delivery_Delivery_schema(many=False)
return success_response({"delivery": delivery_schema.dump(get_delivery)})
@delivery.route("/cash/<int:delivery_id>/<int:type_of_payment>", methods=["PUT"])
@common_login_required
def update_a_delivery_payment(delivery_id, type_of_payment):
"""
This update a delivery for example if user updates to a fill
"""
get_delivery = (db.session
.query(Delivery_Delivery)
.filter(Delivery_Delivery.id == delivery_id)
.first())
get_delivery.payment_type = type_of_payment
db.session.add(get_delivery)
db.session.commit()
return success_response()
@delivery.route("/all/<int:page>", methods=["GET"])
@common_login_required
def get_deliveries_all(page):
"""
This will get deliveries not done
"""
per_page_amount = 50
if page is None:
offset_limit = 0
elif page == 1:
offset_limit = 0
else:
offset_limit = (per_page_amount * page) - per_page_amount
deliveries = (db.session
.query(Delivery_Delivery)
.order_by(Delivery_Delivery.id.desc())
.limit(per_page_amount).offset(offset_limit))
customer_schema = Delivery_Delivery_schema(many=True)
return success_response({"deliveries": customer_schema.dump(deliveries)})
@delivery.route("/customer/<int:customer_id>/<int:page>", methods=["GET"])
@common_login_required
def get_deliveries_from_customer(customer_id, page):
"""
This will get deliveries not done
"""
per_page_amount = 50
if page is None:
offset_limit = 0
elif page == 1:
offset_limit = 0
else:
offset_limit = (per_page_amount * page) - per_page_amount
deliveries = (db.session
.query(Delivery_Delivery)
.filter(Delivery_Delivery.customer_id == customer_id)
.order_by(Delivery_Delivery.id.desc())
.limit(per_page_amount).offset(offset_limit))
customer_schema = Delivery_Delivery_schema(many=True)
return success_response({"deliveries": customer_schema.dump(deliveries)})
@delivery.route("/all/order/<int:page>", methods=["GET"])
@common_login_required
def get_deliveries_not_delivered(page):
"""
This will get deliveries not done
"""
per_page_amount = 50
if page is None:
offset_limit = 0
elif page == 1:
offset_limit = 0
else:
offset_limit = (per_page_amount * page) - per_page_amount
deliveries = (db.session
.query(Delivery_Delivery)
.filter(Delivery_Delivery.delivery_status != 10)
.order_by(Delivery_Delivery.when_ordered.desc())
.limit(per_page_amount).offset(offset_limit))
customer_schema = Delivery_Delivery_schema(many=True)
return success_response({"deliveries": customer_schema.dump(deliveries)})
@delivery.route("/waiting/<int:page>", methods=["GET"])
@common_login_required
def get_deliveries_waiting(page):
"""
This will get deliveries not done
"""
per_page_amount = 50
if page is None:
offset_limit = 0
elif page == 1:
offset_limit = 0
else:
offset_limit = (per_page_amount * page) - per_page_amount
deliveries = (db.session
.query(Delivery_Delivery)
.filter(or_(Delivery_Delivery.delivery_status == 0,
)
)
.filter(Delivery_Delivery.expected_delivery_date != date.today())
.order_by(Delivery_Delivery.when_ordered.desc())
.limit(per_page_amount).offset(offset_limit))
customer_schema = Delivery_Delivery_schema(many=True)
return success_response({"deliveries": customer_schema.dump(deliveries)})
@delivery.route("/pending/<int:page>", methods=["GET"])
@common_login_required
def get_deliveries_pending(page):
"""
"""
per_page_amount = 50
if page is None:
offset_limit = 0
elif page == 1:
offset_limit = 0
else:
offset_limit = (per_page_amount * page) - per_page_amount
deliveries = (db.session
.query(Delivery_Delivery)
.filter(Delivery_Delivery.delivery_status == 9)
.order_by(Delivery_Delivery.delivery_status.asc())
.limit(per_page_amount).offset(offset_limit))
customer_schema = Delivery_Delivery_schema(many=True)
return success_response({"deliveries": customer_schema.dump(deliveries)})
@delivery.route("/outfordelivery/<int:page>", methods=["GET"])
@common_login_required
def get_deliveries_outfordelivery(page):
"""
"""
per_page_amount = 50
if page is None:
offset_limit = 0
elif page == 1:
offset_limit = 0
else:
offset_limit = (per_page_amount * page) - per_page_amount
deliveries = (db.session
.query(Delivery_Delivery)
.filter(Delivery_Delivery.delivery_status == 2)
.order_by(Delivery_Delivery.delivery_status.asc())
.limit(per_page_amount).offset(offset_limit))
customer_schema = Delivery_Delivery_schema(many=True)
return success_response({"deliveries": customer_schema.dump(deliveries)})
@delivery.route("/tommorrow/<int:page>", methods=["GET"])
@common_login_required
def get_deliveries_tommorrow(page):
"""
This will get deliveries not done
"""
tomm = date.today() + timedelta(days=1)
per_page_amount = 50
if page is None:
offset_limit = 0
elif page == 1:
offset_limit = 0
else:
offset_limit = (per_page_amount * page) - per_page_amount
deliveries = (db.session
.query(Delivery_Delivery)
.filter(Delivery_Delivery.delivery_status == 3)
.order_by(Delivery_Delivery.id.desc())
.limit(per_page_amount).offset(offset_limit))
customer_schema = Delivery_Delivery_schema(many=True)
return success_response({"deliveries": customer_schema.dump(deliveries)})
@delivery.route("/finalized/<int:page>", methods=["GET"])
@common_login_required
def get_deliveries_finalized(page):
"""
This will get deliveries not done
"""
per_page_amount = 50
if page is None:
offset_limit = 0
elif page == 1:
offset_limit = 0
else:
offset_limit = (per_page_amount * page) - per_page_amount
deliveries = (db.session
.query(Delivery_Delivery)
.filter(Delivery_Delivery.delivery_status == 10)
.order_by(Delivery_Delivery.id.desc())
.limit(per_page_amount).offset(offset_limit))
customer_schema = Delivery_Delivery_schema(many=True)
return success_response({"deliveries": customer_schema.dump(deliveries)})
@delivery.route("/cancelled/<int:page>", methods=["GET"])
@common_login_required
def get_deliveries_cancelled(page):
"""
This will get deliveries not done
"""
per_page_amount = 50
if page is None:
offset_limit = 0
elif page == 1:
offset_limit = 0
else:
offset_limit = (per_page_amount * page) - per_page_amount
deliveries = (db.session
.query(Delivery_Delivery)
.filter(Delivery_Delivery.delivery_status == 1)
.order_by(Delivery_Delivery.when_ordered.desc())
.limit(per_page_amount).offset(offset_limit))
customer_schema = Delivery_Delivery_schema(many=True)
return success_response({"deliveries": customer_schema.dump(deliveries)})
@delivery.route("/partialdelivery/<int:page>", methods=["GET"])
@common_login_required
def get_deliveries_partial(page):
"""
This will get deliveries not done
"""
per_page_amount = 50
if page is None:
offset_limit = 0
elif page == 1:
offset_limit = 0
else:
offset_limit = (per_page_amount * page) - per_page_amount
deliveries = (db.session
.query(Delivery_Delivery)
.filter(Delivery_Delivery.delivery_status == 4)
.order_by(Delivery_Delivery.when_ordered.desc())
.limit(per_page_amount).offset(offset_limit))
customer_schema = Delivery_Delivery_schema(many=True)
return success_response({"deliveries": customer_schema.dump(deliveries)})
@delivery.route("/issue/<int:page>", methods=["GET"])
@common_login_required
def get_deliveries_issue(page):
"""
This will get deliveries not done
"""
per_page_amount = 50
if page is None:
offset_limit = 0
elif page == 1:
offset_limit = 0
else:
offset_limit = (per_page_amount * page) - per_page_amount
deliveries = (db.session
.query(Delivery_Delivery)
.filter(Delivery_Delivery.delivery_status == 5)
.order_by(Delivery_Delivery.when_ordered.desc())
.limit(per_page_amount).offset(offset_limit))
customer_schema = Delivery_Delivery_schema(many=True)
return success_response({"deliveries": customer_schema.dump(deliveries)})
@delivery.route("/time/today", methods=["GET"])
@common_login_required
def get_deliveries_today():
"""
This will get today's deliveries
"""
deliveries = (db.session
.query(Delivery_Delivery)
.filter(Delivery_Delivery.delivery_status == 2)
.all())
customer_schema = Delivery_Delivery_schema(many=True)
return success_response({"deliveries": customer_schema.dump(deliveries)})
@delivery.route("/edit/<int:delivery_id>", methods=["POST", "PUT"])
@common_login_required
def edit_a_delivery(delivery_id):
"""
This will edit a delivery using a delivery id.
"""
logger.info(f"POST /delivery/edit/{delivery_id} - Editing delivery")
data = request.json
get_delivery = db.session.query(Delivery_Delivery).filter(Delivery_Delivery.id == delivery_id).first()
if not get_delivery:
return error_response("Delivery not found", 404)
# --- Fetch related data ---
get_customer = db.session.query(Customer_Customer).filter(Customer_Customer.id == get_delivery.customer_id).first()
if not get_customer:
return error_response("Associated customer not found", 404)
get_today_price = db.session.query(Pricing_Oil_Oil).order_by(Pricing_Oil_Oil.id.desc()).first()
if not get_today_price:
return error_response("Pricing information not available", 500)
# --- Process Form Input (using .get() for safety) ---
get_delivery.gallons_ordered = data.get("gallons_ordered", get_delivery.gallons_ordered)
get_delivery.delivery_status = data.get("delivery_status", get_delivery.delivery_status)
get_delivery.expected_delivery_date = data.get("expected_delivery_date")
get_delivery.when_ordered = data.get("created_delivery_date")
get_delivery.dispatcher_notes = data.get("dispatcher_notes_taken", "")
get_delivery.payment_type = data.get("payment_type", 0)
# Convert booleans to 1 or 0 for the database
customer_wants_fill = 1 if data.get("customer_asked_for_fill") else 0
get_delivery.customer_asked_for_fill = customer_wants_fill
get_delivery.prime = 1 if data.get("prime") else 0
get_delivery.same_day = 1 if data.get("same_day") else 0
get_delivery.emergency = 1 if data.get("emergency") else 0
# Get tier selections (default to existing or 1)
get_delivery.pricing_tier_same_day = data.get("pricing_tier_same_day", get_delivery.pricing_tier_same_day or 1)
get_delivery.pricing_tier_prime = data.get("pricing_tier_prime", get_delivery.pricing_tier_prime or 1)
get_delivery.pricing_tier_emergency = data.get("pricing_tier_emergency", get_delivery.pricing_tier_emergency or 1)
# --- Handle Driver Assignment ---
driver_id = data.get("driver_employee_id")
if driver_id:
get_driver = db.session.query(Employee_Employee).filter(Employee_Employee.id == driver_id).first()
if get_driver:
get_delivery.driver_employee_id = get_driver.id
get_delivery.driver_first_name = get_driver.employee_first_name
get_delivery.driver_last_name = get_driver.employee_last_name
else:
# Optionally, handle the case where the driver ID is invalid
# For now, we'll just not update the driver info.
pass
else:
# No driver provided, set to empty values
get_delivery.driver_employee_id = 0
get_delivery.driver_first_name = ""
get_delivery.driver_last_name = ""
# --- Handle Promotion ---
promo_id = data.get("promo_id")
if promo_id and promo_id != 0:
get_promo = db.session.query(Promo_Promo).filter(Promo_Promo.id == promo_id).first()
if get_promo:
get_delivery.promo_id = get_promo.id
get_delivery.promo_money_discount = get_promo.money_off_delivery
else: # Clear promo if "No Promotion" is selected
get_delivery.promo_id = None
get_delivery.promo_money_discount = None
# --- Handle Credit Card Assignment ---
card_id = data.get("credit_card_id")
# Only assign card if payment type is Card (1) and a valid card_id is provided
if get_delivery.payment_type == 1 and card_id:
get_card = db.session.query(Card_Card).filter(Card_Card.id == card_id, Card_Card.user_id == get_customer.id).first()
if get_card:
get_delivery.payment_card_id = get_card.id
else: # Clear card if payment is not by card
get_delivery.payment_card_id = None
# --- Recalculate Pricing (This logic could be moved to a helper function) ---
gallons_for_calc = 250 if customer_wants_fill else int(get_delivery.gallons_ordered)
base_price = gallons_for_calc * get_today_price.price_for_customer
# Add fees using tier pricing
total_price = base_price
if get_delivery.prime:
tier_field = f"price_prime_tier{get_delivery.pricing_tier_prime}"
prime_fee = Decimal(getattr(get_today_price, tier_field, get_today_price.price_prime))
total_price += prime_fee
if get_delivery.same_day:
tier_field = f"price_same_day_tier{get_delivery.pricing_tier_same_day}"
same_day_fee = Decimal(getattr(get_today_price, tier_field, get_today_price.price_same_day))
total_price += same_day_fee
if get_delivery.emergency:
tier_field = f"price_emergency_tier{get_delivery.pricing_tier_emergency}"
emergency_fee = Decimal(getattr(get_today_price, tier_field, get_today_price.price_emergency))
total_price += emergency_fee
get_delivery.total_price = base_price # Price before fees
get_delivery.pre_charge_amount = total_price # Price including fees
# --- Commit Changes ---
db.session.add(get_delivery)
db.session.commit()
return success_response({
'message': f"Delivery {delivery_id} updated successfully.",
'customer_id': get_customer.id
})
@delivery.route("/create/<int:user_id>", methods=["POST"])
@common_login_required
def create_a_delivery(user_id):
"""
This will create a delivery using a customer id
"""
logger.info(f"POST /delivery/create/{user_id} - Creating delivery for customer")
get_customer = db.session\
.query(Customer_Customer)\
.filter(Customer_Customer.id == user_id)\
.first()
get_today_price = db.session\
.query(Pricing_Oil_Oil)\
.order_by(Pricing_Oil_Oil.id.desc())\
.first()
if not get_customer:
return error_response("Customer not found")
else:
gallons_ordered = request.json["gallons_ordered"]
customer_wants_fill = request.json["customer_asked_for_fill"]
when_to_deliver = request.json["expected_delivery_date"]
dispatcher_notes_taken = request.json["dispatcher_notes_taken"]
prime_info = request.json["prime"]
same_day_info = request.json["same_day"]
emergency_info = request.json["emergency"]
delivery_driver_id = request.json.get("driver_employee_id", 0)
# Get tier selections (default to tier 1)
pricing_tier_same_day = request.json.get("pricing_tier_same_day", 1)
pricing_tier_prime = request.json.get("pricing_tier_prime", 1)
pricing_tier_emergency = request.json.get("pricing_tier_emergency", 1)
card_payment = request.json["credit"]
cash_payment = request.json["cash"]
check_payment = request.json["check"]
other_payment = request.json["other"]
## promo
try:
if request.json["promo_id"]:
promo_id = request.json["promo_id"]
else:
promo_id = None
except (KeyError, TypeError):
promo_id = None
if promo_id is not None:
get_promo_data = (db.session
.query(Promo_Promo)
.filter(Promo_Promo.id == promo_id)
.first())
promo_id_get_delivery =get_promo_data.id,
promo_money_discount_get_delivery = get_promo_data.money_off_delivery
else:
promo_id_get_delivery =None
promo_money_discount_get_delivery = None
# 0 = cash only
# 1 = CC - Tiger (Physical)
# 11 = CC - Authorize (API)
# 2 = credit /cash
# 3 = check
# 4 = other
if cash_payment is True:
delivery_payment_method = 0
elif card_payment is True:
delivery_payment_method = 1 # Default to Tiger physical machine
elif check_payment is True:
delivery_payment_method = 3
elif other_payment is True:
delivery_payment_method = 4
else:
delivery_payment_method = 5
if delivery_driver_id:
get_driver = (db.session
.query(Employee_Employee)
.filter(Employee_Employee.id == delivery_driver_id)
.first())
driver_l_name = get_driver.employee_last_name
driver_f_name = get_driver.employee_first_name
driver_emp_id = get_driver.id
else:
driver_l_name = ""
driver_f_name = ""
driver_emp_id = 0
try:
if request.json["credit_card_id"]:
card_payment_id = request.json["credit_card_id"]
else:
card_payment_id = None
except (KeyError, TypeError):
card_payment_id = None
if card_payment_id is not None:
get_card = (db.session
.query(Card_Card)
.filter(Card_Card.id == card_payment_id)
.filter(Card_Card.user_id == get_customer.id)
.first())
card_id_from_customer = get_card.id
else:
card_id_from_customer = None
if customer_wants_fill is True:
customer_fill_up = 1
gallons_ordered = 250
else:
customer_fill_up = 0
gallons_ordered = request.json["gallons_ordered"]
if emergency_info is True:
emergency_asked = 1
else:
emergency_asked = 0
if prime_info is True:
prime_asked = 1
else:
prime_asked = 0
if same_day_info is True:
same_day_asked = 1
else:
same_day_asked = 0
customer_filled_name = get_customer.customer_first_name + ' ' + get_customer.customer_last_name
now = datetime.utcnow()
# Pricing
if customer_fill_up == 1:
# Fill
precharge_amount = (250 * get_today_price.price_for_customer)
else:
# Gallons
precharge_amount = int(gallons_ordered) * get_today_price.price_for_customer
# Calculate fees using tier pricing
total_precharge_amount = precharge_amount
if same_day_asked == 1:
tier_field = f"price_same_day_tier{pricing_tier_same_day}"
same_day_fee = Decimal(getattr(get_today_price, tier_field, get_today_price.price_same_day))
total_precharge_amount += same_day_fee
if prime_asked == 1:
tier_field = f"price_prime_tier{pricing_tier_prime}"
prime_fee = Decimal(getattr(get_today_price, tier_field, get_today_price.price_prime))
total_precharge_amount += prime_fee
if emergency_asked == 1:
tier_field = f"price_emergency_tier{pricing_tier_emergency}"
emergency_fee = Decimal(getattr(get_today_price, tier_field, get_today_price.price_emergency))
total_precharge_amount += emergency_fee
new_delivery = Delivery_Delivery(
customer_id=get_customer.id,
customer_address=get_customer.customer_address,
customer_name=customer_filled_name,
customer_town=get_customer.customer_town,
customer_state=get_customer.customer_state,
customer_zip=get_customer.customer_zip,
gallons_ordered=gallons_ordered,
customer_asked_for_fill=customer_fill_up,
gallons_delivered=0,
customer_filled=0,
delivery_status=0,
when_ordered=now,
when_delivered=None,
expected_delivery_date=when_to_deliver,
automatic=get_customer.customer_automatic,
automatic_id=None,
oil_id=get_today_price.id,
supplier_price=get_today_price.price_from_supplier,
customer_price=get_today_price.price_for_customer,
customer_temperature=None,
dispatcher_notes=dispatcher_notes_taken,
prime=prime_asked,
same_day=same_day_asked,
emergency=emergency_asked,
pricing_tier_same_day=pricing_tier_same_day,
pricing_tier_prime=pricing_tier_prime,
pricing_tier_emergency=pricing_tier_emergency,
payment_type=delivery_payment_method,
payment_card_id=card_id_from_customer,
pre_charge_amount=total_precharge_amount,
total_price=precharge_amount,
final_price=0,
driver_last_name=driver_l_name,
driver_first_name=driver_f_name,
driver_employee_id=driver_emp_id,
promo_id=promo_id_get_delivery,
promo_money_discount=promo_money_discount_get_delivery
)
get_stats_customer = (db.session
.query(Stats_Customer)
.filter(Stats_Customer.customer_id == get_customer.id)
.first())
new_calls = int(get_stats_customer.total_calls) + 1
get_stats_customer.total_calls = new_calls
db.session.add(get_stats_customer)
db.session.add(new_delivery)
db.session.commit()
return success_response({'delivery_id': new_delivery.id})
@delivery.route("/cancel/<int:delivery_id>", methods=["POST", "PUT"])
@common_login_required
def cancel_a_delivery(delivery_id):
"""
This will cancel a delivery
"""
logger.info(f"POST /delivery/cancel/{delivery_id} - Cancelling delivery")
get_delivery = db.session\
.query(Delivery_Delivery)\
.filter(Delivery_Delivery.id == delivery_id)\
.first()
get_delivery.delivery_status = 1
db.session.add(get_delivery)
db.session.commit()
return success_response()
@delivery.route("/cancelled/<int:delivery_id>", methods=["PUT"])
@common_login_required
def mark_as_cancelled_alias(delivery_id):
"""
Alias for cancel_a_delivery to support frontend calls to /cancelled/
"""
return cancel_a_delivery(delivery_id)
@delivery.route("/delivered/<int:delivery_id>", methods=["POST"])
@common_login_required
def mark_as_delivered(delivery_id):
"""
This will mark the delivery as delivered
"""
logger.info(f"POST /delivery/delivered/{delivery_id} - Marking delivery as delivered")
# how many gallons delivered
gallons_put_into_tank = request.json["gallons_put_into_tank"]
# was the tank full or not
was_it_filled = request.json["filled"]
get_delivery = db.session\
.query(Delivery_Delivery)\
.filter(Delivery_Delivery.id == delivery_id)\
.first()
get_delivery.delivery_status = 1
get_delivery.gallons_delivered = gallons_put_into_tank
get_delivery.customer_filled = was_it_filled
db.session.add(get_delivery)
db.session.commit()
return success_response()
@delivery.route("/partial/<int:delivery_id>", methods=["POST"])
@common_login_required
def partial_delivery(delivery_id):
"""
This will mark the delivery as delivered
"""
# how many gallons delivered
gallons_put_into_tank = request.json["gallons_put_into_tank"]
get_delivery = db.session\
.query(Delivery_Delivery)\
.filter(Delivery_Delivery.id == delivery_id)\
.first()
get_delivery.delivery_status = 4
get_delivery.gallons_delivered = gallons_put_into_tank
get_delivery.customer_filled = 0
db.session.add(get_delivery)
db.session.commit()
return success_response()
@delivery.route("/note/technician/<int:delivery_id>", methods=["PUT"])
@common_login_required
def delivery_note_driver(delivery_id):
"""
Update a service call diagnosis
"""
#
driver_notes = request.json["driver_notes"]
now = datetime.utcnow()
user = (db.session
.query(Auth_User)
.filter(Auth_User.id == current_user.id)
.first())
get_delivery = (db.session
.query(Delivery_Delivery)
.filter(Delivery_Delivery.id == delivery_id)
.first())
create_new_note = Delivery_Notes_Driver(
delivery_id=get_delivery.id,
driver_comments=driver_notes,
time_added=now,
driver_id=user.id,
driver_name=user.user,
)
db.session.add(create_new_note)
db.session.commit()
return success_response()
@delivery.route("/delete/<int:delivery_id>", methods=["DELETE"])
@common_login_required
def delete_call(delivery_id):
"""
delete a delivery call
"""
logger.info(f"DELETE /delivery/delete/{delivery_id} - Deleting delivery")
get_call_to_delete = (db.session
.query(Delivery_Delivery)
.filter(Delivery_Delivery.id == delivery_id)
.first())
db.session.delete(get_call_to_delete)
db.session.commit()
return success_response()
@delivery.route("/total/<int:delivery_id>", methods=["GET"])
@common_login_required
def calculate_total(delivery_id):
"""
This will get deliveries not done
"""
get_delivery = (db.session
.query(Delivery_Delivery)
.filter(Delivery_Delivery.id == delivery_id)
.first())
# Use gallons_delivered if available and > 0, otherwise use gallons_ordered
gallons = get_delivery.gallons_delivered if get_delivery.gallons_delivered and get_delivery.gallons_delivered > 0 else get_delivery.gallons_ordered
if get_delivery.promo_id:
get_promo_data = (db.session
.query(Promo_Promo)
.filter(Promo_Promo.id == get_delivery.promo_id)
.first())
discount_gallon_price = get_delivery.customer_price - get_promo_data.money_off_delivery
total_amount_after_discount = (gallons * discount_gallon_price)
discount = (gallons * get_delivery.customer_price) - total_amount_after_discount
else:
discount = 0
total_amount_after_discount = gallons * get_delivery.customer_price
get_price_query = (db.session
.query(Pricing_Oil_Oil)
.order_by(Pricing_Oil_Oil.date.desc())
.first())
if get_delivery.prime == 1:
tier = get_delivery.pricing_tier_prime or 1
tier_field = f"price_prime_tier{tier}"
priceprime = float(getattr(get_price_query, tier_field, get_price_query.price_prime))
else:
priceprime = 0
if get_delivery.emergency == 1:
tier = get_delivery.pricing_tier_emergency or 1
tier_field = f"price_emergency_tier{tier}"
priceemergency = float(getattr(get_price_query, tier_field, get_price_query.price_emergency))
else:
priceemergency = 0
if get_delivery.same_day == 1:
tier = get_delivery.pricing_tier_same_day or 1
tier_field = f"price_same_day_tier{tier}"
pricesameday = float(getattr(get_price_query, tier_field, get_price_query.price_same_day))
else:
pricesameday = 0
# Recalculate base price using actual gallons
base_price = gallons * get_delivery.customer_price
total = float(base_price) + float(priceprime) + float(pricesameday) + float(priceemergency)
priceprime = round(priceprime, 2)
pricesameday = round(pricesameday, 2)
priceemergency = round(priceemergency, 2)
total_amount_after_discount = round(total_amount_after_discount, 2)
discount = round(discount, 2)
total = round(total, 2)
return success_response({
'priceprime': priceprime,
'pricesameday': pricesameday,
'priceemergency': priceemergency,
'total_amount': total,
'discount': discount,
'total_amount_after_discount': total_amount_after_discount,
})