Skip to main content

Overview

Core Projects provides comprehensive project management capabilities for construction developments. Each project can contain multiple towers, with configurable floors, pricing policies, and dynamic price adjustments based on sales progress.

Creating Projects

Projects are created through the admin interface with detailed configuration options.

Project Configuration

When creating a project, you can configure:

Basic Information

  • Project name and description
  • Start and end dates
  • Initial and final budget
  • Location (linked to cities/departments)

Construction Details

  • Built square meters
  • Number of towers and floors
  • Stratum (1-6 socioeconomic level)
  • Quantity of apartments, locals, and parking spaces

Sales Configuration

  • Minimum down payment percentage
  • Minimum separation value
  • Down payment term (months)
  • Maximum separation period (days)

Height Premium

  • Base height premium
  • Premium increment per floor
  • Starting floor for premium calculation
  • Enable/disable height premium

Project States

Projects can be activated or deactivated:
public function toggleActivo(Request $request, Proyecto $proyecto)
{
    $proyecto->activo = !$proyecto->activo;
    $proyecto->save();
    
    return back()->with('success', 
        $proyecto->activo ? 'Proyecto activado.' : 'Proyecto desactivado.');
}
Only active projects appear in sales and quotation modules.

Tower Management

Each project can have multiple towers, which serve as containers for floors and units.

Creating Towers

Towers are created within a project with:
  • Tower name/identifier
  • Project association
  • Initial state
  • Starting floor for height premium calculation

Floor Management

Floors (pisos) are organized within towers:
// Floors belong to towers
class PisoTorre extends Model
{
    protected $fillable = [
        'nivel',           // Floor number
        'id_torre',        // Tower ID
    ];
    
    public function torre()
    {
        return $this->belongsTo(Torre::class, 'id_torre');
    }
}
Floors are used to calculate height premiums and organize units vertically.

Pricing Policies

Core Projects supports dynamic pricing through pricing policies that automatically adjust unit prices based on sales progress.

Policy Configuration

Escalating Price Policy

Pricing policies define:
  • Sales per tier (ventas_por_escalon): Number of sales before price increase
  • Percentage increase: Price adjustment per tier
  • Effective date: When the policy becomes active
  • Status: Active or inactive

How Pricing Works

  1. Initial Pricing: Units start at base price from their type
  2. Sales Tracking: System counts total sales in project
  3. Tier Calculation: Current tier = total sales ÷ sales per tier
  4. Price Factor: factor = (1 + percentage/100) ^ current_tier
  5. Final Price: Base price × factor
private function calcularValorConPolitica($valorConPrima, $idProyecto)
{
    $proyecto = Proyecto::with('politicaVigente')->find($idProyecto);
    if (!$proyecto || !$proyecto->politicaVigente) {
        return [
            'valor_politica' => 0,
            'valor_final' => $valorConPrima
        ];
    }
    
    $politica = $proyecto->politicaVigente;
    $ventasActuales = Venta::where('id_proyecto', $idProyecto)
        ->whereIn('tipo_operacion', ['venta', 'separacion'])
        ->count();
    
    $ventasPorEscalon = $politica->ventas_por_escalon ?? 0;
    $porcentajeAumento = $politica->porcentaje_aumento ?? 0;
    
    $escalonActual = $ventasPorEscalon > 0 
        ? floor($ventasActuales / $ventasPorEscalon) : 0;
    $factor = pow(1 + ($porcentajeAumento / 100), $escalonActual);
    
    $valorFinal = $valorConPrima * $factor;
    $valorPolitica = $valorFinal - $valorConPrima;
    
    return [
        'valor_politica' => round($valorPolitica, 2),
        'valor_final' => round($valorFinal, 2)
    ];
}

Example

With a policy of:
  • 10 sales per tier
  • 5% increase per tier
Pricing progression:
  • Sales 0-9: Base price × 1.00
  • Sales 10-19: Base price × 1.05
  • Sales 20-29: Base price × 1.1025
  • Sales 30-39: Base price × 1.1576

Height Premium

Units on higher floors can automatically receive price premiums.

Premium Calculation

private function calcularPrimaAltura($idPisoTorre, $idTorre): float
{
    $piso = PisoTorre::find($idPisoTorre);
    if (!$piso) return 0;
    
    $nivelActual = (int) $piso->nivel;
    $torre = Torre::with('proyecto')->find($idTorre);
    
    if (!$torre || !$torre->proyecto->prima_altura_activa) {
        return 0;
    }
    
    $nivelBase = (int) ($torre->nivel_inicio_prima ?? 2);
    
    // Floors below the base level have no premium
    if ($nivelActual < $nivelBase) {
        return 0;
    }
    
    $base = (float) ($torre->proyecto->prima_altura_base ?? 0);
    $incremento = (float) ($torre->proyecto->prima_altura_incremento ?? 0);
    $pisosCalculables = $nivelActual - $nivelBase;
    
    return $base + ($pisosCalculables * $incremento);
}

Example Premium

Project configuration:
  • Base premium: $5,000,000
  • Increment per floor: $1,000,000
  • Starting floor: 3
Premiums:
  • Floor 1-2: $0
  • Floor 3: $5,000,000
  • Floor 4: $6,000,000
  • Floor 5: $7,000,000
  • Floor 6: $8,000,000

Project Details View

The project detail page shows:

Project Info

  • Name, description, dates
  • Budget and construction details
  • Location information
  • Sales configuration

Towers

  • List of towers in project
  • Tower states
  • Quick access to tower details

Pricing Policies

  • Active and historical policies
  • Current pricing tier
  • Price increase history

Social Areas

  • Common areas
  • Amenities
  • Facilities

Project Relationships

Projects are the central entity connecting:
class Proyecto extends Model
{
    public function torres()
    {
        return $this->hasMany(Torre::class, 'id_proyecto');
    }
    
    public function zonasSociales()
    {
        return $this->hasMany(ZonaSocial::class, 'id_proyecto');
    }
    
    public function tiposApartamento()
    {
        return $this->hasMany(TipoApartamento::class, 'id_proyecto');
    }
    
    public function politicasPrecio()
    {
        return $this->hasMany(PoliticaPrecioProyecto::class, 'id_proyecto');
    }
    
    public function politicasComisiones()
    {
        return $this->hasMany(PoliticaComision::class, 'id_proyecto');
    }
    
    public function metasComerciales()
    {
        return $this->hasMany(ProyectoMetaComercial::class, 'id_proyecto');
    }
}

Validation Rules

Project creation enforces:
[
    'nombre' => 'required|string|max:150',
    'descripcion' => 'nullable|string|max:500',
    'fecha_inicio' => 'nullable|date',
    'fecha_finalizacion' => 'nullable|date|after_or_equal:fecha_inicio',
    'presupuesto_inicial' => 'nullable|numeric|min:0',
    'presupuesto_final' => 'nullable|numeric|min:0',
    'metros_construidos' => 'nullable|numeric|min:0',
    'cantidad_locales' => 'nullable|integer|min:0',
    'cantidad_apartamentos' => 'nullable|integer|min:0',
    'estrato' => 'nullable|integer|min:1|max:6',
    'numero_pisos' => 'nullable|integer|min:1',
    'numero_torres' => 'nullable|integer|min:1',
    'porcentaje_cuota_inicial_min' => 'nullable|numeric|min:0|max:100',
    'valor_min_separacion' => 'nullable|numeric|min:0',
    'plazo_cuota_inicial_meses' => 'nullable|integer|min:1',
    'plazo_max_separacion_dias' => 'nullable|integer|min:1|max:3650',
    'id_estado' => 'required|exists:estados,id_estado',
    'id_ubicacion' => 'required|exists:ubicaciones,id_ubicacion',
]

Best Practices

Set up pricing policies before beginning sales to ensure consistent pricing across the project lifecycle.
Configure height premiums to reflect market value of higher floors and views. Consider starting premiums on upper floors only.
Towers cannot be deleted if they have associated units. Plan your tower structure during project setup.
Track when your project reaches new pricing tiers to inform marketing and sales strategies.

Build docs developers (and LLMs) love