Files
orion/.architecture-rules/money.yaml
Samir Boulahtit 7c43d6f4a2 refactor: fix all architecture validator findings (202 → 0)
Eliminate all 103 errors and 96 warnings from the architecture validator:

Phase 1 - Validator rules & YAML:
- Add NAM-001/NAM-002 exceptions for module-scoped router/service files
- Fix API-004 to detect # public comments on decorator lines
- Add module-specific exception bases to EXC-004 valid_bases
- Exclude storefront files from AUTH-004 store context check
- Add SVC-006 exceptions for loyalty service atomic commits
- Fix _get_rule() to search naming_rules and auth_rules categories
- Use plain # CODE comments instead of # noqa: CODE for custom rules

Phase 2 - Billing module (5 route files):
- Move _resolve_store_to_merchant to subscription_service
- Move tier/feature queries to feature_service, admin_subscription_service
- Extract 22 inline Pydantic schemas to billing/schemas/billing.py
- Replace all HTTPException with domain exceptions

Phase 3 - Loyalty module (4 routes + points_service):
- Add 7 domain exceptions (Apple auth, enrollment, device registration)
- Add service methods to card_service, program_service, apple_wallet_service
- Move all db.query() from routes to service layer
- Fix SVC-001: replace HTTPException in points_service with domain exception

Phase 4 - Remaining modules:
- tenancy: move store stats queries to admin_service
- cms: move platform resolution to content_page_service, add NoPlatformSubscriptionException
- messaging: move user/customer lookups to messaging_service
- Add ConfigDict(from_attributes=True) to ContentPageResponse

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 18:49:24 +01:00

176 lines
5.3 KiB
YAML

# Architecture Rules - Money Handling
# Rules for monetary value handling across the codebase
#
# Reference: docs/architecture/money-handling.md
money_handling_rules:
- id: "MON-001"
name: "Database columns for money must use Integer cents"
severity: "error"
description: |
All monetary values in database models MUST be stored as integers representing
cents (or smallest currency unit). This prevents floating-point precision errors.
CORRECT:
price_cents = Column(Integer)
total_amount_cents = Column(Integer)
sale_price_cents = Column(Integer, nullable=True)
INCORRECT:
price = Column(Float)
total_amount = Column(Numeric(10, 2))
Column naming convention: Use `_cents` suffix for all monetary columns.
pattern:
file_pattern:
- "models/database/**/*.py"
- "app/modules/*/models/**/*.py"
required_patterns:
- "_cents = Column(Integer"
anti_patterns:
- "price = Column(Float"
- "amount = Column(Float"
- "total = Column(Float"
- "Column(Numeric"
- id: "MON-002"
name: "Use Money utility for conversions"
severity: "error"
description: |
All euro/cents conversions MUST use the Money utility class or
the euros_to_cents/cents_to_euros helper functions.
Never use manual multiplication/division for money conversions.
CORRECT:
from app.utils.money import Money, euros_to_cents, cents_to_euros
price_cents = euros_to_cents(19.99)
display_price = cents_to_euros(price_cents)
formatted = Money.format(price_cents, "EUR")
INCORRECT:
price_cents = int(price * 100)
display_price = price_cents / 100
pattern:
file_pattern:
- "app/**/*.py"
- "models/**/*.py"
required_imports:
- "from app.utils.money import"
anti_patterns:
- "* 100" # Manual cents conversion
- "/ 100" # Manual euros conversion
exceptions:
- "app/utils/money.py" # The utility itself
- id: "MON-003"
name: "API responses use euros for display"
severity: "error"
description: |
Pydantic response schemas MUST expose monetary values as floats (euros)
for frontend consumption. Use computed properties to convert from _cents.
CORRECT (in schema):
price_cents: int # Internal
@computed_field
@property
def price(self) -> float:
return cents_to_euros(self.price_cents)
Or use model validators to convert before response serialization.
pattern:
file_pattern:
- "models/schema/**/*.py"
- "app/modules/*/schemas/**/*.py"
check: "money_response_format"
- id: "MON-004"
name: "Frontend must use Money.format() for display"
severity: "error"
description: |
All monetary value display in frontend MUST use the Money utility.
This ensures consistent formatting across locales.
CORRECT (JavaScript):
Money.format(order.total_amount_cents, 'EUR')
Money.formatEuros(order.total_amount)
INCORRECT:
`${order.total_amount} EUR`
order.total_amount.toFixed(2)
pattern:
file_pattern:
- "static/**/*.js"
- "app/templates/**/*.html"
required_patterns:
- "Money.format"
- "formatPrice" # Helper function wrapping Money.format
discouraged_patterns:
- "toFixed(2)"
- "${.*} EUR"
- id: "MON-005"
name: "All arithmetic on money uses integers"
severity: "error"
description: |
All monetary calculations MUST be performed on integer cents values.
Convert to euros ONLY for display purposes, never for calculations.
CORRECT:
subtotal_cents = sum(item.unit_price_cents * item.quantity for item in items)
tax_cents = int(subtotal_cents * Decimal("0.17"))
total_cents = subtotal_cents + tax_cents
INCORRECT:
subtotal = sum(item.price * item.quantity for item in items)
tax = subtotal * 0.17 # Floating point!
total = subtotal + tax
pattern:
file_pattern:
- "app/services/**/*.py"
- "app/modules/*/services/**/*.py"
check: "money_arithmetic"
- id: "MON-006"
name: "CSV/External imports must convert to cents"
severity: "error"
description: |
When importing monetary values from CSV files or external APIs,
always convert to cents immediately upon parsing.
CORRECT:
price_cents = euros_to_cents(parsed_price)
product.price_cents = price_cents
INCORRECT:
product.price = parsed_price # Float assignment
pattern:
file_pattern:
- "app/utils/csv_processor.py"
- "app/services/**/import*.py"
required_patterns:
- "euros_to_cents"
- "_cents"
- id: "MON-007"
name: "Test assertions use cents"
severity: "warning"
description: |
Test assertions for monetary values should use integer cents
to avoid floating-point comparison issues.
CORRECT:
assert order.total_amount_cents == 10591
assert product.price_cents == 1999
INCORRECT:
assert order.total_amount == 105.91 # Float comparison
pattern:
file_pattern: "tests/**/*.py"
encouraged_patterns:
- "_cents =="
- "_cents >"
- "_cents <"