feat: add tier_id FK to VendorSubscription for proper tier relationship

- Add tier_id column with FK to subscription_tiers table
- Add tier_obj relationship to VendorSubscription model
- Update tier_limits property to use database tier when available
- Create migration with SQLite batch mode support
- Backfill tier_id from existing tier code values

This enables proper database relationship between vendors and their
subscription tier, instead of just storing the tier code string.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2025-12-26 07:33:49 +01:00
parent 44de82eb47
commit 0ad54a52e0
3 changed files with 491 additions and 2 deletions

View File

@@ -431,7 +431,10 @@ class VendorSubscription(Base, TimestampMixin):
Integer, ForeignKey("vendors.id"), unique=True, nullable=False, index=True
)
# Tier
# Tier - tier_id is the FK, tier (code) kept for backwards compatibility
tier_id = Column(
Integer, ForeignKey("subscription_tiers.id"), nullable=True, index=True
)
tier = Column(
String(20), default=TierCode.ESSENTIAL.value, nullable=False, index=True
)
@@ -479,6 +482,7 @@ class VendorSubscription(Base, TimestampMixin):
# Relationships
vendor = relationship("Vendor", back_populates="subscription")
tier_obj = relationship("SubscriptionTier", backref="subscriptions")
__table_args__ = (
Index("idx_subscription_vendor_status", "vendor_id", "status"),
@@ -494,7 +498,20 @@ class VendorSubscription(Base, TimestampMixin):
@property
def tier_limits(self) -> dict:
"""Get the limit definitions for current tier."""
"""Get the limit definitions for current tier.
Uses database tier (tier_obj) if available, otherwise falls back
to hardcoded TIER_LIMITS for backwards compatibility.
"""
# Use database tier if relationship is loaded
if self.tier_obj is not None:
return {
"orders_per_month": self.tier_obj.orders_per_month,
"products_limit": self.tier_obj.products_limit,
"team_members": self.tier_obj.team_members,
"features": self.tier_obj.features or [],
}
# Fall back to hardcoded limits
return TIER_LIMITS.get(TierCode(self.tier), TIER_LIMITS[TierCode.ESSENTIAL])
@property