279 lines
8.0 KiB
Python
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
|