Subscription Timeline Cost Forecaster
Problem
Design SubscriptionTimelineCostForecaster to record one scheduled plan change for each product a customer subscribes to and forecast that customer's exact subscription costs for a calendar year.
Requirements
- Keep exact plan prices. Construction copies a nonempty map of unique valid plan IDs to positive monthly prices in integer cents. Invalid catalogs raise
InvalidArgumentError, and later changes to the caller's map do not affect the forecaster. - Record one plan transition.
subscribestores one customer-product timeline. The initial plan is billed from the calendar month containingstartDatethrough the calendar month containing any validinitialPlanEndDate, and the next plan is billed from the following calendar month. The end date need not be month-end, and a valid new timeline returnstrue. - Reject invalid or duplicate subscriptions.
subscriberaisesInvalidArgumentErrorfor malformed identifiers or dates, unknown plans, or an end date before the start date. An existing customer-product pair returnsfalse, and every rejected call leaves stored state unchanged. - Forecast calendar months.
forecastCostreturns 12 integer-cent values from January through December. Each product contributes zero before its start month, its initial plan price from the full start month through the initial plan's end month, and its next plan price in every later month; costs from all of the customer's products are added, including future months. - Report the annual total.
annualCostis the exact sum of the 12monthlyCosts. A valid customer ID with no stored subscriptions receives 12 zeros and an annual cost of zero.
API
| Signature | Returns | Behavior |
|---|---|---|
SubscriptionTimelineCostForecaster(planMonthlyPrices: object) | Not applicable | Creates a forecaster from a map of plan ID strings to monthly prices in cents. |
subscribe(customerId: string, productName: string, initialPlanId: string, startDate: string, initialPlanEndDate: string, nextPlanId: string) | boolean | Attempts to record one subscription timeline. |
forecastCost(customerId: string, year: integer) | CostForecast | Calculates the customer's report for the requested calendar year. |
CostForecast has monthlyCosts: integer[12] and annualCost: integer.
Examples
With planMonthlyPrices = {"BASIC": 999, "PREMIUM": 24999}:
| Step | Operation | Result |
|---|---|---|
| 1 | subscribe("acme-corp", "jira", "BASIC", "2025-03-10", "2025-06-18", "PREMIUM") | true |
| 2 | forecastCost("acme-corp", 2025) | monthlyCosts = [0, 0, 999, 999, 999, 999, 24999, 24999, 24999, 24999, 24999, 24999], annualCost = 153990 |
March and June are charged in full. The next plan starts billing in July.
Constraints
planMonthlyPricescontains 1 to 50 entries.- A plan ID has 1 to 32 characters. Its first character is an uppercase ASCII letter; each later character is an uppercase ASCII letter, digit, or underscore.
- Each monthly price is an integer from 1 through 1,000,000,000 cents.
- Customer IDs and product names have 1 to 64 lowercase ASCII letters, digits, or single internal hyphens. They begin and end with a letter or digit.
- Dates use exact
YYYY-MM-DDform and must be valid Gregorian dates from1970-01-01through9998-12-31. - A forecast year is an integer from 1970 through 9999.
- One instance stores at most 1,000 distinct customer-product pairs and receives at most 10,000 method calls.
- Use signed 64-bit integers for all prices and aggregates.
Notes
Calls are sequential. InvalidArgumentError means ValueError in Python, IllegalArgumentException in Java, and std::invalid_argument in C++.