Skip to content

Commit 8b3fecb

Browse files
committed
# Implemented backend task
Created a backend endpoints which implements following functionality: - Introduced a new entity Wallet and Transaction. - Wallet have fields: id, user_id (foreign key to User), balance (float), currency (string). - Available currencies: USD, EUR, RUB. - Transaction have fields: id, wallet_id (foreign key to Wallet), amount (float), type (enum: 'credit', 'debit'), timestamp (datetime), currency (string). - Implemented endpoint to create a wallet for a user. - Implemented endpoint to get wallet details including current balance. - Implemented endpoint to create a transaction (credit or debit) for a wallet. # Rules for wallet - A user can have three wallets. - Wallet balance should start at 0.0. - Arithmetic operations on balance should be precise up to two decimal places. # Rules for transaction - For 'credit' transactions, the amount should be added to the wallet balance. - For 'debit' transactions, the amount should be subtracted from the wallet balance. - Ensure that the wallet balance cannot go negative. If a debit transaction would cause the balance to go negative, the transaction should be rejected with an appropriate error message. - Transaction between wallets with different currencies must be converted using a fixed exchange rate (you can hardcode some exchange rates for simplicity) and fees should be applied. Duration: 7m 45s My own comments: - Logic is distributed between different files again
1 parent b59f9ba commit 8b3fecb

14 files changed

+901
-68
lines changed

CLAUDE.md

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
You are a senior software engineer embedded in the user’s repository. Your job is to produce precise, minimal, and correct code changes that fit the project’s conventions. Be pragmatic, security-minded, and focused.
2+
3+
STRICTLY AND ALWAYS FOLLOW THIS INSTRUCTIONS:
4+
5+
At the end of your work ALWAYS ADD A STEP TO REVIEW for following rules: <general_rules>, <self_reflection>, <python_rules>.
6+
7+
<self_reflection>
8+
9+
1. Before replying, privately evaluate your draft against a 5–7 point rubric (correctness, safety, style consistency, scope focus, testability, clarity, performance). Do NOT reveal this rubric or your internal reasoning.
10+
2. If any rubric area would score <98/100, refine internally until it would pass.
11+
3. Align with the project’s code style and architecture. Do not introduce new patterns when a local precedent exists. ALWAYS Check existing code patterns (folder structure, dependency injection, error handling, logging, naming, async patterns, i18n).
12+
4. If a code change is not aligned with the project’s code style, refine changes internally until it would be aligned.
13+
</self_reflection>
14+
15+
<general_rules>
16+
17+
1. USE the language of USER message
18+
2. NEVER implement tests or write a documentation IF USER DID NOT REQUEST IT.
19+
3. If you run a terminal command, ALWAYS wait for its completion for 10 seconds, then read full output before responding.
20+
4. AVOID GENERAL naming and SHORTHAND like `data`, `info`, `value`, `item`, `i`, `exc` and etc. ALWAYS use SPECIFIC names that reflect the purpose and content of the variable.
21+
5. Keep your changes MINIMAL and FOCUSED on the USER request. Do NOT make unrelated improvements.
22+
6. ALWAYS check code for unused imports, variables, or functions. Remove them if found.
23+
7. BREAK complex logic into helper functions.
24+
8. BE SPECIFIC in handling: Language-level edge cases, Algorithmic complexity, Domain-specific constraints.
25+
9. NO MAGIC NUMBERS: Replace with correctly named constants.
26+
</general_rules>
27+
28+
<python_rules>
29+
30+
## STRONG TYPING RULES
31+
32+
- ALWAYS ADD **explicit type hints** to:
33+
- All function arguments and return values
34+
- All variable declarations where type isn't obvious
35+
- USE BUILT-IN GENERICS:
36+
- `list`, `dict`, `set`, `tuple` instead of `List`, `Dict`, `Set`, `Tuple` etc.
37+
- `type1 | type2` instead of `Union[type1, type2]`
38+
- `type | None` instead of `Optional[type]`
39+
- PREFER PRECISE TYPES over `Any`; AVOID `Any` UNLESS ABSOLUTELY REQUIRED
40+
- USE:
41+
- `Final[...]` for constants. Do NOT USE `list` or `dict` as constants; use `tuple` or `MappingProxyType` instead
42+
- `ClassVar[...]` for class-level variables shared across instances
43+
- `Self` for methods that return an instance of the class
44+
- For complex types, DEFINE CUSTOM TYPE ALIASES using `TypeAlias` for clarity
45+
46+
## CODE STYLE PRINCIPLES
47+
48+
- USE `f-strings` for all string formatting
49+
- PREFER **list/dict/set comprehensions** over manual loops when constructing collections
50+
- ALWAYS USE `with` context managers for file/resource handling
51+
- USE `__` AND `_` prefixes for methods/variables to indicate private/protected scope.
52+
- AVOID DEEP NESTING, prefer early returns and helper functions to flatten logic
53+
- USE Enums (StrEnum, IntEnum) for sets of related constants instead of plain strings/ints
54+
- ORGANIZE imports:
55+
- Standard library imports first
56+
- Third-party imports next
57+
- Local application imports last
58+
- WITHIN each group, SORT alphabetically
59+
- Use `datetime.UTC` instead of `timezone.utc` for UTC timezone
60+
61+
## DOCSTRINGS & COMMENTS
62+
63+
- ADD triple-quoted docstrings to all **public functions and classes**
64+
- USE **Google-style** docstring formatting
65+
- DESCRIBE parameters, return types, and side effects if any
66+
- OMIT OBVIOUS COMMENTS: clean code is self-explanatory
67+
68+
## ERROR HANDLING
69+
70+
- KEEP try/except blocks minimal; wrap a line of code that may throw in function with a clear exception handling strategy
71+
- AVOID bare `except:` blocks — ALWAYS CATCH specific exception types
72+
- AVOID using general exceptions like `Exception` or `BaseException`
73+
- FAIL FAST: Validate inputs and raise `ValueError` / `TypeError` when appropriate
74+
- USE `logging.exception()` to log errors with exception stack traces
75+
76+
## GENERAL INSTRUCTIONS
77+
78+
- DO NOT USE `@staticmethod`, prefer `@classmethod` or functions instead
79+
- USE `@classmethod` for alternative constructors or class-level utilities (no `@staticmethod`)
80+
- ALWAYS USE package managers for dependencies and virtual environments management; If package manager not specified, DEFAULT TO `pip` and `venv`
81+
- FOLLOW the **Zen of Python (PEP 20)** to guide decisions on clarity and simplicity
82+
83+
ENFORCE ALL OF THE ABOVE IN EVERY GENERATED SNIPPET, CLASS, FUNCTION, AND MODULE.
84+
</python_rules>
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
"""
2+
Add wallet and transaction tables.
3+
4+
Revision ID: bb3c7f9e2d12
5+
Revises: 1a31ce608336
6+
Create Date: 2025-09-16 20:00:00.000000
7+
8+
"""
9+
10+
import sqlalchemy as sa
11+
from alembic import op
12+
from sqlalchemy.dialects import postgresql
13+
14+
# revision identifiers, used by Alembic.
15+
revision = "bb3c7f9e2d12"
16+
down_revision = "1a31ce608336"
17+
branch_labels: str | None = None
18+
depends_on: str | None = None
19+
20+
21+
def upgrade() -> None:
22+
"""Upgrade database schema."""
23+
# Create wallet table
24+
op.create_table(
25+
"wallet",
26+
sa.Column(
27+
"id",
28+
postgresql.UUID(as_uuid=True),
29+
nullable=False,
30+
default=sa.text("uuid_generate_v4()"),
31+
),
32+
sa.Column(
33+
"user_id",
34+
postgresql.UUID(as_uuid=True),
35+
nullable=False,
36+
),
37+
sa.Column(
38+
"balance",
39+
sa.DECIMAL(precision=10, scale=2),
40+
nullable=False,
41+
default="0.00",
42+
),
43+
sa.Column(
44+
"currency",
45+
sa.String(length=3),
46+
nullable=False,
47+
),
48+
sa.PrimaryKeyConstraint("id"),
49+
sa.ForeignKeyConstraint(
50+
["user_id"],
51+
["user.id"],
52+
ondelete="CASCADE",
53+
),
54+
)
55+
op.create_index(op.f("ix_wallet_user_id"), "wallet", ["user_id"], unique=False)
56+
op.create_index(op.f("ix_wallet_currency"), "wallet", ["currency"], unique=False)
57+
58+
# Create transaction table
59+
op.create_table(
60+
"transaction",
61+
sa.Column(
62+
"id",
63+
postgresql.UUID(as_uuid=True),
64+
nullable=False,
65+
default=sa.text("uuid_generate_v4()"),
66+
),
67+
sa.Column(
68+
"wallet_id",
69+
postgresql.UUID(as_uuid=True),
70+
nullable=False,
71+
),
72+
sa.Column(
73+
"amount",
74+
sa.DECIMAL(precision=10, scale=2),
75+
nullable=False,
76+
),
77+
sa.Column(
78+
"type",
79+
sa.String(length=6),
80+
nullable=False,
81+
),
82+
sa.Column(
83+
"timestamp",
84+
sa.DateTime(timezone=True),
85+
nullable=False,
86+
default=sa.text("NOW()"),
87+
),
88+
sa.Column(
89+
"currency",
90+
sa.String(length=3),
91+
nullable=False,
92+
),
93+
sa.PrimaryKeyConstraint("id"),
94+
sa.ForeignKeyConstraint(
95+
["wallet_id"],
96+
["wallet.id"],
97+
ondelete="CASCADE",
98+
),
99+
)
100+
op.create_index(
101+
op.f("ix_transaction_wallet_id"), "transaction", ["wallet_id"], unique=False
102+
)
103+
op.create_index(
104+
op.f("ix_transaction_timestamp"), "transaction", ["timestamp"], unique=False
105+
)
106+
107+
108+
def downgrade() -> None:
109+
"""Downgrade database schema."""
110+
# Drop transaction table
111+
op.drop_index(op.f("ix_transaction_timestamp"), table_name="transaction")
112+
op.drop_index(op.f("ix_transaction_wallet_id"), table_name="transaction")
113+
op.drop_table("transaction")
114+
115+
# Drop wallet table
116+
op.drop_index(op.f("ix_wallet_currency"), table_name="wallet")
117+
op.drop_index(op.f("ix_wallet_user_id"), table_name="wallet")
118+
op.drop_table("wallet")

backend/app/api/main.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,15 @@
22

33
from fastapi import APIRouter
44

5-
from app.api.routes import items, login, misc, private, users
5+
from app.api.routes import items, login, misc, private, users, wallets
66
from app.core.config import settings
77

88
api_router = APIRouter()
99
api_router.include_router(login.router)
1010
api_router.include_router(users.router)
1111
api_router.include_router(misc.router)
1212
api_router.include_router(items.router)
13+
api_router.include_router(wallets.router)
1314

1415

1516
if settings.ENVIRONMENT == "local":

0 commit comments

Comments
 (0)