data model whew
This commit is contained in:
parent
03b43ce3ab
commit
6178fdd15d
28
alembic/versions/cc7dd65bcdd9_changing_db_bigly.py
Normal file
28
alembic/versions/cc7dd65bcdd9_changing_db_bigly.py
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
"""changing db bigly
|
||||||
|
|
||||||
|
Revision ID: cc7dd65bcdd9
|
||||||
|
Revises: 9fb73424598c
|
||||||
|
Create Date: 2025-04-19 13:36:41.784661
|
||||||
|
|
||||||
|
"""
|
||||||
|
from typing import Sequence, Union
|
||||||
|
|
||||||
|
from alembic import op
|
||||||
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision: str = 'cc7dd65bcdd9'
|
||||||
|
down_revision: Union[str, None] = '9fb73424598c'
|
||||||
|
branch_labels: Union[str, Sequence[str], None] = None
|
||||||
|
depends_on: Union[str, Sequence[str], None] = None
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade() -> None:
|
||||||
|
"""Upgrade schema."""
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade() -> None:
|
||||||
|
"""Downgrade schema."""
|
||||||
|
pass
|
339
alembic/versions/d4d3f43ce86a_changing_db_bigly.py
Normal file
339
alembic/versions/d4d3f43ce86a_changing_db_bigly.py
Normal file
@ -0,0 +1,339 @@
|
|||||||
|
"""changing db bigly
|
||||||
|
|
||||||
|
Revision ID: d4d3f43ce86a
|
||||||
|
Revises: cc7dd65bcdd9
|
||||||
|
Create Date: 2025-04-19 13:46:27.330261
|
||||||
|
|
||||||
|
"""
|
||||||
|
from typing import Sequence, Union
|
||||||
|
|
||||||
|
from alembic import op
|
||||||
|
import sqlalchemy as sa
|
||||||
|
from sqlalchemy.dialects import postgresql
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision: str = 'd4d3f43ce86a'
|
||||||
|
down_revision: Union[str, None] = 'cc7dd65bcdd9'
|
||||||
|
branch_labels: Union[str, Sequence[str], None] = None
|
||||||
|
depends_on: Union[str, Sequence[str], None] = None
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade() -> None:
|
||||||
|
"""Upgrade schema."""
|
||||||
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
|
op.create_table('customers',
|
||||||
|
sa.Column('id', sa.Integer(), nullable=False),
|
||||||
|
sa.Column('name', sa.String(), nullable=True),
|
||||||
|
sa.Column('created_at', sa.DateTime(timezone=True), nullable=True),
|
||||||
|
sa.Column('updated_at', sa.DateTime(timezone=True), nullable=True),
|
||||||
|
sa.PrimaryKeyConstraint('id')
|
||||||
|
)
|
||||||
|
op.create_index(op.f('ix_customers_id'), 'customers', ['id'], unique=False)
|
||||||
|
op.create_index(op.f('ix_customers_name'), 'customers', ['name'], unique=True)
|
||||||
|
op.create_table('products',
|
||||||
|
sa.Column('id', sa.Integer(), nullable=False),
|
||||||
|
sa.Column('name', sa.String(), nullable=True),
|
||||||
|
sa.Column('tcgplayer_id', sa.String(), nullable=True),
|
||||||
|
sa.Column('created_at', sa.DateTime(timezone=True), nullable=True),
|
||||||
|
sa.Column('updated_at', sa.DateTime(timezone=True), nullable=True),
|
||||||
|
sa.Column('deleted_at', sa.DateTime(timezone=True), nullable=True),
|
||||||
|
sa.PrimaryKeyConstraint('id')
|
||||||
|
)
|
||||||
|
op.create_table('tcgplayer_inventory',
|
||||||
|
sa.Column('id', sa.Integer(), nullable=False),
|
||||||
|
sa.Column('tcgplayer_id', sa.String(), nullable=True),
|
||||||
|
sa.Column('product_line', sa.String(), nullable=True),
|
||||||
|
sa.Column('set_name', sa.String(), nullable=True),
|
||||||
|
sa.Column('product_name', sa.String(), nullable=True),
|
||||||
|
sa.Column('title', sa.String(), nullable=True),
|
||||||
|
sa.Column('number', sa.String(), nullable=True),
|
||||||
|
sa.Column('rarity', sa.String(), nullable=True),
|
||||||
|
sa.Column('condition', sa.String(), nullable=True),
|
||||||
|
sa.Column('tcg_market_price', sa.Float(), nullable=True),
|
||||||
|
sa.Column('tcg_direct_low', sa.Float(), nullable=True),
|
||||||
|
sa.Column('tcg_low_price_with_shipping', sa.Float(), nullable=True),
|
||||||
|
sa.Column('tcg_low_price', sa.Float(), nullable=True),
|
||||||
|
sa.Column('total_quantity', sa.Integer(), nullable=True),
|
||||||
|
sa.Column('add_to_quantity', sa.Integer(), nullable=True),
|
||||||
|
sa.Column('tcg_marketplace_price', sa.Float(), nullable=True),
|
||||||
|
sa.Column('photo_url', sa.String(), nullable=True),
|
||||||
|
sa.Column('created_at', sa.DateTime(timezone=True), server_default=sa.text('CURRENT_TIMESTAMP'), nullable=True),
|
||||||
|
sa.Column('updated_at', sa.DateTime(timezone=True), nullable=True),
|
||||||
|
sa.Column('deleted_at', sa.DateTime(timezone=True), nullable=True),
|
||||||
|
sa.PrimaryKeyConstraint('id')
|
||||||
|
)
|
||||||
|
op.create_index(op.f('ix_tcgplayer_inventory_id'), 'tcgplayer_inventory', ['id'], unique=False)
|
||||||
|
op.create_index(op.f('ix_tcgplayer_inventory_tcgplayer_id'), 'tcgplayer_inventory', ['tcgplayer_id'], unique=True)
|
||||||
|
op.create_table('vendors',
|
||||||
|
sa.Column('id', sa.Integer(), nullable=False),
|
||||||
|
sa.Column('name', sa.String(), nullable=True),
|
||||||
|
sa.Column('created_at', sa.DateTime(timezone=True), nullable=True),
|
||||||
|
sa.Column('updated_at', sa.DateTime(timezone=True), nullable=True),
|
||||||
|
sa.Column('deleted_at', sa.DateTime(timezone=True), nullable=True),
|
||||||
|
sa.PrimaryKeyConstraint('id')
|
||||||
|
)
|
||||||
|
op.create_index(op.f('ix_vendors_id'), 'vendors', ['id'], unique=False)
|
||||||
|
op.create_index(op.f('ix_vendors_name'), 'vendors', ['name'], unique=True)
|
||||||
|
op.create_table('physical_items',
|
||||||
|
sa.Column('id', sa.Integer(), nullable=False),
|
||||||
|
sa.Column('item_type', sa.String(), nullable=True),
|
||||||
|
sa.Column('product_id', sa.Integer(), nullable=False),
|
||||||
|
sa.Column('created_at', sa.DateTime(timezone=True), nullable=True),
|
||||||
|
sa.Column('updated_at', sa.DateTime(timezone=True), nullable=True),
|
||||||
|
sa.Column('deleted_at', sa.DateTime(timezone=True), nullable=True),
|
||||||
|
sa.ForeignKeyConstraint(['product_id'], ['products.id'], ),
|
||||||
|
sa.PrimaryKeyConstraint('id')
|
||||||
|
)
|
||||||
|
op.create_table('transactions',
|
||||||
|
sa.Column('id', sa.Integer(), nullable=False),
|
||||||
|
sa.Column('vendor_id', sa.Integer(), nullable=True),
|
||||||
|
sa.Column('customer_id', sa.Integer(), nullable=True),
|
||||||
|
sa.Column('transaction_type', sa.String(), nullable=True),
|
||||||
|
sa.Column('transaction_date', sa.DateTime(timezone=True), nullable=True),
|
||||||
|
sa.Column('transaction_total_amount', sa.Float(), nullable=True),
|
||||||
|
sa.Column('transaction_notes', sa.String(), nullable=True),
|
||||||
|
sa.Column('created_at', sa.DateTime(timezone=True), nullable=True),
|
||||||
|
sa.Column('updated_at', sa.DateTime(timezone=True), nullable=True),
|
||||||
|
sa.Column('deleted_at', sa.DateTime(timezone=True), nullable=True),
|
||||||
|
sa.ForeignKeyConstraint(['customer_id'], ['customers.id'], ),
|
||||||
|
sa.ForeignKeyConstraint(['vendor_id'], ['vendors.id'], ),
|
||||||
|
sa.PrimaryKeyConstraint('id')
|
||||||
|
)
|
||||||
|
op.create_index(op.f('ix_transactions_id'), 'transactions', ['id'], unique=False)
|
||||||
|
op.create_table('inventory_items',
|
||||||
|
sa.Column('id', sa.Integer(), nullable=False),
|
||||||
|
sa.Column('physical_item_id', sa.Integer(), nullable=True),
|
||||||
|
sa.Column('cost_basis', sa.Float(), nullable=True),
|
||||||
|
sa.Column('parent_id', sa.Integer(), nullable=True),
|
||||||
|
sa.Column('created_at', sa.DateTime(timezone=True), nullable=True),
|
||||||
|
sa.Column('updated_at', sa.DateTime(timezone=True), nullable=True),
|
||||||
|
sa.Column('deleted_at', sa.DateTime(timezone=True), nullable=True),
|
||||||
|
sa.ForeignKeyConstraint(['parent_id'], ['inventory_items.id'], ),
|
||||||
|
sa.ForeignKeyConstraint(['physical_item_id'], ['physical_items.id'], ),
|
||||||
|
sa.PrimaryKeyConstraint('id'),
|
||||||
|
sa.UniqueConstraint('physical_item_id')
|
||||||
|
)
|
||||||
|
op.create_index(op.f('ix_inventory_items_id'), 'inventory_items', ['id'], unique=False)
|
||||||
|
op.create_table('sealed_cases',
|
||||||
|
sa.Column('id', sa.Integer(), nullable=False),
|
||||||
|
sa.ForeignKeyConstraint(['id'], ['physical_items.id'], ),
|
||||||
|
sa.PrimaryKeyConstraint('id')
|
||||||
|
)
|
||||||
|
op.create_table('transaction_items',
|
||||||
|
sa.Column('id', sa.Integer(), nullable=False),
|
||||||
|
sa.Column('transaction_id', sa.Integer(), nullable=True),
|
||||||
|
sa.Column('physical_item_id', sa.Integer(), nullable=True),
|
||||||
|
sa.Column('unit_price', sa.Float(), nullable=False),
|
||||||
|
sa.Column('created_at', sa.DateTime(timezone=True), nullable=True),
|
||||||
|
sa.Column('updated_at', sa.DateTime(timezone=True), nullable=True),
|
||||||
|
sa.Column('deleted_at', sa.DateTime(timezone=True), nullable=True),
|
||||||
|
sa.ForeignKeyConstraint(['physical_item_id'], ['physical_items.id'], ),
|
||||||
|
sa.ForeignKeyConstraint(['transaction_id'], ['transactions.id'], ),
|
||||||
|
sa.PrimaryKeyConstraint('id')
|
||||||
|
)
|
||||||
|
op.create_index(op.f('ix_transaction_items_id'), 'transaction_items', ['id'], unique=False)
|
||||||
|
op.create_table('sealed_boxes',
|
||||||
|
sa.Column('id', sa.Integer(), nullable=False),
|
||||||
|
sa.Column('case_id', sa.Integer(), nullable=True),
|
||||||
|
sa.ForeignKeyConstraint(['case_id'], ['sealed_cases.id'], ),
|
||||||
|
sa.ForeignKeyConstraint(['id'], ['physical_items.id'], ),
|
||||||
|
sa.PrimaryKeyConstraint('id')
|
||||||
|
)
|
||||||
|
op.create_table('open_events',
|
||||||
|
sa.Column('id', sa.Integer(), nullable=False),
|
||||||
|
sa.Column('sealed_case_id', sa.Integer(), nullable=True),
|
||||||
|
sa.Column('sealed_box_id', sa.Integer(), nullable=True),
|
||||||
|
sa.Column('open_date', sa.DateTime(timezone=True), nullable=True),
|
||||||
|
sa.Column('created_at', sa.DateTime(timezone=True), nullable=True),
|
||||||
|
sa.Column('updated_at', sa.DateTime(timezone=True), nullable=True),
|
||||||
|
sa.Column('deleted_at', sa.DateTime(timezone=True), nullable=True),
|
||||||
|
sa.ForeignKeyConstraint(['sealed_box_id'], ['sealed_boxes.id'], ),
|
||||||
|
sa.ForeignKeyConstraint(['sealed_case_id'], ['sealed_cases.id'], ),
|
||||||
|
sa.PrimaryKeyConstraint('id')
|
||||||
|
)
|
||||||
|
op.create_index(op.f('ix_open_events_id'), 'open_events', ['id'], unique=False)
|
||||||
|
op.create_table('open_cards',
|
||||||
|
sa.Column('id', sa.Integer(), nullable=False),
|
||||||
|
sa.Column('open_event_id', sa.Integer(), nullable=True),
|
||||||
|
sa.Column('box_id', sa.Integer(), nullable=True),
|
||||||
|
sa.ForeignKeyConstraint(['box_id'], ['open_boxes.id'], ),
|
||||||
|
sa.ForeignKeyConstraint(['id'], ['physical_items.id'], ),
|
||||||
|
sa.ForeignKeyConstraint(['open_event_id'], ['open_events.id'], ),
|
||||||
|
sa.PrimaryKeyConstraint('id')
|
||||||
|
)
|
||||||
|
op.create_table('cost_basis',
|
||||||
|
sa.Column('id', sa.Integer(), nullable=False),
|
||||||
|
sa.Column('transaction_item_id', sa.Integer(), nullable=True),
|
||||||
|
sa.Column('sealed_case_id', sa.Integer(), nullable=True),
|
||||||
|
sa.Column('sealed_box_id', sa.Integer(), nullable=True),
|
||||||
|
sa.Column('open_box_id', sa.Integer(), nullable=True),
|
||||||
|
sa.Column('open_card_id', sa.Integer(), nullable=True),
|
||||||
|
sa.Column('quantity', sa.Integer(), nullable=False),
|
||||||
|
sa.Column('unit_cost', sa.Float(), nullable=False),
|
||||||
|
sa.Column('created_at', sa.DateTime(timezone=True), nullable=True),
|
||||||
|
sa.Column('updated_at', sa.DateTime(timezone=True), nullable=True),
|
||||||
|
sa.Column('deleted_at', sa.DateTime(timezone=True), nullable=True),
|
||||||
|
sa.ForeignKeyConstraint(['open_box_id'], ['open_boxes.id'], ),
|
||||||
|
sa.ForeignKeyConstraint(['open_card_id'], ['open_cards.id'], ),
|
||||||
|
sa.ForeignKeyConstraint(['sealed_box_id'], ['sealed_boxes.id'], ),
|
||||||
|
sa.ForeignKeyConstraint(['sealed_case_id'], ['sealed_cases.id'], ),
|
||||||
|
sa.ForeignKeyConstraint(['transaction_item_id'], ['transaction_items.id'], ),
|
||||||
|
sa.PrimaryKeyConstraint('id')
|
||||||
|
)
|
||||||
|
op.create_index(op.f('ix_cost_basis_id'), 'cost_basis', ['id'], unique=False)
|
||||||
|
|
||||||
|
# Drop tables in correct dependency order
|
||||||
|
# First drop foreign key constraints
|
||||||
|
op.execute('DROP TABLE IF EXISTS open_cards CASCADE')
|
||||||
|
op.execute('DROP TABLE IF EXISTS cost_basis CASCADE')
|
||||||
|
op.execute('DROP TABLE IF EXISTS open_boxes CASCADE')
|
||||||
|
op.execute('DROP TABLE IF EXISTS boxes CASCADE')
|
||||||
|
op.execute('DROP TABLE IF EXISTS games CASCADE')
|
||||||
|
|
||||||
|
op.drop_index('ix_inventory_id', table_name='inventory')
|
||||||
|
# ### end Alembic commands ###
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade() -> None:
|
||||||
|
"""Downgrade schema."""
|
||||||
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
|
# Create tables in correct dependency order
|
||||||
|
op.create_table('games',
|
||||||
|
sa.Column('id', sa.INTEGER(), autoincrement=True, nullable=False),
|
||||||
|
sa.Column('name', sa.VARCHAR(), autoincrement=False, nullable=True),
|
||||||
|
sa.Column('description', sa.VARCHAR(), autoincrement=False, nullable=True),
|
||||||
|
sa.Column('image_url', sa.VARCHAR(), autoincrement=False, nullable=True),
|
||||||
|
sa.Column('created_at', postgresql.TIMESTAMP(timezone=True), server_default=sa.text('now()'), autoincrement=False, nullable=True),
|
||||||
|
sa.Column('updated_at', postgresql.TIMESTAMP(timezone=True), autoincrement=False, nullable=True),
|
||||||
|
sa.PrimaryKeyConstraint('id', name='games_pkey')
|
||||||
|
)
|
||||||
|
op.create_index('ix_games_id', 'games', ['id'], unique=False)
|
||||||
|
|
||||||
|
op.create_table('boxes',
|
||||||
|
sa.Column('id', sa.INTEGER(), autoincrement=True, nullable=False),
|
||||||
|
sa.Column('product_id', sa.INTEGER(), autoincrement=False, nullable=True),
|
||||||
|
sa.Column('type', sa.VARCHAR(), autoincrement=False, nullable=True),
|
||||||
|
sa.Column('set_code', sa.VARCHAR(), autoincrement=False, nullable=True),
|
||||||
|
sa.Column('sku', sa.INTEGER(), autoincrement=False, nullable=True),
|
||||||
|
sa.Column('name', sa.VARCHAR(), autoincrement=False, nullable=True),
|
||||||
|
sa.Column('game_id', sa.INTEGER(), autoincrement=False, nullable=True),
|
||||||
|
sa.Column('expected_number_of_cards', sa.INTEGER(), autoincrement=False, nullable=True),
|
||||||
|
sa.Column('description', sa.VARCHAR(), autoincrement=False, nullable=True),
|
||||||
|
sa.Column('image_url', sa.VARCHAR(), autoincrement=False, nullable=True),
|
||||||
|
sa.Column('created_at', postgresql.TIMESTAMP(timezone=True), server_default=sa.text('now()'), autoincrement=False, nullable=True),
|
||||||
|
sa.Column('updated_at', postgresql.TIMESTAMP(timezone=True), autoincrement=False, nullable=True),
|
||||||
|
sa.ForeignKeyConstraint(['game_id'], ['games.id'], name='boxes_game_id_fkey'),
|
||||||
|
sa.PrimaryKeyConstraint('id', name='boxes_pkey')
|
||||||
|
)
|
||||||
|
op.create_index('ix_boxes_id', 'boxes', ['id'], unique=False)
|
||||||
|
|
||||||
|
op.create_table('open_boxes',
|
||||||
|
sa.Column('id', sa.INTEGER(), autoincrement=True, nullable=False),
|
||||||
|
sa.Column('box_id', sa.INTEGER(), autoincrement=False, nullable=True),
|
||||||
|
sa.Column('date_opened', postgresql.TIMESTAMP(timezone=True), autoincrement=False, nullable=True),
|
||||||
|
sa.Column('number_of_cards', sa.INTEGER(), autoincrement=False, nullable=True),
|
||||||
|
sa.Column('created_at', postgresql.TIMESTAMP(timezone=True), server_default=sa.text('now()'), autoincrement=False, nullable=True),
|
||||||
|
sa.Column('updated_at', postgresql.TIMESTAMP(timezone=True), autoincrement=False, nullable=True),
|
||||||
|
sa.ForeignKeyConstraint(['box_id'], ['boxes.id'], name='open_boxes_box_id_fkey'),
|
||||||
|
sa.PrimaryKeyConstraint('id', name='open_boxes_pkey')
|
||||||
|
)
|
||||||
|
op.create_index('ix_open_boxes_id', 'open_boxes', ['id'], unique=False)
|
||||||
|
|
||||||
|
op.create_table('open_cards',
|
||||||
|
sa.Column('id', sa.INTEGER(), autoincrement=True, nullable=False),
|
||||||
|
sa.Column('box_id', sa.INTEGER(), autoincrement=False, nullable=True),
|
||||||
|
sa.Column('created_at', postgresql.TIMESTAMP(timezone=True), server_default=sa.text('now()'), autoincrement=False, nullable=True),
|
||||||
|
sa.Column('updated_at', postgresql.TIMESTAMP(timezone=True), autoincrement=False, nullable=True),
|
||||||
|
sa.ForeignKeyConstraint(['box_id'], ['open_boxes.id'], name='open_cards_box_id_fkey'),
|
||||||
|
sa.PrimaryKeyConstraint('id', name='open_cards_pkey')
|
||||||
|
)
|
||||||
|
op.create_index('ix_open_cards_id', 'open_cards', ['id'], unique=False)
|
||||||
|
|
||||||
|
op.create_table('cost_basis',
|
||||||
|
sa.Column('id', sa.INTEGER(), autoincrement=True, nullable=False),
|
||||||
|
sa.Column('open_box_id', sa.INTEGER(), autoincrement=False, nullable=True),
|
||||||
|
sa.Column('created_at', postgresql.TIMESTAMP(timezone=True), server_default=sa.text('now()'), autoincrement=False, nullable=True),
|
||||||
|
sa.Column('updated_at', postgresql.TIMESTAMP(timezone=True), autoincrement=False, nullable=True),
|
||||||
|
sa.ForeignKeyConstraint(['open_box_id'], ['open_boxes.id'], name='cost_basis_open_box_id_fkey'),
|
||||||
|
sa.PrimaryKeyConstraint('id', name='cost_basis_pkey')
|
||||||
|
)
|
||||||
|
op.create_index('ix_cost_basis_id', 'cost_basis', ['id'], unique=False)
|
||||||
|
|
||||||
|
op.create_table('cards',
|
||||||
|
sa.Column('id', sa.INTEGER(), autoincrement=True, nullable=False),
|
||||||
|
sa.Column('name', sa.VARCHAR(), autoincrement=False, nullable=True),
|
||||||
|
sa.Column('rarity', sa.VARCHAR(), autoincrement=False, nullable=True),
|
||||||
|
sa.Column('set_name', sa.VARCHAR(), autoincrement=False, nullable=True),
|
||||||
|
sa.Column('price', sa.DOUBLE_PRECISION(precision=53), autoincrement=False, nullable=True),
|
||||||
|
sa.Column('quantity', sa.INTEGER(), autoincrement=False, nullable=True),
|
||||||
|
sa.Column('tcgplayer_sku', sa.VARCHAR(), autoincrement=False, nullable=True),
|
||||||
|
sa.Column('product_line', sa.VARCHAR(), autoincrement=False, nullable=True),
|
||||||
|
sa.Column('product_name', sa.VARCHAR(), autoincrement=False, nullable=True),
|
||||||
|
sa.Column('title', sa.VARCHAR(), autoincrement=False, nullable=True),
|
||||||
|
sa.Column('number', sa.VARCHAR(), autoincrement=False, nullable=True),
|
||||||
|
sa.Column('condition', sa.VARCHAR(), autoincrement=False, nullable=True),
|
||||||
|
sa.Column('tcg_market_price', sa.DOUBLE_PRECISION(precision=53), autoincrement=False, nullable=True),
|
||||||
|
sa.Column('tcg_direct_low', sa.DOUBLE_PRECISION(precision=53), autoincrement=False, nullable=True),
|
||||||
|
sa.Column('tcg_low_price_with_shipping', sa.DOUBLE_PRECISION(precision=53), autoincrement=False, nullable=True),
|
||||||
|
sa.Column('tcg_low_price', sa.DOUBLE_PRECISION(precision=53), autoincrement=False, nullable=True),
|
||||||
|
sa.Column('total_quantity', sa.INTEGER(), autoincrement=False, nullable=True),
|
||||||
|
sa.Column('add_to_quantity', sa.INTEGER(), autoincrement=False, nullable=True),
|
||||||
|
sa.Column('tcg_marketplace_price', sa.DOUBLE_PRECISION(precision=53), autoincrement=False, nullable=True),
|
||||||
|
sa.Column('photo_url', sa.VARCHAR(), autoincrement=False, nullable=True),
|
||||||
|
sa.Column('created_at', postgresql.TIMESTAMP(timezone=True), server_default=sa.text('now()'), autoincrement=False, nullable=True),
|
||||||
|
sa.Column('updated_at', postgresql.TIMESTAMP(timezone=True), autoincrement=False, nullable=True),
|
||||||
|
sa.PrimaryKeyConstraint('id', name='cards_pkey')
|
||||||
|
)
|
||||||
|
op.create_index('ix_cards_tcgplayer_sku', 'cards', ['tcgplayer_sku'], unique=True)
|
||||||
|
op.create_index('ix_cards_set_name', 'cards', ['set_name'], unique=False)
|
||||||
|
op.create_index('ix_cards_name', 'cards', ['name'], unique=False)
|
||||||
|
op.create_index('ix_cards_id', 'cards', ['id'], unique=False)
|
||||||
|
op.create_table('inventory',
|
||||||
|
sa.Column('id', sa.INTEGER(), autoincrement=True, nullable=False),
|
||||||
|
sa.Column('tcgplayer_id', sa.VARCHAR(), autoincrement=False, nullable=True),
|
||||||
|
sa.Column('product_line', sa.VARCHAR(), autoincrement=False, nullable=True),
|
||||||
|
sa.Column('set_name', sa.VARCHAR(), autoincrement=False, nullable=True),
|
||||||
|
sa.Column('product_name', sa.VARCHAR(), autoincrement=False, nullable=True),
|
||||||
|
sa.Column('title', sa.VARCHAR(), autoincrement=False, nullable=True),
|
||||||
|
sa.Column('number', sa.VARCHAR(), autoincrement=False, nullable=True),
|
||||||
|
sa.Column('rarity', sa.VARCHAR(), autoincrement=False, nullable=True),
|
||||||
|
sa.Column('condition', sa.VARCHAR(), autoincrement=False, nullable=True),
|
||||||
|
sa.Column('tcg_market_price', sa.DOUBLE_PRECISION(precision=53), autoincrement=False, nullable=True),
|
||||||
|
sa.Column('tcg_direct_low', sa.DOUBLE_PRECISION(precision=53), autoincrement=False, nullable=True),
|
||||||
|
sa.Column('tcg_low_price_with_shipping', sa.DOUBLE_PRECISION(precision=53), autoincrement=False, nullable=True),
|
||||||
|
sa.Column('tcg_low_price', sa.DOUBLE_PRECISION(precision=53), autoincrement=False, nullable=True),
|
||||||
|
sa.Column('total_quantity', sa.INTEGER(), autoincrement=False, nullable=True),
|
||||||
|
sa.Column('add_to_quantity', sa.INTEGER(), autoincrement=False, nullable=True),
|
||||||
|
sa.Column('tcg_marketplace_price', sa.DOUBLE_PRECISION(precision=53), autoincrement=False, nullable=True),
|
||||||
|
sa.Column('photo_url', sa.VARCHAR(), autoincrement=False, nullable=True),
|
||||||
|
sa.Column('created_at', postgresql.TIMESTAMP(timezone=True), server_default=sa.text('now()'), autoincrement=False, nullable=True),
|
||||||
|
sa.Column('updated_at', postgresql.TIMESTAMP(timezone=True), autoincrement=False, nullable=True),
|
||||||
|
sa.PrimaryKeyConstraint('id', name='inventory_pkey')
|
||||||
|
)
|
||||||
|
op.create_index('ix_inventory_tcgplayer_id', 'inventory', ['tcgplayer_id'], unique=True)
|
||||||
|
op.create_index('ix_inventory_id', 'inventory', ['id'], unique=False)
|
||||||
|
op.drop_index(op.f('ix_cost_basis_id'), table_name='cost_basis')
|
||||||
|
op.drop_table('cost_basis')
|
||||||
|
op.drop_table('open_cards')
|
||||||
|
op.drop_index(op.f('ix_open_events_id'), table_name='open_events')
|
||||||
|
op.drop_table('open_events')
|
||||||
|
op.drop_table('sealed_boxes')
|
||||||
|
op.drop_index(op.f('ix_transaction_items_id'), table_name='transaction_items')
|
||||||
|
op.drop_table('transaction_items')
|
||||||
|
op.drop_table('sealed_cases')
|
||||||
|
op.drop_index(op.f('ix_inventory_items_id'), table_name='inventory_items')
|
||||||
|
op.drop_table('inventory_items')
|
||||||
|
op.drop_index(op.f('ix_transactions_id'), table_name='transactions')
|
||||||
|
op.drop_table('transactions')
|
||||||
|
op.drop_table('physical_items')
|
||||||
|
op.drop_index(op.f('ix_vendors_name'), table_name='vendors')
|
||||||
|
op.drop_index(op.f('ix_vendors_id'), table_name='vendors')
|
||||||
|
op.drop_table('vendors')
|
||||||
|
op.drop_index(op.f('ix_tcgplayer_inventory_tcgplayer_id'), table_name='tcgplayer_inventory')
|
||||||
|
op.drop_index(op.f('ix_tcgplayer_inventory_id'), table_name='tcgplayer_inventory')
|
||||||
|
op.drop_table('tcgplayer_inventory')
|
||||||
|
op.drop_table('products')
|
||||||
|
op.drop_index(op.f('ix_customers_name'), table_name='customers')
|
||||||
|
op.drop_index(op.f('ix_customers_id'), table_name='customers')
|
||||||
|
op.drop_table('customers')
|
||||||
|
# ### end Alembic commands ###
|
@ -58,7 +58,7 @@ async def lifespan(app: FastAPI):
|
|||||||
db = SessionLocal()
|
db = SessionLocal()
|
||||||
try:
|
try:
|
||||||
data_init_service = service_manager.get_service('data_initialization')
|
data_init_service = service_manager.get_service('data_initialization')
|
||||||
data_init = await data_init_service.initialize_data(db, game_ids=[1, 3], use_cache=False, init_categories=False, init_products=False, init_groups=False, init_archived_prices=True, init_mtgjson=False, archived_prices_start_date="2024-03-05", archived_prices_end_date="2025-04-17")
|
data_init = await data_init_service.initialize_data(db, game_ids=[1, 3], use_cache=False, init_categories=False, init_products=False, init_groups=False, init_archived_prices=False, init_mtgjson=False, archived_prices_start_date="2024-03-05", archived_prices_end_date="2025-04-17")
|
||||||
logger.info(f"Data initialization results: {data_init}")
|
logger.info(f"Data initialization results: {data_init}")
|
||||||
|
|
||||||
# Start the scheduler
|
# Start the scheduler
|
||||||
|
@ -1,26 +1,50 @@
|
|||||||
from app.models.box import Box
|
|
||||||
from app.models.card import Card
|
|
||||||
from app.models.file import File
|
from app.models.file import File
|
||||||
from app.models.game import Game
|
from app.models.inventory_management import (
|
||||||
from app.models.inventory import Inventory
|
PhysicalItem,
|
||||||
|
InventoryItem,
|
||||||
|
TransactionItem,
|
||||||
|
OpenEvent,
|
||||||
|
Vendor,
|
||||||
|
Customer,
|
||||||
|
Transaction,
|
||||||
|
CostBasis
|
||||||
|
)
|
||||||
from app.models.mtgjson_card import MTGJSONCard
|
from app.models.mtgjson_card import MTGJSONCard
|
||||||
from app.models.mtgjson_sku import MTGJSONSKU
|
from app.models.mtgjson_sku import MTGJSONSKU
|
||||||
|
from app.models.product import Product
|
||||||
from app.models.tcgplayer_category import TCGPlayerCategory
|
from app.models.tcgplayer_category import TCGPlayerCategory
|
||||||
from app.models.tcgplayer_group import TCGPlayerGroup
|
from app.models.tcgplayer_group import TCGPlayerGroup
|
||||||
from app.models.tcgplayer_order import TCGPlayerOrder
|
from app.models.tcgplayer_inventory import TCGPlayerInventory
|
||||||
|
from app.models.tcgplayer_order import (
|
||||||
|
TCGPlayerOrder,
|
||||||
|
TCGPlayerOrderTransaction,
|
||||||
|
TCGPlayerOrderProduct,
|
||||||
|
TCGPlayerOrderRefund
|
||||||
|
)
|
||||||
|
from app.models.tcgplayer_price_history import TCGPlayerPriceHistory
|
||||||
from app.models.tcgplayer_product import TCGPlayerProduct
|
from app.models.tcgplayer_product import TCGPlayerProduct
|
||||||
|
|
||||||
# This makes all models available for Alembic to discover
|
# This makes all models available for Alembic to discover
|
||||||
__all__ = [
|
__all__ = [
|
||||||
'Box',
|
|
||||||
'Card',
|
|
||||||
'File',
|
'File',
|
||||||
'Game',
|
'PhysicalItem',
|
||||||
'Inventory',
|
'InventoryItem',
|
||||||
|
'TransactionItem',
|
||||||
|
'OpenEvent',
|
||||||
|
'Vendor',
|
||||||
|
'Customer',
|
||||||
|
'Transaction',
|
||||||
|
'CostBasis',
|
||||||
'MTGJSONCard',
|
'MTGJSONCard',
|
||||||
'MTGJSONSKU',
|
'MTGJSONSKU',
|
||||||
|
'Product',
|
||||||
'TCGPlayerCategory',
|
'TCGPlayerCategory',
|
||||||
'TCGPlayerGroup',
|
'TCGPlayerGroup',
|
||||||
|
'TCGPlayerInventory',
|
||||||
'TCGPlayerOrder',
|
'TCGPlayerOrder',
|
||||||
|
'TCGPlayerOrderTransaction',
|
||||||
|
'TCGPlayerOrderProduct',
|
||||||
|
'TCGPlayerOrderRefund',
|
||||||
|
'TCGPlayerPriceHistory',
|
||||||
'TCGPlayerProduct'
|
'TCGPlayerProduct'
|
||||||
]
|
]
|
@ -1,30 +0,0 @@
|
|||||||
from sqlalchemy import Column, Integer, String, DateTime, ForeignKey
|
|
||||||
from sqlalchemy.orm import relationship
|
|
||||||
from sqlalchemy.sql import func
|
|
||||||
from app.db.database import Base
|
|
||||||
|
|
||||||
class Box(Base):
|
|
||||||
__tablename__ = "boxes"
|
|
||||||
|
|
||||||
id = Column(Integer, primary_key=True, index=True)
|
|
||||||
product_id = Column(Integer)
|
|
||||||
type = Column(String)
|
|
||||||
set_code = Column(String)
|
|
||||||
sku = Column(Integer)
|
|
||||||
name = Column(String)
|
|
||||||
game_id = Column(Integer, ForeignKey("games.id"))
|
|
||||||
expected_number_of_cards = Column(Integer)
|
|
||||||
description = Column(String)
|
|
||||||
image_url = Column(String)
|
|
||||||
created_at = Column(DateTime(timezone=True), server_default=func.now())
|
|
||||||
updated_at = Column(DateTime(timezone=True), onupdate=func.now())
|
|
||||||
|
|
||||||
class OpenBox(Base):
|
|
||||||
__tablename__ = "open_boxes"
|
|
||||||
|
|
||||||
id = Column(Integer, primary_key=True, index=True)
|
|
||||||
box_id = Column(Integer, ForeignKey("boxes.id"))
|
|
||||||
number_of_cards = Column(Integer)
|
|
||||||
date_opened = Column(DateTime(timezone=True))
|
|
||||||
created_at = Column(DateTime(timezone=True), server_default=func.now())
|
|
||||||
updated_at = Column(DateTime(timezone=True), onupdate=func.now())
|
|
@ -1,37 +0,0 @@
|
|||||||
from typing import List, Optional
|
|
||||||
from datetime import datetime
|
|
||||||
from sqlalchemy import Column, Integer, String, Float, ForeignKey, DateTime
|
|
||||||
from sqlalchemy.orm import relationship
|
|
||||||
from sqlalchemy.sql import func
|
|
||||||
from app.db.database import Base
|
|
||||||
|
|
||||||
|
|
||||||
class Card(Base):
|
|
||||||
__tablename__ = "cards"
|
|
||||||
|
|
||||||
id = Column(Integer, primary_key=True, index=True)
|
|
||||||
name = Column(String, index=True)
|
|
||||||
rarity = Column(String)
|
|
||||||
set_name = Column(String, index=True)
|
|
||||||
price = Column(Float)
|
|
||||||
quantity = Column(Integer, default=0)
|
|
||||||
|
|
||||||
# TCGPlayer specific fields
|
|
||||||
tcgplayer_sku = Column(String, unique=True, index=True)
|
|
||||||
product_line = Column(String)
|
|
||||||
product_name = Column(String)
|
|
||||||
title = Column(String)
|
|
||||||
number = Column(String)
|
|
||||||
condition = Column(String)
|
|
||||||
tcg_market_price = Column(Float)
|
|
||||||
tcg_direct_low = Column(Float)
|
|
||||||
tcg_low_price_with_shipping = Column(Float)
|
|
||||||
tcg_low_price = Column(Float)
|
|
||||||
total_quantity = Column(Integer)
|
|
||||||
add_to_quantity = Column(Integer)
|
|
||||||
tcg_marketplace_price = Column(Float)
|
|
||||||
photo_url = Column(String)
|
|
||||||
|
|
||||||
# Timestamps
|
|
||||||
created_at = Column(DateTime(timezone=True), server_default=func.now())
|
|
||||||
updated_at = Column(DateTime(timezone=True), onupdate=func.now())
|
|
@ -1,14 +0,0 @@
|
|||||||
from sqlalchemy import Column, Integer, String, DateTime
|
|
||||||
from sqlalchemy.orm import relationship
|
|
||||||
from sqlalchemy.sql import func
|
|
||||||
from app.db.database import Base
|
|
||||||
|
|
||||||
class Game(Base):
|
|
||||||
__tablename__ = "games"
|
|
||||||
|
|
||||||
id = Column(Integer, primary_key=True, index=True)
|
|
||||||
name = Column(String)
|
|
||||||
description = Column(String)
|
|
||||||
image_url = Column(String)
|
|
||||||
created_at = Column(DateTime(timezone=True), server_default=func.now())
|
|
||||||
updated_at = Column(DateTime(timezone=True), onupdate=func.now())
|
|
185
app/models/inventory_management.py
Normal file
185
app/models/inventory_management.py
Normal file
@ -0,0 +1,185 @@
|
|||||||
|
from sqlalchemy import Column, Integer, String, Float, DateTime, ForeignKey, Table
|
||||||
|
from sqlalchemy.orm import relationship
|
||||||
|
from app.db.database import Base
|
||||||
|
|
||||||
|
class PhysicalItem(Base):
|
||||||
|
__tablename__ = "physical_items"
|
||||||
|
|
||||||
|
id = Column(Integer, primary_key=True)
|
||||||
|
item_type = Column(String)
|
||||||
|
product_id = Column(Integer, ForeignKey("products.id"), nullable=False)
|
||||||
|
created_at = Column(DateTime(timezone=True))
|
||||||
|
updated_at = Column(DateTime(timezone=True))
|
||||||
|
deleted_at = Column(DateTime(timezone=True), nullable=True)
|
||||||
|
|
||||||
|
__mapper_args__ = {
|
||||||
|
'polymorphic_on': item_type,
|
||||||
|
'polymorphic_identity': 'physical_item'
|
||||||
|
}
|
||||||
|
|
||||||
|
# Relationships
|
||||||
|
product = relationship("Product")
|
||||||
|
inventory_item = relationship("InventoryItem", uselist=False, back_populates="physical_item")
|
||||||
|
transaction_items = relationship("TransactionItem", back_populates="physical_item")
|
||||||
|
|
||||||
|
class SealedCase(PhysicalItem):
|
||||||
|
__tablename__ = "sealed_cases"
|
||||||
|
|
||||||
|
id = Column(Integer, ForeignKey('physical_items.id'), primary_key=True)
|
||||||
|
|
||||||
|
__mapper_args__ = {
|
||||||
|
'polymorphic_identity': 'sealed_case'
|
||||||
|
}
|
||||||
|
|
||||||
|
# Relationships
|
||||||
|
boxes = relationship("SealedBox", back_populates="case")
|
||||||
|
open_event = relationship("OpenEvent", uselist=False, back_populates="sealed_case")
|
||||||
|
|
||||||
|
class SealedBox(PhysicalItem):
|
||||||
|
__tablename__ = "sealed_boxes"
|
||||||
|
|
||||||
|
id = Column(Integer, ForeignKey('physical_items.id'), primary_key=True)
|
||||||
|
case_id = Column(Integer, ForeignKey("sealed_cases.id"), nullable=True)
|
||||||
|
|
||||||
|
__mapper_args__ = {
|
||||||
|
'polymorphic_identity': 'sealed_box'
|
||||||
|
}
|
||||||
|
|
||||||
|
# Relationships
|
||||||
|
case = relationship("SealedCase", back_populates="boxes")
|
||||||
|
open_event = relationship("OpenEvent", uselist=False, back_populates="sealed_box")
|
||||||
|
|
||||||
|
class OpenBox(PhysicalItem):
|
||||||
|
__tablename__ = "open_boxes"
|
||||||
|
|
||||||
|
id = Column(Integer, ForeignKey('physical_items.id'), primary_key=True)
|
||||||
|
open_event_id = Column(Integer, ForeignKey("open_events.id"))
|
||||||
|
sealed_box_id = Column(Integer, ForeignKey("sealed_boxes.id"))
|
||||||
|
|
||||||
|
__mapper_args__ = {
|
||||||
|
'polymorphic_identity': 'open_box'
|
||||||
|
}
|
||||||
|
|
||||||
|
# Relationships
|
||||||
|
open_event = relationship("OpenEvent", back_populates="resulting_boxes")
|
||||||
|
sealed_box = relationship("SealedBox")
|
||||||
|
cards = relationship("OpenCard", back_populates="box")
|
||||||
|
|
||||||
|
class OpenCard(PhysicalItem):
|
||||||
|
__tablename__ = "open_cards"
|
||||||
|
|
||||||
|
id = Column(Integer, ForeignKey('physical_items.id'), primary_key=True)
|
||||||
|
open_event_id = Column(Integer, ForeignKey("open_events.id"))
|
||||||
|
box_id = Column(Integer, ForeignKey("open_boxes.id"), nullable=True)
|
||||||
|
|
||||||
|
__mapper_args__ = {
|
||||||
|
'polymorphic_identity': 'open_card'
|
||||||
|
}
|
||||||
|
|
||||||
|
# Relationships
|
||||||
|
open_event = relationship("OpenEvent", back_populates="resulting_cards")
|
||||||
|
box = relationship("OpenBox", back_populates="cards")
|
||||||
|
|
||||||
|
class InventoryItem(Base):
|
||||||
|
__tablename__ = "inventory_items"
|
||||||
|
|
||||||
|
id = Column(Integer, primary_key=True, index=True)
|
||||||
|
physical_item_id = Column(Integer, ForeignKey("physical_items.id"), unique=True)
|
||||||
|
cost_basis = Column(Float) # Current cost basis for this item
|
||||||
|
parent_id = Column(Integer, ForeignKey("inventory_items.id"), nullable=True) # For tracking hierarchy
|
||||||
|
created_at = Column(DateTime(timezone=True))
|
||||||
|
updated_at = Column(DateTime(timezone=True))
|
||||||
|
deleted_at = Column(DateTime(timezone=True), nullable=True)
|
||||||
|
|
||||||
|
# Relationships
|
||||||
|
physical_item = relationship("PhysicalItem", back_populates="inventory_item")
|
||||||
|
parent = relationship("InventoryItem", remote_side=[id])
|
||||||
|
children = relationship("InventoryItem")
|
||||||
|
|
||||||
|
class TransactionItem(Base):
|
||||||
|
__tablename__ = "transaction_items"
|
||||||
|
|
||||||
|
id = Column(Integer, primary_key=True, index=True)
|
||||||
|
transaction_id = Column(Integer, ForeignKey("transactions.id"))
|
||||||
|
physical_item_id = Column(Integer, ForeignKey("physical_items.id"))
|
||||||
|
unit_price = Column(Float, nullable=False)
|
||||||
|
created_at = Column(DateTime(timezone=True))
|
||||||
|
updated_at = Column(DateTime(timezone=True))
|
||||||
|
deleted_at = Column(DateTime(timezone=True), nullable=True)
|
||||||
|
|
||||||
|
# Relationships
|
||||||
|
transaction = relationship("Transaction", back_populates="transaction_items")
|
||||||
|
physical_item = relationship("PhysicalItem", back_populates="transaction_items")
|
||||||
|
|
||||||
|
class OpenEvent(Base):
|
||||||
|
__tablename__ = "open_events"
|
||||||
|
|
||||||
|
id = Column(Integer, primary_key=True, index=True)
|
||||||
|
sealed_case_id = Column(Integer, ForeignKey("sealed_cases.id"), nullable=True)
|
||||||
|
sealed_box_id = Column(Integer, ForeignKey("sealed_boxes.id"), nullable=True)
|
||||||
|
open_date = Column(DateTime(timezone=True))
|
||||||
|
created_at = Column(DateTime(timezone=True))
|
||||||
|
updated_at = Column(DateTime(timezone=True))
|
||||||
|
deleted_at = Column(DateTime(timezone=True), nullable=True)
|
||||||
|
|
||||||
|
# Relationships
|
||||||
|
sealed_case = relationship("SealedCase", back_populates="open_event")
|
||||||
|
sealed_box = relationship("SealedBox", back_populates="open_event")
|
||||||
|
resulting_boxes = relationship("OpenBox", back_populates="open_event")
|
||||||
|
resulting_cards = relationship("OpenCard", back_populates="open_event")
|
||||||
|
|
||||||
|
class Vendor(Base):
|
||||||
|
__tablename__ = "vendors"
|
||||||
|
|
||||||
|
id = Column(Integer, primary_key=True, index=True)
|
||||||
|
name = Column(String, unique=True, index=True)
|
||||||
|
created_at = Column(DateTime(timezone=True))
|
||||||
|
updated_at = Column(DateTime(timezone=True))
|
||||||
|
deleted_at = Column(DateTime(timezone=True), nullable=True)
|
||||||
|
|
||||||
|
class Customer(Base):
|
||||||
|
__tablename__ = "customers"
|
||||||
|
|
||||||
|
id = Column(Integer, primary_key=True, index=True)
|
||||||
|
name = Column(String, unique=True, index=True)
|
||||||
|
created_at = Column(DateTime(timezone=True))
|
||||||
|
updated_at = Column(DateTime(timezone=True))
|
||||||
|
|
||||||
|
class Transaction(Base):
|
||||||
|
__tablename__ = "transactions"
|
||||||
|
|
||||||
|
id = Column(Integer, primary_key=True, index=True)
|
||||||
|
vendor_id = Column(Integer, ForeignKey("vendors.id"), nullable=True)
|
||||||
|
customer_id = Column(Integer, ForeignKey("customers.id"), nullable=True)
|
||||||
|
transaction_type = Column(String) # 'purchase' or 'sale'
|
||||||
|
transaction_date = Column(DateTime(timezone=True))
|
||||||
|
transaction_total_amount = Column(Float)
|
||||||
|
transaction_notes = Column(String)
|
||||||
|
created_at = Column(DateTime(timezone=True))
|
||||||
|
updated_at = Column(DateTime(timezone=True))
|
||||||
|
deleted_at = Column(DateTime(timezone=True), nullable=True)
|
||||||
|
|
||||||
|
# Relationships
|
||||||
|
transaction_items = relationship("TransactionItem", back_populates="transaction")
|
||||||
|
|
||||||
|
class CostBasis(Base):
|
||||||
|
__tablename__ = "cost_basis"
|
||||||
|
|
||||||
|
id = Column(Integer, primary_key=True, index=True)
|
||||||
|
transaction_item_id = Column(Integer, ForeignKey("transaction_items.id"))
|
||||||
|
sealed_case_id = Column(Integer, ForeignKey("sealed_cases.id"), nullable=True)
|
||||||
|
sealed_box_id = Column(Integer, ForeignKey("sealed_boxes.id"), nullable=True)
|
||||||
|
open_box_id = Column(Integer, ForeignKey("open_boxes.id"), nullable=True)
|
||||||
|
open_card_id = Column(Integer, ForeignKey("open_cards.id"), nullable=True)
|
||||||
|
quantity = Column(Integer, nullable=False, default=1)
|
||||||
|
unit_cost = Column(Float, nullable=False)
|
||||||
|
created_at = Column(DateTime(timezone=True))
|
||||||
|
updated_at = Column(DateTime(timezone=True))
|
||||||
|
deleted_at = Column(DateTime(timezone=True), nullable=True)
|
||||||
|
|
||||||
|
# Relationships
|
||||||
|
transaction_item = relationship("TransactionItem")
|
||||||
|
sealed_case = relationship("SealedCase")
|
||||||
|
sealed_box = relationship("SealedBox")
|
||||||
|
open_box = relationship("OpenBox")
|
||||||
|
open_card = relationship("OpenCard")
|
12
app/models/product.py
Normal file
12
app/models/product.py
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
from sqlalchemy import Column, Integer, String, DateTime
|
||||||
|
from app.db.database import Base
|
||||||
|
|
||||||
|
class Product(Base):
|
||||||
|
__tablename__ = "products"
|
||||||
|
|
||||||
|
id = Column(Integer, primary_key=True)
|
||||||
|
name = Column(String)
|
||||||
|
tcgplayer_id = Column(String)
|
||||||
|
created_at = Column(DateTime(timezone=True))
|
||||||
|
updated_at = Column(DateTime(timezone=True))
|
||||||
|
deleted_at = Column(DateTime(timezone=True), nullable=True)
|
@ -2,8 +2,8 @@ from sqlalchemy import Column, Integer, String, Float, DateTime
|
|||||||
from sqlalchemy.sql import func
|
from sqlalchemy.sql import func
|
||||||
from app.db.database import Base
|
from app.db.database import Base
|
||||||
|
|
||||||
class Inventory(Base):
|
class TCGPlayerInventory(Base):
|
||||||
__tablename__ = "inventory"
|
__tablename__ = "tcgplayer_inventory"
|
||||||
|
|
||||||
id = Column(Integer, primary_key=True, index=True)
|
id = Column(Integer, primary_key=True, index=True)
|
||||||
tcgplayer_id = Column(String, unique=True, index=True)
|
tcgplayer_id = Column(String, unique=True, index=True)
|
||||||
@ -22,7 +22,6 @@ class Inventory(Base):
|
|||||||
add_to_quantity = Column(Integer)
|
add_to_quantity = Column(Integer)
|
||||||
tcg_marketplace_price = Column(Float)
|
tcg_marketplace_price = Column(Float)
|
||||||
photo_url = Column(String)
|
photo_url = Column(String)
|
||||||
|
created_at = Column(DateTime(timezone=True), server_default=func.current_timestamp())
|
||||||
# Timestamps
|
updated_at = Column(DateTime(timezone=True), onupdate=func.current_timestamp())
|
||||||
created_at = Column(DateTime(timezone=True), server_default=func.now())
|
deleted_at = Column(DateTime(timezone=True), nullable=True)
|
||||||
updated_at = Column(DateTime(timezone=True), onupdate=func.now())
|
|
@ -3,11 +3,6 @@ from sqlalchemy.orm import Session
|
|||||||
from app.db.database import get_db
|
from app.db.database import get_db
|
||||||
from app.models.file import File as FileModel
|
from app.models.file import File as FileModel
|
||||||
from app.schemas.file import FileCreate, FileUpdate, FileDelete, FileList, FileInDB
|
from app.schemas.file import FileCreate, FileUpdate, FileDelete, FileList, FileInDB
|
||||||
from app.models.box import Box as BoxModel, OpenBox as OpenBoxModel
|
|
||||||
from app.schemas.box import BoxCreate, BoxUpdate, BoxDelete, BoxList, OpenBoxCreate, OpenBoxUpdate, OpenBoxDelete, OpenBoxList, BoxInDB, OpenBoxInDB
|
|
||||||
from app.models.game import Game as GameModel
|
|
||||||
from app.schemas.game import GameCreate, GameUpdate, GameDelete, GameList, GameInDB
|
|
||||||
from app.models.card import Card as CardModel
|
|
||||||
from app.routes.set_label_routes import router as set_label_router
|
from app.routes.set_label_routes import router as set_label_router
|
||||||
from app.routes.order_routes import router as order_router
|
from app.routes.order_routes import router as order_router
|
||||||
|
|
||||||
@ -48,61 +43,3 @@ async def update_file(file_id: int, file: FileUpdate):
|
|||||||
@router.delete("/files/{file_id}", response_model=FileDelete)
|
@router.delete("/files/{file_id}", response_model=FileDelete)
|
||||||
async def delete_file(file_id: int):
|
async def delete_file(file_id: int):
|
||||||
return {"message": "File deleted successfully"}
|
return {"message": "File deleted successfully"}
|
||||||
|
|
||||||
# ============================================================================
|
|
||||||
# Box Management Endpoints
|
|
||||||
# ============================================================================
|
|
||||||
@router.get("/boxes", response_model=BoxList)
|
|
||||||
async def get_boxes(page: int = 1, limit: int = 10, type: str = None, id: int = None):
|
|
||||||
return {"boxes": [], "total": 0, "page": page, "limit": limit}
|
|
||||||
|
|
||||||
@router.post("/boxes", response_model=BoxInDB)
|
|
||||||
async def create_box(box: BoxCreate):
|
|
||||||
return {"message": "Box created successfully"}
|
|
||||||
|
|
||||||
@router.put("/boxes/{box_id}", response_model=BoxInDB)
|
|
||||||
async def update_box(box_id: int, box: BoxUpdate):
|
|
||||||
return {"message": "Box updated successfully"}
|
|
||||||
|
|
||||||
@router.delete("/boxes/{box_id}", response_model=BoxDelete)
|
|
||||||
async def delete_box(box_id: int):
|
|
||||||
return {"message": "Box deleted successfully"}
|
|
||||||
|
|
||||||
# ============================================================================
|
|
||||||
# Open Box Management Endpoints
|
|
||||||
# ============================================================================
|
|
||||||
@router.get("/open_boxes", response_model=OpenBoxList)
|
|
||||||
async def get_open_boxes(page: int = 1, limit: int = 10, type: str = None, id: int = None):
|
|
||||||
return {"open_boxes": [], "total": 0, "page": page, "limit": limit}
|
|
||||||
|
|
||||||
@router.post("/open_boxes", response_model=OpenBoxInDB)
|
|
||||||
async def create_open_box(open_box: OpenBoxCreate):
|
|
||||||
return {"message": "Open box created successfully"}
|
|
||||||
|
|
||||||
@router.put("/open_boxes/{open_box_id}", response_model=OpenBoxInDB)
|
|
||||||
async def update_open_box(open_box_id: int, open_box: OpenBoxUpdate):
|
|
||||||
return {"message": "Open box updated successfully"}
|
|
||||||
|
|
||||||
@router.delete("/open_boxes/{open_box_id}", response_model=OpenBoxDelete)
|
|
||||||
async def delete_open_box(open_box_id: int):
|
|
||||||
return {"message": "Open box deleted successfully"}
|
|
||||||
|
|
||||||
# ============================================================================
|
|
||||||
# Game Management Endpoints
|
|
||||||
# ============================================================================
|
|
||||||
@router.get("/games", response_model=GameList)
|
|
||||||
async def get_games(page: int = 1, limit: int = 10, type: str = None, id: int = None):
|
|
||||||
return {"games": [], "total": 0, "page": page, "limit": limit}
|
|
||||||
|
|
||||||
@router.post("/games", response_model=GameInDB)
|
|
||||||
async def create_game(game: GameCreate):
|
|
||||||
return {"message": "Game created successfully"}
|
|
||||||
|
|
||||||
@router.put("/games/{game_id}", response_model=GameInDB)
|
|
||||||
async def update_game(game_id: int, game: GameUpdate):
|
|
||||||
return {"message": "Game updated successfully"}
|
|
||||||
|
|
||||||
@router.delete("/games/{game_id}", response_model=GameDelete)
|
|
||||||
async def delete_game(game_id: int):
|
|
||||||
return {"message": "Game deleted successfully"}
|
|
||||||
|
|
||||||
|
@ -1,72 +0,0 @@
|
|||||||
from typing import List, Optional
|
|
||||||
from datetime import datetime
|
|
||||||
from pydantic import BaseModel
|
|
||||||
|
|
||||||
# Base schema with common attributes
|
|
||||||
class BoxBase(BaseModel):
|
|
||||||
name: str
|
|
||||||
description: Optional[str] = None
|
|
||||||
game_id: int
|
|
||||||
set_id: Optional[int] = None
|
|
||||||
price: Optional[float] = None
|
|
||||||
quantity: Optional[int] = 0
|
|
||||||
status: Optional[str] = "available" # available, sold, reserved
|
|
||||||
|
|
||||||
# Schema for creating a new box
|
|
||||||
class BoxCreate(BoxBase):
|
|
||||||
pass
|
|
||||||
|
|
||||||
# Schema for updating a box
|
|
||||||
class BoxUpdate(BoxBase):
|
|
||||||
pass
|
|
||||||
|
|
||||||
# Schema for reading a box
|
|
||||||
class BoxInDB(BoxBase):
|
|
||||||
id: int
|
|
||||||
created_at: datetime
|
|
||||||
updated_at: Optional[datetime] = None
|
|
||||||
|
|
||||||
class Config:
|
|
||||||
from_attributes = True
|
|
||||||
|
|
||||||
# Schema for deleting a box
|
|
||||||
class BoxDelete(BaseModel):
|
|
||||||
message: str
|
|
||||||
|
|
||||||
# Schema for listing boxes
|
|
||||||
class BoxList(BaseModel):
|
|
||||||
boxes: List[BoxInDB]
|
|
||||||
total: int
|
|
||||||
page: int
|
|
||||||
limit: int
|
|
||||||
|
|
||||||
# OpenBox schemas
|
|
||||||
class OpenBoxBase(BaseModel):
|
|
||||||
box_id: int
|
|
||||||
opened_at: Optional[datetime] = None
|
|
||||||
opened_by: Optional[str] = None
|
|
||||||
contents: Optional[List[dict]] = None
|
|
||||||
status: Optional[str] = "pending" # pending, opened, verified, listed
|
|
||||||
|
|
||||||
class OpenBoxCreate(OpenBoxBase):
|
|
||||||
pass
|
|
||||||
|
|
||||||
class OpenBoxUpdate(OpenBoxBase):
|
|
||||||
pass
|
|
||||||
|
|
||||||
class OpenBoxInDB(OpenBoxBase):
|
|
||||||
id: int
|
|
||||||
created_at: datetime
|
|
||||||
updated_at: Optional[datetime] = None
|
|
||||||
|
|
||||||
class Config:
|
|
||||||
from_attributes = True
|
|
||||||
|
|
||||||
class OpenBoxDelete(BaseModel):
|
|
||||||
message: str
|
|
||||||
|
|
||||||
class OpenBoxList(BaseModel):
|
|
||||||
open_boxes: List[OpenBoxInDB]
|
|
||||||
total: int
|
|
||||||
page: int
|
|
||||||
limit: int
|
|
@ -1,55 +0,0 @@
|
|||||||
from typing import List, Optional
|
|
||||||
from datetime import datetime
|
|
||||||
from pydantic import BaseModel
|
|
||||||
|
|
||||||
# Base schema with common attributes
|
|
||||||
class CardBase(BaseModel):
|
|
||||||
name: str
|
|
||||||
rarity: Optional[str] = None
|
|
||||||
set_name: Optional[str] = None
|
|
||||||
price: Optional[float] = None
|
|
||||||
quantity: Optional[int] = 0
|
|
||||||
|
|
||||||
# TCGPlayer specific fields
|
|
||||||
tcgplayer_sku: Optional[str] = None
|
|
||||||
product_line: Optional[str] = None
|
|
||||||
product_name: Optional[str] = None
|
|
||||||
title: Optional[str] = None
|
|
||||||
number: Optional[str] = None
|
|
||||||
condition: Optional[str] = None
|
|
||||||
tcg_market_price: Optional[float] = None
|
|
||||||
tcg_direct_low: Optional[float] = None
|
|
||||||
tcg_low_price_with_shipping: Optional[float] = None
|
|
||||||
tcg_low_price: Optional[float] = None
|
|
||||||
total_quantity: Optional[int] = None
|
|
||||||
add_to_quantity: Optional[int] = None
|
|
||||||
tcg_marketplace_price: Optional[float] = None
|
|
||||||
photo_url: Optional[str] = None
|
|
||||||
|
|
||||||
# Schema for creating a new card
|
|
||||||
class CardCreate(CardBase):
|
|
||||||
pass
|
|
||||||
|
|
||||||
# Schema for updating a card
|
|
||||||
class CardUpdate(CardBase):
|
|
||||||
pass
|
|
||||||
|
|
||||||
# Schema for reading a card (includes id and relationships)
|
|
||||||
class CardInDB(CardBase):
|
|
||||||
id: int
|
|
||||||
created_at: datetime
|
|
||||||
updated_at: Optional[datetime] = None
|
|
||||||
|
|
||||||
class Config:
|
|
||||||
from_attributes = True
|
|
||||||
|
|
||||||
# Schema for listing cards
|
|
||||||
class CardList(BaseModel):
|
|
||||||
cards: List[CardInDB]
|
|
||||||
total: int
|
|
||||||
page: int
|
|
||||||
limit: int
|
|
||||||
|
|
||||||
# Schema for deleting a card
|
|
||||||
class CardDelete(BaseModel):
|
|
||||||
message: str
|
|
@ -1,41 +0,0 @@
|
|||||||
from typing import List, Optional
|
|
||||||
from datetime import datetime
|
|
||||||
from pydantic import BaseModel
|
|
||||||
|
|
||||||
# Base schema with common attributes
|
|
||||||
class GameBase(BaseModel):
|
|
||||||
name: str
|
|
||||||
publisher: Optional[str] = None
|
|
||||||
release_date: Optional[datetime] = None
|
|
||||||
description: Optional[str] = None
|
|
||||||
website: Optional[str] = None
|
|
||||||
logo_url: Optional[str] = None
|
|
||||||
status: Optional[str] = "active" # active, inactive, discontinued
|
|
||||||
|
|
||||||
# Schema for creating a new game
|
|
||||||
class GameCreate(GameBase):
|
|
||||||
pass
|
|
||||||
|
|
||||||
# Schema for updating a game
|
|
||||||
class GameUpdate(GameBase):
|
|
||||||
pass
|
|
||||||
|
|
||||||
# Schema for reading a game
|
|
||||||
class GameInDB(GameBase):
|
|
||||||
id: int
|
|
||||||
created_at: datetime
|
|
||||||
updated_at: Optional[datetime] = None
|
|
||||||
|
|
||||||
class Config:
|
|
||||||
from_attributes = True
|
|
||||||
|
|
||||||
# Schema for deleting a game
|
|
||||||
class GameDelete(BaseModel):
|
|
||||||
message: str
|
|
||||||
|
|
||||||
# Schema for listing games
|
|
||||||
class GameList(BaseModel):
|
|
||||||
games: List[GameInDB]
|
|
||||||
total: int
|
|
||||||
page: int
|
|
||||||
limit: int
|
|
@ -6,7 +6,7 @@ import json
|
|||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from sqlalchemy.orm import Session
|
from sqlalchemy.orm import Session
|
||||||
from app.db.database import transaction
|
from app.db.database import transaction
|
||||||
from app.models.inventory import Inventory
|
from app.models.tcgplayer_inventory import TCGPlayerInventory
|
||||||
from app.models.tcgplayer_product import TCGPlayerProduct
|
from app.models.tcgplayer_product import TCGPlayerProduct
|
||||||
from app.services.inventory_service import InventoryService
|
from app.services.inventory_service import InventoryService
|
||||||
|
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
from typing import List, Optional, Dict
|
from typing import List, Optional, Dict
|
||||||
from sqlalchemy.orm import Session
|
from sqlalchemy.orm import Session
|
||||||
from app.models.inventory import Inventory
|
from app.models.tcgplayer_inventory import TCGPlayerInventory
|
||||||
from app.services.base_service import BaseService
|
from app.services.base_service import BaseService
|
||||||
|
|
||||||
class InventoryService(BaseService[Inventory]):
|
class InventoryService(BaseService[TCGPlayerInventory]):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super().__init__(Inventory)
|
super().__init__(TCGPlayerInventory)
|
||||||
|
|
||||||
def create(self, db: Session, obj_in: Dict) -> Inventory:
|
def create(self, db: Session, obj_in: Dict) -> TCGPlayerInventory:
|
||||||
"""
|
"""
|
||||||
Create a new inventory item in the database.
|
Create a new inventory item in the database.
|
||||||
|
|
||||||
@ -20,7 +20,7 @@ class InventoryService(BaseService[Inventory]):
|
|||||||
"""
|
"""
|
||||||
return super().create(db, obj_in)
|
return super().create(db, obj_in)
|
||||||
|
|
||||||
def update(self, db: Session, db_obj: Inventory, obj_in: Dict) -> Inventory:
|
def update(self, db: Session, db_obj: TCGPlayerInventory, obj_in: Dict) -> TCGPlayerInventory:
|
||||||
"""
|
"""
|
||||||
Update an existing inventory item in the database.
|
Update an existing inventory item in the database.
|
||||||
|
|
||||||
@ -34,7 +34,7 @@ class InventoryService(BaseService[Inventory]):
|
|||||||
"""
|
"""
|
||||||
return super().update(db, db_obj, obj_in)
|
return super().update(db, db_obj, obj_in)
|
||||||
|
|
||||||
def get_by_tcgplayer_id(self, db: Session, tcgplayer_id: str) -> Optional[Inventory]:
|
def get_by_tcgplayer_id(self, db: Session, tcgplayer_id: str) -> Optional[TCGPlayerInventory]:
|
||||||
"""
|
"""
|
||||||
Get an inventory item by its TCGPlayer ID.
|
Get an inventory item by its TCGPlayer ID.
|
||||||
|
|
||||||
@ -43,11 +43,11 @@ class InventoryService(BaseService[Inventory]):
|
|||||||
tcgplayer_id: The TCGPlayer ID to find
|
tcgplayer_id: The TCGPlayer ID to find
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Optional[Inventory]: The inventory item if found, None otherwise
|
Optional[TCGPlayerInventory]: The inventory item if found, None otherwise
|
||||||
"""
|
"""
|
||||||
return db.query(self.model).filter(self.model.tcgplayer_id == tcgplayer_id).first()
|
return db.query(self.model).filter(self.model.tcgplayer_id == tcgplayer_id).first()
|
||||||
|
|
||||||
def get_by_set(self, db: Session, set_name: str, skip: int = 0, limit: int = 100) -> List[Inventory]:
|
def get_by_set(self, db: Session, set_name: str, skip: int = 0, limit: int = 100) -> List[TCGPlayerInventory]:
|
||||||
"""
|
"""
|
||||||
Get all inventory items from a specific set.
|
Get all inventory items from a specific set.
|
||||||
|
|
||||||
@ -58,6 +58,6 @@ class InventoryService(BaseService[Inventory]):
|
|||||||
limit: Maximum number of records to return
|
limit: Maximum number of records to return
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
List[Inventory]: List of inventory items from the specified set
|
List[TCGPlayerInventory]: List of inventory items from the specified set
|
||||||
"""
|
"""
|
||||||
return db.query(self.model).filter(self.model.set_name == set_name).offset(skip).limit(limit).all()
|
return db.query(self.model).filter(self.model.set_name == set_name).offset(skip).limit(limit).all()
|
@ -336,6 +336,125 @@ async function submitPirateShipLabel() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Show set labels modal
|
||||||
|
function showSetLabelsModal() {
|
||||||
|
const modal = document.getElementById('setLabelsModal');
|
||||||
|
modal.classList.remove('hidden');
|
||||||
|
modal.classList.add('flex');
|
||||||
|
fetchAvailableSets();
|
||||||
|
|
||||||
|
// Add event listener for search input
|
||||||
|
const searchInput = document.getElementById('setSearch');
|
||||||
|
searchInput.addEventListener('input', filterSets);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close set labels modal
|
||||||
|
function closeSetLabelsModal() {
|
||||||
|
const modal = document.getElementById('setLabelsModal');
|
||||||
|
modal.classList.remove('flex');
|
||||||
|
modal.classList.add('hidden');
|
||||||
|
|
||||||
|
// Clear search input
|
||||||
|
document.getElementById('setSearch').value = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filter sets based on search input
|
||||||
|
function filterSets() {
|
||||||
|
const searchTerm = document.getElementById('setSearch').value.toLowerCase();
|
||||||
|
const setItems = document.querySelectorAll('#setLabelsList > div');
|
||||||
|
|
||||||
|
setItems.forEach(item => {
|
||||||
|
const label = item.querySelector('label');
|
||||||
|
const text = label.textContent.toLowerCase();
|
||||||
|
if (text.includes(searchTerm)) {
|
||||||
|
item.style.display = 'flex';
|
||||||
|
} else {
|
||||||
|
item.style.display = 'none';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch available sets from the API
|
||||||
|
async function fetchAvailableSets() {
|
||||||
|
try {
|
||||||
|
setLoading(true);
|
||||||
|
const response = await fetch(`${API_BASE_URL}/set-labels/available-sets`);
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error('Failed to fetch available sets');
|
||||||
|
}
|
||||||
|
|
||||||
|
const sets = await response.json();
|
||||||
|
displayAvailableSets(sets);
|
||||||
|
} catch (error) {
|
||||||
|
showToast('Error fetching available sets: ' + error.message, 'error');
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Display available sets in the modal
|
||||||
|
function displayAvailableSets(sets) {
|
||||||
|
const setList = document.getElementById('setLabelsList');
|
||||||
|
setList.innerHTML = '';
|
||||||
|
|
||||||
|
if (!sets || sets.length === 0) {
|
||||||
|
setList.innerHTML = '<div class="text-center text-gray-400 py-4">No sets available</div>';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sort sets alphabetically by name
|
||||||
|
sets.sort((a, b) => a.name.localeCompare(b.name));
|
||||||
|
|
||||||
|
sets.forEach(set => {
|
||||||
|
const setItem = document.createElement('div');
|
||||||
|
setItem.className = 'flex items-center p-2 hover:bg-gray-600 rounded-lg cursor-pointer';
|
||||||
|
setItem.innerHTML = `
|
||||||
|
<input type="checkbox" id="set-${set.code}" class="rounded border-gray-600 bg-gray-800 text-teal-600 focus:ring-teal-500">
|
||||||
|
<label for="set-${set.code}" class="ml-2 text-gray-300">${set.name} (${set.code})</label>
|
||||||
|
`;
|
||||||
|
setList.appendChild(setItem);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Trigger initial filter in case there's text in the search box
|
||||||
|
filterSets();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Submit set labels request
|
||||||
|
async function submitSetLabels() {
|
||||||
|
try {
|
||||||
|
const selectedSets = Array.from(document.querySelectorAll('#setLabelsList input[type="checkbox"]:checked'))
|
||||||
|
.map(checkbox => checkbox.id.replace('set-', ''));
|
||||||
|
|
||||||
|
if (selectedSets.length === 0) {
|
||||||
|
showToast('Please select at least one set', 'error');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setLoading(true);
|
||||||
|
const response = await fetch(`${API_BASE_URL}/set-labels/generate`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
sets: selectedSets
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
const errorData = await response.json();
|
||||||
|
throw new Error(errorData.detail || 'Failed to generate set labels');
|
||||||
|
}
|
||||||
|
|
||||||
|
showToast('Set labels generated successfully');
|
||||||
|
closeSetLabelsModal();
|
||||||
|
} catch (error) {
|
||||||
|
showToast('Error generating set labels: ' + error.message, 'error');
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Load orders when page loads
|
// Load orders when page loads
|
||||||
document.addEventListener('DOMContentLoaded', () => {
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
fetchOrders();
|
fetchOrders();
|
||||||
|
@ -45,6 +45,9 @@
|
|||||||
<button onclick="showPirateShipModal()" class="px-4 py-2 bg-yellow-600 text-white rounded-lg hover:bg-yellow-700 focus:outline-none focus:ring-2 focus:ring-yellow-500 focus:ring-offset-2 transition-colors">
|
<button onclick="showPirateShipModal()" class="px-4 py-2 bg-yellow-600 text-white rounded-lg hover:bg-yellow-700 focus:outline-none focus:ring-2 focus:ring-yellow-500 focus:ring-offset-2 transition-colors">
|
||||||
Upload Pirate Ship Label
|
Upload Pirate Ship Label
|
||||||
</button>
|
</button>
|
||||||
|
<button onclick="showSetLabelsModal()" class="px-4 py-2 bg-teal-600 text-white rounded-lg hover:bg-teal-700 focus:outline-none focus:ring-2 focus:ring-teal-500 focus:ring-offset-2 transition-colors">
|
||||||
|
Generate Set Labels
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div id="labelOptions" class="bg-gray-700 rounded-lg p-4">
|
<div id="labelOptions" class="bg-gray-700 rounded-lg p-4">
|
||||||
<label class="block text-sm font-medium text-gray-300 mb-2">Label Type</label>
|
<label class="block text-sm font-medium text-gray-300 mb-2">Label Type</label>
|
||||||
@ -93,6 +96,31 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Set Labels Modal -->
|
||||||
|
<div id="setLabelsModal" class="fixed inset-0 bg-black bg-opacity-50 hidden items-center justify-center">
|
||||||
|
<div class="bg-gray-800 rounded-lg p-6 max-w-md w-full mx-4">
|
||||||
|
<h3 class="text-xl font-semibold text-gray-100 mb-4">Generate Set Labels</h3>
|
||||||
|
<div class="mb-4">
|
||||||
|
<div class="mb-2">
|
||||||
|
<label for="setSearch" class="block text-sm font-medium text-gray-300 mb-2">Search Sets</label>
|
||||||
|
<input type="text" id="setSearch" placeholder="Search sets..." class="w-full rounded-lg border-gray-600 bg-gray-700 text-gray-100 focus:ring-blue-500 focus:border-blue-500">
|
||||||
|
</div>
|
||||||
|
<label class="block text-sm font-medium text-gray-300 mb-2">Select Sets</label>
|
||||||
|
<div id="setLabelsList" class="max-h-60 overflow-y-auto bg-gray-700 rounded-lg p-2">
|
||||||
|
<!-- Sets will be populated here -->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex justify-end space-x-3">
|
||||||
|
<button onclick="closeSetLabelsModal()" class="px-4 py-2 bg-gray-600 text-white rounded-lg hover:bg-gray-700 focus:outline-none focus:ring-2 focus:ring-gray-500 focus:ring-offset-2 transition-colors">
|
||||||
|
Cancel
|
||||||
|
</button>
|
||||||
|
<button onclick="submitSetLabels()" class="px-4 py-2 bg-teal-600 text-white rounded-lg hover:bg-teal-700 focus:outline-none focus:ring-2 focus:ring-teal-500 focus:ring-offset-2 transition-colors">
|
||||||
|
Generate
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Order List Section -->
|
<!-- Order List Section -->
|
||||||
<div class="bg-gray-800 rounded-xl shadow-sm p-6">
|
<div class="bg-gray-800 rounded-xl shadow-sm p-6">
|
||||||
<div class="flex items-center justify-between mb-6">
|
<div class="flex items-center justify-between mb-6">
|
||||||
|
Loading…
x
Reference in New Issue
Block a user