diff --git a/app/core/core_endpoints/schemas_core.py b/app/core/core_endpoints/schemas_core.py index e2356cd0a9..d99679417c 100644 --- a/app/core/core_endpoints/schemas_core.py +++ b/app/core/core_endpoints/schemas_core.py @@ -1,6 +1,7 @@ """Common schemas file for endpoint /users et /groups because it would cause circular import""" from enum import Enum +from typing import Sequence from pydantic import BaseModel, ConfigDict, Field @@ -17,8 +18,8 @@ class CoreInformation(BaseModel): class ModuleVisibility(BaseModel): root: str - allowed_group_ids: list[str] - allowed_account_types: list[AccountType] + allowed_group_ids: Sequence[str] + allowed_account_types: Sequence[AccountType | str] model_config = ConfigDict(from_attributes=True) diff --git a/app/core/mypayment/factory_mypayment.py b/app/core/mypayment/factory_mypayment.py index ad936ca1d2..dec2297102 100644 --- a/app/core/mypayment/factory_mypayment.py +++ b/app/core/mypayment/factory_mypayment.py @@ -33,7 +33,8 @@ async def create_structures(cls, db: AsyncSession): schemas_mypayment.StructureSimple( id=uuid.uuid4(), short_id="".join(faker.random_letters(3)).upper(), - name=CoreUsersFactory.demo_users[i].nickname, + name=CoreUsersFactory.demo_users[i].nickname + or CoreUsersFactory.demo_users[i].name, manager_user_id=user_id, siege_address_street=faker.street_address(), siege_address_city=faker.city(), diff --git a/app/core/mypayment/schemas_mypayment.py b/app/core/mypayment/schemas_mypayment.py index 8825a457de..08fb0c3811 100644 --- a/app/core/mypayment/schemas_mypayment.py +++ b/app/core/mypayment/schemas_mypayment.py @@ -1,4 +1,5 @@ from datetime import datetime +from typing import Sequence from uuid import UUID from pydantic import ( @@ -284,10 +285,10 @@ class IntegrityCheckData(BaseModel): """Schema for Hyperion data""" date: datetime - wallets: list[WalletBase] - transactions: list[TransactionBase] - transfers: list[Transfer] - refunds: list[RefundBase] + wallets: Sequence[WalletBase] + transactions: Sequence[TransactionBase] + transfers: Sequence[Transfer] + refunds: Sequence[RefundBase] class BankAccountHolderEdit(BaseModel): diff --git a/app/core/payment/payment_tool.py b/app/core/payment/payment_tool.py index a2f4a95e72..436b2f5a23 100644 --- a/app/core/payment/payment_tool.py +++ b/app/core/payment/payment_tool.py @@ -23,6 +23,7 @@ from app.core.utils import security from app.types.exceptions import ( MissingHelloAssoCheckoutIdError, + MissingHelloAssoRedirectUrlError, PaymentToolCredentialsNotSetException, UnsetRedirectionUriError, ) @@ -167,20 +168,27 @@ async def init_checkout( firstName=payer_user.firstname, lastName=payer_user.name, email=payer_user.email, - dateOfBirth=payer_user.birthday, + dateOfBirth=datetime( + payer_user.birthday.year, + payer_user.birthday.month, + payer_user.birthday.day, + tzinfo=UTC, + ) + if payer_user.birthday + else None, ) checkout_model_id = uuid.uuid4() secret = security.generate_token(nbytes=12) init_checkout_body = HelloAssoApiV5ModelsCartsInitCheckoutBody( - total_amount=checkout_amount, - initial_amount=checkout_amount, - item_name=checkout_name, - back_url=redirection_uri, - error_url=redirection_uri, - return_url=redirection_uri, - contains_donation=False, + totalAmount=checkout_amount, + initialAmount=checkout_amount, + itemName=checkout_name, + backUrl=redirection_uri, + errorUrl=redirection_uri, + returnUrl=redirection_uri, + containsDonation=False, payer=payer, metadata=schemas_payment.HelloAssoCheckoutMetadata( secret=secret, @@ -232,14 +240,19 @@ async def init_checkout( secret=secret, ) - await cruds_payment.create_checkout(db=db, checkout=checkout_model) - - return schemas_payment.Checkout( - id=checkout_model_id, - payment_url=response.redirect_url, + if response.redirect_url is None: + hyperion_error_logger.error( + f"Payment: failed to init a checkout with HA for module {module} and name {checkout_name}. No checkout redirect_url returned", ) - hyperion_error_logger.error( - f"Payment: failed to init a checkout with HA for module {module} and name {checkout_name}. No checkout id returned", + raise MissingHelloAssoRedirectUrlError() # noqa: TRY301 + + checkout_model = models_payment.Checkout( + id=checkout_model_id, + module=module, + name=checkout_name, + amount=checkout_amount, + hello_asso_checkout_id=response.id, + secret=secret, ) raise MissingHelloAssoCheckoutIdError() # noqa: TRY301 diff --git a/app/core/users/schemas_users.py b/app/core/users/schemas_users.py index d4cdf9f23e..eefbd45c2d 100644 --- a/app/core/users/schemas_users.py +++ b/app/core/users/schemas_users.py @@ -5,6 +5,7 @@ from app.core.groups.groups_type import AccountType from app.core.schools.schemas_schools import CoreSchool +from app.core.users.models_users import CoreUser from app.utils import validators from app.utils.examples import examples_core @@ -34,6 +35,18 @@ class CoreUserSimple(CoreUserBase): model_config = ConfigDict(from_attributes=True) + @classmethod + def from_model(cls, user_model: CoreUser) -> "CoreUserSimple": + """Create a CoreUserSimple from a CoreUser model""" + return CoreUserSimple( + id=user_model.id, + account_type=user_model.account_type, + school_id=user_model.school_id, + name=user_model.name, + firstname=user_model.firstname, + nickname=user_model.nickname, + ) + class CoreUser(CoreUserSimple): """Schema for user's model similar to core_user table in database""" diff --git a/app/core/utils/log.py b/app/core/utils/log.py index 07a74335e2..95c201e730 100644 --- a/app/core/utils/log.py +++ b/app/core/utils/log.py @@ -6,7 +6,7 @@ from pathlib import Path from typing import Any -import uvicorn +import uvicorn.logging from app.core.utils.config import Settings diff --git a/app/modules/amap/endpoints_amap.py b/app/modules/amap/endpoints_amap.py index 4d2f34af7f..51cf6c69bd 100644 --- a/app/modules/amap/endpoints_amap.py +++ b/app/modules/amap/endpoints_amap.py @@ -11,6 +11,7 @@ from app.core.permissions.type_permissions import ModulePermissions from app.core.users import cruds_users, models_users, schemas_users from app.core.users.endpoints_users import read_user +from app.core.users.schemas_users import CoreUserSimple from app.dependencies import ( get_db, get_notification_tool, @@ -20,7 +21,9 @@ ) from app.modules.amap import cruds_amap, models_amap, schemas_amap from app.modules.amap.factory_amap import AmapFactory +from app.modules.amap.schemas_amap import ProductComplete from app.modules.amap.types_amap import DeliveryStatusType +from app.types.exceptions import NewlyAddedObjectInDbNotFoundError from app.types.module import Module from app.utils.communication.notifications import NotificationTool from app.utils.redis import locker_get, locker_set @@ -417,10 +420,32 @@ async def get_order_by_id( products = await cruds_amap.get_products_of_order(db=db, order_id=order_id) return schemas_amap.OrderReturn( - productsdetail=products, - delivery_name=order.delivery.name, - delivery_date=order.delivery.delivery_date, - **order.__dict__, + productsdetail=[ + schemas_amap.ProductQuantity( + quantity=product.quantity, + product=ProductComplete( + name=product.product.name, + price=product.product.price, + category=product.product.category, + id=product.product.id, + ), + ) + for product in products + ], + user=schemas_users.CoreUserSimple( + id=order.user.id, + name=order.user.name, + firstname=order.user.firstname, + nickname=order.user.nickname, + school_id=order.user.school_id, + account_type=order.user.account_type, + ), + delivery_id=order.delivery_id, + collection_slot=order.collection_slot, + order_id=order.order_id, + amount=order.amount, + ordering_date=order.ordering_date, + delivery_date=order.delivery_date, ) @@ -532,11 +557,41 @@ async def add_order_to_delievery( ) orderret = await cruds_amap.get_order_by_id(order_id=db_order.order_id, db=db) + if orderret is None: + raise NewlyAddedObjectInDbNotFoundError(db_order.order_id) productsret = await cruds_amap.get_products_of_order(db=db, order_id=order_id) hyperion_amap_logger.info( f"Add_order_to_delivery: An order has been created for user {order.user_id} for an amount of {amount}€. ({request_id})", ) + return schemas_amap.OrderReturn( + user=CoreUserSimple( + id=orderret.user.id, + account_type=orderret.user.account_type, + school_id=orderret.user.school_id, + name=orderret.user.name, + firstname=orderret.user.firstname, + nickname=orderret.user.nickname, + ), + productsdetail=[ + schemas_amap.ProductQuantity( + quantity=product.quantity, + product=ProductComplete( + name=product.product.name, + price=product.product.price, + category=product.product.category, + id=product.product.id, + ), + ) + for product in productsret + ], + delivery_id=orderret.delivery_id, + collection_slot=orderret.collection_slot, + order_id=orderret.order_id, + amount=orderret.amount, + ordering_date=orderret.ordering_date, + delivery_date=orderret.delivery_date, + ) if orderret is None: raise HTTPException(status_code=404, detail="added order not found") @@ -1055,16 +1110,37 @@ async def get_orders_of_user( db=db, order_id=order.order_id, ) - if order is None: - raise HTTPException(status_code=404, detail="at least one order not found") res.append( schemas_amap.OrderReturn( - productsdetail=products, - delivery_date=order.delivery.delivery_date, - delivery_name=order.delivery.name, - **order.__dict__, + user=CoreUserSimple( + id=order.user.id, + account_type=order.user.account_type, + school_id=order.user.school_id, + name=order.user.name, + firstname=order.user.firstname, + nickname=order.user.nickname, + ), + productsdetail=[ + schemas_amap.ProductQuantity( + quantity=product.quantity, + product=ProductComplete( + name=product.product.name, + price=product.product.price, + category=product.product.category, + id=product.product.id, + ), + ) + for product in products + ], + delivery_id=order.delivery_id, + collection_slot=order.collection_slot, + order_id=order.order_id, + amount=order.amount, + ordering_date=order.ordering_date, + delivery_date=order.delivery_date, ), ) + return res diff --git a/app/modules/phonebook/schemas_phonebook.py b/app/modules/phonebook/schemas_phonebook.py index 1b452a7792..b5b138c2ac 100644 --- a/app/modules/phonebook/schemas_phonebook.py +++ b/app/modules/phonebook/schemas_phonebook.py @@ -1,3 +1,4 @@ +from typing import Sequence from uuid import UUID from pydantic import BaseModel, ConfigDict @@ -6,7 +7,7 @@ class RoleTagsReturn(BaseModel): - tags: list[str] + tags: Sequence[str] model_config = ConfigDict(from_attributes=True) @@ -76,7 +77,7 @@ class MemberBase(CoreUserSimple): class MemberComplete(MemberBase): - memberships: list[MembershipComplete] + memberships: Sequence[MembershipComplete] model_config = ConfigDict(from_attributes=True) diff --git a/app/modules/raffle/endpoints_raffle.py b/app/modules/raffle/endpoints_raffle.py index 0e087158c0..0df8b57b91 100644 --- a/app/modules/raffle/endpoints_raffle.py +++ b/app/modules/raffle/endpoints_raffle.py @@ -11,6 +11,7 @@ from app.core.permissions.type_permissions import ModulePermissions from app.core.users import cruds_users, models_users from app.core.users.endpoints_users import read_user +from app.core.users.schemas_users import CoreUserSimple from app.dependencies import ( get_db, get_redis_client, @@ -901,7 +902,11 @@ async def get_cash_by_id( # We want to return a balance of 0 but we don't want to add it to the database # An admin AMAP has indeed to add a cash to the user the first time # TODO: this is a strange behaviour - return schemas_raffle.CashComplete(balance=0, user_id=user_id, user=user_db) + return schemas_raffle.CashComplete( + balance=0, + user_id=user_id, + user=CoreUserSimple.from_model(user_db), + ) raise HTTPException( status_code=403, detail="Users that are not member of the group admin can only access the endpoint for their own user_id.", diff --git a/app/types/exceptions.py b/app/types/exceptions.py index 9309743d21..b0606f6fd2 100644 --- a/app/types/exceptions.py +++ b/app/types/exceptions.py @@ -189,6 +189,13 @@ def __init__(self): ) +class MissingHelloAssoRedirectUrlError(Exception): + def __init__(self): + super().__init__( + "HelloAsso redirect URL is missing in response", + ) + + class InvalidS3BucketNameError(Exception): def __init__(self, bucket_name: str): super().__init__(f"Invalid S3 bucket name: {bucket_name}")