160 lines
5.1 KiB
PHP
160 lines
5.1 KiB
PHP
<?php
|
|
|
|
namespace Koneko\VuexyContacts\Services;
|
|
|
|
use Illuminate\Http\UploadedFile;
|
|
use SimpleXMLElement;
|
|
use Exception;
|
|
|
|
class FacturaXmlService
|
|
{
|
|
protected $xml;
|
|
protected $data = [];
|
|
|
|
/**
|
|
* Procesa un archivo XML subido y extrae los datos.
|
|
*
|
|
* @param UploadedFile $file
|
|
* @return array
|
|
* @throws Exception
|
|
*/
|
|
public function processUploadedFile(UploadedFile $file): array
|
|
{
|
|
if (!$file->isValid()) {
|
|
throw new Exception("El archivo no es válido o está corrupto.");
|
|
}
|
|
|
|
// Validamos que sea XML
|
|
if ($file->getClientOriginalExtension() !== 'xml') {
|
|
throw new Exception("Solo se permiten archivos XML.");
|
|
}
|
|
|
|
// Cargar XML desde el contenido del archivo
|
|
$xmlContent = file_get_contents($file->getRealPath());
|
|
$this->loadXml($xmlContent);
|
|
|
|
// Extraer datos del XML
|
|
return $this->extractData();
|
|
}
|
|
|
|
/**
|
|
* Carga el XML y lo convierte en un objeto SimpleXMLElement
|
|
*
|
|
* @param string $xmlContent
|
|
* @throws Exception
|
|
*/
|
|
public function loadXml(string $xmlContent): void
|
|
{
|
|
try {
|
|
$this->xml = new SimpleXMLElement($xmlContent);
|
|
|
|
// Registrar espacios de nombres
|
|
$this->xml->registerXPathNamespace('cfdi', 'http://www.sat.gob.mx/cfd/4');
|
|
$this->xml->registerXPathNamespace('tfd', 'http://www.sat.gob.mx/TimbreFiscalDigital');
|
|
|
|
} catch (Exception $e) {
|
|
throw new Exception('Error al cargar el XML: ' . $e->getMessage());
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Extrae los datos clave de la factura XML
|
|
*
|
|
* @return array
|
|
*/
|
|
public function extractData(): array
|
|
{
|
|
if (!$this->xml) {
|
|
throw new Exception('No se ha cargado un XML válido.');
|
|
}
|
|
|
|
$this->data['comprobante'] = $this->extractComprobante();
|
|
$this->data['emisor'] = $this->extractEmisor();
|
|
$this->data['receptor'] = $this->extractReceptor();
|
|
$this->data['conceptos'] = $this->extractConceptos();
|
|
$this->data['impuestos'] = $this->extractImpuestos();
|
|
$this->data['timbreFiscal'] = $this->extractTimbreFiscal();
|
|
|
|
return $this->data;
|
|
}
|
|
|
|
protected function extractComprobante(): array
|
|
{
|
|
return [
|
|
'fecha' => (string) $this->xml['Fecha'] ?? null,
|
|
'total' => (float) $this->xml['Total'] ?? 0.0,
|
|
'moneda' => (string) $this->xml['Moneda'] ?? 'MXN',
|
|
'tipoDeComprobante' => (string) $this->xml['TipoDeComprobante'] ?? null,
|
|
'metodoPago' => (string) $this->xml['MetodoPago'] ?? null,
|
|
'lugarExpedicion' => (string) $this->xml['LugarExpedicion'] ?? null,
|
|
];
|
|
}
|
|
|
|
protected function extractEmisor(): array
|
|
{
|
|
$emisor = $this->xml->xpath('//cfdi:Emisor')[0] ?? null;
|
|
if (!$emisor) return [];
|
|
|
|
return [
|
|
'rfc' => (string) $emisor['Rfc'] ?? null,
|
|
'nombre' => (string) $emisor['Nombre'] ?? null,
|
|
'regimenFiscal' => (string) $emisor['RegimenFiscal'] ?? null,
|
|
];
|
|
}
|
|
|
|
protected function extractReceptor(): array
|
|
{
|
|
$receptor = $this->xml->xpath('//cfdi:Receptor')[0] ?? null;
|
|
if (!$receptor) return [];
|
|
|
|
return [
|
|
'rfc' => (string) $receptor['Rfc'] ?? null,
|
|
'nombre' => (string) $receptor['Nombre'] ?? null,
|
|
'usoCFDI' => (string) $receptor['UsoCFDI'] ?? null,
|
|
'regimenFiscal' => (string) $receptor['RegimenFiscalReceptor'] ?? null,
|
|
'domicilioFiscal' => (string) $receptor['DomicilioFiscalReceptor'] ?? null,
|
|
];
|
|
}
|
|
|
|
protected function extractConceptos(): array
|
|
{
|
|
$conceptos = [];
|
|
foreach ($this->xml->xpath('//cfdi:Concepto') as $concepto) {
|
|
$conceptos[] = [
|
|
'descripcion' => (string) $concepto['Descripcion'] ?? null,
|
|
'cantidad' => (float) $concepto['Cantidad'] ?? 1.0,
|
|
'valorUnitario' => (float) $concepto['ValorUnitario'] ?? 0.0,
|
|
'importe' => (float) $concepto['Importe'] ?? 0.0,
|
|
'claveProdServ' => (string) $concepto['ClaveProdServ'] ?? null,
|
|
'claveUnidad' => (string) $concepto['ClaveUnidad'] ?? null,
|
|
];
|
|
}
|
|
return $conceptos;
|
|
}
|
|
|
|
protected function extractImpuestos(): array
|
|
{
|
|
$impuestos = $this->xml->xpath('//cfdi:Impuestos')[0] ?? null;
|
|
if (!$impuestos) return [];
|
|
|
|
return [
|
|
'totalImpuestosTrasladados' => (float) $impuestos['TotalImpuestosTrasladados'] ?? 0.0,
|
|
'totalImpuestosRetenidos' => (float) $impuestos['TotalImpuestosRetenidos'] ?? 0.0,
|
|
];
|
|
}
|
|
|
|
protected function extractTimbreFiscal(): array
|
|
{
|
|
$timbre = $this->xml->xpath('//tfd:TimbreFiscalDigital')[0] ?? null;
|
|
if (!$timbre) return [];
|
|
|
|
return [
|
|
'uuid' => (string) $timbre['UUID'] ?? null,
|
|
'fechaTimbrado' => (string) $timbre['FechaTimbrado'] ?? null,
|
|
'selloCFD' => (string) $timbre['SelloCFD'] ?? null,
|
|
'selloSAT' => (string) $timbre['SelloSAT'] ?? null,
|
|
];
|
|
}
|
|
}
|