The company used BusinessObjects (BOXI) for static PDFs and Power BI for dashboards. BOXI scheduling worked, but metric logic was inconsistent across reports and hard to audit.
Leadership asked to consolidate on Power BI. I took ownership of the paginated layer, reverse engineered BOXI universes, designed reusable SQL datasets, and matched figures exactly.
I requested a monthly export of all BOXI schedules and recipients. From that I built two working indexes:
Report index ranked by number of schedules and customer exposure.
This identified the highest value reports to migrate first.
Recipient index grouped by user and department.
This showed dependency clusters and consolidation opportunities.
These indexes became the plan of record for migration and communication.
Each BOXI report was reviewed and categorised:
I created a small canonical SQL layer using views and CTEs for shared business metrics. This replaced BOXI variables and removed one off definitions. I also moved away from hard coded variables such as warehouse_1, warehouse_2 by using dynamic groups in Report Builder, so new values appear automatically.
Aggregate the fact first by keys and date, then join dimensions for descriptions. This keeps queries readable and index friendly.
WITH fact_summary AS (
SELECT
s.dim_customer_key,
s.dim_salesperson_key,
d.year,
d.month,
SUM(s.cases_sold) AS cases_sold
FROM fact_sales s
JOIN dim_date d
ON s.dim_order_date_key = d.dim_date_key
WHERE d.year = @year
GROUP BY
s.dim_customer_key,
s.dim_salesperson_key,
d.year,
d.month
)
SELECT
c.customer_number,
c.customer_name,
u.full_name AS salesperson_name,
f.year,
f.month,
f.cases_sold
FROM fact_summary f
JOIN dim_customer c
ON f.dim_customer_key = c.dim_customer_key
JOIN dim_user u
ON f.dim_salesperson_key = u.dim_user_key;
Why this works well:
GROUP BY on keys and date.Legacy reports were often fixed to a period such as this month versus last month.
Each paginated report was rebuilt with parameters so a user can pick any period. SQL calculates the comparison period and headers render with real month and year, for example Sales 2024 Jan and Sales 2023 Dec.
This reduced duplicate schedules and improved self service.
I wrote a small Pandas utility to compare BOXI and PBI outputs by shape, columns, and totals. Finance signed off each report once the diff was empty.
from mylibrary import check_reports
import pandas as pd
pbi = pd.read_excel("pbi_report.xlsx", index=["customer_number","year","month"])
boxi = pd.read_excel("boxi_report.xlsx", index=["customer_number","year","month"])
result = check_reports(pbi, boxi)
result.summary() # shows shape, column, and total differences
BOXI schedules were static and hard to maintain. In Power BI I introduced dynamic per recipient schedules.
user_id, name, and email.The migration replaced a fragile BOXI estate with a governed, reusable reporting layer in Power BI.
By standardising the logic, validating results, and automating delivery, reporting moved from reactive maintenance to a predictable and auditable process that the business can trust.