Laravel Moloni
Integracao completa com a API de faturacao Moloni para Laravel. Gestao de clientes, produtos, faturas e todos os documentos fiscais com type safety total.
Overview
O Laravel Moloni e um pacote que integra a sua aplicacao Laravel com a API do Moloni, a plataforma de faturacao mais utilizada em Portugal. Oferece uma interface fluente e tipada para gerir empresas, clientes, produtos, faturas e todos os outros recursos Moloni, com type safety total atraves de DTOs e enums.
Tipos de documentos suportados
- Faturas
- Recibos
- Notas de Credito
- Notas de Debito
- Faturas Simplificadas
- Fatura-Recibos
- Guias de Remessa
- Guias de Transporte
- Cartas de Porte
- Orcamentos
- Documentos Genericos
Instalacao
Instale o pacote via Composer, publique a configuracao e execute as migrations.
composer require digitaldev-lx/laravel-moloni
php artisan vendor:publish --tag=moloni-config
php artisan migrate
Configuracao
Adicione as credenciais da API Moloni ao ficheiro .env. As credenciais sao obtidas no Moloni Developer Portal.
MOLONI_CLIENT_ID=your-client-id
MOLONI_CLIENT_SECRET=your-client-secret
MOLONI_USERNAME=your-username
MOLONI_PASSWORD=your-password
MOLONI_COMPANY_ID=your-company-id
Variaveis de ambiente
| Variavel | Descricao |
|---|---|
| MOLONI_CLIENT_ID | Client ID da aplicacao registada no Moloni |
| MOLONI_CLIENT_SECRET | Client Secret da aplicacao |
| MOLONI_USERNAME | Email da conta Moloni |
| MOLONI_PASSWORD | Password da conta Moloni |
| MOLONI_COMPANY_ID | ID da empresa no Moloni |
Clientes
Gerir clientes atraves da facade Moloni. Pode listar, pesquisar por NIF e criar novos clientes usando DTOs tipados ou arrays simples.
use DigitaldevLx\LaravelMoloni\Facades\Moloni;
$companyId = config('moloni.company_id');
// Listar todos os clientes
$customers = Moloni::customers()->getAll($companyId);
// Pesquisar por NIF
$customer = Moloni::customers()->getByVat($companyId, '123456789');
use DigitaldevLx\LaravelMoloni\DataTransferObjects\Customer as CustomerDto;
$dto = new CustomerDto(
vat: '123456789',
number: 'C001',
name: 'John Doe',
email: 'john@example.com',
address: '123 Main Street',
city: 'Lisbon',
zipCode: '1000-001',
countryId: 1,
);
$customer = Moloni::customers()->insert($companyId, $dto);
$customer = Moloni::customers()->insert($companyId, [
'vat' => '123456789',
'number' => 'C001',
'name' => 'John Doe',
'email' => 'john@example.com',
]);
Produtos
Gestao completa do catalogo de produtos com suporte a categorias, tipos e unidades de medida.
$companyId = config('moloni.company_id');
// Listar todos
$products = Moloni::products()->getAll($companyId);
// Pesquisar por referencia
$product = Moloni::products()->getByReference($companyId, 'PROD-001');
use DigitaldevLx\LaravelMoloni\DataTransferObjects\Product as ProductDto;
use DigitaldevLx\LaravelMoloni\Enums\ProductType;
$dto = new ProductDto(
name: 'Widget',
reference: 'PROD-001',
type: ProductType::Product,
categoryId: 1,
unitId: 1,
price: 29.99,
);
$product = Moloni::products()->insert($companyId, $dto);
Documentos e Faturas
Emita faturas e outros documentos fiscais com DTOs tipados para produtos, pagamentos e datas. O pacote suporta todos os tipos de documentos do Moloni.
use DigitaldevLx\LaravelMoloni\DataTransferObjects\Document as DocumentDto;
use DigitaldevLx\LaravelMoloni\DataTransferObjects\DocumentProduct;
use DigitaldevLx\LaravelMoloni\DataTransferObjects\Payment;
$dto = new DocumentDto(
documentSetId: 1,
customerId: 1,
date: '2026-03-31',
expirationDate: '2026-04-30',
products: [
new DocumentProduct(
productId: 1,
qty: 2,
price: 29.99,
),
],
payments: [
new Payment(
paymentMethodId: 1,
value: 59.98,
date: '2026-03-31',
),
],
);
$invoice = Moloni::invoices()->insert($companyId, $dto);
$pdfLink = Moloni::invoices()->getPdfLink($companyId, $invoiceId);
Todos os tipos de documentos
Cada tipo de documento segue a mesma interface. Substitua invoices() pelo metodo correspondente:
Moloni::invoices() // Faturas
Moloni::receipts() // Recibos
Moloni::creditNotes() // Notas de Credito
Moloni::debitNotes() // Notas de Debito
Moloni::simplifiedInvoices() // Faturas Simplificadas
Moloni::invoiceReceipts() // Fatura-Recibos
Moloni::deliveryNotes() // Guias de Remessa
Moloni::billsOfLading() // Cartas de Porte
Moloni::waybills() // Guias de Transporte
Moloni::estimates() // Orcamentos
Moloni::documents() // Documentos Genericos
Trait HasMoloniDocuments
Associe documentos Moloni a qualquer model Eloquent:
use DigitaldevLx\LaravelMoloni\Concerns\HasMoloniDocuments;
class Order extends Model
{
use HasMoloniDocuments;
}
// Aceder aos documentos
$order->moloniDocuments;
Recursos e Configuracoes
Aceda a todos os recursos auxiliares do Moloni -- impostos, metodos de pagamento, series de documentos, armazens e mais.
$companyId = config('moloni.company_id');
$taxes = Moloni::taxes()->getAll($companyId);
$paymentMethods = Moloni::paymentMethods()->getAll($companyId);
$documentSets = Moloni::documentSets()->getAll($companyId);
$warehouses = Moloni::warehouses()->getAll($companyId);
$units = Moloni::measurementUnits()->getAll($companyId);
$maturityDates = Moloni::maturityDates()->getAll($companyId);
$deliveryMethods = Moloni::deliveryMethods()->getAll($companyId);
$bankAccounts = Moloni::bankAccounts()->getAll($companyId);
$countries = Moloni::countries()->getAll();
$currencies = Moloni::currencies()->getAll();
$languages = Moloni::languages()->getAll();
$exemptions = Moloni::taxExemptions()->getAll();
$fiscalZones = Moloni::fiscalZones()->getAll($countryId);
Data Transfer Objects
O pacote fornece DTOs tipados no namespace DigitaldevLx\LaravelMoloni\DataTransferObjects para garantir type safety:
Eventos
O pacote emite eventos para todas as mutacoes, permitindo reagir a criacoes, atualizacoes e alteracoes de estado de documentos.
| Evento | Trigger |
|---|---|
| DocumentCreated | Documento criado com sucesso |
| DocumentCancelled | Documento anulado |
| DocumentClosed | Documento fechado/finalizado |
| CustomerCreated | Novo cliente criado |
| CustomerUpdated | Cliente atualizado |
| ProductCreated | Novo produto criado |
| ProductUpdated | Produto atualizado |
| TokenRefreshed | Token OAuth renovado automaticamente |
Registar listeners
use DigitaldevLx\LaravelMoloni\Events\DocumentCreated;
class EventServiceProvider extends ServiceProvider
{
protected $listen = [
DocumentCreated::class => [
SendInvoiceNotification::class,
],
];
}
Tratamento de Erros
O pacote fornece excepcoes tipadas para cada cenario de erro, todas a estender MoloniException.
| Excepcao | Cenario |
|---|---|
| AuthenticationException | Erros OAuth2 (credenciais invalidas, token expirado) |
| ValidationException | Falhas de validacao de dados |
| RateLimitException | Rate limit da API atingido (HTTP 429) |
| MoloniException | Todos os outros erros da API |
use DigitaldevLx\LaravelMoloni\Exceptions\MoloniException;
use DigitaldevLx\LaravelMoloni\Exceptions\AuthenticationException;
use DigitaldevLx\LaravelMoloni\Exceptions\ValidationException;
use DigitaldevLx\LaravelMoloni\Exceptions\RateLimitException;
try {
$invoice = Moloni::invoices()->insert($companyId, $data);
} catch (AuthenticationException $e) {
// $e->authError - AuthError enum
// $e->errorDescription - Descricao do erro
Log::error('Moloni auth failed', ['error' => $e->authError]);
} catch (ValidationException $e) {
// $e->errors - Array de erros
// $e->getFieldErrors() - Erros por campo
// $e->hasFieldError('vat') - Verificar campo especifico
return back()->withErrors($e->getFieldErrors());
} catch (RateLimitException $e) {
// Retry apos espera
dispatch(fn () => retry(...))->delay(now()->addMinutes(1));
} catch (MoloniException $e) {
// Erro generico da API
Log::error('Moloni error', ['message' => $e->getMessage()]);
}
Erros de validacao
A ValidationException fornece metodos para inspecionar erros campo a campo:
try {
$customer = Moloni::customers()->insert($companyId, $data);
} catch (ValidationException $e) {
// Array completo de erros
$errors = $e->errors;
// Erros organizados por campo
$fieldErrors = $e->getFieldErrors();
// ['vat' => ['NIF invalido'], 'email' => ['Email invalido']]
// Verificar se um campo especifico falhou
if ($e->hasFieldError('vat')) {
// Tratar erro de NIF
}
}
Boas Praticas
Usar DTOs em vez de arrays
Prefira sempre DTOs tipados a arrays associativos. Os DTOs garantem type safety em compile-time, autocompletar no IDE e validacao implicita da estrutura dos dados.
Tratar todas as excepcoes
A API do Moloni pode retornar erros de autenticacao, validacao ou rate limit. Trate cada tipo de excepcao de forma adequada em vez de capturar apenas MoloniException.
Emitir documentos em background
Use jobs e queues para emitir faturas e outros documentos. Isto evita timeouts em requests HTTP e permite retries automaticos em caso de falha.
// App\Jobs\CreateInvoiceJob.php
class CreateInvoiceJob implements ShouldQueue
{
public function __construct(
private Order $order,
private DocumentDto $document,
) {}
public function handle(): void
{
$companyId = config('moloni.company_id');
Moloni::invoices()->insert($companyId, $this->document);
}
}
// Despachar o job
CreateInvoiceJob::dispatch($order, $documentDto);
Reagir a eventos
Use os eventos do pacote para manter a sua aplicacao sincronizada. Por exemplo, envie um email ao cliente quando uma fatura e criada, ou atualize o stock quando um produto e modificado.
Guardar o company_id na config
Use config('moloni.company_id') em vez de hardcoded IDs. Isto facilita a mudanca entre ambientes e empresas de teste.
Usar HasMoloniDocuments nos models
Adicione a trait HasMoloniDocuments aos models que geram documentos fiscais (encomendas, subscricoes, etc.) para manter uma relacao directa entre os registos da aplicacao e os documentos no Moloni.
Testes
O pacote utiliza Pest PHP para testes, PHPStan para analise estatica e Pint para formatacao de codigo.
# Testes
vendor/bin/pest
# Analise estatica
vendor/bin/phpstan analyse
# Formatacao de codigo
vendor/bin/pint
$ composer require digitaldev-lx/laravel-moloni
Pronto para automatizar a faturacao?
Consulte o repositorio no GitHub para a documentacao completa, issues e contribuicoes.