data model whew

This commit is contained in:
zman 2025-04-19 13:56:58 -04:00
parent 03b43ce3ab
commit 6178fdd15d
19 changed files with 789 additions and 2099 deletions

View 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

View 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 ###

1788
app.log

File diff suppressed because it is too large Load Diff

View File

@ -58,7 +58,7 @@ async def lifespan(app: FastAPI):
db = SessionLocal()
try:
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}")
# Start the scheduler

View File

@ -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.game import Game
from app.models.inventory import Inventory
from app.models.inventory_management import (
PhysicalItem,
InventoryItem,
TransactionItem,
OpenEvent,
Vendor,
Customer,
Transaction,
CostBasis
)
from app.models.mtgjson_card import MTGJSONCard
from app.models.mtgjson_sku import MTGJSONSKU
from app.models.product import Product
from app.models.tcgplayer_category import TCGPlayerCategory
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
# This makes all models available for Alembic to discover
__all__ = [
'Box',
'Card',
'File',
'Game',
'Inventory',
'PhysicalItem',
'InventoryItem',
'TransactionItem',
'OpenEvent',
'Vendor',
'Customer',
'Transaction',
'CostBasis',
'MTGJSONCard',
'MTGJSONSKU',
'Product',
'TCGPlayerCategory',
'TCGPlayerGroup',
'TCGPlayerInventory',
'TCGPlayerOrder',
'TCGPlayerOrderTransaction',
'TCGPlayerOrderProduct',
'TCGPlayerOrderRefund',
'TCGPlayerPriceHistory',
'TCGPlayerProduct'
]

View File

@ -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())

View File

@ -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())

View File

@ -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())

View 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
View 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)

View File

@ -2,8 +2,8 @@ from sqlalchemy import Column, Integer, String, Float, DateTime
from sqlalchemy.sql import func
from app.db.database import Base
class Inventory(Base):
__tablename__ = "inventory"
class TCGPlayerInventory(Base):
__tablename__ = "tcgplayer_inventory"
id = Column(Integer, primary_key=True, index=True)
tcgplayer_id = Column(String, unique=True, index=True)
@ -22,7 +22,6 @@ class Inventory(Base):
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())
created_at = Column(DateTime(timezone=True), server_default=func.current_timestamp())
updated_at = Column(DateTime(timezone=True), onupdate=func.current_timestamp())
deleted_at = Column(DateTime(timezone=True), nullable=True)

View File

@ -3,11 +3,6 @@ from sqlalchemy.orm import Session
from app.db.database import get_db
from app.models.file import File as FileModel
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.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)
async def delete_file(file_id: int):
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"}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -6,7 +6,7 @@ import json
from datetime import datetime
from sqlalchemy.orm import Session
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.services.inventory_service import InventoryService

View File

@ -1,13 +1,13 @@
from typing import List, Optional, Dict
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
class InventoryService(BaseService[Inventory]):
class InventoryService(BaseService[TCGPlayerInventory]):
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.
@ -20,7 +20,7 @@ class InventoryService(BaseService[Inventory]):
"""
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.
@ -34,7 +34,7 @@ class InventoryService(BaseService[Inventory]):
"""
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.
@ -43,11 +43,11 @@ class InventoryService(BaseService[Inventory]):
tcgplayer_id: The TCGPlayer ID to find
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()
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.
@ -58,6 +58,6 @@ class InventoryService(BaseService[Inventory]):
limit: Maximum number of records to return
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()

View File

@ -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
document.addEventListener('DOMContentLoaded', () => {
fetchOrders();

View File

@ -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">
Upload Pirate Ship Label
</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 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>
@ -93,6 +96,31 @@
</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 -->
<div class="bg-gray-800 rounded-xl shadow-sm p-6">
<div class="flex items-center justify-between mb-6">