feat(calculadora): complete cost-split coverage in all modes
CI / Build Native (push) Successful in 6m56s
CI / Build Native (push) Successful in 6m56s
- Ranking de rentabilidad: add 3 columns (C. Cristales / C. Ore / C. Fee) alongside the existing Costo column. Update rankingFiltrado() to include the 3 sub-costs in its map output. - Totales Presupuesto (3 sub-vistas: simple/maximizado/mezcla): add Distribución del costo block with bar + legend under existing 3 cards. - Totales Objetivo: extend objetivoTotales() to aggregate sub-costs, then add the same distribution block. - Totales Plan cristales: same distribution block (planTotalesCristales already aggregated sub-costs). - Add 3 generic helpers pctCOf/pctOOf/pctFOf that take a totals object (handles both 'costoTotal' and 'costo' field names). - Fix x-show condition for Presupuesto distribution: use .costo (the field actually returned by planTotalesPresupuesto) instead of .costoTotal. Verified in browser across all 5 distribution points; percentages sum to 100% and math matches per-row calculations.
This commit is contained in:
@@ -271,6 +271,9 @@
|
|||||||
<th class="cell-label text-right sortable" @click="rankingOrdenarPor('shotsObtenidos')">
|
<th class="cell-label text-right sortable" @click="rankingOrdenarPor('shotsObtenidos')">
|
||||||
Shots <span class="sort-icon" x-text="rankingSortIcon('shotsObtenidos')"></span>
|
Shots <span class="sort-icon" x-text="rankingSortIcon('shotsObtenidos')"></span>
|
||||||
</th>
|
</th>
|
||||||
|
<th class="cell-label text-right" title="Costo en cristales">C. Cristales</th>
|
||||||
|
<th class="cell-label text-right" title="Costo en ore">C. Ore</th>
|
||||||
|
<th class="cell-label text-right" title="Fee Dwarven">C. Fee</th>
|
||||||
<th class="cell-label text-right">Costo</th>
|
<th class="cell-label text-right">Costo</th>
|
||||||
<th class="cell-label text-right">Venta</th>
|
<th class="cell-label text-right">Venta</th>
|
||||||
<th class="cell-label text-center">vs Mercado</th>
|
<th class="cell-label text-center">vs Mercado</th>
|
||||||
@@ -294,6 +297,9 @@
|
|||||||
:class="r.margenPct > 0 ? 'text-success' : r.margenPct < 0 ? 'text-error' : ''"
|
:class="r.margenPct > 0 ? 'text-success' : r.margenPct < 0 ? 'text-error' : ''"
|
||||||
x-text="fmtPct(r.margenPct)"></td>
|
x-text="fmtPct(r.margenPct)"></td>
|
||||||
<td class="text-mono text-right" x-text="r.shotsObtenidos"></td>
|
<td class="text-mono text-right" x-text="r.shotsObtenidos"></td>
|
||||||
|
<td class="text-mono text-right" x-text="fmtAdena(r.costoCristales)"></td>
|
||||||
|
<td class="text-mono text-right" x-text="fmtAdena(r.costoOre)"></td>
|
||||||
|
<td class="text-mono text-right text-muted" x-text="fmtAdena(r.costoDwarven)"></td>
|
||||||
<td class="text-mono text-right" x-text="fmtAdena(r.costo)"></td>
|
<td class="text-mono text-right" x-text="fmtAdena(r.costo)"></td>
|
||||||
<td class="text-mono text-right" x-text="fmtAdena(r.venta)"></td>
|
<td class="text-mono text-right" x-text="fmtAdena(r.venta)"></td>
|
||||||
<td class="text-center">
|
<td class="text-center">
|
||||||
@@ -473,6 +479,19 @@
|
|||||||
<p class="text-lg text-mono text-bold" x-text="fmtAdena(Math.max(0, presupuestoAdena - planTotalesPresupuesto().costo))"></p>
|
<p class="text-lg text-mono text-bold" x-text="fmtAdena(Math.max(0, presupuestoAdena - planTotalesPresupuesto().costo))"></p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="mt-4" x-show="planTotalesPresupuesto().costo > 0">
|
||||||
|
<p class="text-xs text-muted mb-2">Distribución del costo</p>
|
||||||
|
<div class="cost-split-bar" style="width:100%;max-width:32rem">
|
||||||
|
<div class="cost-split-segment-cristales" :style="'width:' + pctCOf(planTotalesPresupuesto()) + '%'" :title="'Cristales: ' + fmtAdena(planTotalesPresupuesto().costoCristales) + ' (' + pctCOf(planTotalesPresupuesto()).toFixed(1) + '%)'"></div>
|
||||||
|
<div class="cost-split-segment-ore" :style="'width:' + pctOOf(planTotalesPresupuesto()) + '%'" :title="'Ore: ' + fmtAdena(planTotalesPresupuesto().costoOre) + ' (' + pctOOf(planTotalesPresupuesto()).toFixed(1) + '%)'"></div>
|
||||||
|
<div class="cost-split-segment-fee" :style="'width:' + pctFOf(planTotalesPresupuesto()) + '%'" :title="'Fee: ' + fmtAdena(planTotalesPresupuesto().costoDwarven) + ' (' + pctFOf(planTotalesPresupuesto()).toFixed(1) + '%)'"></div>
|
||||||
|
</div>
|
||||||
|
<div class="flex-row gap-3 mt-2" style="font-size:0.75rem;flex-wrap:wrap">
|
||||||
|
<span class="text-mono"><span class="cost-legend-dot cost-split-segment-cristales"></span> Cristales <span x-text="pctCOf(planTotalesPresupuesto()).toFixed(1) + '%'"></span></span>
|
||||||
|
<span class="text-mono"><span class="cost-legend-dot cost-split-segment-ore"></span> Ore <span x-text="pctOOf(planTotalesPresupuesto()).toFixed(1) + '%'"></span></span>
|
||||||
|
<span class="text-mono"><span class="cost-legend-dot cost-split-segment-fee"></span> Fee <span x-text="pctFOf(planTotalesPresupuesto()).toFixed(1) + '%'"></span></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template x-if="presupuestoMaximizar && !presupuestoMezclar && planMaximizado().length > 0">
|
<template x-if="presupuestoMaximizar && !presupuestoMezclar && planMaximizado().length > 0">
|
||||||
@@ -533,6 +552,19 @@
|
|||||||
<p class="text-lg text-mono text-bold" x-text="fmtAdena(Math.max(0, presupuestoAdena - planTotalesPresupuesto().costo))"></p>
|
<p class="text-lg text-mono text-bold" x-text="fmtAdena(Math.max(0, presupuestoAdena - planTotalesPresupuesto().costo))"></p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="mt-4" x-show="planTotalesPresupuesto().costo > 0">
|
||||||
|
<p class="text-xs text-muted mb-2">Distribución del costo</p>
|
||||||
|
<div class="cost-split-bar" style="width:100%;max-width:32rem">
|
||||||
|
<div class="cost-split-segment-cristales" :style="'width:' + pctCOf(planTotalesPresupuesto()) + '%'" :title="'Cristales: ' + fmtAdena(planTotalesPresupuesto().costoCristales) + ' (' + pctCOf(planTotalesPresupuesto()).toFixed(1) + '%)'"></div>
|
||||||
|
<div class="cost-split-segment-ore" :style="'width:' + pctOOf(planTotalesPresupuesto()) + '%'" :title="'Ore: ' + fmtAdena(planTotalesPresupuesto().costoOre) + ' (' + pctOOf(planTotalesPresupuesto()).toFixed(1) + '%)'"></div>
|
||||||
|
<div class="cost-split-segment-fee" :style="'width:' + pctFOf(planTotalesPresupuesto()) + '%'" :title="'Fee: ' + fmtAdena(planTotalesPresupuesto().costoDwarven) + ' (' + pctFOf(planTotalesPresupuesto()).toFixed(1) + '%)'"></div>
|
||||||
|
</div>
|
||||||
|
<div class="flex-row gap-3 mt-2" style="font-size:0.75rem;flex-wrap:wrap">
|
||||||
|
<span class="text-mono"><span class="cost-legend-dot cost-split-segment-cristales"></span> Cristales <span x-text="pctCOf(planTotalesPresupuesto()).toFixed(1) + '%'"></span></span>
|
||||||
|
<span class="text-mono"><span class="cost-legend-dot cost-split-segment-ore"></span> Ore <span x-text="pctOOf(planTotalesPresupuesto()).toFixed(1) + '%'"></span></span>
|
||||||
|
<span class="text-mono"><span class="cost-legend-dot cost-split-segment-fee"></span> Fee <span x-text="pctFOf(planTotalesPresupuesto()).toFixed(1) + '%'"></span></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template x-if="presupuestoMezclar && planMezcla().length > 0">
|
<template x-if="presupuestoMezclar && planMezcla().length > 0">
|
||||||
@@ -595,6 +627,19 @@
|
|||||||
<p class="text-lg text-mono text-bold" x-text="fmtAdena(Math.max(0, presupuestoAdena - planTotalesPresupuesto().costo))"></p>
|
<p class="text-lg text-mono text-bold" x-text="fmtAdena(Math.max(0, presupuestoAdena - planTotalesPresupuesto().costo))"></p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="mt-4" x-show="planTotalesPresupuesto().costo > 0">
|
||||||
|
<p class="text-xs text-muted mb-2">Distribución del costo</p>
|
||||||
|
<div class="cost-split-bar" style="width:100%;max-width:32rem">
|
||||||
|
<div class="cost-split-segment-cristales" :style="'width:' + pctCOf(planTotalesPresupuesto()) + '%'" :title="'Cristales: ' + fmtAdena(planTotalesPresupuesto().costoCristales) + ' (' + pctCOf(planTotalesPresupuesto()).toFixed(1) + '%)'"></div>
|
||||||
|
<div class="cost-split-segment-ore" :style="'width:' + pctOOf(planTotalesPresupuesto()) + '%'" :title="'Ore: ' + fmtAdena(planTotalesPresupuesto().costoOre) + ' (' + pctOOf(planTotalesPresupuesto()).toFixed(1) + '%)'"></div>
|
||||||
|
<div class="cost-split-segment-fee" :style="'width:' + pctFOf(planTotalesPresupuesto()) + '%'" :title="'Fee: ' + fmtAdena(planTotalesPresupuesto().costoDwarven) + ' (' + pctFOf(planTotalesPresupuesto()).toFixed(1) + '%)'"></div>
|
||||||
|
</div>
|
||||||
|
<div class="flex-row gap-3 mt-2" style="font-size:0.75rem;flex-wrap:wrap">
|
||||||
|
<span class="text-mono"><span class="cost-legend-dot cost-split-segment-cristales"></span> Cristales <span x-text="pctCOf(planTotalesPresupuesto()).toFixed(1) + '%'"></span></span>
|
||||||
|
<span class="text-mono"><span class="cost-legend-dot cost-split-segment-ore"></span> Ore <span x-text="pctOOf(planTotalesPresupuesto()).toFixed(1) + '%'"></span></span>
|
||||||
|
<span class="text-mono"><span class="cost-legend-dot cost-split-segment-fee"></span> Fee <span x-text="pctFOf(planTotalesPresupuesto()).toFixed(1) + '%'"></span></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template x-if="presupuestoAdena > 0 && planActual().length === 0">
|
<template x-if="presupuestoAdena > 0 && planActual().length === 0">
|
||||||
@@ -724,6 +769,19 @@
|
|||||||
<p class="text-lg text-mono text-bold" x-text="objetivoTotales().shots"></p>
|
<p class="text-lg text-mono text-bold" x-text="objetivoTotales().shots"></p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="mt-4" x-show="objetivoTotales().costo > 0">
|
||||||
|
<p class="text-xs text-muted mb-2">Distribución del costo</p>
|
||||||
|
<div class="cost-split-bar" style="width:100%;max-width:32rem">
|
||||||
|
<div class="cost-split-segment-cristales" :style="'width:' + pctCOf(objetivoTotales()) + '%'" :title="'Cristales: ' + fmtAdena(objetivoTotales().costoCristales) + ' (' + pctCOf(objetivoTotales()).toFixed(1) + '%)'"></div>
|
||||||
|
<div class="cost-split-segment-ore" :style="'width:' + pctOOf(objetivoTotales()) + '%'" :title="'Ore: ' + fmtAdena(objetivoTotales().costoOre) + ' (' + pctOOf(objetivoTotales()).toFixed(1) + '%)'"></div>
|
||||||
|
<div class="cost-split-segment-fee" :style="'width:' + pctFOf(objetivoTotales()) + '%'" :title="'Fee: ' + fmtAdena(objetivoTotales().costoDwarven) + ' (' + pctFOf(objetivoTotales()).toFixed(1) + '%)'"></div>
|
||||||
|
</div>
|
||||||
|
<div class="flex-row gap-3 mt-2" style="font-size:0.75rem;flex-wrap:wrap">
|
||||||
|
<span class="text-mono"><span class="cost-legend-dot cost-split-segment-cristales"></span> Cristales <span x-text="pctCOf(objetivoTotales()).toFixed(1) + '%'"></span></span>
|
||||||
|
<span class="text-mono"><span class="cost-legend-dot cost-split-segment-ore"></span> Ore <span x-text="pctOOf(objetivoTotales()).toFixed(1) + '%'"></span></span>
|
||||||
|
<span class="text-mono"><span class="cost-legend-dot cost-split-segment-fee"></span> Fee <span x-text="pctFOf(objetivoTotales()).toFixed(1) + '%'"></span></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</article>
|
</article>
|
||||||
|
|
||||||
@@ -836,6 +894,19 @@
|
|||||||
x-text="fmtAdena(planTotalesCristales().ganancia)"></p>
|
x-text="fmtAdena(planTotalesCristales().ganancia)"></p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="mt-4" x-show="planTotalesCristales().costo > 0">
|
||||||
|
<p class="text-xs text-muted mb-2">Distribución del costo</p>
|
||||||
|
<div class="cost-split-bar" style="width:100%;max-width:32rem">
|
||||||
|
<div class="cost-split-segment-cristales" :style="'width:' + pctCOf(planTotalesCristales()) + '%'" :title="'Cristales: ' + fmtAdena(planTotalesCristales().costoCristales) + ' (' + pctCOf(planTotalesCristales()).toFixed(1) + '%)'"></div>
|
||||||
|
<div class="cost-split-segment-ore" :style="'width:' + pctOOf(planTotalesCristales()) + '%'" :title="'Ore: ' + fmtAdena(planTotalesCristales().costoOre) + ' (' + pctOOf(planTotalesCristales()).toFixed(1) + '%)'"></div>
|
||||||
|
<div class="cost-split-segment-fee" :style="'width:' + pctFOf(planTotalesCristales()) + '%'" :title="'Fee: ' + fmtAdena(planTotalesCristales().costoDwarven) + ' (' + pctFOf(planTotalesCristales()).toFixed(1) + '%)'"></div>
|
||||||
|
</div>
|
||||||
|
<div class="flex-row gap-3 mt-2" style="font-size:0.75rem;flex-wrap:wrap">
|
||||||
|
<span class="text-mono"><span class="cost-legend-dot cost-split-segment-cristales"></span> Cristales <span x-text="pctCOf(planTotalesCristales()).toFixed(1) + '%'"></span></span>
|
||||||
|
<span class="text-mono"><span class="cost-legend-dot cost-split-segment-ore"></span> Ore <span x-text="pctOOf(planTotalesCristales()).toFixed(1) + '%'"></span></span>
|
||||||
|
<span class="text-mono"><span class="cost-legend-dot cost-split-segment-fee"></span> Fee <span x-text="pctFOf(planTotalesCristales()).toFixed(1) + '%'"></span></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -972,6 +1043,21 @@
|
|||||||
return t.costoTotal > 0 ? (t.costoDwarven * 100.0) / t.costoTotal : 0;
|
return t.costoTotal > 0 ? (t.costoDwarven * 100.0) / t.costoTotal : 0;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
pctCOf(t) {
|
||||||
|
const total = t.costoTotal != null ? t.costoTotal : t.costo;
|
||||||
|
return total > 0 ? (t.costoCristales * 100.0) / total : 0;
|
||||||
|
},
|
||||||
|
|
||||||
|
pctOOf(t) {
|
||||||
|
const total = t.costoTotal != null ? t.costoTotal : t.costo;
|
||||||
|
return total > 0 ? (t.costoOre * 100.0) / total : 0;
|
||||||
|
},
|
||||||
|
|
||||||
|
pctFOf(t) {
|
||||||
|
const total = t.costoTotal != null ? t.costoTotal : t.costo;
|
||||||
|
return total > 0 ? (t.costoDwarven * 100.0) / total : 0;
|
||||||
|
},
|
||||||
|
|
||||||
canSave() {
|
canSave() {
|
||||||
return this.totalesGlobal().cristalesUsados > 0 && this.saveStatus !== 'saving';
|
return this.totalesGlobal().cristalesUsados > 0 && this.saveStatus !== 'saving';
|
||||||
},
|
},
|
||||||
@@ -1016,6 +1102,9 @@
|
|||||||
margenPct,
|
margenPct,
|
||||||
shotsObtenidos: c.shotsObtenidos,
|
shotsObtenidos: c.shotsObtenidos,
|
||||||
costo: c.costoTotal,
|
costo: c.costoTotal,
|
||||||
|
costoCristales: c.costoCristales,
|
||||||
|
costoOre: c.costoOre,
|
||||||
|
costoDwarven: c.costoDwarven,
|
||||||
venta: c.valorVenta,
|
venta: c.valorVenta,
|
||||||
precioMercado,
|
precioMercado,
|
||||||
costoPorShot,
|
costoPorShot,
|
||||||
@@ -1141,16 +1230,20 @@
|
|||||||
|
|
||||||
objetivoTotales() {
|
objetivoTotales() {
|
||||||
const s = this.store().state;
|
const s = this.store().state;
|
||||||
if (!s) return { costo: 0, venta: 0, ganancia: 0, shots: 0 };
|
if (!s) return { costo: 0, venta: 0, ganancia: 0, shots: 0, costoCristales: 0, costoOre: 0, costoDwarven: 0 };
|
||||||
let costo = 0, venta = 0, shots = 0;
|
let costo = 0, venta = 0, shots = 0;
|
||||||
|
let costoCristales = 0, costoOre = 0, costoDwarven = 0;
|
||||||
for (const f of s.formulas) {
|
for (const f of s.formulas) {
|
||||||
const s2 = this.objetivoShots[f.id] || 0;
|
const s2 = this.objetivoShots[f.id] || 0;
|
||||||
if (s2 <= 0) continue;
|
if (s2 <= 0) continue;
|
||||||
shots += s2;
|
shots += s2;
|
||||||
costo += this.objetivoCosto(f);
|
costo += this.objetivoCosto(f);
|
||||||
venta += this.objetivoVenta(f);
|
venta += this.objetivoVenta(f);
|
||||||
|
costoCristales += this.objetivoCostoCristales(f);
|
||||||
|
costoOre += this.objetivoCostoOre(f);
|
||||||
|
costoDwarven += this.objetivoCostoDwarven(f);
|
||||||
}
|
}
|
||||||
return { costo, venta, ganancia: venta - costo, shots };
|
return { costo, venta, ganancia: venta - costo, shots, costoCristales, costoOre, costoDwarven };
|
||||||
},
|
},
|
||||||
|
|
||||||
planPorPresupuesto() {
|
planPorPresupuesto() {
|
||||||
|
|||||||
Reference in New Issue
Block a user