Skip to content

fix: Project screen budget data and decimal precision#166

Open
EdiWeeks wants to merge 1 commit intomainfrom
fix/157-151-project-planned-hours-and-decimals
Open

fix: Project screen budget data and decimal precision#166
EdiWeeks wants to merge 1 commit intomainfrom
fix/157-151-project-planned-hours-and-decimals

Conversation

@EdiWeeks
Copy link
Contributor

@EdiWeeks EdiWeeks commented Mar 4, 2026

Summary

  • Time Budgeted and Budget Cost now use Billable Job Planning Lines (contracted budget) instead of Budget lines, which are weekly allocations from the Plan screen
  • PlanResourceModal UOM conversion fix: hours are now properly converted to/from the resource's base unit (DAY/HOUR) when reading and writing planning lines
  • Chart: extended window to show future planned hours up to 12 weeks ahead; dynamic Y-axis labels for Spend vs Budget chart
  • Decimal precision: two decimal places for all time values on the Project screen (except tasks table)
image image

Issues Fixed

Files Changed

  • src/components/plan/PlanResourceModal.tsx — UOM conversion on read/write
  • src/components/projects/ProjectCharts.tsx — future weeks, Y-axis scaling, decimals
  • src/components/projects/ProjectKPICards.tsx — two decimals, tooltip update
  • src/services/bc/projectDetailsService.ts — Billable lines for budget data
  • src/services/bc/projectService.ts — Billable lines for project list budget

Test plan

  • Verify Time Budgeted shows correct contracted hours from Billable lines
  • Verify Budget Cost shows correct amount (unitPrice × quantity)
  • Verify adding planned hours via Plan screen does not affect Time Budgeted
  • Verify planned hours show in chart for future weeks
  • Verify Spend vs Budget chart Y-axis scales correctly for large budgets
  • Verify two decimal places on KPI cards and chart tooltips

🤖 Generated with Claude Code

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR updates how the Project screen derives budgeted time/cost data from Business Central (switching to contracted “Billable” planning lines), fixes UOM (DAY/HOUR) conversions when editing planning lines, and improves Project charts/precision for time display.

Changes:

  • Use Billable Job Planning Lines for “Time Budgeted” and budget-related totals instead of weekly Plan/Budget allocations.
  • Fix PlanResourceModal read/write to convert quantities correctly between resource base units (DAY/HOUR) and hours.
  • Extend charts to include up to 12 future weeks of planned hours and standardize time values to 2 decimal places in tooltips/labels.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
src/services/bc/projectService.ts Updates project-list budget hours aggregation to filter Billable/Both line types.
src/services/bc/projectDetailsService.ts Updates project analytics to compute planned hours and budget totals from Billable lines.
src/components/projects/ProjectKPICards.tsx Displays time values with 2-decimal precision; updates “Time Budgeted” tooltip copy.
src/components/projects/ProjectCharts.tsx Extends weekly window into the future; improves y-axis scaling; uses 2-decimal precision in tooltips.
src/components/plan/PlanResourceModal.tsx Adds UOM conversion map usage and converts quantities to/from hours when reading/writing planning lines.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +460 to 470
// Billable lines represent the contracted project budget
// Budget lines are weekly allocations from the Plan screen and are not included in budget/price totals
const billableLines = planningLines.filter((line: BCJobPlanningLine) =>
isBillableLine(line.lineType)
);
budgetCost = budgetLines.reduce(
(sum: number, line: BCJobPlanningLine) => sum + line.totalCost,

// Budget Cost: sum totalPrice from Billable lines (contracted budget) with breakdown by type
budgetCost = billableLines.reduce(
(sum: number, line: BCJobPlanningLine) => sum + line.totalPrice,
0
);
Copy link

Copilot AI Mar 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

budgetCost is now computed from billableLines by summing line.totalPrice. A few lines later, billablePrice is also computed from the same billableLines by summing line.totalPrice, which makes budgetCost and billablePrice identical and breaks the intended cost-vs-price distinction (and the existing UI/tooltips that say Budget Cost comes from totalCost). If Budget Cost is meant to remain an internal cost budget, this should sum totalCost (and breakdowns should use totalCost) while billablePrice continues to use totalPrice.

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Intentional. The user confirmed Budget Cost should show the contracted amount (totalPrice from Billable lines), not internal cost. For their workflow the project budget IS the contracted price.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wasn't sure about this one ... needs further confirmation / investigation I think.

.then((resourceUOMs) => {
setUomMap(buildUOMConversionMap(resourceUOMs));
})
.catch(() => {
Copy link

Copilot AI Mar 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If getResourceUnitsOfMeasure() fails, the code silently falls back to an empty UOM map. In that case convertToHours/convertFromHours will behave like identity functions, potentially reintroducing the DAY/HOUR UOM bug and writing incorrect quantities back to BC. Consider surfacing an error (e.g., toast) and preventing save until UOM data is available (or retrying fetch on submit).

Suggested change
.catch(() => {
.catch((error) => {
console.error('Failed to load resource units of measure', error);
toast.error('Failed to load resource units of measure');

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Low risk since empty map means identity conversion (hours pass through as-is, which is correct for HOUR-based resources). Will consider adding error surfacing in a follow-up.

- Use Billable lines for Time Budgeted and Budget Cost instead of Budget lines
- Fix PlanResourceModal UOM conversion on read/write paths
- Extend chart window to show future planned hours
- Use two decimal places for time values
- Dynamic Y-axis labels for Spend vs Budget chart

Fixes #151, #157, #158, #159, #160, #161, #162, #165

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@EdiWeeks EdiWeeks force-pushed the fix/157-151-project-planned-hours-and-decimals branch from 5c0f044 to c4141b7 Compare March 4, 2026 18:33
EdiWeeks

This comment was marked as duplicate.

@EdiWeeks
Copy link
Contributor Author

EdiWeeks commented Mar 4, 2026

Tested, addressed Copilot comments, check please @BenGWeeks

@EdiWeeks EdiWeeks requested review from akash2017sky and removed request for akash2017sky March 4, 2026 18:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment