|
97 | 97 | } |
98 | 98 | ) |
99 | 99 |
|
100 | | - # Assign the rate card to a plan so the catalog is wired into an offer. |
| 100 | + platform_fee_card = RateCards::CreateService.call!( |
| 101 | + product_item: ProductItem.find_by!(organization:, code: "platform_fee"), |
| 102 | + params: { |
| 103 | + name: "Platform fee USD", |
| 104 | + code: "platform_fee_usd", |
| 105 | + currency: "USD" |
| 106 | + } |
| 107 | + ).rate_card |
| 108 | + |
| 109 | + RateCardRates::CreateService.call!( |
| 110 | + rate_card: platform_fee_card, |
| 111 | + params: { |
| 112 | + effective_datetime: 1.month.ago, |
| 113 | + rate_model: "standard", |
| 114 | + rate_properties: {amount: "99"}, |
| 115 | + billing_interval_unit: "month" |
| 116 | + } |
| 117 | + ) |
| 118 | + |
| 119 | + # Assign the rate cards to a plan so the catalog is wired into an offer. |
101 | 120 | plan = Plans::CreateService.call!({ |
102 | 121 | organization_id: organization.id, |
103 | 122 | name: "Growth", |
|
106 | 125 | pricing_type: "product_catalog" |
107 | 126 | }).plan |
108 | 127 |
|
109 | | - PlanProductItems::CreateService.call!( |
| 128 | + usage_entry = PlanProductItems::CreateService.call!( |
110 | 129 | plan:, |
111 | 130 | params: {rate_card_code: rate_card.code} |
| 131 | + ).plan_product_item |
| 132 | + |
| 133 | + PlanProductItems::CreateService.call!( |
| 134 | + plan:, |
| 135 | + params: {rate_card_code: platform_fee_card.code, units: 1} |
| 136 | + ) |
| 137 | + |
| 138 | + # Phases must be authored before the first subscription: a subscribed plan |
| 139 | + # is immutable (cards, phases and rates all lock). |
| 140 | + RatePhases::ReplaceService.call!( |
| 141 | + plan_product_item: usage_entry, |
| 142 | + phases_params: [ |
| 143 | + { |
| 144 | + position: 1, |
| 145 | + name: "Launch offer", |
| 146 | + billing_interval_cycle_count: 3, |
| 147 | + rate_override: {rate_model: "standard", rate_properties: {amount: "0.005"}} |
| 148 | + }, |
| 149 | + {position: 2, name: "Standard", billing_interval_cycle_count: nil} |
| 150 | + ] |
| 151 | + ) |
| 152 | + |
| 153 | + billing_entity = organization.billing_entities.find_by!(code: "hooli_v2") |
| 154 | + |
| 155 | + # An active subscription: materialization gives each plan card its own |
| 156 | + # billing lifecycle on the subscription, pricing stays on the plan. |
| 157 | + richard = Customer.create_with( |
| 158 | + name: "Richard Hendricks", |
| 159 | + currency: "USD", |
| 160 | + email: "richard@piedpiper.com" |
| 161 | + ).find_or_create_by!(organization:, billing_entity:, external_id: "cust_hooli_v2_1") |
| 162 | + |
| 163 | + active_subscription = Subscription.create_with( |
| 164 | + organization:, |
| 165 | + started_at: 1.month.ago, |
| 166 | + subscription_at: 1.month.ago, |
| 167 | + status: :active, |
| 168 | + billing_time: :anniversary |
| 169 | + ).find_or_create_by!(customer: richard, plan:, external_id: "sub_hooli_v2_1") |
| 170 | + |
| 171 | + Subscriptions::ProductCatalog::MaterializeService.call!(subscription: active_subscription) |
| 172 | + |
| 173 | + # A pending subscription still in its authoring window: rate cards and |
| 174 | + # phases can be added, edited or removed until activation. |
| 175 | + monica = Customer.create_with( |
| 176 | + name: "Monica Hall", |
| 177 | + currency: "USD", |
| 178 | + email: "monica@raviga.com" |
| 179 | + ).find_or_create_by!(organization:, billing_entity:, external_id: "cust_hooli_v2_2") |
| 180 | + |
| 181 | + pending_subscription = Subscription.create_with( |
| 182 | + organization:, |
| 183 | + subscription_at: 1.month.from_now, |
| 184 | + status: :pending, |
| 185 | + billing_time: :anniversary |
| 186 | + ).find_or_create_by!(customer: monica, plan:, external_id: "sub_hooli_v2_2") |
| 187 | + |
| 188 | + negotiated_entry = SubscriptionProductItems::CreateService.call!( |
| 189 | + subscription: pending_subscription, |
| 190 | + params: {rate_card_code: platform_fee_card.code, units: 1, started_at: 1.month.from_now.iso8601} |
| 191 | + ).subscription_product_item |
| 192 | + |
| 193 | + RatePhases::ReplaceService.call!( |
| 194 | + subscription_product_item: negotiated_entry, |
| 195 | + phases_params: [ |
| 196 | + { |
| 197 | + position: 1, |
| 198 | + name: "Negotiated intro", |
| 199 | + billing_interval_cycle_count: 3, |
| 200 | + rate_override: {rate_model: "standard", rate_properties: {amount: "49"}} |
| 201 | + }, |
| 202 | + {position: 2, name: "Standard", billing_interval_cycle_count: nil} |
| 203 | + ] |
112 | 204 | ) |
113 | 205 | end |
0 commit comments