Payment tracking and contract management analytics for subscription-based solar power business
SubTrak is an analytics engineering project that models payment tracking and contract performance for a subscription-based solar power business operating in Nigeria. The project transforms raw transactional data into analytics-ready models that power business intelligence, rep performance tracking, and risk management.
The company operates a hybrid business model:
- Outright purchases: Full payment upfront
- 12-month installment plans: Upfront payment + 11 monthly installments (30-day cycles)
- Regional sales network: Performance-based rep incentive structure
- Solar power products: Critical infrastructure requiring consistent payment compliance
- Contract Status Tracking: Active, Owner, Lost, Retrieved
- Payment Compliance: On-time rate, grace period, late payments
- Rep Performance: Incentive qualification, portfolio health
- Risk Management: Early identification of potential defaults (31-60+ days overdue)
Source (OLTP PostgreSQL)
β
Staging Layer (cleaned, typed, renamed)
β
Intermediate Layer (joins, business logic)
β
Analytics Layer (marts for consumption)
β
BI Layer (Power BI dashboards)
subtrak_db/
βββ oltp/ # Source OLTP tables
βββ staging/ # Cleaned staging models
βββ analytics/ # Analytics-ready marts
Purpose: Clean, type, and standardize raw OLTP data
stg_regions- Geographic regionsstg_reps- Sales representativesstg_customers- Customer master datastg_deals- Product/pricing plansstg_contracts- Contract master recordsstg_payments- Payment transactions
Transformations:
- Standardized column naming (snake_case)
- Data type casting
- Null handling
- Deduplication
- Basic data quality checks
Grain: One row per contract
Master dimension with all contract attributes including customer, rep, region, and deal details.
Key Fields:
- Contract identifiers and dates
- Customer demographics
- Rep information and performance flags
- Product/deal details
- Time-based attributes (year, quarter, month)
- Calculated fields (contract age, repossession status)
Use Cases:
- Customer segmentation
- Rep/region performance analysis
- Product mix analysis
- Cohort definitions
Grain: One row per contract per billing cycle
Tracks expected vs actual payments for each billing period with detailed payment behavior metrics.
Key Fields:
- Cycle identification (cycle_number: 1 = upfront, 2-12 = monthly)
- Expected vs actual amounts
- Payment timing (on_time, grace_period, late)
- Days from due date (negative = early, positive = late)
- Rep incentive qualification flag
- Cycle status (paid, pending, overdue)
- Days overdue calculation
Business Logic:
Payment Timing:
- On-time: payment_date <= due_date
- Grace period: payment_date <= due_date + 5 days (qualifies for rep incentive)
- Late: payment_date > due_date + 5 daysUse Cases:
- Monthly collections reporting
- Payment behavior analysis
- Rep incentive calculations
- Cash flow forecasting
- Delinquency tracking
Grain: One row per contract
Comprehensive contract health scorecard with current status, risk level, and performance metrics.
Key Metrics:
| Category | Metrics |
|---|---|
| Payment Progress | total_billing_cycles, cycles_paid, cycles_unpaid, percent_paid |
| Financial | total_contract_value, total_amount_paid, total_amount_outstanding |
| Performance | on_time_payment_rate, incentive_qualifying_payments |
| Timing | last_payment_date, next_payment_due, days_since_last_payment |
| Risk | max_days_overdue, risk_category, overdue_cycle_count |
Status Classifications:
System Status (Application-driven):
enabled: Service active (current_date < next_payment_due)locked: Service suspended (payment overdue)retrieved: Physical repossessioncompleted: All payments made
Business Status (Analytics classification):
active: Contract ongoing, < 61 days overdueowner: Completed all payments + past contract_end_datelost: 61+ days in default (business write-off)retrieved: Repossessed
Risk Categories:
good_standing: 0-5 days overduelow_risk: 6-30 days overduemedium_risk: 31-60 days overduehigh_risk: 61+ days overdue
Use Cases:
- Executive dashboards
- Portfolio health monitoring
- Risk management
- Collections prioritization
- Rep performance scorecards
Grain: One row per contract per month-end snapshot
Time-series tracking of contract portfolio health with monthly granularity. Enables trend analysis, cohort performance tracking, and historical reporting.
Key Metrics and Status Classifications:
Same as contract_status_current
Use Cases:
- Month-over-month portfolio health trends
- Risk migration analysis (contracts moving between risk categories)
- Cohort performance comparison
- Executive month-end reporting
- Year-over-year growth metrics
- Uniqueness tests: All primary keys
- Not null tests: Required fields
- Referential integrity: Foreign key relationships
- Accepted values: Categorical fields (status, risk_category, etc.)
- Business logic tests:
- Total paid β€ total contract value
- Cycles paid + unpaid = total cycles
- Payment timing categories are valid
# Run all tests
dbt test
# Run tests for specific model
dbt test --select contract_status_current
# Run tests for analytics layer
dbt test --select analytics.*- Python 3.8+
- PostgreSQL 14+
- dbt-core 1.0+
- dbt-postgres adapter
# Clone repository
git clone <your-repo-url>
cd subtrak-analytics
# Install dependencies
pip install dbt-core dbt-postgres
# Configure connection
cp profiles.yml.example ~/.dbt/profiles.yml
# Edit ~/.dbt/profiles.yml with your database credentials# Install dependencies (if using packages)
dbt deps
# Run staging models
dbt run --select staging.*
# Run analytics models
dbt run --select analytics.*
# Run everything
dbt run
# Test everything
dbt test
# Generate documentation
dbt docs generate
dbt docs serveThis project demonstrates:
-
Financial Data Modeling β
- Complex payment schedules and contract tracking
- Interest calculations logic
- Balance and payment reconciliation
-
Analytics Engineering Best Practices β
- Dimensional modeling (facts + dimensions)
- Clear data lineage (staging β analytics)
- Comprehensive testing and documentation
- Reusable, modular SQL
-
Business Acumen β
- Understanding of subscription/recurring payment models
- Risk classification and early warning systems
- Performance incentive structures
- Customer lifecycle tracking
-
Nigerian Market Context β
- Solar power sector (infrastructure, payment challenges)
- Regional sales network model
- Payment behavior patterns in emerging markets
-
Technical Skills β
- Advanced SQL (window functions, CTEs, date math)
- dbt (models, tests, documentation)
- PostgreSQL
- Data quality frameworks
- Cycle: A billing period (upfront or monthly installment)
- On-time Payment: Payment made on or before due date
- Grace Period: Payment made 1-5 days after due date (still qualifies for rep incentive)
- Late Payment: Payment made 6+ days after due date
- Days Overdue: Current date - due date (for unpaid cycles past due date)
- Active Contract: Not completed, not lost (< 91 days overdue), completed payment but contract has not ended
- Owner Status: All payments complete + past contract end date
- Lost Contract: 90+ days in default (business write-off threshold)
stg_regions βββββ
stg_reps ββββββββΌβββ dim_contracts_enriched βββ
stg_customers βββ€ β
stg_deals βββββββ€ β
stg_contracts βββ΄βββ fct_billing_cycles ββββββββΌβββ BI Dashboards
β
stg_payments βββββββ payment aggregations ββββββ΄βββ contract_status_current
- Add customer churn prediction model
- Build rep territory optimization analysis
- Create automated alerting for high-risk contracts
- Implement incremental models for better performance
- Add seasonal payment pattern analysis
- Build customer lifetime value (CLV) prediction
- Create macros for common calculations
- Add exposure metrics (dbt exposures) for downstream BI
Chidinma Okoro
- Email: okoromannie@gmail.com
- LinkedIn: [Chidinma Okoro]
This project is for portfolio demonstration purposes.
Project completed: [ongoing] dbt version: 1.0+ Database: PostgreSQL 14