Slop/sloppy fixes #1
No files matched your search
@@ -1,5 +1,4 @@
|
|||||||
import strawberry
|
import strawberry
|
||||||
import strawberry_django
|
|
||||||
from django.contrib.auth.models import User as DjangoUser
|
from django.contrib.auth.models import User as DjangoUser
|
||||||
from strawberry import ID
|
from strawberry import ID
|
||||||
|
|
||||||
@@ -8,7 +7,9 @@ from bbs.api.types.users import User
|
|||||||
|
|
||||||
@strawberry.type
|
@strawberry.type
|
||||||
class UsersQueries:
|
class UsersQueries:
|
||||||
all_users: list[User] = strawberry_django.field()
|
@strawberry.field
|
||||||
|
def all_users(self) -> list[User]:
|
||||||
|
return DjangoUser.objects.filter(profile__isnull=False)
|
||||||
|
|
||||||
@strawberry.field
|
@strawberry.field
|
||||||
def user(self, user_id: ID) -> User:
|
def user(self, user_id: ID) -> User:
|
||||||
|
|||||||
@@ -0,0 +1,151 @@
|
|||||||
|
import os
|
||||||
|
from datetime import datetime, time, timedelta
|
||||||
|
from decimal import Decimal
|
||||||
|
|
||||||
|
from django.contrib.auth import get_user_model
|
||||||
|
from django.core.management import BaseCommand
|
||||||
|
from django.db.models import Sum
|
||||||
|
from django.utils import timezone
|
||||||
|
|
||||||
|
from bbs.models import InventoryTransaction, Order, OrderLine, Product, Transaction, UserProfile
|
||||||
|
|
||||||
|
DEMO_USERS = (
|
||||||
|
("demo-ada", "Ada", "Nord", "ada@example.test", Decimal("300.0")),
|
||||||
|
("demo-bjørn", "Bjørn", "Berg", "bjorn@example.test", Decimal("250.0")),
|
||||||
|
("demo-casey", "Casey", "Sol", "casey@example.test", Decimal("200.0")),
|
||||||
|
)
|
||||||
|
|
||||||
|
DEMO_PRODUCTS = (
|
||||||
|
("Demo Cola", Decimal("25.0"), Decimal("30.0"), 30),
|
||||||
|
("Demo Kaffe", Decimal("15.0"), Decimal("20.0"), 30),
|
||||||
|
("Demo Sjokolade", Decimal("20.0"), Decimal("25.0"), 30),
|
||||||
|
("Demo Energidrikk", Decimal("35.0"), Decimal("40.0"), 30),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class Command(BaseCommand):
|
||||||
|
help = "Create repeatable demo users, products, deposits, and orders for local testing."
|
||||||
|
|
||||||
|
def handle(self, *args, **options): # noqa: ARG002
|
||||||
|
users = self.seed_users()
|
||||||
|
products = self.seed_products(users[0][0])
|
||||||
|
self.seed_deposits(users)
|
||||||
|
self.seed_orders(users, products)
|
||||||
|
self.update_balances(users)
|
||||||
|
self.create_admin()
|
||||||
|
|
||||||
|
self.stdout.write(self.style.SUCCESS("Demo data is ready."))
|
||||||
|
if not os.environ.get("BBS_SEED_ADMIN_PASSWORD"):
|
||||||
|
self.stdout.write("Create an admin login with: uv run manage.py createsuperuser")
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def seed_users():
|
||||||
|
user_model = get_user_model()
|
||||||
|
users = []
|
||||||
|
for username, first_name, last_name, email, deposit_amount in DEMO_USERS:
|
||||||
|
user, _ = user_model.objects.get_or_create(
|
||||||
|
username=username,
|
||||||
|
defaults={"first_name": first_name, "last_name": last_name, "email": email},
|
||||||
|
)
|
||||||
|
UserProfile.objects.get_or_create(user=user)
|
||||||
|
users.append((user, deposit_amount))
|
||||||
|
return users
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def seed_products(stock_user):
|
||||||
|
products = {}
|
||||||
|
for name, sale_price_int, sale_price_ext, stock in DEMO_PRODUCTS:
|
||||||
|
product, created = Product.objects.get_or_create(
|
||||||
|
name=name,
|
||||||
|
defaults={
|
||||||
|
"sale_price_int": sale_price_int,
|
||||||
|
"sale_price_ext": sale_price_ext,
|
||||||
|
"inventory_amount": stock,
|
||||||
|
"description": "Demo product for local development.",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
products[name] = product
|
||||||
|
if created:
|
||||||
|
InventoryTransaction.objects.get_or_create(
|
||||||
|
user=stock_user,
|
||||||
|
product=product,
|
||||||
|
comment="Demo opening stock",
|
||||||
|
defaults={"amount": stock, "unit_price": Decimal("10.0")},
|
||||||
|
)
|
||||||
|
return products
|
||||||
|
|
||||||
|
def seed_deposits(self, users):
|
||||||
|
for index, (user, deposit_amount) in enumerate(users):
|
||||||
|
created_at = self.timestamp(days_ago=32 + index)
|
||||||
|
deposit, created = Transaction.objects.get_or_create(
|
||||||
|
user=user,
|
||||||
|
created=created_at,
|
||||||
|
defaults={"amount": deposit_amount},
|
||||||
|
)
|
||||||
|
if created:
|
||||||
|
deposit.created = created_at
|
||||||
|
deposit.save(update_fields=["created"])
|
||||||
|
|
||||||
|
def seed_orders(self, users, products):
|
||||||
|
orders = (
|
||||||
|
(0, 24, (("Demo Cola", 2), ("Demo Sjokolade", 1))),
|
||||||
|
(1, 19, (("Demo Kaffe", 2), ("Demo Sjokolade", 1))),
|
||||||
|
(2, 11, (("Demo Energidrikk", 1),)),
|
||||||
|
(0, 4, (("Demo Kaffe", 1), ("Demo Cola", 1))),
|
||||||
|
(1, 1, (("Demo Cola", 2),)),
|
||||||
|
)
|
||||||
|
for user_index, days_ago, lines in orders:
|
||||||
|
user = users[user_index][0]
|
||||||
|
created_at = self.timestamp(days_ago=days_ago)
|
||||||
|
total = sum(
|
||||||
|
(products[name].sale_price_int * amount for name, amount in lines),
|
||||||
|
start=Decimal("0"),
|
||||||
|
)
|
||||||
|
order, created = Order.objects.get_or_create(
|
||||||
|
customer=user,
|
||||||
|
created=created_at,
|
||||||
|
defaults={"order_sum": total},
|
||||||
|
)
|
||||||
|
if created:
|
||||||
|
order.created = created_at
|
||||||
|
order.save(update_fields=["created"])
|
||||||
|
for name, amount in lines:
|
||||||
|
product = products[name]
|
||||||
|
OrderLine.objects.create(
|
||||||
|
order=order,
|
||||||
|
product=product,
|
||||||
|
amount=amount,
|
||||||
|
unit_price=product.sale_price_int,
|
||||||
|
)
|
||||||
|
product.inventory_amount -= amount
|
||||||
|
product.save(update_fields=["inventory_amount"])
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def update_balances(users):
|
||||||
|
for user, _ in users:
|
||||||
|
deposits = Transaction.objects.filter(user=user).aggregate(total=Sum("amount"))["total"] or Decimal("0")
|
||||||
|
purchases = Order.objects.filter(customer=user).aggregate(total=Sum("order_sum"))["total"] or Decimal("0")
|
||||||
|
profile = user.profile
|
||||||
|
profile.balance = deposits - purchases
|
||||||
|
profile.save(update_fields=["balance"])
|
||||||
|
|
||||||
|
def create_admin(self):
|
||||||
|
admin_password = os.environ.get("BBS_SEED_ADMIN_PASSWORD")
|
||||||
|
if not admin_password:
|
||||||
|
return
|
||||||
|
|
||||||
|
user_model = get_user_model()
|
||||||
|
if user_model.objects.filter(username="admin").exists():
|
||||||
|
self.stdout.write("Existing 'admin' account left unchanged.")
|
||||||
|
else:
|
||||||
|
user_model.objects.create_superuser(
|
||||||
|
username="admin",
|
||||||
|
email="admin@example.test",
|
||||||
|
password=admin_password,
|
||||||
|
)
|
||||||
|
self.stdout.write("Created superuser 'admin'.")
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def timestamp(days_ago: int) -> datetime:
|
||||||
|
date = timezone.localdate() - timedelta(days=days_ago)
|
||||||
|
return timezone.make_aware(datetime.combine(date, time(hour=12)))
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
import pytest # noqa: INP001
|
||||||
|
from django.contrib.auth import get_user_model
|
||||||
|
from django.core.management import call_command
|
||||||
|
from django.test import Client
|
||||||
|
from strawberry.django.test import GraphQLTestClient
|
||||||
|
|
||||||
|
from bbs.models import InventoryTransaction, Order, Product, Transaction
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.django_db
|
||||||
|
def test_seed_demo_data_is_repeatable():
|
||||||
|
call_command("seed_demo_data")
|
||||||
|
call_command("seed_demo_data")
|
||||||
|
|
||||||
|
assert get_user_model().objects.filter(username__startswith="demo-").count() == 3
|
||||||
|
assert Product.objects.filter(name__startswith="Demo ").count() == 4
|
||||||
|
assert Order.objects.count() == 5
|
||||||
|
assert Transaction.objects.count() == 3
|
||||||
|
assert InventoryTransaction.objects.count() == 4
|
||||||
|
assert sum(product.inventory_amount for product in Product.objects.filter(name__startswith="Demo ")) == 109
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.django_db
|
||||||
|
def test_demo_data_is_available_when_an_admin_has_no_profile():
|
||||||
|
user_model = get_user_model()
|
||||||
|
user_model.objects.create_superuser(username="admin", email="admin@example.test", password="password")
|
||||||
|
call_command("seed_demo_data")
|
||||||
|
|
||||||
|
gql_client = GraphQLTestClient(Client())
|
||||||
|
result = gql_client.query(
|
||||||
|
"""
|
||||||
|
{
|
||||||
|
allProducts(filters: {active: true}) { name }
|
||||||
|
allUsers { firstName lastName profile { balance } }
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
|
assert not result.errors
|
||||||
|
assert result.data
|
||||||
|
assert len(result.data["allProducts"]) == 4
|
||||||
|
assert len(result.data["allUsers"]) == 3
|
||||||
Reference in new issue
Block a user