| 1 | # frozen_string_literal: true |
| 2 | |
| 3 | # A user's siGit plan + Stripe subscription state. Source of truth is Stripe; |
| 4 | # this caches what the cloud gate needs. A user with no Subscription row is |
| 5 | # treated as Free (local-only, no cloud). |
| 6 | class Subscription < ApplicationRecord |
| 7 | belongs_to :user |
| 8 | |
| 9 | enum :plan, { free: 0, pro: 1, team: 2 } |
| 10 | enum :status, { active: 0, past_due: 1, canceled: 2, trialing: 3, incomplete: 4 } |
| 11 | |
| 12 | # Monthly siGit Code Cloud request allowance per plan. Tunable pricing knobs — |
| 13 | # these are the included cloud requests before the cap kicks in. |
| 14 | CLOUD_ALLOWANCE = { "pro" => 2_000, "team" => 6_000 }.freeze |
| 15 | |
| 16 | # During the free trial, cap cloud usage well below the paid allowance so a |
| 17 | # trial can't run up an unbounded upstream bill. Enough to feel the product. |
| 18 | TRIAL_ALLOWANCE = 300 |
| 19 | |
| 20 | # Entitled to siGit Code Cloud: on a paid plan in good standing (incl. trial). |
| 21 | def entitled_to_cloud? |
| 22 | (pro? || team?) && (active? || trialing?) |
| 23 | end |
| 24 | |
| 25 | # Included cloud requests for this period. The trial gets a tighter cap. |
| 26 | def cloud_allowance |
| 27 | return TRIAL_ALLOWANCE if trialing? |
| 28 | |
| 29 | CLOUD_ALLOWANCE.fetch(plan, 0) |
| 30 | end |
| 31 | |
| 32 | # Identifies the current billing period so usage resets each cycle. Uses the |
| 33 | # Stripe period end when known; falls back to the calendar month. |
| 34 | def billing_period_key |
| 35 | current_period_end&.utc&.to_date&.iso8601 || Time.current.utc.strftime("%Y-%m") |
| 36 | end |
| 37 | end |