products.py 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249
  1. """
  2. Product Routes Module
  3. This module defines all API endpoints for product management including:
  4. - Fetching products (all users)
  5. - Creating/editing products (permissions >= 1)
  6. - Deleting products (permissions == 2 only)
  7. Permission levels:
  8. - 0: Regular user (read-only access)
  9. - 1: Manager (can create/edit products)
  10. - 2: Admin (full access including delete)
  11. """
  12. # Standard library imports
  13. from logging import getLogger
  14. import time
  15. from typing import Optional
  16. # Third-party imports
  17. from fastapi import APIRouter, Depends, Query
  18. # Local imports
  19. from auth.security import get_current_user
  20. from models.user import User
  21. from models.items import Product, ProductCreateRequest, ProductEditRequest
  22. from services.data_service import DataServiceFactory
  23. from config.messages import ErrorResponse, SuccessResponse, UserResponse
  24. from utils.responses import error_response, success_response
  25. from services.toteat_service import get_products_by_table
  26. # Initialize logger for this module
  27. logger = getLogger(__name__)
  28. # Initialize data services for products and users
  29. product_data_service = DataServiceFactory.get_product_service()
  30. user_data_service = DataServiceFactory.get_user_service()
  31. # Create router instance for product-related endpoints
  32. product_router = APIRouter()
  33. def apply_promo_price(product: Product):
  34. """Apply promotional price to a product if applicable."""
  35. # dia de la semana 1-7
  36. day_of_week = time.localtime().tm_wday + 1 # Convert to 1-7 range
  37. product_dict = product.model_dump(exclude={"promo_id", "promo_price", "promo_day"})
  38. if product.promo_id and product.promo_price and product.promo_day == day_of_week:
  39. product_dict['promotion'] = {
  40. "id": product.promo_id,
  41. "price": product.promo_price,
  42. }
  43. return product_dict
  44. @product_router.get("/")
  45. async def get_products(status: Optional[int] = Query(None), current_user = Depends(get_current_user)):
  46. """
  47. Get all products - Available to all authenticated users
  48. Returns:
  49. JSONResponse: List of all products with success message
  50. """
  51. logger.debug(f"Current user: {current_user.email if current_user else 'Anonymous'}")
  52. logger.info("Fetching all products")
  53. # Retrieve all products and convert to dictionary format
  54. all_products = await product_data_service.get_all()
  55. all_products = list(map(apply_promo_price, all_products))
  56. if status is not None:
  57. all_products = [product for product in all_products if product['status'] == status]
  58. return success_response({"products": all_products}, message= SuccessResponse.PRODUCTS_FETCH_SUCCESS)
  59. @product_router.get("/{product_id}")
  60. async def get_product(product_id: str, current_user = Depends(get_current_user)):
  61. """
  62. Get a specific product by ID - Available to all authenticated users
  63. Args:
  64. product_id (int): The ID of the product to retrieve
  65. current_user: Authenticated user (dependency injection)
  66. Returns:
  67. JSONResponse: Product data if found, error message if not found
  68. """
  69. logger.debug(f"Current user: {current_user.email if current_user else 'Anonymous'}")
  70. logger.info(f"Fetching product with ID: {product_id}")
  71. # Attempt to find product by ID
  72. product = product_data_service.get_by_id(product_id)
  73. if product:
  74. return success_response({"product": product.model_dump(exclude={"promo_id", "promo_price", "promo_day"})}, message = SuccessResponse.PRODUCTS_FETCH_SUCCESS)
  75. # Return 404 if product not found
  76. return error_response(message = UserResponse.USER_NOT_FOUND.format(user_id=product_id), status_code=404)
  77. @product_router.get("/free-beer/{table_id}")
  78. async def get_free_beer(table_id: int, current_user:User = Depends(get_current_user)):
  79. """
  80. Get the free beer product - Available to all authenticated users
  81. Returns:
  82. JSONResponse: Free beer product data if found, error message if not found
  83. """
  84. logger.debug(f"Current user: {current_user.email if current_user else 'Anonymous'}")
  85. logger.info("Fetching free beer ticket")
  86. if current_user.reward_progress >= 100:
  87. user_data_service.set_reward_progress(current_user.id, 0)
  88. return success_response(message= SuccessResponse.REWARD_SUCCESS, status_code=200)
  89. # Return 404 if free beer product not found
  90. return error_response(message= UserResponse.USER_NOT_FOUND.format(user_id="free_beer"), status_code=404)
  91. # MODERATE RISK OPERATIONS - Requires permissions >= 1 (Manager level or above)
  92. @product_router.post("/create")
  93. async def create_product(product: ProductCreateRequest, current_user = Depends(get_current_user)):
  94. """
  95. Create a new product - Requires manager permissions (level >= 1)
  96. Args:
  97. product (ProductCreateRequest): New product data
  98. current_user: Authenticated user (dependency injection)
  99. Returns:
  100. JSONResponse: Success message or permission denied message
  101. """
  102. logger.debug(f"Current user: {current_user.email if current_user else 'Anonymous'}")
  103. logger.info("Creating a new product")
  104. # Check if user has sufficient permissions (manager level or above)
  105. if user_data_service.permissions(current_user.id) > 0:
  106. # Create new product with provided data
  107. product_data_service.create(**product.model_dump(exclude_unset=True))
  108. return success_response(product.model_dump(), message= SuccessResponse.PRODUCT_CREATE_SUCCESS, status_code=201)
  109. # Return 403 if user lacks permissions
  110. return error_response(message= UserResponse.NOT_PERMITTED, status_code=403)
  111. @product_router.patch("/{product_id}/swap-status")
  112. async def switch_product_status(product_id: str, current_user = Depends(get_current_user)):
  113. """
  114. Toggle product status between active/inactive - Requires manager permissions (level >= 1)
  115. Args:
  116. product_id (int): ID of the product to update
  117. status (int): New status value (0=inactive, 1=active)
  118. current_user: Authenticated user (dependency injection)
  119. Returns:
  120. JSONResponse: Success message or permission denied message
  121. """
  122. logger.debug(f"Current user: {current_user.email if current_user else 'Anonymous'}")
  123. logger.info(f"Switching status for product with ID: {product_id}")
  124. # Check if user has sufficient permissions (manager level or above)
  125. if user_data_service.permissions(current_user.id) > 0:
  126. # Update only the status field of the specified product
  127. product = product_data_service.get_by_id(product_id)
  128. if not product:
  129. return error_response(message= ErrorResponse.PRODDUCT_NOT_FOUND.format(product_id=product_id), status_code=404)
  130. status = 0 if product.status == 1 else 1
  131. product_data_service.update(product_id, status=status)
  132. return success_response(message=SuccessResponse.PRODUCT_EDIT_SUCCESS)
  133. # Return 403 if user lacks permissions
  134. return error_response(message=UserResponse.NOT_PERMITTED, status_code=403)
  135. # HIGH RISK OPERATIONS - Requires permissions == 2 (Admin level only)
  136. @product_router.delete("/{product_id}")
  137. async def delete_product(product_id: str, current_user = Depends(get_current_user)):
  138. """
  139. Delete a product permanently - Requires admin permissions (level == 2)
  140. This is a high-risk operation that permanently removes product data.
  141. Only users with admin-level permissions can perform this action.
  142. Args:
  143. product_id (int): ID of the product to delete
  144. current_user: Authenticated user (dependency injection)
  145. Returns:
  146. JSONResponse: Success message or permission denied message
  147. """
  148. logger.debug(f"Current user: {current_user.email if current_user else 'Anonymous'}")
  149. logger.info(f"Deleting product with ID: {product_id}")
  150. # Check if user has admin permissions (exactly level 2)
  151. if user_data_service.permissions(current_user.id) == 2:
  152. # Permanently delete the product
  153. product_data_service.delete(product_id)
  154. return success_response(message=SuccessResponse.PRODUCT_DELETE_SUCCESS)
  155. # Return 403 if user lacks admin permissions
  156. return error_response(message=UserResponse.NOT_PERMITTED, status_code=403)
  157. @product_router.patch("/{product_id}/edit")
  158. async def edit_product(product_id: str, product: ProductEditRequest, current_user = Depends(get_current_user)):
  159. """
  160. Edit an existing product - Requires manager permissions (level >= 1)
  161. Args:
  162. product (ProductEditRequest): Product data to update
  163. current_user: Authenticated user (dependency injection)
  164. Returns:
  165. JSONResponse: Updated product data or permission denied message
  166. """
  167. logger.debug(f"Current user: {current_user.email if current_user else 'Anonymous'}")
  168. logger.info(f"Editing product: {product_id}")
  169. # Check if user has sufficient permissions (manager level or above)
  170. if user_data_service.permissions(current_user.id) > 1:
  171. # Update product with provided data (excluding unset fields)
  172. product_data_service.update(product_id, **product.model_dump(exclude_unset=True))
  173. # Retrieve updated product to return in response
  174. edited_product = product_data_service.get_by_id(product_id)
  175. if not edited_product:
  176. return error_response(message=UserResponse.USER_NOT_FOUND.format(user_id=product_id), status_code=404)
  177. logger.info(f"Product {product_id} edited successfully")
  178. return success_response(data=edited_product.model_dump(), message=SuccessResponse.PRODUCT_EDIT_SUCCESS)
  179. # Return 403 if user lacks permissions
  180. return error_response(message=UserResponse.NOT_PERMITTED, status_code=403)
  181. @product_router.get("/table/{table_number}")
  182. async def get_table_items(table_number: int, _: User = Depends(get_current_user)):
  183. """Get items for a specific table"""
  184. logger.info(f"Fetching items for table {table_number}")
  185. # Retrieve items for table
  186. items = get_products_by_table(table_number)
  187. if not items:
  188. return error_response(message=ErrorResponse.SALE_NOT_FOUND, status_code=404)
  189. logger.info(f"Items for table {table_number} retrieved successfully")
  190. return success_response(data=items, message=SuccessResponse.PRODUCTS_FETCH_SUCCESS)