Creating Plugins for FacturaScripts

Complete guide to developing plugins from scratch

Introduction to Plugin Development

Plugins let you extend FacturaScripts' functionality without modifying the base code. In this guide you'll learn to create a complete plugin from scratch.

🎯 What you'll learn

  • Basic structure of a plugin
  • Creating controllers
  • Defining data models
  • Implementing views
  • Configuring the INI file

📦 Example Plugin

  • Name: MiPrimerPlugin
  • Function: Simple task management
  • Features: Full CRUD
  • Database: One new table

Plugin Structure

Every plugin in FacturaScripts follows a specific structure. Let's create the base directory:

Plugins/
└── MiPrimerPlugin/
    ├── Controller/
    │   └── ListTarea.php
    ├── Model/
    │   └── Tarea.php
    ├── View/
    │   └── ListTarea.html.twig
    ├── Translation/
    │   └── es_ES.json
    └── facturascripts.ini

Step 1: Plugin Configuration (facturascripts.ini)

This file defines the plugin's basic information:

[plugin]
name = MiPrimerPlugin
description = "Mi primer plugin para FacturaScripts - Gestión de tareas"
version = 1.0
author = Tu Nombre
email = tu@email.com
min_version = 2024

[models]
MiPrimerPlugin/Model/Tarea = Tarea

[controllers]
MiPrimerPlugin/Controller/ListTarea = ListTarea

Step 2: Create the Data Model

The model defines the data structure and the table in the database:

<?php
namespace FacturaScripts\Plugins\MiPrimerPlugin\Model;

use FacturaScripts\Core\Model\Base\ModelClass;
use FacturaScripts\Core\Model\Base\ModelTrait;

class Tarea extends ModelClass
{
    use ModelTrait;

    /**
     * Primary key (autoincrement)
     * @var int
     */
    public $id;

    /**
     * Task title
     * @var string
     */
    public $titulo;

    /**
     * Task description
     * @var string
     */
    public $descripcion;

    /**
     * Creation date
     * @var string
     */
    public $fechacreacion;

    /**
     * Due date
     * @var string
     */
    public $fechavencimiento;

    /**
     * Completed flag
     * @var bool
     */
    public $completada;

    /**
     * Returns the name of the table in the database
     * @return string
     */
    public static function tableName(): string
    {
        return 'tareas';
    }

    /**
     * Returns the name of the primary column
     * @return string
     */
    public static function primaryColumn(): string
    {
        return 'id';
    }

    /**
     * Returns the mapping of fields in the table
     * @return array
     */
    public function getFields(): array
    {
        return [
            'id' => ['type' => 'INT', 'auto_increment' => true],
            'titulo' => ['type' => 'VARCHAR', 'length' => 100],
            'descripcion' => ['type' => 'TEXT'],
            'fechacreacion' => ['type' => 'DATETIME'],
            'fechavencimiento' => ['type' => 'DATETIME'],
            'completada' => ['type' => 'BOOLEAN']
        ];
    }

    /**
     * Executed before saving the model
     * @return bool
     */
    protected function beforeSave(): bool
    {
        if ($this->isInsert()) {
            $this->fechacreacion = date('Y-m-d H:i:s');
        }
        return parent::beforeSave();
    }
}

Step 3: Create the Controller

The controller handles the application's logic and user interactions:

<?php
namespace FacturaScripts\Plugins\MiPrimerPlugin\Controller;

use FacturaScripts\Core\Lib\ExtendedController\ListController;
use FacturaScripts\Plugins\MiPrimerPlugin\Model\Tarea;

class ListTarea extends ListController
{
    /**
     * Returns the class name of the model
     * @return string
     */
    public function getModelClassName(): string
    {
        return 'Tarea';
    }

    /**
     * Returns the page title
     * @return string
     */
    public function getPageData(): array
    {
        $pageData = parent::getPageData();
        $pageData['title'] = 'Tareas';
        $pageData['menu'] = 'admin';
        $pageData['icon'] = 'fas fa-tasks';
        return $pageData;
    }

    /**
     * Create the views to display
     */
    protected function createViews()
    {
        $this->addListView('ListTarea', 'Tarea', 'tareas', 'fas fa-tasks');
        $this->setSettings('ListTarea', 'btnNew', true);
    }

    /**
     * Load view data procedure
     * @param string $viewName
     * @param BaseView $view
     */
    protected function loadData($viewName, $view)
    {
        switch ($viewName) {
            case 'ListTarea':
                $view->loadData();
                break;
        }
    }
}

Step 4: Create the View (Template)

The view defines the user interface. We use FacturaScripts' template system:

{% extends "Master/MenuTemplate.html.twig" %}

{% block body %}
    <div class="container-fluid">
        <div class="row">
            <div class="col-12">
                <div class="card shadow mb-4">
                    <div class="card-header py-3">
                        <h6 class="m-0 font-weight-bold text-primary">
                            <i class="fas fa-tasks mr-2"></i>
                            Gestión de Tareas                        </h6>
                    </div>
                    <div class="card-body">
                        {% set grid = fsc.getView('ListTarea') %}
                        {{ grid.render() | raw }}
                    </div>
                </div>
            </div>
        </div>
    </div>
{% endblock %}

{% block javascripts %}
    {{ parent() }}
    <script>
        document.addEventListener('DOMContentLoaded', function() {
            // JavaScript personalizado para la vista de tareas            console.log('Vista de tareas cargada');
        });
    </script>
{% endblock %}

Step 5: Translations

Create the translations file for internationalization:

{
    "tarea": "Tarea",
    "tareas": "Tareas",
    "nueva-tarea": "Nueva Tarea",
    "titulo": "Título",
    "descripcion": "Descripción",
    "fechacreacion": "Fecha de Creación",
    "fechavencimiento": "Fecha de Vencimiento",
    "completada": "Completada",
    "pendiente": "Pendiente",
    "mi-primer-plugin": "Mi Primer Plugin"
}

Step 6: Install and Test the Plugin

1. Copy the files

Copy the MiPrimerPlugin folder entirely to the Plugins/ directory of your FacturaScripts installation.

2. Activate the plugin

  1. Go to the admin panel
  2. Navigate to "Administration → Plugins"
  3. Find "MiPrimerPlugin" in the list
  4. Click "Activate"

3. Verifying the installation

  • Check that the new "Tareas" menu appears
  • Verify that the tareas table was created in the database
  • Test creating, editing and deleting tasks

Complete Example: Quick Notes Plugin

Let's create a more complete plugin for managing quick notes:

Model: Model/Nota.php

<?php
namespace FacturaScripts\Plugins\MiPrimerPlugin\Model;

use FacturaScripts\Core\Model\Base\ModelClass;

class Nota extends ModelClass
{
    public $id;
    public $titulo;
    public $contenido;
    public $fechacreacion;
    public $importante;

    public static function tableName(): string
    {
        return 'notas';
    }

    public static function primaryColumn(): string
    {
        return 'id';
    }

    public function getFields(): array
    {
        return [
            'id' => ['type' => 'INT', 'auto_increment' => true],
            'titulo' => ['type' => 'VARCHAR', 'length' => 200],
            'contenido' => ['type' => 'TEXT'],
            'fechacreacion' => ['type' => 'DATETIME'],
            'importante' => ['type' => 'BOOLEAN']
        ];
    }

    protected function beforeSave(): bool
    {
        if ($this->isInsert()) {
            $this->fechacreacion = date('Y-m-d H:i:s');
        }
        return parent::beforeSave();
    }
}

Controller: Controller/ListNota.php

<?php
namespace FacturaScripts\Plugins\MiPrimerPlugin\Controller;

use FacturaScripts\Core\Lib\ExtendedController\ListController;

class ListNota extends ListController
{
    public function getModelClassName(): string
    {
        return 'Nota';
    }

    public function getPageData(): array
    {
        $pageData = parent::getPageData();
        $pageData['title'] = 'Notas Rápidas';
        $pageData['menu'] = 'admin';
        $pageData['icon'] = 'fas fa-sticky-note';
        return $pageData;
    }

    protected function createViews()
    {
        $this->addListView('ListNota', 'Nota', 'notas', 'fas fa-sticky-note');
        $this->setSettings('ListNota', 'btnNew', true);
    }

    protected function loadData($viewName, $view)
    {
        switch ($viewName) {
            case 'ListNota':
                $view->loadData();
                break;
        }
    }
}

Common Troubleshooting

Plugin doesn't appear

  • Verify that facturascripts.ini is well-formed
  • Check the directory permissions
  • Check the logs in MyFiles/Logs/

Database errors

  • Verify that the model extends ModelClass
  • Check the table and column names
  • Verify that the data types are correct

Errors in views

  • Verify the Twig syntax
  • Check that the views exist
  • Check the class names in the controllers

Best Practices

📁 Code Structure

  • Use namespaces correctly
  • Follow naming conventions
  • Document your code
  • Maintain MVC separation

🔒 Security

  • Always validate input data
  • Use prepared statements
  • Respect user permissions
  • Sanitize HTML output

🎯 UX/UI

  • Use FacturaScripts' template system
  • Maintain visual consistency
  • Make your views responsive
  • Provide translations

Next Steps

🚀 Advanced Features

  • Create custom REST APIs
  • Implement hooks and events
  • Develop dashboard widgets
  • Create custom reports

🛠️ Tools

  • PHPStan for static analysis
  • Composer for dependencies
  • Git for version control
  • VS Code with PHP extension