Files
api/schemas.py
2026-01-17 15:21:41 -05:00

279 lines
8.0 KiB
Python

from pydantic import BaseModel, field_validator
from typing import Optional
from datetime import datetime, date
from decimal import Decimal
# Customer schemas
class CustomerBase(BaseModel):
auth_net_profile_id: Optional[str] = None
account_number: str
customer_last_name: str
customer_first_name: str
customer_town: str
customer_state: int
customer_zip: str
customer_first_call: Optional[datetime] = None
customer_email: str
customer_automatic: int
customer_phone_number: str
customer_home_type: int
customer_apt: str
customer_address: str
company_id: int
customer_latitude: str
customer_longitude: str
correct_address: bool
class CustomerCreate(BaseModel):
customer_first_name: str
customer_last_name: str
customer_phone_number: str
customer_email: str
customer_address: str
customer_apt: Optional[str] = None
customer_town: str
customer_zip: str
customer_home_type: int = 0
personal_notes: Optional[str] = None
house_description: Optional[str] = None
class CustomerCreateStep1(BaseModel):
customer_first_name: str
customer_last_name: str
customer_phone_number: str # Now accepts formatted: (123) 456-7890
customer_address: str
customer_apt: Optional[str] = None
customer_town: str
customer_zip: str
customer_home_type: int = 0
house_description: Optional[str] = None
class CustomerAccountCreate(BaseModel):
account_number: str
password: str
confirm_password: str
customer_email: str
class NewCustomerCreate(CustomerCreate):
password: str
confirm_password: str
class CustomerUpdate(BaseModel):
auth_net_profile_id: Optional[str] = None
account_number: Optional[str] = None
customer_last_name: Optional[str] = None
customer_first_name: Optional[str] = None
customer_town: Optional[str] = None
customer_state: Optional[int] = None
customer_zip: Optional[str] = None
customer_first_call: Optional[datetime] = None
customer_email: Optional[str] = None
customer_automatic: Optional[int] = None
customer_phone_number: Optional[str] = None
customer_home_type: Optional[int] = None
customer_apt: Optional[str] = None
customer_address: Optional[str] = None
company_id: Optional[int] = None
customer_latitude: Optional[str] = None
customer_longitude: Optional[str] = None
correct_address: Optional[bool] = None
class CustomerResponse(CustomerBase):
id: int
# Order schemas (Delivery_Delivery)
class OrderBase(BaseModel):
customer_id: int
customer_name: str
customer_address: str
customer_town: str
customer_state: str
customer_zip: int
gallons_ordered: int
customer_asked_for_fill: int
gallons_delivered: Decimal
customer_filled: int
delivery_status: int
when_ordered: Optional[date] = None
when_delivered: Optional[date] = None
expected_delivery_date: Optional[date] = None
automatic: int
automatic_id: int
oil_id: int
supplier_price: Decimal
customer_price: Decimal
customer_temperature: Decimal
dispatcher_notes: str
prime: int
same_day: int
emergency: int
payment_type: int
payment_card_id: int
cash_recieved: Decimal
driver_employee_id: int
driver_first_name: str
driver_last_name: str
pre_charge_amount: Decimal
total_price: Decimal
final_price: Decimal
check_number: str
promo_id: int
promo_money_discount: Decimal
class OrderCreate(OrderBase):
pass
class OrderUpdate(BaseModel):
customer_id: Optional[int] = None
customer_name: Optional[str] = None
customer_address: Optional[str] = None
customer_town: Optional[str] = None
customer_state: Optional[str] = None
customer_zip: Optional[int] = None
gallons_ordered: Optional[int] = None
customer_asked_for_fill: Optional[int] = None
gallons_delivered: Optional[Decimal] = None
customer_filled: Optional[int] = None
delivery_status: Optional[int] = None
when_ordered: Optional[date] = None
when_delivered: Optional[date] = None
expected_delivery_date: Optional[date] = None
automatic: Optional[int] = None
automatic_id: Optional[int] = None
oil_id: Optional[int] = None
supplier_price: Optional[Decimal] = None
customer_price: Optional[Decimal] = None
customer_temperature: Optional[Decimal] = None
dispatcher_notes: Optional[str] = None
prime: Optional[int] = None
same_day: Optional[int] = None
emergency: Optional[int] = None
payment_type: Optional[int] = None
payment_card_id: Optional[int] = None
cash_recieved: Optional[Decimal] = None
driver_employee_id: Optional[int] = None
driver_first_name: Optional[str] = None
driver_last_name: Optional[str] = None
pre_charge_amount: Optional[Decimal] = None
total_price: Optional[Decimal] = None
final_price: Optional[Decimal] = None
check_number: Optional[str] = None
promo_id: Optional[int] = None
promo_money_discount: Optional[Decimal] = None
class OrderResponse(OrderBase):
id: int
class DeliveryCreate(BaseModel):
gallons_ordered: int
expected_delivery_date: date
dispatcher_notes: Optional[str] = None
payment_type: int # 0=cash, 1=credit
prime: Optional[int] = 0
same_day: Optional[int] = 0
emergency: Optional[int] = 0
pre_charge_amount: Optional[float] = None
# Card schemas
class CardBase(BaseModel):
auth_net_payment_profile_id: Optional[str] = None
card_number: Optional[str] = None
last_four_digits: int
name_on_card: Optional[str] = None
expiration_month: str
expiration_year: str
type_of_card: Optional[str] = None
security_number: Optional[str] = None
accepted_or_declined: Optional[int] = None
main_card: Optional[bool] = None
zip_code: Optional[str] = None
class CardCreate(BaseModel):
card_number: str
name_on_card: str
expiration_month: str
expiration_year: str
security_number: str
zip_code: str
save_card: bool = False
class CardResponse(CardBase):
id: int
date_added: datetime
user_id: int
class CardUpdate(BaseModel):
name_on_card: Optional[str] = None
expiration_month: Optional[str] = None
expiration_year: Optional[str] = None
zip_code: Optional[str] = None
# Payment schemas
class PaymentCreate(BaseModel):
amount: Decimal
card_id: Optional[int] = None # For saved cards
card_details: Optional[CardCreate] = None # For new cards
delivery_id: Optional[int] = None
class PaymentResponse(BaseModel):
transaction_id: str
status: str
amount: Decimal
auth_net_transaction_id: str
# Auth schemas
class UserCreate(BaseModel):
account_number: str
house_number: str
email: str
password: str
confirm_password: str
@field_validator('house_number')
@classmethod
def validate_house_number(cls, v: str) -> str:
"""Validate house_number contains only safe characters.
Allows alphanumeric characters, spaces, hyphens, and forward slashes
(for addresses like "123-A" or "45 1/2").
"""
import re
if not v or not v.strip():
raise ValueError('House number cannot be empty')
# Allow alphanumeric, spaces, hyphens, forward slashes
if not re.match(r'^[a-zA-Z0-9\s\-/]+$', v):
raise ValueError('House number contains invalid characters')
if len(v) > 20:
raise ValueError('House number is too long')
return v.strip()
class UserLogin(BaseModel):
email: str
password: str
class Token(BaseModel):
access_token: str
token_type: str
class TokenData(BaseModel):
username: Optional[str] = None
class UserResponse(BaseModel):
id: int
username: str
email: str
member_since: datetime
last_seen: datetime
admin: int
admin_role: int
confirmed: int
active: int
class ForgotPasswordRequest(BaseModel):
email: str
class ResetPasswordRequest(BaseModel):
token: str
password: str
confirm_password: str