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>
This commit is contained in:
@@ -0,0 +1,67 @@
|
||||
"""Add 5-tier pricing support for service fees
|
||||
|
||||
Revision ID: 3d217261c994
|
||||
Revises: c7d2e8f1a3b9
|
||||
Create Date: 2026-02-07 22:10:07.946719
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '3d217261c994'
|
||||
down_revision = 'c7d2e8f1a3b9'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# Add pricing tier columns to delivery_delivery table
|
||||
with op.batch_alter_table('delivery_delivery', schema=None) as batch_op:
|
||||
batch_op.add_column(sa.Column('pricing_tier_same_day', sa.INTEGER(), nullable=True))
|
||||
batch_op.add_column(sa.Column('pricing_tier_prime', sa.INTEGER(), nullable=True))
|
||||
batch_op.add_column(sa.Column('pricing_tier_emergency', sa.INTEGER(), nullable=True))
|
||||
|
||||
# Add tier pricing columns to pricing_oil_oil table
|
||||
with op.batch_alter_table('pricing_oil_oil', schema=None) as batch_op:
|
||||
batch_op.add_column(sa.Column('price_same_day_tier1', sa.DECIMAL(precision=6, scale=2), nullable=True))
|
||||
batch_op.add_column(sa.Column('price_same_day_tier2', sa.DECIMAL(precision=6, scale=2), nullable=True))
|
||||
batch_op.add_column(sa.Column('price_same_day_tier3', sa.DECIMAL(precision=6, scale=2), nullable=True))
|
||||
batch_op.add_column(sa.Column('price_same_day_tier4', sa.DECIMAL(precision=6, scale=2), nullable=True))
|
||||
batch_op.add_column(sa.Column('price_same_day_tier5', sa.DECIMAL(precision=6, scale=2), nullable=True))
|
||||
batch_op.add_column(sa.Column('price_prime_tier1', sa.DECIMAL(precision=6, scale=2), nullable=True))
|
||||
batch_op.add_column(sa.Column('price_prime_tier2', sa.DECIMAL(precision=6, scale=2), nullable=True))
|
||||
batch_op.add_column(sa.Column('price_prime_tier3', sa.DECIMAL(precision=6, scale=2), nullable=True))
|
||||
batch_op.add_column(sa.Column('price_prime_tier4', sa.DECIMAL(precision=6, scale=2), nullable=True))
|
||||
batch_op.add_column(sa.Column('price_prime_tier5', sa.DECIMAL(precision=6, scale=2), nullable=True))
|
||||
batch_op.add_column(sa.Column('price_emergency_tier1', sa.DECIMAL(precision=6, scale=2), nullable=True))
|
||||
batch_op.add_column(sa.Column('price_emergency_tier2', sa.DECIMAL(precision=6, scale=2), nullable=True))
|
||||
batch_op.add_column(sa.Column('price_emergency_tier3', sa.DECIMAL(precision=6, scale=2), nullable=True))
|
||||
batch_op.add_column(sa.Column('price_emergency_tier4', sa.DECIMAL(precision=6, scale=2), nullable=True))
|
||||
batch_op.add_column(sa.Column('price_emergency_tier5', sa.DECIMAL(precision=6, scale=2), nullable=True))
|
||||
|
||||
|
||||
def downgrade():
|
||||
# Remove tier pricing columns from pricing_oil_oil table
|
||||
with op.batch_alter_table('pricing_oil_oil', schema=None) as batch_op:
|
||||
batch_op.drop_column('price_emergency_tier5')
|
||||
batch_op.drop_column('price_emergency_tier4')
|
||||
batch_op.drop_column('price_emergency_tier3')
|
||||
batch_op.drop_column('price_emergency_tier2')
|
||||
batch_op.drop_column('price_emergency_tier1')
|
||||
batch_op.drop_column('price_prime_tier5')
|
||||
batch_op.drop_column('price_prime_tier4')
|
||||
batch_op.drop_column('price_prime_tier3')
|
||||
batch_op.drop_column('price_prime_tier2')
|
||||
batch_op.drop_column('price_prime_tier1')
|
||||
batch_op.drop_column('price_same_day_tier5')
|
||||
batch_op.drop_column('price_same_day_tier4')
|
||||
batch_op.drop_column('price_same_day_tier3')
|
||||
batch_op.drop_column('price_same_day_tier2')
|
||||
batch_op.drop_column('price_same_day_tier1')
|
||||
|
||||
# Remove pricing tier columns from delivery_delivery table
|
||||
with op.batch_alter_table('delivery_delivery', schema=None) as batch_op:
|
||||
batch_op.drop_column('pricing_tier_emergency')
|
||||
batch_op.drop_column('pricing_tier_prime')
|
||||
batch_op.drop_column('pricing_tier_same_day')
|
||||
69
migrations/versions/c7d2e8f1a3b9_add_kfactor_history.py
Normal file
69
migrations/versions/c7d2e8f1a3b9_add_kfactor_history.py
Normal file
@@ -0,0 +1,69 @@
|
||||
"""Add K-factor history table and estimation columns
|
||||
|
||||
Revision ID: c7d2e8f1a3b9
|
||||
Revises: b43a39b1cf25
|
||||
Create Date: 2026-02-07 00:00:00.000000
|
||||
|
||||
NOTE: Move this file to migrations/versions/ before running flask db upgrade.
|
||||
The migrations/versions/ directory is root-owned and cannot be written to directly.
|
||||
Run: sudo mv eamco_office_api/c7d2e8f1a3b9_add_kfactor_history.py eamco_office_api/migrations/versions/
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = 'c7d2e8f1a3b9'
|
||||
down_revision = 'b43a39b1cf25'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# Create auto_kfactor_history table
|
||||
op.create_table(
|
||||
'auto_kfactor_history',
|
||||
sa.Column('id', sa.Integer(), autoincrement=True, nullable=False),
|
||||
sa.Column('customer_id', sa.Integer(), nullable=False),
|
||||
sa.Column('ticket_id', sa.Integer(), nullable=True),
|
||||
sa.Column('fill_date', sa.Date(), nullable=True),
|
||||
sa.Column('gallons_delivered', sa.DECIMAL(precision=6, scale=2), nullable=True),
|
||||
sa.Column('total_hdd', sa.DECIMAL(precision=8, scale=2), nullable=True),
|
||||
sa.Column('days_in_period', sa.Integer(), nullable=True),
|
||||
sa.Column('k_factor', sa.DECIMAL(precision=7, scale=4), nullable=True),
|
||||
sa.Column('is_budget_fill', sa.Boolean(), server_default='false', nullable=False),
|
||||
sa.Column('is_outlier', sa.Boolean(), server_default='false', nullable=False),
|
||||
sa.Column('created_at', sa.Date(), nullable=True),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
op.create_index('ix_auto_kfactor_history_customer_id', 'auto_kfactor_history', ['customer_id'])
|
||||
op.create_index('ix_auto_kfactor_history_customer_fill', 'auto_kfactor_history', ['customer_id', sa.text('fill_date DESC')])
|
||||
|
||||
# Add columns to auto_delivery
|
||||
with op.batch_alter_table('auto_delivery', schema=None) as batch_op:
|
||||
batch_op.add_column(sa.Column('confidence_score', sa.Integer(), server_default='20', nullable=True))
|
||||
batch_op.add_column(sa.Column('k_factor_source', sa.VARCHAR(length=20), server_default='default', nullable=True))
|
||||
batch_op.alter_column('house_factor',
|
||||
existing_type=sa.DECIMAL(precision=5, scale=2),
|
||||
type_=sa.DECIMAL(precision=7, scale=4),
|
||||
existing_nullable=True)
|
||||
|
||||
# Add is_budget_fill to auto_tickets
|
||||
with op.batch_alter_table('auto_tickets', schema=None) as batch_op:
|
||||
batch_op.add_column(sa.Column('is_budget_fill', sa.Boolean(), server_default='false', nullable=True))
|
||||
|
||||
|
||||
def downgrade():
|
||||
with op.batch_alter_table('auto_tickets', schema=None) as batch_op:
|
||||
batch_op.drop_column('is_budget_fill')
|
||||
|
||||
with op.batch_alter_table('auto_delivery', schema=None) as batch_op:
|
||||
batch_op.alter_column('house_factor',
|
||||
existing_type=sa.DECIMAL(precision=7, scale=4),
|
||||
type_=sa.DECIMAL(precision=5, scale=2),
|
||||
existing_nullable=True)
|
||||
batch_op.drop_column('k_factor_source')
|
||||
batch_op.drop_column('confidence_score')
|
||||
|
||||
op.drop_index('ix_auto_kfactor_history_customer_fill', table_name='auto_kfactor_history')
|
||||
op.drop_index('ix_auto_kfactor_history_customer_id', table_name='auto_kfactor_history')
|
||||
op.drop_table('auto_kfactor_history')
|
||||
Reference in New Issue
Block a user