Documentation Index
Fetch the complete documentation index at: https://mintlify.com/daecheverri9801/core-projects/llms.txt
Use this file to discover all available pages before exploring further.
Overview
Core Projects provides comprehensive analytics through the Gerencia (Management) dashboard, offering real-time insights into sales performance, inventory status, advisor metrics, and financial projections.
Management Dashboard
The Gerencia dashboard is the central hub for business intelligence:
class GerenciaDashboardWebController extends Controller
{
public function index(Request $request, GerenciaEstadisticasService $service)
{
$filtros = [
'desde' => $request->query('desde'),
'hasta' => $request->query('hasta'),
'proyecto_id' => $request->query('proyecto_id'),
'asesor_id' => $request->query('asesor_id'),
'estado_inmueble' => $request->query('estado_inmueble'),
];
$dashboard = $service->obtenerDashboard($filtros);
$planPagosCI = $service->planPagosCI($filtros, $desde, $hasta);
return Inertia::render('Gerencia/Dashboard/Index', array_merge($dashboard, [
'proyectos' => Proyecto::orderBy('nombre')->get(),
'empleados' => Empleado::comerciales()->get(),
'estadosInmueble' => EstadoInmueble::orderBy('nombre')->get(),
'filtros' => $filtros,
'planPagosCI' => $planPagosCI,
]));
}
}
Dashboard Components
The dashboard provides multiple analytical views:
1. Global Summary
Key Performance Indicators
Real-time metrics:
- Total sales value (period)
- Units sold
- Available inventory
- Average sale price
- Sales velocity
public function resumenGlobal(array $filtros, Carbon $desde, Carbon $hasta): array
{
$ventasQuery = Venta::query()
->where('tipo_operacion', 'venta')
->whereBetween('fecha_venta', [$desde, $hasta]);
if (!empty($filtros['proyecto_id'])) {
$ventasQuery->where('id_proyecto', $filtros['proyecto_id']);
}
if (!empty($filtros['asesor_id'])) {
$ventasQuery->where('id_empleado', $filtros['asesor_id']);
}
$ventasTotales = (float) $ventasQuery->sum('valor_total');
$unidadesVendidas = (int) $ventasQuery->count();
$estadoDisponibleId = EstadoInmueble::whereRaw('LOWER(nombre) = ?', ['disponible'])
->value('id_estado_inmueble');
$inventarioAptos = Apartamento::where('id_estado_inmueble', $estadoDisponibleId)->count();
$inventarioLocs = Local::where('id_estado_inmueble', $estadoDisponibleId)->count();
return [
'ventas_totales' => $ventasTotales,
'unidades_vendidas' => $unidadesVendidas,
'inventario_disponible' => $inventarioAptos + $inventarioLocs,
];
}
2. Sales by Project
Project Performance
Comparison across projects:
- Total sales value per project
- Units sold per project
- Average price per project
- Sell-through rate
public function ventasPorProyecto(array $filtros, Carbon $desde, Carbon $hasta): array
{
$rows = Venta::query()
->select(
'id_proyecto',
DB::raw('SUM(valor_total) as total_valor'),
DB::raw('COUNT(*) as unidades')
)
->where('tipo_operacion', 'venta')
->whereBetween('fecha_venta', [$desde, $hasta])
->groupBy('id_proyecto')
->get();
$proyectos = Proyecto::whereIn('id_proyecto', $rows->pluck('id_proyecto'))
->get()
->keyBy('id_proyecto');
return $rows->map(function ($r) use ($proyectos) {
$proyecto = $proyectos[$r->id_proyecto] ?? null;
return [
'id_proyecto' => $r->id_proyecto,
'nombre' => $proyecto->nombre ?? 'Desconocido',
'total_valor' => (float) $r->total_valor,
'unidades' => (int) $r->unidades,
];
})->values()->all();
}
3. Projection vs. Reality
Goal Tracking
Compare goals against actual performance:
- Monthly sales goals (units & value)
- Actual sales achieved
- Variance (over/under performance)
- Progress percentage
public function proyeccionVsRealMensual(array $filtros, Carbon $desde, Carbon $hasta): array
{
$ano = (int) $desde->year;
$mes = (int) $desde->month;
// Get goals from Meta model
$metas = Meta::where('ano', $ano)
->where('mes', $mes)
->when(!empty($filtros['proyecto_id']),
fn($q) => $q->where('id_proyecto', $filtros['proyecto_id'])
)
->get()
->keyBy('id_proyecto');
// Get actual sales
$ventas = Venta::query()
->select(
'id_proyecto',
DB::raw('COUNT(*) as real_unidades'),
DB::raw('SUM(valor_total) as real_valor')
)
->where('tipo_operacion', 'venta')
->whereYear('fecha_venta', $ano)
->whereMonth('fecha_venta', $mes)
->groupBy('id_proyecto')
->get();
// Combine and compare
$resultado = [];
foreach ($proyectos as $proyecto) {
$meta = $metas[$proyecto->id_proyecto] ?? null;
$venta = $ventas->firstWhere('id_proyecto', $proyecto->id_proyecto);
$resultado[] = [
'proyecto' => $proyecto->nombre,
'meta_unidades' => (int) ($meta->meta_unidades ?? 0),
'meta_valor' => (float) ($meta->meta_valor ?? 0),
'real_unidades' => (int) ($venta->real_unidades ?? 0),
'real_valor' => (float) ($venta->real_valor ?? 0),
];
}
return $resultado;
}
4. Sales Velocity
Time-to-Sale Metrics
Analyze sales speed:
- Average days from project start to sale
- Days to sell by project
- Velocity trends over time
- Fastest/slowest moving units
public function velocidadVentasPorProyecto(array $filtros, Carbon $desde, Carbon $hasta): array
{
$ventasGrouped = Venta::where('tipo_operacion', 'venta')
->whereBetween('fecha_venta', [$desde, $hasta])
->get()
->groupBy('id_proyecto');
$resultado = [];
foreach ($ventasGrouped as $idProyecto => $ventasProyecto) {
$proyecto = Proyecto::find($idProyecto);
if (!$proyecto || !$proyecto->fecha_inicio) continue;
$inicio = Carbon::parse($proyecto->fecha_inicio);
$dias = $ventasProyecto->map(function ($v) use ($inicio) {
if (!$v->fecha_venta) return null;
return $inicio->diffInDays(Carbon::parse($v->fecha_venta));
})->filter(fn($x) => $x !== null);
$promedio = $dias->count() ? round($dias->avg(), 1) : null;
$resultado[] = [
'proyecto' => $proyecto->nombre,
'dias_promedio_venta' => $promedio,
];
}
return $resultado;
}
5. Separation Effectiveness
Conversion Metrics
Track separation outcomes:
- Total separations by advisor
- Separations converted to sales
- Expired/cancelled separations
- Conversion rate
public function separacionesYEfectividad(array $filtros, Carbon $desde, Carbon $hasta): array
{
$separaciones = Venta::where('tipo_operacion', 'separacion')
->whereBetween('fecha_venta', [$desde, $hasta])
->get()
->groupBy('id_empleado');
$hoy = Carbon::today();
$resultado = [];
foreach ($separaciones as $idEmpleado => $seps) {
$empleado = Empleado::find($idEmpleado);
$total = $seps->count();
$caducadas = $seps->filter(function ($v) use ($hoy) {
return $v->fecha_limite_separacion
? Carbon::parse($v->fecha_limite_separacion)->lt($hoy)
: false;
})->count();
$ejecutadas = $total - $caducadas;
$resultado[] = [
'empleado' => $empleado->nombre . ' ' . $empleado->apellido,
'total_separaciones' => $total,
'separaciones_caducadas' => $caducadas,
'separaciones_ejecutadas' => $ejecutadas,
'tasa_efectividad' => $total > 0 ? round(($ejecutadas / $total) * 100, 1) : 0,
];
}
return $resultado;
}
6. Inventory by Project
Detailed Inventory
Unit-level inventory tracking:
- All units by project and tower
- Current state (available, sold, separated)
- Base and current prices
- Assigned advisor (if sold)
- Sale date
public function inventarioPorProyecto(array $filtros): array
{
$proyectos = Proyecto::with([
'torres.apartamentos.estadoInmueble',
'torres.apartamentos.tipoApartamento',
'torres.apartamentos.ventas',
'torres.locales.estadoInmueble',
'torres.locales.ventas',
])->get();
$result = [];
foreach ($proyectos as $proyecto) {
$items = [];
foreach ($proyecto->torres as $torre) {
foreach ($torre->apartamentos as $apto) {
$ultimaVenta = $apto->ventas->first();
$asesor = $ultimaVenta?->empleado;
$items[] = [
'tipo' => 'Apartamento',
'etiqueta' => 'Apto ' . $apto->numero,
'precio_base' => (float)($apto->tipoApartamento->valor_estimado ?? 0),
'precio_vigente' => (float)($apto->valor_final ?? $apto->valor_total ?? 0),
'estado' => $apto->estadoInmueble->nombre,
'asesor' => $asesor ? ($asesor->nombre . ' ' . $asesor->apellido) : null,
'fecha_operacion' => $ultimaVenta?->fecha_venta,
];
}
}
$result[] = [
'proyecto' => $proyecto->nombre,
'inmuebles' => $items,
];
}
return $result;
}
7. Sales by Advisor and Project
Advisor Performance
Individual advisor metrics:
- Sales per advisor
- Separations created
- Conversion rates
- Performance by project
public function ventasPorAsesorProyecto(array $filtros, Carbon $desde, Carbon $hasta): array
{
$rows = Venta::query()
->select(
'id_proyecto',
'id_empleado',
DB::raw("SUM(CASE WHEN tipo_operacion = 'venta' THEN 1 ELSE 0 END) as ventas"),
DB::raw("SUM(CASE WHEN tipo_operacion = 'separacion' THEN 1 ELSE 0 END) as separaciones"),
DB::raw("SUM(CASE WHEN tipo_operacion = 'separacion'
AND fecha_limite_separacion >= CURRENT_DATE THEN 1 ELSE 0 END) as separaciones_ejecutadas"),
DB::raw("SUM(CASE WHEN tipo_operacion = 'separacion'
AND fecha_limite_separacion < CURRENT_DATE THEN 1 ELSE 0 END) as separaciones_caducadas")
)
->whereBetween('fecha_venta', [$desde, $hasta])
->groupBy('id_proyecto', 'id_empleado')
->get();
return $rows->map(function ($r) use ($proyectos, $empleados) {
$proyecto = $proyectos[$r->id_proyecto] ?? null;
$empleado = $empleados[$r->id_empleado] ?? null;
return [
'proyecto' => $proyecto->nombre ?? 'Desconocido',
'empleado' => $empleado ? ($empleado->nombre . ' ' . $empleado->apellido) : 'Sin asignar',
'ventas' => (int) $r->ventas,
'separaciones' => (int) $r->separaciones,
'separaciones_ejecutadas' => (int) $r->separaciones_ejecutadas,
'separaciones_caducadas' => (int) $r->separaciones_caducadas,
];
})->values()->all();
}
Additional Analytics
Inventory State Distribution
Inventory Breakdown
Visual distribution of inventory:
- Available units
- Sold units
- Separated units
- Blocked/frozen units
- By project comparison
public function estadoInventario()
{
return Proyecto::with([
'torres.apartamentos.estadoInmueble',
'torres.locales.estadoInmueble',
])
->get()
->map(function ($p) {
$estados = [
'Disponible' => 0,
'Vendido' => 0,
'Separado' => 0,
'Bloqueado' => 0,
'Congelado' => 0,
];
foreach ($p->torres as $torre) {
foreach ($torre->apartamentos as $a) {
$nombre = $a->estadoInmueble?->nombre;
if ($nombre && array_key_exists($nombre, $estados)) {
$estados[$nombre]++;
}
}
foreach ($torre->locales as $l) {
$nombre = $l->estadoInmueble?->nombre;
if ($nombre && array_key_exists($nombre, $estados)) {
$estados[$nombre]++;
}
}
}
return [
'proyecto' => $p->nombre,
'estados' => $estados,
];
});
}
Advisor Rankings
Top Performers
Leaderboard of sales advisors:
- Total sales value
- Number of units sold
- Average sale value
- Ranking by performance
public function rankingAsesores()
{
return DB::table('ventas')
->join('empleados', 'empleados.id_empleado', '=', 'ventas.id_empleado')
->where('ventas.tipo_operacion', 'venta')
->select(
'empleados.id_empleado',
DB::raw("CONCAT(empleados.nombre, ' ', empleados.apellido) as asesor"),
DB::raw("SUM(ventas.valor_total) as total_ventas")
)
->groupBy('empleados.id_empleado', 'empleados.nombre', 'empleados.apellido')
->orderByDesc('total_ventas')
->get();
}
Monthly Absorption
Sales Trends
Time series analysis:
- Units sold per month
- By project
- Trend identification
- Seasonal patterns
public function absorcionMensual()
{
return DB::table('ventas')
->join('proyectos', 'proyectos.id_proyecto', '=', 'ventas.id_proyecto')
->where('ventas.tipo_operacion', 'venta')
->select(
'proyectos.nombre as proyecto',
DB::raw("TO_CHAR(fecha_venta, 'YYYY-MM') as mes"),
DB::raw("COUNT(*) as unidades")
)
->groupBy('proyectos.nombre', DB::raw("TO_CHAR(fecha_venta, 'YYYY-MM')"))
->orderBy('mes')
->get();
}
Filtering & Date Ranges
All reports support flexible filtering:
Date Range
Select custom start and end dates
By Project
Filter to specific project
By Advisor
View specific advisor’s performance
By Property State
Filter inventory by state
Data Exports
Reports can be exported for external analysis:
Payment Plan Export
public function exportPlanPagosCI(Request $request, GerenciaEstadisticasService $service)
{
$filtros = [
'desde' => $request->query('desde'),
'hasta' => $request->query('hasta'),
'proyecto_id' => $request->query('proyecto_id'),
'asesor_id' => $request->query('asesor_id'),
];
[$desde, $hasta] = $service->rangoFechas($filtros);
$plan = $service->planPagosCI($filtros, $desde, $hasta);
return Excel::download(
new PlanPagosCIExport($plan['encabezados'], $plan['filas'], $plan['totales']),
'plan_pagos_cuota_inicial.xlsx'
);
}
Excel Export Features
- Formatted spreadsheets
- Multiple sheets (summary, details)
- Formulas and totals
- Date formatting
- Currency formatting
Access Control
Dashboard access is role-based:
// Only commercial roles can access dashboard
$cargo = Cargo::whereIn('nombre', [
'Directora Comercial',
'Asesora Comercial'
])->get();
$empleados = Empleado::whereIn('id_cargo', $cargo->pluck('id_cargo'))
->select('id_empleado', 'nombre', 'apellido')
->get();
Best Practices
Review dashboards regularly
Management should review key metrics weekly to identify trends and issues early.
Configure monthly sales goals based on historical performance and market conditions.
Track separation-to-sale conversion rates to improve sales processes.
Export data for deep analysis
Use Excel exports for custom analysis and presentations to stakeholders.
Compare project performance
Regularly compare projects to identify best practices and areas for improvement.