Skip to content

Commit

Permalink
Merge pull request #90 from ElemarJR/Jan_1_bug_fixes
Browse files Browse the repository at this point in the history
Jan 1 bug fixes
  • Loading branch information
ElemarJR authored Jan 1, 2025
2 parents f334fd7 + 06fdb66 commit 8458989
Show file tree
Hide file tree
Showing 5 changed files with 91 additions and 30 deletions.
2 changes: 2 additions & 0 deletions backend/api/src/analytics/schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -1006,6 +1006,8 @@ type YearlyForecast {
year: Int!
goal: Float!
byMonth: [YearlyForecastByMonth!]!
workingDays: Int!
realizedWorkingDays: Int!
}

type YearlyForecastByMonth {
Expand Down
35 changes: 27 additions & 8 deletions backend/api/src/analytics/yearly_forecast.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,25 +14,27 @@ def resolve_yearly_forecast(_, info, year=None):
main_goal = 30000000

actual = 0
current_date = datetime.now()
current_year = current_date.year
current_month = current_date.month

by_month = []
revenue_tracking = None
for month in range(1, 13):
m = month - 1 if month > 1 else 12
y = year if month > 1 else year - 1
expected_consulting_fee = get_expected_regular_consulting_revenue(y, m)
expected_pre_contracted_revenue = get_expected_pre_contracted_revenue(y, m)

current_date = datetime.now()
current_year = current_date.year
current_month = current_date.month

revenue_tracking = None
discount = main_goal / (13 - month)
if (y == current_year and m == current_month):
revenue_tracking = compute_revenue_tracking(current_date)
elif y < current_year or (y == current_year and m < current_month):
discount = revenue_tracking["total"]
revenue_tracking = compute_revenue_tracking(get_last_day_of_month(datetime(y, m, 1)))
last_day_of_month = get_last_day_of_month(datetime(y, m, 1))
revenue_tracking = compute_revenue_tracking(last_day_of_month)
discount = revenue_tracking["total"]
else:
revenue_tracking = None

actual += revenue_tracking["total"] if revenue_tracking else 0

Expand All @@ -50,10 +52,27 @@ def resolve_yearly_forecast(_, info, year=None):
main_goal -= discount


total_working_days = sum(len(get_working_days_in_month(y, m)) for m in range(1, 13))

realized_working_days = 0
current_date = datetime.now()

for month in range(1, 13):
y = year if month > 1 else year - 1
m = month - 1 if month > 1 else 12

if y < current_date.year or (y == current_date.year and m < current_date.month):
realized_working_days += len(get_working_days_in_month(y, m))
elif y == current_date.year and m == current_date.month:
working_days = get_working_days_in_month(y, m)
realized_working_days += sum(1 for day in working_days if day.date() <= current_date.date())

return {
"year": year,
"goal": 30000000,
"by_month": by_month
"by_month": by_month,
"working_days": total_working_days,
"realized_working_days": realized_working_days
}


Expand Down
6 changes: 4 additions & 2 deletions backend/models/src/omni_models/analytics/forecast.py
Original file line number Diff line number Diff line change
Expand Up @@ -352,7 +352,8 @@ def filter_items(items):

def adjust_entity(entity):
if slug == 'consulting':
entity.projected = (entity.in_analysis / forecast_working_days.in_analysis_partial) * forecast_working_days.in_analysis
divisor = forecast_working_days.in_analysis_partial or 1
entity.projected = (entity.in_analysis / divisor) * forecast_working_days.in_analysis

previous_value = entity.one_month_ago
two_months_ago_value = entity.two_months_ago
Expand All @@ -368,7 +369,8 @@ def adjust_entity(entity):
entity.expected_historical = previous_value * 0.6 + two_months_ago_value * 0.25 + three_months_ago_value * 0.15

elif slug == 'consulting_pre':
entity.projected = (entity.in_analysis / forecast_working_days.in_analysis_partial) * forecast_working_days.in_analysis
divisor = forecast_working_days.in_analysis_partial or 1
entity.projected = (entity.in_analysis / divisor) * forecast_working_days.in_analysis


for client in by_client:
Expand Down
28 changes: 18 additions & 10 deletions backend/models/src/omni_models/analytics/revenue_tracking.py
Original file line number Diff line number Diff line change
Expand Up @@ -1273,6 +1273,7 @@ def build(consultant_name, pre_contracted, regular):
for sponsor in client["by_sponsor"]
for case in sponsor["by_case"]
for project in case["by_project"]
if "by_worker" in project
for worker in project["by_worker"]
if worker["name"] == consultant_name
)
Expand All @@ -1284,6 +1285,7 @@ def build(consultant_name, pre_contracted, regular):
for sponsor in client["by_sponsor"]
for case in sponsor["by_case"]
for project in case["by_project"]
if "by_worker" in project
for worker in project["by_worker"]
if worker["name"] == consultant_name
)
Expand All @@ -1295,8 +1297,9 @@ def build(consultant_name, pre_contracted, regular):
for sponsor in client["by_sponsor"]
for case in sponsor["by_case"]
for project in case["by_project"]
if "by_worker" in project and project["kind"] == "consulting"
for worker in project["by_worker"]
if worker["name"] == consultant_name and project["kind"] == "consulting"
if worker["name"] == consultant_name
)

consultant = globals.omni_models.workers.get_by_name(consultant_name)
Expand All @@ -1311,17 +1314,22 @@ def build(consultant_name, pre_contracted, regular):

@staticmethod
def build_list(pre_contracted, regular):
consultant_names = set()

# Collect consultant names safely checking for by_worker
for data in [regular, pre_contracted]:
for account_manager in data["monthly"]["by_account_manager"]:
for client in account_manager["by_client"]:
for sponsor in client["by_sponsor"]:
for case in sponsor["by_case"]:
for project in case["by_project"]:
if "by_worker" in project:
for worker in project["by_worker"]:
consultant_names.add(worker["name"])

return [
ConsultantSummary.build(consultant_name, pre_contracted, regular)
for consultant_name in set(
worker["name"]
for account_manager in regular["monthly"]["by_account_manager"]
for client in account_manager["by_client"]
for sponsor in client["by_sponsor"]
for case in sponsor["by_case"]
for project in case["by_project"]
for worker in project["by_worker"]
)
for consultant_name in consultant_names
]

def compute_summaries(pre_contracted, regular):
Expand Down
50 changes: 40 additions & 10 deletions frontend/src/app/financial/2025/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ const YEARLY_FORECAST_QUERY = gql`
yearlyForecast(year: $year) {
year
goal
workingDays
realizedWorkingDays
byMonth {
month
goal
Expand Down Expand Up @@ -64,7 +66,9 @@ interface ForecastTableProps {
}

const ForecastTable = ({ months, forecast }: ForecastTableProps) => {
const currentMonth = new Date().getMonth() + 1;
const today = new Date();
const currentMonth = today.getMonth() + 1;
const currentYear = today.getFullYear();

const totalGoal = months.reduce((sum, month) => sum + month.goal, 0);
const totalWorkingDays = months.reduce(
Expand Down Expand Up @@ -100,6 +104,13 @@ const ForecastTable = ({ months, forecast }: ForecastTableProps) => {
totalExpectedHandsOnFee +
totalExpectedSquadFee;

function isMonthInPast(month: number) {
if (month === 12) {
return currentYear > 2024 || (currentYear === 2024 && currentMonth >= 12);
}
return currentYear > 2025 || (currentYear === 2025 && currentMonth >= month);
}

return (
<Table>
<TableHeader className="bg-gray-50">
Expand Down Expand Up @@ -284,18 +295,17 @@ const ForecastTable = ({ months, forecast }: ForecastTableProps) => {
{months.map((month, idx) => (
<TableCellComponent
key={month.month}
value={month.month <= currentMonth ? month.actual : 0}
normalizedValue={month.month <= currentMonth ? month.actual : 0}
previousValue={idx > 0 ? (months[idx - 1].month <= currentMonth ? months[idx - 1].actual : 0) : undefined}
normalizedPreviousValue={idx > 0 ? (months[idx - 1].month <= currentMonth ? months[idx - 1].actual : 0) : undefined}
value={isMonthInPast(month.month) ? month.actual : 0}
normalizedValue={isMonthInPast(month.month) ? month.actual : 0}
previousValue={idx > 0 ? (isMonthInPast(months[idx - 1].month) ? months[idx - 1].actual : 0) : undefined}
normalizedPreviousValue={idx > 0 ? (isMonthInPast(months[idx - 1].month) ? months[idx - 1].actual : 0) : undefined}
normalized={false}
className={`border-x border-gray-400 ${month.month === currentMonth ? 'bg-blue-50' : ''}`}

/>
))}
<TableCellComponent
value={months.reduce((sum, month) => sum + (month.month <= currentMonth ? month.actual : 0), 0)}
normalizedValue={months.reduce((sum, month) => sum + (month.month <= currentMonth ? month.actual : 0), 0)}
value={months.reduce((sum, month) => sum + (isMonthInPast(month.month) ? month.actual : 0), 0)}
normalizedValue={months.reduce((sum, month) => sum + (isMonthInPast(month.month) ? month.actual : 0), 0)}
normalized={false}
className="border-x border-gray-400"
/>
Expand All @@ -314,8 +324,19 @@ export default function YearlyForecast2025() {
if (error) return <div>Error loading data: {error.message}</div>;

const forecast = data?.yearlyForecast;
const today = new Date();
const currentMonth = today.getMonth() + 1;
const currentYear = today.getFullYear();

function isMonthInPast(month: number) {
if (month === 12) {
return currentYear > 2024 || (currentYear === 2024 && currentMonth >= 12);
}
return currentYear > 2025 || (currentYear === 2025 && currentMonth >= month);
}

const totalActual = forecast.byMonth.reduce((sum: number, month: any) =>
sum + (month.month <= new Date().getMonth() + 1 ? month.actual : 0), 0
sum + (isMonthInPast(month.month) ? month.actual : 0), 0
);
const remaining = forecast.goal - totalActual;

Expand All @@ -328,7 +349,7 @@ export default function YearlyForecast2025() {
Yearly Forecast {forecast.year}
</h2>

<div className="grid grid-cols-3 gap-4 mb-8 text-center">
<div className="grid grid-cols-4 gap-4 mb-8 text-center">
<div className="p-4 bg-blue-50 rounded-lg">
<div className="text-lg text-gray-600">Annual Goal</div>
<div className="text-2xl font-bold text-blue-600">{formatCurrency(forecast.goal)}</div>
Expand All @@ -342,6 +363,15 @@ export default function YearlyForecast2025() {
</div>
</div>
</div>
<div className="p-4 bg-purple-50 rounded-lg">
<div className="text-lg text-gray-600">Working Days</div>
<div className="text-2xl font-bold text-purple-600">
{forecast.realizedWorkingDays}/{forecast.workingDays}
<div className="text-sm">
{((forecast.realizedWorkingDays / forecast.workingDays) * 100).toFixed(1)}%
</div>
</div>
</div>
<div className="p-4 bg-orange-50 rounded-lg">
<div className="text-lg text-gray-600">Remaining</div>
<div className="text-2xl font-bold text-orange-600">
Expand Down

0 comments on commit 8458989

Please sign in to comment.