Fix repartition rounding

This commit is contained in:
wpetit 2020-04-23 14:00:56 +02:00
parent 9dd9167267
commit 40759f59d6
3 changed files with 27 additions and 18 deletions

View File

@ -11,13 +11,18 @@ export interface EstimationRangeProps {
export const EstimationRange: FunctionalComponent<EstimationRangeProps> = ({ project, estimation }) => { export const EstimationRange: FunctionalComponent<EstimationRangeProps> = ({ project, estimation }) => {
const roundUp = getRoundUpEstimations(project); const roundUp = getRoundUpEstimations(project);
let e: number|string = roundUp ? Math.ceil(estimation.e) : estimation.e; let e: number|string = estimation.e;
let sd: number|string = roundUp ? Math.ceil(estimation.sd) : estimation.sd; let sd: number|string = estimation.sd;
const max = e+sd; let max = e+sd;
const min = Math.max(e-sd, 0); let min = Math.max(e-sd, 0);
if (!roundUp) { if (roundUp) {
sd = sd.toFixed(2); sd = Math.ceil(sd);
e = e.toFixed(2); e = Math.ceil(e);
max = Math.ceil(max);
min = Math.ceil(min);
} else {
sd = sd.toFixed(2);
e = e.toFixed(2);
} }
return ( return (
<Fragment> <Fragment>

View File

@ -1,7 +1,7 @@
import { FunctionalComponent, h } from "preact"; import { FunctionalComponent, h } from "preact";
import { Project } from "../../models/project"; import { Project } from "../../models/project";
import { useProjectEstimations } from "../../hooks/use-project-estimations"; import { useProjectEstimations } from "../../hooks/use-project-estimations";
import { getCurrency, defaults, getTaskCategoryCost } from "../../models/params"; import { getCurrency, defaults, getTaskCategoryCost, getRoundUpEstimations } from "../../models/params";
import { getMinMaxCosts, Cost } from "../../util/stat"; import { getMinMaxCosts, Cost } from "../../util/stat";
import * as style from './style.module.css'; import * as style from './style.module.css';
import ProjectTimeUnit from "../../components/project-time-unit"; import ProjectTimeUnit from "../../components/project-time-unit";
@ -13,7 +13,7 @@ export interface FinancialPreviewProps {
const FinancialPreview: FunctionalComponent<FinancialPreviewProps> = ({ project }) => { const FinancialPreview: FunctionalComponent<FinancialPreviewProps> = ({ project }) => {
const estimations = useProjectEstimations(project); const estimations = useProjectEstimations(project);
const costs = getMinMaxCosts(project, estimations.p99); const costs = getMinMaxCosts(project, estimations.p99);
const roundUp = getRoundUpEstimations(project);
return ( return (
<div class="table-container"> <div class="table-container">
<table class="table is-bordered is-striped is-fullwidth"> <table class="table is-bordered is-striped is-fullwidth">
@ -33,13 +33,13 @@ const FinancialPreview: FunctionalComponent<FinancialPreviewProps> = ({ project
<tr> <tr>
<td class="is-narrow">Maximum</td> <td class="is-narrow">Maximum</td>
<td> <td>
<CostDetails project={project} cost={costs.max} /> <CostDetails project={project} cost={costs.max} roundUp={roundUp} />
</td> </td>
</tr> </tr>
<tr> <tr>
<td class="is-narrow">Minimum</td> <td class="is-narrow">Minimum</td>
<td> <td>
<CostDetails project={project} cost={costs.min} /> <CostDetails project={project} cost={costs.min} roundUp={roundUp} />
</td> </td>
</tr> </tr>
</tbody> </tbody>
@ -51,14 +51,15 @@ const FinancialPreview: FunctionalComponent<FinancialPreviewProps> = ({ project
export interface CostDetailsProps { export interface CostDetailsProps {
project: Project project: Project
cost: Cost cost: Cost
roundUp: boolean
} }
export const CostDetails:FunctionalComponent<CostDetailsProps> = ({ project, cost }) => { export const CostDetails:FunctionalComponent<CostDetailsProps> = ({ project, cost, roundUp }) => {
return ( return (
<details> <details>
<summary><strong> <summary><strong>
{cost.totalCost} {getCurrency(project)}</strong> {cost.totalCost} {getCurrency(project)}</strong>
<span class="is-pulled-right">{cost.totalTime} <ProjectTimeUnit project={project} /></span> <span class="is-pulled-right">{ roundUp ? Math.ceil(cost.totalTime) : cost.totalTime.toFixed(2) } <ProjectTimeUnit project={project} /></span>
</summary> </summary>
<table class={`table is-fullwidth`}> <table class={`table is-fullwidth`}>
<tbody> <tbody>
@ -70,7 +71,7 @@ export const CostDetails:FunctionalComponent<CostDetailsProps> = ({ project, cos
<tr key={`task-category-cost-${taskCategory.id}`}> <tr key={`task-category-cost-${taskCategory.id}`}>
<td class={`${style.noBorder} is-size-6`}>{taskCategory.label}</td> <td class={`${style.noBorder} is-size-6`}>{taskCategory.label}</td>
<td class={`${style.noBorder} is-size-6`}>{details.cost} {getCurrency(project)}</td> <td class={`${style.noBorder} is-size-6`}>{details.cost} {getCurrency(project)}</td>
<td class={`${style.noBorder} is-size-6`}>{details.time} <ProjectTimeUnit project={project} /> × {getTaskCategoryCost(taskCategory)} {getCurrency(project)}</td> <td class={`${style.noBorder} is-size-6`}>{ roundUp ? Math.ceil(details.time) : details.time.toFixed(2) } <ProjectTimeUnit project={project} /> × {getTaskCategoryCost(taskCategory)} {getCurrency(project)}</td>
</tr> </tr>
) )
}) })

View File

@ -39,11 +39,14 @@ export interface MeanRepartition {
export function getTaskCategoriesMeanRepartition(project: Project): MeanRepartition { export function getTaskCategoriesMeanRepartition(project: Project): MeanRepartition {
let projectMean = getProjectWeightedMean(project); let projectMean = getProjectWeightedMean(project);
const repartition: MeanRepartition = {}; const repartition: MeanRepartition = {};
Object.values(project.params.taskCategories).forEach(tc => { Object.values(project.params.taskCategories).forEach(tc => {
repartition[tc.id] = getTaskCategoryWeightedMean(tc.id, project) / projectMean; repartition[tc.id] = getTaskCategoryWeightedMean(tc.id, project) / projectMean;
if (Number.isNaN(repartition[tc.id])) repartition[tc.id] = 0; if (Number.isNaN(repartition[tc.id])) repartition[tc.id] = 0;
}); });
return repartition; return repartition;
} }
@ -67,18 +70,18 @@ export function getMinMaxCosts(project: Project, estimation: Estimation): MinMax
Object.values(project.params.taskCategories).forEach(tc => { Object.values(project.params.taskCategories).forEach(tc => {
const cost = getTaskCategoryCost(tc); const cost = getTaskCategoryCost(tc);
const maxTime = Math.ceil((estimation.e + estimation.sd) * repartition[tc.id]); const maxTime = Math.round((estimation.e + estimation.sd) * repartition[tc.id]);
max.details[tc.id] = { max.details[tc.id] = {
time: maxTime, time: maxTime,
cost: maxTime * cost, cost: Math.ceil(maxTime) * cost,
}; };
max.totalTime += max.details[tc.id].time; max.totalTime += max.details[tc.id].time;
max.totalCost += max.details[tc.id].cost; max.totalCost += max.details[tc.id].cost;
const minTime = Math.ceil((estimation.e - estimation.sd) * repartition[tc.id]); const minTime = Math.round((estimation.e - estimation.sd) * repartition[tc.id]);
min.details[tc.id] = { min.details[tc.id] = {
time: minTime, time: minTime,
cost: minTime * cost, cost: Math.ceil(minTime) * cost,
}; };
min.totalTime += min.details[tc.id].time; min.totalTime += min.details[tc.id].time;
min.totalCost += min.details[tc.id].cost; min.totalCost += min.details[tc.id].cost;