Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/juanVillamilEchavarria/Leo_Counter-app/llms.txt

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

When a user deletes a supported record in Leo Counter — an account, a category, a pending movement, or a budget — the record is not immediately removed from the database. Instead, Laravel’s soft-delete mechanism stamps a deleted_at timestamp on the row and filters it out of all regular queries. The data remains intact and fully recoverable until an admin decides to permanently erase it. This design prevents accidental loss of financial history and gives administrators a safety net before data is gone for good.
The soft-delete management panel is exclusively accessible to administrators. All routes under /configuracion/deleted/* are wrapped in AdminMiddleware, which aborts with HTTP 403 if the authenticated user’s role is not admin.

Supported Domains

Leo Counter’s SoftDeleteManagerTypes enum defines the four domains that participate in soft deletion:
// app/Domains/Configuracion/Enums/SoftDeleteManagerTypes.php
enum SoftDeleteManagerTypes : string
{
    case CUENTAS               = 'cuentas';
    case CATEGORIAS            = 'categorias';
    case MOVIMIENTOS_PENDIENTES = 'movimientosPendientes';
    case PRESUPUESTOS          = 'presupuestos';
}
Each case maps a URL-safe domain slug to a human-readable label and a dedicated Inertia view. The table below summarises where soft deletes originate in the database schema:
Domain slugLabelTableMigration
cuentasCuentascuentas2026_02_08_215547_add_sof_deletes_to_cuentas_table.php
categoriasCategoriascategorias2026_01_14_200821_create_categorias_table.php
movimientosPendientesMovimientos Pendientesmovimiento_pendientes2026_01_27_190641_create_movimiento_pendientes_table.php
presupuestosPresupuestospresupuestos2026_01_21_225025_create_presupuestos_table.php
The cuentas table received its deleted_at column via a dedicated additive migration (the original create_cuentas_table migration also dropped the legacy archived boolean column at the same time). The remaining three tables had softDeletes() included in their creation migrations from the start.

Routes

All three soft-delete actions share the same URL pattern, using {domain} as the slug from the table above.
MethodPathRoute nameDescription
GET/configuracion/deleted/{domain}configuracion.deleted.indexList all soft-deleted records for a domain
PUT/configuracion/deleted/{domain}/restore/{id}configuracion.deleted.restoreRestore a single soft-deleted record
DELETE/configuracion/deleted/{domain}/hard-delete/{id}configuracion.deleted.hardDeletePermanently remove a record from the database

Example URLs

GET    /configuracion/deleted/cuentas
GET    /configuracion/deleted/categorias
GET    /configuracion/deleted/movimientosPendientes
GET    /configuracion/deleted/presupuestos

PUT    /configuracion/deleted/cuentas/restore/550e8400-e29b-41d4-a716-446655440000
DELETE /configuracion/deleted/cuentas/hard-delete/550e8400-e29b-41d4-a716-446655440000

How It Works

The SoftDeleteRecordsController resolves the correct domain type from the URL slug and delegates to CQRS command/query handlers:
// app/Http/Controllers/Configuracion/SoftDeleteRecordsController.php

public function index(string $domain)
{
    $type = SoftDeleteManagerTypes::try($domain);
    $data = $this->queryBus->ask(new ListDomainRecordsDeletedQuery($type));
    $resource = $this->resourceResolver->resolve($type, $data);

    return Inertia::render($this->viewByType($type), [
        'title' => "{$type->label()} Eliminados",
        'data'  => $resource,
    ]);
}

public function restore(string $domain, string $id)
{
    $type = SoftDeleteManagerTypes::try($domain);
    $this->dispatcher->dispatch(new RestoreRecordCommand(id: $id, domain: $type));
    Inertia::flash('success', 'Restaurado con exito');
    return back();
}

public function hardDelete(string $domain, string $id)
{
    $type = SoftDeleteManagerTypes::try($domain);
    $this->dispatcher->dispatch(new HardDeleteRecordCommand(id: $id, domain: $type));
    Inertia::flash('success', 'Eliminado con exito');
    return back();
}
Passing an unrecognised domain slug causes SoftDeleteManagerTypes::try() to throw a LogicException, so only the four valid slugs are accepted.

Viewing Soft-Deleted Records

1

Open the Configuration panel

Navigate to /configuracion (requires admin role). The configuration index lists available management sections.
2

Select a domain

Go to the appropriate deleted-records URL, for example /configuracion/deleted/cuentas. The page renders a list of all records whose deleted_at is not null, presented through a domain-specific Inertia view.
3

Choose an action

For each listed record you have two options: Restore or Hard Delete (see sections below).

Restoring a Record

Submitting a PUT request to /configuracion/deleted/{domain}/restore/{id} dispatches RestoreRecordCommand. The handler clears the deleted_at timestamp on the matching row, making the record visible again in all regular application queries.
Restoring a soft-deleted account makes it appear again in /cuentas and become selectable when recording movements.

Permanently Deleting a Record

Submitting a DELETE request to /configuracion/deleted/{domain}/hard-delete/{id} dispatches HardDeleteRecordCommand. The handler executes a true SQL DELETE against the row, bypassing Laravel’s soft-delete filter entirely. The record and all data it contains are removed from the database permanently.
Hard deletion is irreversible. Once a record is hard-deleted, it cannot be recovered through the application. Ensure you have a database backup before using this action, especially for categories or accounts that may have historical associations with other records.

Inertia Views by Domain

The controller selects the correct React page component based on the resolved domain type:
DomainInertia View
cuentasConfiguracion/Deleted/Cuentas
categoriasConfiguracion/Deleted/Categorias
movimientosPendientesConfiguracion/Deleted/MovimientosPendientes
presupuestosConfiguracion/Deleted/Presupuestos
Each view receives a data prop containing the domain’s deleted records (transformed through a DeletedRecordsResourceResolver) and a title prop such as "Cuentas Eliminados".

Build docs developers (and LLMs) love