finance_defaults.py 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475
  1. from sqlalchemy import select
  2. from sqlalchemy.ext.asyncio import AsyncSession
  3. from backend.app.models.finance import CostCenter, CostCenterMember, UserWallet
  4. from backend.app.models.settings import Settings as AppSettingModel
  5. from backend.app.models.user import User
  6. from backend.app.schemas.settings import AppSettings as AppSettingsSchema
  7. async def ensure_user_finance_defaults(db: AsyncSession, user: User) -> bool:
  8. """Ensure wallet and private cost center defaults exist for a user.
  9. Returns True when database objects were created or changed.
  10. """
  11. changed = False
  12. wallet = (await db.execute(select(UserWallet).where(UserWallet.user_id == user.id))).scalar_one_or_none()
  13. if wallet is None:
  14. # Respect admin-configured currency if present, otherwise fall back to app default
  15. default_currency = AppSettingsSchema().currency
  16. result = await db.execute(select(AppSettingModel).where(AppSettingModel.key == "currency"))
  17. setting = result.scalar_one_or_none()
  18. currency = setting.value if setting and setting.value else default_currency
  19. db.add(UserWallet(user_id=user.id, balance=0.0, currency=currency))
  20. changed = True
  21. private_center = (
  22. (
  23. await db.execute(
  24. select(CostCenter)
  25. .where(
  26. CostCenter.is_private.is_(True),
  27. CostCenter.owner_user_id == user.id,
  28. )
  29. .order_by(CostCenter.id.asc())
  30. )
  31. )
  32. .scalars()
  33. .first()
  34. )
  35. if private_center is None:
  36. private_center = CostCenter(
  37. name=user.username,
  38. is_active=True,
  39. is_private=True,
  40. owner_user_id=user.id,
  41. )
  42. db.add(private_center)
  43. await db.flush()
  44. changed = True
  45. else:
  46. # A private center is the billing fallback for its owner and therefore
  47. # must remain active. A zero budget is the supported way to prevent
  48. # printing from it.
  49. if not private_center.is_active:
  50. private_center.is_active = True
  51. changed = True
  52. if private_center.name != user.username:
  53. private_center.name = user.username
  54. changed = True
  55. membership = (
  56. await db.execute(
  57. select(CostCenterMember).where(
  58. CostCenterMember.cost_center_id == private_center.id,
  59. CostCenterMember.user_id == user.id,
  60. )
  61. )
  62. ).scalar_one_or_none()
  63. if membership is None:
  64. db.add(CostCenterMember(cost_center_id=private_center.id, user_id=user.id, can_print=True))
  65. changed = True
  66. return changed