squareetlabs/laravel-verifactu

Paquete Laravel para gestión y registro de facturación electrónica VeriFactu. Compatible con Laravel 10 a 12.x

Installs: 58

Dependents: 0

Suggesters: 0

Security: 0

Stars: 13

Watchers: 0

Forks: 10

Open Issues: 2

pkg:composer/squareetlabs/laravel-verifactu

v1.0.1 2025-11-24 16:31 UTC

This package is auto-updated.

Last update: 2025-11-24 16:33:58 UTC


README

Paquete Laravel 10/11/12 para gestión y registro de facturación electrónica VeriFactu

Quality Score Code Intelligence Latest Stable Version Total Downloads License

Características principales

  • Modelos Eloquent para invoices, breakdowns y recipients
  • Enum types para campos fiscales (invoice type, tax type, regime, etc.)
  • Helpers para operaciones de fecha, string y hash
  • Servicio AEAT client (configurable e inyectable)
  • Form Requests para validación
  • API Resources para respuestas RESTful
  • Factories y tests unitarios para todos los componentes core
  • Listo para extensión y uso en producción

Instalación

composer require squareetlabs/laravel-verifactu

Publica la configuración y migraciones:

php artisan vendor:publish --provider="Squareetlabs\VeriFactu\Providers\VeriFactuServiceProvider"
php artisan migrate

Configuración

Edita tu archivo .env o config/verifactu.php según tus necesidades:

return [
    'enabled' => true,
    'default_currency' => 'EUR',
    'issuer' => [
        'name' => env('VERIFACTU_ISSUER_NAME', ''),
        'vat' => env('VERIFACTU_ISSUER_VAT', ''),
    ],
    
    // Define si se cargan las migraciones del paquete (por defecto false)
    'load_migrations' => env('VERIFACTU_LOAD_MIGRATIONS', false),
];

Integración

Este paquete soporta dos modos de uso:

1. Proyectos Nuevos (Uso completo)

Si estás empezando un proyecto desde cero, puedes usar los modelos y migraciones que incluye el paquete.

  1. Habilita las migraciones en tu .env:
    VERIFACTU_LOAD_MIGRATIONS=true
  2. Ejecuta las migraciones:
    php artisan migrate
  3. Usa los modelos Squareetlabs\VeriFactu\Models\Invoice, Breakdown y Recipient directamente.

2. Sistemas Existentes (Adaptador)

Si ya tienes tu propio sistema de facturación, no necesitas usar nuestras migraciones. Solo necesitas implementar los contratos en tus modelos.

  1. Asegúrate de que VERIFACTU_LOAD_MIGRATIONS es false (por defecto).
  2. Genera un adaptador para tu modelo de factura:
    php artisan verifactu:make-adapter Invoice
  3. Esto generará el código necesario en tu terminal. Cópialo a tu modelo App\Models\Invoice e implementa la interfaz VeriFactuInvoice.

Ejemplo de implementación:

use Squareetlabs\VeriFactu\Contracts\VeriFactuInvoice;
use Illuminate\Database\Eloquent\Model;

class Invoice extends Model implements VeriFactuInvoice
{
    // Implementa los métodos requeridos por la interfaz
    public function getInvoiceNumber(): string
    {
        return $this->invoice_number; // Tu campo personalizado
    }
    
    // ... resto de métodos
}

Uso rápido

Crear una Invoice (Ejemplo de Controller)

use Squareetlabs\VeriFactu\Http\Requests\StoreInvoiceRequest;
use Squareetlabs\VeriFactu\Models\Invoice;
use Squareetlabs\VeriFactu\Http\Resources\InvoiceResource;

public function store(StoreInvoiceRequest $request)
{
    $invoice = Invoice::create($request->validated());
    // Opcionalmente puedes asociar breakdowns y recipients
    // $invoice->breakdowns()->createMany([...]);
    // $invoice->recipients()->createMany([...]);
    return new InvoiceResource($invoice->load(['breakdowns', 'recipients']));
}

Ejemplos de tipos de Invoice

A continuación, ejemplos de cómo crear cada tipo de invoice usando el modelo y enums:

Factura estándar

use Squareetlabs\VeriFactu\Models\Invoice;
use Squareetlabs\VeriFactu\Enums\InvoiceType;

$invoice = Invoice::create([
    'number' => 'INV-STD-001',
    'date' => '2024-07-01',
    'customer_name' => 'Standard Customer',
    'customer_tax_id' => 'C12345678',
    'issuer_name' => 'Issuer S.A.',
    'issuer_tax_id' => 'B87654321',
    'amount' => 100.00,
    'tax' => 21.00,
    'total' => 121.00,
    'type' => InvoiceType::STANDARD,
]);

Factura simplificada

$invoice = Invoice::create([
    'number' => 'INV-SIMP-001',
    'date' => '2024-07-01',
    'customer_name' => 'Simplified Customer',
    'customer_tax_id' => 'C87654321',
    'issuer_name' => 'Issuer S.A.',
    'issuer_tax_id' => 'B87654321',
    'amount' => 50.00,
    'tax' => 10.50,
    'total' => 60.50,
    'type' => InvoiceType::SIMPLIFIED,
]);

Factura de sustitución

$invoice = Invoice::create([
    'number' => 'INV-SUB-001',
    'date' => '2024-07-01',
    'customer_name' => 'Substitute Customer',
    'customer_tax_id' => 'C11223344',
    'issuer_name' => 'Issuer S.A.',
    'issuer_tax_id' => 'B87654321',
    'amount' => 80.00,
    'tax' => 16.80,
    'total' => 96.80,
    'type' => InvoiceType::SUBSTITUTE,
    // Puedes añadir aquí la relación con facturas sustituidas si implementas la lógica
]);

Factura rectificativa (R1)

$invoice = Invoice::create([
    'number' => 'INV-RECT-001',
    'date' => '2024-07-01',
    'customer_name' => 'Rectified Customer',
    'customer_tax_id' => 'C55667788',
    'issuer_name' => 'Issuer S.A.',
    'issuer_tax_id' => 'B87654321',
    'amount' => 120.00,
    'tax' => 25.20,
    'total' => 145.20,
    'type' => InvoiceType::RECTIFICATIVE_R1,
    // Puedes añadir aquí la relación con facturas rectificadas y el motivo si implementas la lógica
]);

Nota: Para facturas rectificativas y sustitutivas, si implementas los campos y relaciones adicionales (como facturas rectificadas/sustituidas, tipo de rectificación, importe de rectificación), deberás añadirlos en el array de creación.

Envío de Invoice a AEAT (Ejemplo de Controller)

use Illuminate\Http\Request;
use Squareetlabs\VeriFactu\Services\AeatClient;
use Squareetlabs\VeriFactu\Models\Invoice;

class InvoiceAeatController extends Controller
{
    public function send(Request $request, AeatClient $aeatClient, $invoiceId)
    {
        $invoice = Invoice::with(['breakdowns', 'recipients'])->findOrFail($invoiceId);
        $result = $aeatClient->sendInvoice($invoice);
        // Puedes registrar el resultado, lanzar eventos, etc.
        return response()->json($result, $result['status'] === 'success' ? 200 : 422);
    }
}

Nota: Protege este endpoint con autenticación/autorización adecuada.

El resultado incluirá el XML enviado y recibido, útil para depuración.

Si el certificado no es válido o hay error de validación, el array tendrá 'status' => 'error' y 'message'.

Validación y creación de Breakdown (Ejemplo de Controller)

use Squareetlabs\VeriFactu\Http\Requests\StoreBreakdownRequest;
use Squareetlabs\VeriFactu\Models\Breakdown;

public function storeBreakdown(StoreBreakdownRequest $request)
{
    $breakdown = Breakdown::create($request->validated());
    return response()->json($breakdown);
}

Uso de Helpers

use Squareetlabs\VeriFactu\Helpers\DateTimeHelper;
use Squareetlabs\VeriFactu\Helpers\StringHelper;
use Squareetlabs\VeriFactu\Helpers\HashHelper;

$dateIso = DateTimeHelper::formatIso8601('2024-01-01 12:00:00');
$sanitized = StringHelper::sanitize('  &Hello <World>  ');
$hash = HashHelper::generateInvoiceHash([
    'issuer_tax_id' => 'A12345678',
    'invoice_number' => 'INV-001',
    'issue_date' => '2024-01-01',
    'invoice_type' => 'F1',
    'total_tax' => '21.00',
    'total_amount' => '121.00',
    'previous_hash' => '',
    'generated_at' => '2024-01-01T12:00:00+01:00',
]);

Uso avanzado

Integración de eventos y listeners

Puedes disparar eventos cuando se crean, actualizan o envían invoices a AEAT. Ejemplo:

// app/Events/InvoiceSentToAeat.php
namespace App\Events;

use Squareetlabs\VeriFactu\Models\Invoice;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;

class InvoiceSentToAeat
{
    use Dispatchable, SerializesModels;
    public function __construct(public Invoice $invoice, public array $aeatResponse) {}
}

Despacha el evento tras el envío:

use App\Events\InvoiceSentToAeat;

// ... después de enviar a AEAT
InvoiceSentToAeat::dispatch($invoice, $result);

Crea un listener para notificaciones o logging:

// app/Listeners/LogAeatResponse.php
namespace App\Listeners;

use App\Events\InvoiceSentToAeat;
use Illuminate\Support\Facades\Log;

class LogAeatResponse
{
    public function handle(InvoiceSentToAeat $event)
    {
        Log::info('AEAT response', [
            'invoice_id' => $event->invoice->id,
            'response' => $event->aeatResponse,
        ]);
    }
}

Registra tu evento y listener en EventServiceProvider:

protected $listen = [
    \App\Events\InvoiceSentToAeat::class => [
        \App\Listeners\LogAeatResponse::class,
    ],
];

Políticas de autorización

Puedes restringir el acceso a invoices usando policies de Laravel:

// app/Policies/InvoicePolicy.php
namespace App\Policies;

use App\Models\User;
use Squareetlabs\VeriFactu\Models\Invoice;

class InvoicePolicy
{
    public function view(User $user, Invoice $invoice): bool
    {
        return $user->id === $invoice->user_id;
    }

    public function update(User $user, Invoice $invoice): bool
    {
        return $user->id === $invoice->user_id && $invoice->status === 'draft';
    }
}

Registra la policy en AuthServiceProvider:

protected $policies = [
    \Squareetlabs\VeriFactu\Models\Invoice::class => \App\Policies\InvoicePolicy::class,
];

Úsala en tu controller:

public function update(Request $request, Invoice $invoice)
{
    $this->authorize('update', $invoice);
    // ...
}

Integración de notificaciones

Puedes notificar a usuarios o admins cuando una invoice se envía o falla:

// app/Notifications/InvoiceSentNotification.php
namespace App\Notifications;

use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Notification;
use Squareetlabs\VeriFactu\Models\Invoice;

class InvoiceSentNotification extends Notification
{
    use Queueable;
    public function __construct(public Invoice $invoice) {}

    public function via($notifiable)
    {
        return ['mail'];
    }

    public function toMail($notifiable)
    {
        return (new \Illuminate\Notifications\Messages\MailMessage)
            ->subject('Invoice Sent to AEAT')
            ->line('Invoice #' . $this->invoice->number . ' was sent to AEAT successfully.');
    }
}

Despacha la notificación en tu job o listener:

$user->notify(new \App\Notifications\InvoiceSentNotification($invoice));

Integración con colas (queues)

Puedes enviar invoices a AEAT de forma asíncrona usando colas:

use Squareetlabs\VeriFactu\Models\Invoice;
use App\Jobs\SendInvoiceToAeatJob;

// Despacha el job a la cola
SendInvoiceToAeatJob::dispatch($invoice->id);

En tu job, implementa ShouldQueue:

use Illuminate\Contracts\Queue\ShouldQueue;

class SendInvoiceToAeatJob implements ShouldQueue
{
    // ...
}

Configura tu conexión de cola en .env y ejecuta el worker:

php artisan queue:work

Auditoría

Puedes usar paquetes como owen-it/laravel-auditing para auditar cambios en invoices:

  1. Instala el paquete:
    composer require owen-it/laravel-auditing
  2. Añade el contrato \OwenIt\Auditing\Contracts\Auditable a tu modelo:
    use OwenIt\Auditing\Contracts\Auditable;
    
    class Invoice extends Model implements Auditable
    {
        use \OwenIt\Auditing\Auditable;
        // ...
    }
  3. Ahora todos los cambios en invoices serán auditados automáticamente. Puedes ver los logs:
    $audits = $invoice->audits;

Testing

Ejecuta todos los tests unitarios:

php artisan test
# o
vendor/bin/phpunit

Contribuir

Las contribuciones son bienvenidas. Por favor:

  1. Fork el proyecto
  2. Crea una rama para tu feature
  3. Commit tus cambios
  4. Push a la rama
  5. Abre un Pull Request

Licencia

Este paquete es open-source bajo la Licencia MIT.

Soporte

Autores

Si este paquete te ha sido útil, ¡no olvides darle una estrella en GitHub!