Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/scooller/Leben-site/llms.txt

Use this file to discover all available pages before exploring further.

In iLeben, a Proyecto (Project) represents a real estate development — a building or complex with its own location, etapa (construction stage), advisors, and payment configuration. A Planta (Plant) is an individual unit within a project: a departamento, estacionamiento, bodega, or local. Both entities are Salesforce-sourced; Salesforce is the system of record, and the local database is kept in sync automatically. The Filament admin panel gives operators a rich interface to view, filter, and override selected local attributes without losing their values on the next sync.

Projects (Proyectos)

CRUD and Navigation

Projects are managed under the Real Estate navigation group via ProyectoResource. Both admin and marketing users can view the list, but only admin can create, edit, or delete records. The table shows a navigation badge with the total project count. Each project row exposes three inline record actions: Edit, Toggle Active/Inactive, and Ver en Salesforce (opens the Salesforce record directly in a new tab, visible only when salesforce_id is filled).

Key Fields

FieldTypeNotes
nametextRead-only, sourced from Salesforce
salesforce_idtextSalesforce Proyecto__c ID
slugtextAuto-generated from name on create/update
tipomultiselect (array)Values: best, broker, home, icon, invest
etapaselectNormalized construction stage (see below)
descripciontextRead-only, sourced from Salesforce
direcciontextRead-only, sourced from Salesforce
comuna / provincia / regiontextRead-only, sourced from Salesforce
is_activebooleanControls visibility in public catalog
entrega_inmediatabooleanFlags immediate-delivery units
transbank_commerce_codeselectPer-project Transbank Mall commerce code
descuento_defecto_cotizacion_webdecimalDefault web discount %, sourced from Salesforce
descuento_maximo_unidaddecimalMax unit discount %, sourced from Salesforce
valor_reserva_exigido_defecto_pesodecimalDefault reservation amount in CLP
valor_reserva_exigido_min_pesodecimalMinimum reservation amount in CLP
project_image_idFK → MediaManual Curator image override for project card
manual_payment_instructionstextPer-project deposit instructions
manual_payment_bank_accountsarrayPer-project bank account list for manual payments
manual_payment_linktextPer-project manual payment URL

Etapa Normalization

The etapa field is automatically normalized on read and write using Proyecto::normalizeEtapa(). Legacy Salesforce values and encoding variations are mapped to canonical keys:
public const ETAPA_OPTIONS = [
    'permiso_edificacion'              => 'Permiso de edificación',
    'demolicion'                       => 'Demolición',
    'inicio_obra'                      => 'Inicio de obra',
    'excavacion_masiva'                => 'Excavación masiva',
    'obra_gruesa'                      => 'Obra gruesa',
    'terminaciones'                    => 'Terminaciones',
    'recepcion_municipal_y_copropiedad'=> 'Recepción Municipal y Copropiedad',
    'escrituracion'                    => 'Escrituración',
    'entrega'                          => 'Entrega',
    'postventa'                        => 'Postventa',
];

public const ETAPA_ALIASES = [
    'permiso_de_edificacion' => 'permiso_edificacion',
    'inicio_de_obra'         => 'inicio_obra',
    'construccion'           => 'obra_gruesa',
    'entrega_inmediata'      => 'entrega',
    'preventa'               => 'permiso_edificacion',
    'venta'                  => 'inicio_obra',
];

Filters

The projects table ships four sidebar filters:
  • Etapa — multi-select from all normalized options
  • Región — multi-select populated dynamically from the database
  • Tipo — multi-select (best, broker, home, icon, invest)
  • Entrega Inmediata — boolean yes/no

Advisors (Asesores)

Each project can be associated with multiple Asesor records through the asesor_proyecto pivot table (BelongsToMany). The project form renders a searchable multi-select that lists advisors by full name and email. Advisors are also synced from Salesforce automatically when running php artisan test:sync-projects.

Transbank Mall Commerce Code

The transbank_commerce_code field maps this project to a specific Transbank commerce code. The admin form populates the select options from the payments.gateways.transbank.commerce_codes config key (sourced from TRANSBANK_STORE_CODES in .env). If no value is stored in the DB, the attribute accessor falls back to the config-level mapping by project slug.

Pricing Fields

descuento_defecto_cotizacion_web and descuento_maximo_unidad are synced from Salesforce and displayed as read-only in the admin form. They feed the Plant::resolveFinalPrice() calculation:
public function resolveFinalPrice(bool $eventoSaleActivo = false): float
{
    $precioLista = (float) ($this->precio_lista ?? 0);
    $precioBase  = (float) ($this->precio_base  ?? 0);

    $projectDiscount = $this->proyecto?->descuento_defecto_cotizacion_web;

    $porcentajeDescuento = $eventoSaleActivo
        ? (float) ($this->porcentaje_maximo_unidad ?? 0)
        : (float) ($projectDiscount ?? 0);

    if ($precioLista > 0 && $porcentajeDescuento > 0) {
        $precioConDescuento = $precioLista - (($precioLista * $porcentajeDescuento) / 100);

        return max(0, $precioConDescuento);
    }

    return max(0, $precioBase);
}

Plants (Plantas)

CRUD and Navigation

Plants live under the same Real Estate group via PlantResource. Like projects, both roles can view them, but only admin may create, edit, or delete. The badge shows the total plant count.

Key Fields

FieldTypeNotes
salesforce_product_idtextSalesforce Product2 ID (read-only)
salesforce_proyecto_idtextFK to Proyecto.salesforce_id (read-only)
nametextUnit name
product_codetextInternal product code
tipo_productotextDEPARTAMENTO, ESTACIONAMIENTO, BODEGA, LOCAL
is_activebooleanControls catalog visibility
unidad_salebooleanMarks unit as part of a sale event
precio_basedecimalBase price in UF
precio_listadecimalList price in UF (read-only, from Salesforce)
porcentaje_maximo_unidaddecimalMax unit discount %
descuento_defecto_cotizacion_webdecimalPer-plant web discount % synced from Salesforce
asesor_idFK → AsesorOptional per-plant advisor override
contact_linktextOptional direct contact URL for this unit
cover_image_idFK → MediaCover image via Curator picker
interior_image_idFK → MediaInterior image via Curator picker
salesforce_interior_image_urltextFallback interior URL synced from Salesforce
pisotextFloor number
orientaciontextUnit orientation
programatextProgram name (e.g., layout type)
programa2textSecondary program name
superficie_total_principaldecimalTotal principal area in m²
superficie_interiordecimalInterior area in m²
superficie_utildecimalUsable area in m²
superficie_terrazadecimalTerrace area in m²
last_synced_atdatetimeTimestamp of last Salesforce sync

Images via Curator

Cover and interior images are selected using CuratorPicker fields in the plant form. They resolve to Media records in the curator table. The table view shows a circular thumbnail for the cover image. If no Curator image is set for the interior, the frontend falls back to salesforce_interior_image_url.

Table Filters

FilterOptions
Estado (is_active)Activo / Inactivo
Unidad SaleSí / No
Tipo de plantaDEPARTAMENTO, ESTACIONAMIENTO, BODEGA, LOCAL (defaults to DEPARTAMENTO)
ProyectoRelationship search
ProgramaDynamic list from DB

Inline Record Actions

Each plant row in the table exposes four actions:
  • Toggle Active/Inactive — flips is_active in-place
  • Toggle Unidad Sale — flips unidad_sale in-place
  • Ver en Salesforce — opens the Product2 record (only when salesforce_product_id is filled)
  • Edit — opens the full edit form

Export

The plants table includes an ExportAction powered by PlantExporter. Exports run asynchronously on the database queue, and a persistent Filament notification appears in the panel when the file is ready.
ExportAction::make()
    ->label('Exportar Plantas')
    ->exporter(PlantExporter::class)
Exports require the queue worker to be running. On cPanel, make sure both the schedule:run cron and the queue:work cron are active. See the cPanel queue setup in the README for the exact commands.

Bulk Actions

From the toolbar, admins can select multiple plants and:
  • Activar / Desactivar seleccionadas — bulk toggle is_active
  • Activar en sale / Desactivar en sale — bulk toggle unidad_sale
  • Eliminar — bulk delete (requires confirmation)

Availability Logic

A plant is considered unavailable when either of these conditions is true:
  1. Reserved — an active PlantReservation exists with status = ACTIVE and expires_at in the future.
  2. Paid — a PlantReservation with status = COMPLETED exists, or a Payment record with status IN (COMPLETED, AUTHORIZED) exists for the plant.
These checks are exposed via Plant::isReserved() and Plant::isPaid(). The scheduler runs php artisan reservations:expire every minute to transition expired-but-still-active reservations to EXPIRED status, freeing units automatically.

PlantReservation Model

FieldTypeNotes
plant_idFKThe reserved plant
user_idFKBuyer who initiated the reservation
session_tokenstringAnonymous session identifier
statusenumACTIVE, EXPIRED, COMPLETED, RELEASED
expires_atdatetimeConfigurable via gateway_reservation_timeout_minutes in SiteSettings
completed_atdatetimeSet when the payment succeeds
released_at / released_bydatetime / stringSet when manually or automatically released
metadataarraySupports manual_lock flag to prevent auto-release

Salesforce Sync

Both projects and plants are synced from Salesforce using dedicated Artisan commands. Salesforce is the source of truth for all fields listed in Proyecto::syncableFields() and Plant::syncableFields(). The sync logic deliberately skips overwriting local-only attributes (like cover_image_id, interior_image_id, transbank_commerce_code) to preserve changes made in the admin panel.
Run php artisan sync:plants to manually trigger a plant sync. For projects, use php artisan test:sync-projects. The scheduler automatically runs sync:plants every 5 minutes via a queued job.You can also configure which fields are excluded from sync in SiteSettings → Sincronización Salesforce → Campos excluidos.
# Sync plants manually
php artisan sync:plants

# Sync projects manually
php artisan test:sync-projects

# Check Salesforce authentication
php artisan salesforce:test-auth
The scheduler also runs SyncSalesforceOpportunitiesJob every 60 minutes and salesforce:sync-broker-metrics every 15 minutes for broker metric snapshots.

Build docs developers (and LLMs) love