Alanube Service - Ejemplos Prácticos
Ejemplos por Casos de Uso
1. E-commerce - Factura de Venta Online (República Dominicana)
<?php
use App\Services\AlanubeService;
use App\Models\Pacconnection;
// Configuración para tienda online
class EcommerceInvoiceExample
{
public function emitOnlineSaleInvoice($order)
{
$alanubeService = new AlanubeService();
$pacConnection = Pacconnection::where('organization_id', $order->organization_id)
->where('pac_type', 'alanube')
->first();
$documentData = [
'header' => [
'numero_comprobante_fiscal' => $this->generateInvoiceNumber('B01'),
'tipo_documento' => 'FISCAL',
'fecha_comprobante' => now()->format('Y-m-d'),
'fecha_vencimiento' => now()->addDays(30)->format('Y-m-d'),
'moneda' => 'DOP',
'tipo_cambio' => 1.00,
'observaciones' => 'Venta realizada através de tienda online',
'cliente' => [
'tipo_identificacion' => $order->customer->id_type, // 1=RNC, 2=Cédula
'numero_identificacion' => $order->customer->tax_id,
'razon_social' => $order->customer->business_name,
'nombre_comercial' => $order->customer->trade_name,
'direccion' => $order->billing_address->street,
'municipio' => $order->billing_address->city,
'provincia' => $order->billing_address->state,
'pais' => 'DO',
'telefono' => $order->customer->phone,
'email' => $order->customer->email
]
],
'items' => $this->formatOrderItems($order->items),
'totales' => [
'subtotal' => $order->subtotal,
'descuento_total' => $order->discount_amount,
'impuesto_total' => $order->tax_amount,
'total' => $order->total_amount
],
'informacion_adicional' => [
'numero_orden' => $order->order_number,
'metodo_pago' => $order->payment_method,
'canal_venta' => 'ONLINE',
'vendedor' => $order->sales_rep->name ?? 'Sistema Automatizado'
]
];
return $alanubeService->emitDocument($documentData, $pacConnection);
}
private function formatOrderItems($items)
{
return $items->map(function ($item) {
return [
'codigo_producto' => $item->sku,
'descripcion' => $item->product_name,
'cantidad' => $item->quantity,
'precio_unitario' => $item->unit_price,
'descuento' => $item->discount_amount,
'tipo_impuesto' => 'ITBIS',
'tasa_impuesto' => 18.00,
'categoria' => $item->product_category,
'unidad_medida' => $item->unit_of_measure ?? 'UND'
];
})->toArray();
}
}
2. Servicios Profesionales - Factura de Consultoría (Panamá)
<?php
use App\Services\AlanubeService;
class ConsultingInvoiceExample
{
public function emitConsultingInvoice($project, $client)
{
$alanubeService = new AlanubeService();
$pacConnection = $this->getPanamaConnection();
$documentData = [
'header' => [
'numero_documento' => $this->generatePanamaInvoiceNumber(),
'tipo_documento' => 'FACTURA',
'fecha_emision' => now()->toISOString(),
'fecha_vencimiento' => now()->addDays(15)->toISOString(),
'moneda' => 'PAB',
'actividad_economica' => '7020', // Consultoría en gestión empresarial
'cliente' => [
'tipo_identificacion' => 'RUC',
'numero_identificacion' => $client->ruc,
'dv' => $client->verification_digit,
'razon_social' => $client->business_name,
'nombre_comercial' => $client->trade_name,
'direccion' => [
'provincia' => $client->address->province,
'distrito' => $client->address->district,
'corregimiento' => $client->address->corregimiento,
'direccion_detallada' => $client->address->detailed_address,
'codigo_postal' => $client->address->postal_code
],
'telefono' => $client->phone,
'email' => $client->email
]
],
'items' => [
[
'codigo_producto' => 'CONS-001',
'descripcion' => "Consultoría para proyecto: {$project->name}",
'cantidad' => $project->hours_worked,
'unidad_medida' => 'HORA',
'precio_unitario' => $project->hourly_rate,
'descuento' => 0.00,
'codigo_impuesto' => 'ITBMS',
'tasa_impuesto' => 7.00,
'informacion_adicional' => [
'periodo_servicios' => $project->service_period,
'entregables' => $project->deliverables,
'metodologia' => $project->methodology
]
]
],
'forma_pago' => [
'tipo' => 'TRANSFERENCIA',
'plazo' => 15,
'banco' => $this->getCompanyBankInfo()
]
];
return $alanubeService->emitDocument($documentData, $pacConnection);
}
}
3. Retail - Factura de Consumo Final (República Dominicana)
<?php
class RetailInvoiceExample
{
public function emitRetailSale($sale)
{
$alanubeService = new AlanubeService();
$pacConnection = $this->getDominicanConnection();
$documentData = [
'header' => [
'numero_comprobante_fiscal' => $this->generateConsumerInvoiceNumber('B02'),
'tipo_documento' => 'CONSUMO',
'fecha_comprobante' => now()->format('Y-m-d'),
'hora_comprobante' => now()->format('H:i:s'),
'moneda' => 'DOP',
'caja' => $sale->register_number,
'cajero' => $sale->cashier->name,
'cliente' => [
'tipo_identificacion' => 2, // Cédula o consumidor final
'numero_identificacion' => $sale->customer->cedula ?? '00000000000',
'nombre' => $sale->customer->name ?? 'CONSUMIDOR FINAL',
'direccion' => $sale->customer->address ?? 'NO APLICA'
]
],
'items' => $sale->items->map(function ($item) {
return [
'codigo_producto' => $item->barcode,
'descripcion' => $item->product_name,
'cantidad' => $item->quantity,
'precio_unitario' => $item->unit_price,
'descuento' => $item->discount_amount,
'tipo_impuesto' => $item->tax_exempt ? 'EXENTO' : 'ITBIS',
'tasa_impuesto' => $item->tax_exempt ? 0.00 : 18.00,
'lote' => $item->lot_number,
'fecha_vencimiento' => $item->expiry_date
];
})->toArray(),
'totales' => [
'subtotal' => $sale->subtotal,
'descuento_total' => $sale->total_discount,
'impuesto_total' => $sale->total_tax,
'total' => $sale->total_amount,
'efectivo_recibido' => $sale->cash_received,
'cambio' => $sale->change_amount
],
'forma_pago' => [
'efectivo' => $sale->cash_amount,
'tarjeta' => $sale->card_amount,
'transferencia' => $sale->transfer_amount
]
];
return $alanubeService->emitDocument($documentData, $pacConnection);
}
}
4. Devoluciones - Nota de Crédito por Garantía
<?php
class RefundCreditNoteExample
{
public function emitWarrantyRefund($originalInvoice, $refundItems, $reason)
{
$alanubeService = new AlanubeService();
$pacConnection = $this->getConnectionByCountry($originalInvoice->country);
if ($originalInvoice->country === 'panama') {
return $this->emitPanamaCreditNote($originalInvoice, $refundItems, $reason);
} else {
return $this->emitDominicanCreditNote($originalInvoice, $refundItems, $reason);
}
}
private function emitPanamaCreditNote($originalInvoice, $refundItems, $reason)
{
$creditNoteData = [
'header' => [
'numero_documento' => $this->generateCreditNoteNumber(),
'tipo_documento' => 'NOTA_CREDITO',
'fecha_emision' => now()->toISOString(),
'documento_referencia' => [
'numero_documento' => $originalInvoice->document_number,
'fecha_emision' => $originalInvoice->issue_date,
'tipo_documento' => 'FACTURA',
'uuid' => $originalInvoice->uuid
],
'motivo' => $reason,
'tipo_operacion' => 'DEVOLUCION',
'cliente' => [
'tipo_identificacion' => $originalInvoice->customer_id_type,
'numero_identificacion' => $originalInvoice->customer_tax_id,
'razon_social' => $originalInvoice->customer_name
]
],
'items' => $refundItems->map(function ($item) {
return [
'codigo_producto' => $item->product_code,
'descripcion' => "DEVOLUCIÓN: {$item->description}",
'cantidad' => $item->refund_quantity,
'precio_unitario' => $item->unit_price,
'motivo_devolucion' => $item->refund_reason,
'estado_producto' => $item->product_condition, // NUEVO, USADO, DEFECTUOSO
'codigo_impuesto' => 'ITBMS',
'tasa_impuesto' => 7.00
];
})->toArray(),
'proceso_devolucion' => [
'autorizado_por' => auth()->user()->name,
'fecha_autorizacion' => now()->toISOString(),
'metodo_reembolso' => 'TRANSFERENCIA_BANCARIA',
'numero_autorizacion' => $this->generateAuthorizationNumber()
]
];
return $this->alanubeService->emitCreditNoteDocument($creditNoteData, $this->pacConnection);
}
}
5. Exportación - Factura de Exportación (República Dominicana)
<?php
class ExportInvoiceExample
{
public function emitExportInvoice($exportOrder)
{
$alanubeService = new AlanubeService();
$pacConnection = $this->getDominicanConnection();
$documentData = [
'header' => [
'numero_comprobante_fiscal' => $this->generateExportInvoiceNumber('B16'),
'tipo_documento' => 'EXPORTACION',
'fecha_comprobante' => now()->format('Y-m-d'),
'moneda' => $exportOrder->currency, // USD, EUR, etc.
'tipo_cambio' => $exportOrder->exchange_rate,
'incoterm' => $exportOrder->incoterm, // FOB, CIF, EXW, etc.
'puerto_embarque' => $exportOrder->port_of_shipment,
'puerto_destino' => $exportOrder->destination_port,
'cliente' => [
'tipo_identificacion' => 3, // Pasaporte o documento extranjero
'numero_identificacion' => $exportOrder->customer->foreign_tax_id,
'razon_social' => $exportOrder->customer->business_name,
'direccion' => $exportOrder->customer->address,
'ciudad' => $exportOrder->customer->city,
'pais' => $exportOrder->customer->country_code,
'telefono' => $exportOrder->customer->phone,
'email' => $exportOrder->customer->email
]
],
'items' => $exportOrder->items->map(function ($item) {
return [
'codigo_producto' => $item->sku,
'descripcion' => $item->description,
'codigo_arancelario' => $item->hs_code,
'cantidad' => $item->quantity,
'unidad_medida' => $item->unit_of_measure,
'peso_unitario' => $item->unit_weight,
'precio_unitario' => $item->unit_price_usd,
'precio_unitario_dop' => $item->unit_price_dop,
'pais_origen' => $item->country_of_origin,
'tipo_impuesto' => 'EXENTO', // Exportaciones generalmente exentas
'tasa_impuesto' => 0.00
];
})->toArray(),
'transporte' => [
'tipo' => $exportOrder->transport_type, // MARITIMO, AEREO, TERRESTRE
'empresa_transportista' => $exportOrder->carrier_company,
'numero_contenedor' => $exportOrder->container_number,
'numero_precinto' => $exportOrder->seal_number,
'peso_total' => $exportOrder->total_weight,
'volumen_total' => $exportOrder->total_volume
],
'documentos_aduaneros' => [
'declaracion_exportacion' => $exportOrder->export_declaration,
'certificado_origen' => $exportOrder->origin_certificate,
'lista_empaque' => $exportOrder->packing_list
],
'totales' => [
'subtotal_usd' => $exportOrder->subtotal_usd,
'total_usd' => $exportOrder->total_usd,
'subtotal_dop' => $exportOrder->subtotal_dop,
'total_dop' => $exportOrder->total_dop
]
];
return $alanubeService->emitDocument($documentData, $pacConnection);
}
}
6. Manejo de Errores y Reintentos
<?php
class AlanubeErrorHandlingExample
{
private $maxRetries = 3;
private $retryDelay = 5; // segundos
public function emitDocumentWithRetry($documentData, $pacConnection)
{
$attempt = 1;
while ($attempt <= $this->maxRetries) {
try {
$alanubeService = new AlanubeService();
// Validar configuración antes de intentar
if (!$alanubeService->validatePacConfiguration($pacConnection)) {
throw new Exception('Configuración PAC inválida');
}
$response = $alanubeService->emitDocument($documentData, $pacConnection);
if ($response['success']) {
Log::info("Documento emitido exitosamente en intento {$attempt}");
return $response;
}
// Manejar errores específicos
$this->handleSpecificErrors($response);
} catch (ConnectException $e) {
Log::warning("Error de conexión en intento {$attempt}: " . $e->getMessage());
if ($attempt === $this->maxRetries) {
throw new Exception('No se pudo conectar con Alanube después de ' . $this->maxRetries . ' intentos');
}
} catch (ClientException $e) {
$statusCode = $e->getResponse()->getStatusCode();
if (in_array($statusCode, [400, 401, 403])) {
// Errores que no se deben reintentar
throw new Exception('Error de cliente Alanube: ' . $e->getMessage());
}
Log::warning("Error HTTP {$statusCode} en intento {$attempt}");
} catch (Exception $e) {
Log::error("Error general en intento {$attempt}: " . $e->getMessage());
if ($attempt === $this->maxRetries) {
throw $e;
}
}
$attempt++;
if ($attempt <= $this->maxRetries) {
sleep($this->retryDelay);
}
}
throw new Exception('Se agotaron todos los intentos de emisión');
}
private function handleSpecificErrors($response)
{
$errorMessage = $response['message'] ?? '';
// Errores comunes y sus soluciones
if (strpos($errorMessage, 'token') !== false) {
Log::error('Error de token - Verificar configuración PAC');
throw new Exception('Token PAC inválido o expirado');
}
if (strpos($errorMessage, 'validation') !== false) {
Log::error('Error de validación de datos', $response['errors'] ?? []);
throw new Exception('Datos del documento inválidos: ' . $errorMessage);
}
if (strpos($errorMessage, 'duplicate') !== false) {
Log::error('Documento duplicado detectado');
throw new Exception('El número de documento ya existe');
}
}
}
7. Emisión Masiva de Documentos
<?php
use Illuminate\Support\Collection;
class BulkInvoiceExample
{
public function emitBulkInvoices(Collection $orders)
{
$results = collect();
$alanubeService = new AlanubeService();
// Agrupar por conexión PAC (país)
$ordersByConnection = $orders->groupBy('pac_connection_id');
foreach ($ordersByConnection as $connectionId => $connectionOrders) {
$pacConnection = Pacconnection::find($connectionId);
$country = $alanubeService->detectCountryFromPacConnection($pacConnection);
Log::info("Procesando {$connectionOrders->count()} documentos para {$country}");
foreach ($connectionOrders as $order) {
try {
$documentData = $this->formatOrderData($order, $country);
$response = $alanubeService->emitDocument($documentData, $pacConnection);
$results->push([
'order_id' => $order->id,
'success' => $response['success'],
'document_id' => $response['data']['id'] ?? null,
'message' => $response['message'] ?? 'Emitido correctamente'
]);
// Actualizar estado de la orden
$order->update([
'invoice_status' => $response['success'] ? 'emitted' : 'failed',
'alanube_document_id' => $response['data']['id'] ?? null
]);
} catch (Exception $e) {
Log::error("Error emitiendo documento para orden {$order->id}: " . $e->getMessage());
$results->push([
'order_id' => $order->id,
'success' => false,
'message' => $e->getMessage()
]);
$order->update(['invoice_status' => 'failed']);
}
// Pausa para evitar saturar la API
usleep(500000); // 0.5 segundos
}
}
return $results;
}
private function formatOrderData($order, $country)
{
if ($country === 'panama') {
return $this->formatPanamaOrder($order);
} else {
return $this->formatDominicanOrder($order);
}
}
}
8. Testing y Validación
<?php
class AlanubeTestingExample
{
public function testDualCountrySupport()
{
$alanubeService = new AlanubeService();
// Test República Dominicana
$domConnection = $this->createTestConnection('https://sandbox.alanube.co/dom/v1');
$domCountry = $alanubeService->detectCountryFromPacConnection($domConnection);
assert($domCountry === 'dominican_republic');
// Test Panamá
$panConnection = $this->createTestConnection('https://sandbox-api.alanube.co/pan/v1');
$panCountry = $alanubeService->detectCountryFromPacConnection($panConnection);
assert($panCountry === 'panama');
// Test construcción de URLs
$domUrl = $alanubeService->buildApiUrl($domConnection, 'fiscal-invoices');
assert($domUrl === 'https://sandbox.alanube.co/dom/v1/fiscal-invoices');
$panUrl = $alanubeService->buildApiUrl($panConnection, 'credit-notes');
assert($panUrl === 'https://sandbox-api.alanube.co/pan/v1/credit-notes');
echo "✅ Todos los tests de dual-country pasaron correctamente\n";
}
public function testDocumentValidation()
{
$alanubeService = new AlanubeService();
// Test validación de datos mínimos
$invalidData = ['header' => []]; // Datos incompletos
try {
$response = $alanubeService->emitDocument($invalidData, $this->getTestConnection());
assert(false, 'Debería haber fallado con datos inválidos');
} catch (Exception $e) {
echo "✅ Validación de datos funcionando correctamente\n";
}
}
}
Estos ejemplos cubren casos de uso comunes en DocuCenter y demuestran la flexibilidad del servicio Alanube para manejar diferentes tipos de documentos en ambos países.