feat: introduced plans pricing
This commit is contained in:
41
alembic/versions/bb542353b092_edited_pricing_added_addons.py
Normal file
41
alembic/versions/bb542353b092_edited_pricing_added_addons.py
Normal file
@@ -0,0 +1,41 @@
|
||||
"""edited pricing + added addons
|
||||
|
||||
Revision ID: bb542353b092
|
||||
Revises: 74bd9cedebdd
|
||||
Create Date: 2026-07-25 00:35:38.927479
|
||||
|
||||
"""
|
||||
from typing import Sequence, Union
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision: str = 'bb542353b092'
|
||||
down_revision: Union[str, Sequence[str], None] = '74bd9cedebdd'
|
||||
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('addons',
|
||||
sa.Column('id', sa.TEXT(), nullable=False),
|
||||
sa.Column('name', sa.TEXT(), nullable=False),
|
||||
sa.Column('price', sa.FLOAT(), nullable=False),
|
||||
sa.Column('free_threshold', sa.INTEGER(), nullable=False),
|
||||
sa.PrimaryKeyConstraint('id'),
|
||||
sa.UniqueConstraint('id')
|
||||
)
|
||||
op.create_unique_constraint(None, 'pricing_configs', ['id'])
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
"""Downgrade schema."""
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_constraint(None, 'pricing_configs', type_='unique')
|
||||
op.drop_table('addons')
|
||||
# ### end Alembic commands ###
|
||||
@@ -1,5 +1,6 @@
|
||||
from .addons import Addon
|
||||
from .pricing import PricingConfig
|
||||
from .sessions import Session
|
||||
from .users import User
|
||||
|
||||
__all__ = ["PricingConfig", "Session", "User"]
|
||||
__all__ = ["Addon", "PricingConfig", "Session", "User"]
|
||||
|
||||
13
db/models/addons.py
Normal file
13
db/models/addons.py
Normal file
@@ -0,0 +1,13 @@
|
||||
from sqlalchemy import FLOAT, INTEGER, TEXT
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
|
||||
from db.base import Base
|
||||
|
||||
|
||||
class Addon(Base):
|
||||
__tablename__ = "addons"
|
||||
|
||||
id: Mapped[str] = mapped_column(TEXT, nullable=False, unique=True, primary_key=True)
|
||||
name: Mapped[str] = mapped_column(TEXT, nullable=False, unique=False)
|
||||
price: Mapped[float] = mapped_column(FLOAT, nullable=False)
|
||||
free_threshold: Mapped[int] = mapped_column(INTEGER, default=-1)
|
||||
@@ -9,11 +9,13 @@ from db.base import Base
|
||||
class PricingConfig(Base):
|
||||
__tablename__ = "pricing_configs"
|
||||
|
||||
__table_args__ = (
|
||||
CheckConstraint("id = 1", name="single_row_check"),
|
||||
)
|
||||
__table_args__ = (CheckConstraint("id = 1", name="single_row_check"),)
|
||||
|
||||
id: Mapped[int] = mapped_column(INTEGER, autoincrement=True, unique=True, nullable=False, primary_key=True)
|
||||
id: Mapped[int] = mapped_column(
|
||||
INTEGER, autoincrement=True, unique=True, nullable=False, primary_key=True
|
||||
)
|
||||
device_price: Mapped[float] = mapped_column(FLOAT, nullable=False)
|
||||
|
||||
updated_at: Mapped[datetime] = mapped_column(TIMESTAMP, server_default=func.now(), onupdate=func.now())
|
||||
updated_at: Mapped[datetime] = mapped_column(
|
||||
TIMESTAMP, server_default=func.now(), onupdate=func.now()
|
||||
)
|
||||
|
||||
15
repositories/addons.py
Normal file
15
repositories/addons.py
Normal file
@@ -0,0 +1,15 @@
|
||||
from sqlalchemy import select
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from db.models import Addon
|
||||
|
||||
|
||||
class AddonsRepository:
|
||||
def __init__(self, session: AsyncSession) -> None:
|
||||
self.session = session
|
||||
|
||||
async def get_all(self) -> list[Addon]:
|
||||
stmt = select(Addon)
|
||||
r = await self.session.execute(stmt)
|
||||
|
||||
return list(r.scalars().all())
|
||||
15
repositories/pricing.py
Normal file
15
repositories/pricing.py
Normal file
@@ -0,0 +1,15 @@
|
||||
from sqlalchemy import select
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from db.models.pricing import PricingConfig
|
||||
|
||||
|
||||
class PricingRepository:
|
||||
def __init__(self, session: AsyncSession) -> None:
|
||||
self.session = session
|
||||
|
||||
async def get(self) -> PricingConfig | None:
|
||||
stmt = select(PricingConfig).where(PricingConfig.id == 1)
|
||||
r = await self.session.execute(stmt)
|
||||
|
||||
return r.scalar_one_or_none()
|
||||
@@ -1,5 +1,6 @@
|
||||
from fastapi import APIRouter
|
||||
|
||||
from .auth import router as auth_router
|
||||
from .plans import router as plans_router
|
||||
|
||||
routers: list[APIRouter] = [auth_router]
|
||||
routers: list[APIRouter] = [auth_router, plans_router]
|
||||
|
||||
19
routes/plans.py
Normal file
19
routes/plans.py
Normal file
@@ -0,0 +1,19 @@
|
||||
from fastapi import APIRouter, Depends
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from db.session import get_db
|
||||
from repositories.addons import AddonsRepository
|
||||
from repositories.pricing import PricingRepository
|
||||
from schemas.plans import PricingPlans
|
||||
from services.plans import get_pricing_model
|
||||
|
||||
router = APIRouter(prefix="/plans")
|
||||
|
||||
|
||||
@router.get("/", response_model=PricingPlans)
|
||||
async def get_plans(session: AsyncSession = Depends(get_db)):
|
||||
addons_repo = AddonsRepository(session)
|
||||
pricing_repo = PricingRepository(session)
|
||||
|
||||
res = await get_pricing_model(addons_repo, pricing_repo)
|
||||
return res
|
||||
13
schemas/plans.py
Normal file
13
schemas/plans.py
Normal file
@@ -0,0 +1,13 @@
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class AddonData(BaseModel):
|
||||
id: str
|
||||
name: str
|
||||
price: float
|
||||
free_threshold: int
|
||||
|
||||
|
||||
class PricingPlans(BaseModel):
|
||||
device_price: int
|
||||
addons: list[AddonData]
|
||||
17
services/plans.py
Normal file
17
services/plans.py
Normal file
@@ -0,0 +1,17 @@
|
||||
from repositories.addons import AddonsRepository
|
||||
from repositories.pricing import PricingRepository
|
||||
from schemas.plans import AddonData, PricingPlans
|
||||
|
||||
|
||||
async def get_pricing_model(addons_repo: AddonsRepository, pricing_repo: PricingRepository):
|
||||
pricing = await pricing_repo.get()
|
||||
if not pricing:
|
||||
return None
|
||||
|
||||
addons = await addons_repo.get_all()
|
||||
addons_data = [
|
||||
AddonData(id=a.id, name=a.name, price=a.price, free_threshold=a.free_threshold)
|
||||
for a in addons
|
||||
]
|
||||
|
||||
return PricingPlans(device_price=pricing.device_price, addons=addons_data)
|
||||
Reference in New Issue
Block a user