| Conditions | 13 |
| Paths | 1344 |
| Total Lines | 177 |
| Code Lines | 130 |
| Lines | 0 |
| Ratio | 0 % |
| Changes | 2 | ||
| Bugs | 0 | Features | 0 |
Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.
For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.
Commonly applied refactorings include:
If many parameters/temporary variables are present:
| 1 | <?php |
||
| 96 | public function sendInvoice(\Squareetlabs\VeriFactu\Contracts\VeriFactuInvoice $invoice, ?array $previous = null): array |
||
| 97 | { |
||
| 98 | // 1. Obtener datos del emisor desde config |
||
| 99 | $issuer = config('verifactu.issuer'); |
||
| 100 | $issuerName = $issuer['name'] ?? ''; |
||
| 101 | $issuerVat = $issuer['vat'] ?? ''; |
||
| 102 | |||
| 103 | // 2. Mapear Invoice a estructura AEAT |
||
| 104 | $cabecera = [ |
||
| 105 | 'ObligadoEmision' => [ |
||
| 106 | 'NombreRazon' => $issuerName, |
||
| 107 | 'NIF' => $issuerVat, |
||
| 108 | ], |
||
| 109 | ]; |
||
| 110 | |||
| 111 | // 3. Mapear desgloses (Breakdown) con campos requeridos |
||
| 112 | $breakdowns = $invoice->getBreakdowns(); |
||
| 113 | |||
| 114 | $detalle = []; |
||
| 115 | foreach ($breakdowns as $breakdown) { |
||
| 116 | $detalle[] = [ |
||
| 117 | 'ClaveRegimen' => $breakdown->getRegimeType(), |
||
| 118 | 'CalificacionOperacion' => $breakdown->getOperationType(), |
||
| 119 | 'TipoImpositivo' => (float) $breakdown->getTaxRate(), |
||
| 120 | 'BaseImponibleOimporteNoSujeto' => $this->fmt2($breakdown->getBaseAmount()), |
||
| 121 | 'CuotaRepercutida' => $this->fmt2($breakdown->getTaxAmount()), |
||
| 122 | ]; |
||
| 123 | } |
||
| 124 | |||
| 125 | // Si no hay desgloses, crear uno por defecto |
||
| 126 | if (count($detalle) === 0) { |
||
| 127 | $base = $this->fmt2($invoice->getTotalAmount() - $invoice->getTaxAmount()); |
||
| 128 | $detalle[] = [ |
||
| 129 | 'ClaveRegimen' => '01', |
||
| 130 | 'CalificacionOperacion' => 'S1', |
||
| 131 | 'TipoImpositivo' => 0.0, |
||
| 132 | 'BaseImponibleOimporteNoSujeto' => $base, |
||
| 133 | 'CuotaRepercutida' => $this->fmt2(0), |
||
| 134 | ]; |
||
| 135 | } |
||
| 136 | |||
| 137 | // 4. Generar timestamp y preparar datos para huella |
||
| 138 | $ts = \Carbon\Carbon::now('UTC')->format('c'); |
||
| 139 | $numSerie = (string) $invoice->getInvoiceNumber(); |
||
| 140 | $fechaExp = $invoice->getIssueDate()->format('d-m-Y'); |
||
| 141 | $fechaExpYMD = $invoice->getIssueDate()->format('Y-m-d'); |
||
|
|
|||
| 142 | $tipoFactura = $invoice->getInvoiceType(); |
||
| 143 | $cuotaTotal = $this->fmt2($invoice->getTaxAmount()); |
||
| 144 | $importeTotal = $this->fmt2($invoice->getTotalAmount()); |
||
| 145 | $prevHash = $previous['hash'] ?? $invoice->getPreviousHash() ?? ''; |
||
| 146 | |||
| 147 | // 5. Generar huella (hash) |
||
| 148 | $huella = $this->buildFingerprint( |
||
| 149 | $issuerVat, |
||
| 150 | $numSerie, |
||
| 151 | $fechaExp, |
||
| 152 | $tipoFactura, |
||
| 153 | $cuotaTotal, |
||
| 154 | $importeTotal, |
||
| 155 | $ts, |
||
| 156 | $prevHash |
||
| 157 | ); |
||
| 158 | |||
| 159 | // 6. Construir Encadenamiento |
||
| 160 | $encadenamiento = $previous |
||
| 161 | ? [ |
||
| 162 | 'RegistroAnterior' => [ |
||
| 163 | 'IDEmisorFactura' => $issuerVat, |
||
| 164 | 'NumSerieFactura' => $previous['number'], |
||
| 165 | 'FechaExpedicionFactura' => $previous['date'], |
||
| 166 | 'Huella' => $previous['hash'], |
||
| 167 | ], |
||
| 168 | ] |
||
| 169 | : ['PrimerRegistro' => 'S']; |
||
| 170 | |||
| 171 | // 7. Construir RegistroAlta |
||
| 172 | $registroAlta = [ |
||
| 173 | 'IDVersion' => '1.0', |
||
| 174 | 'IDFactura' => [ |
||
| 175 | 'IDEmisorFactura' => $issuerVat, |
||
| 176 | 'NumSerieFactura' => $numSerie, |
||
| 177 | 'FechaExpedicionFactura' => $fechaExp, |
||
| 178 | ], |
||
| 179 | 'NombreRazonEmisor' => $issuerName, |
||
| 180 | 'TipoFactura' => $tipoFactura, |
||
| 181 | 'DescripcionOperacion' => $invoice->getOperationDescription(), |
||
| 182 | 'Desglose' => ['DetalleDesglose' => $detalle], |
||
| 183 | 'CuotaTotal' => $cuotaTotal, |
||
| 184 | 'ImporteTotal' => $importeTotal, |
||
| 185 | 'Encadenamiento' => $encadenamiento, |
||
| 186 | 'SistemaInformatico' => [ |
||
| 187 | 'NombreRazon' => $issuerName, |
||
| 188 | 'NIF' => $issuerVat, |
||
| 189 | 'NombreSistemaInformatico' => env('APP_NAME', 'LaravelVerifactu'), |
||
| 190 | 'IdSistemaInformatico' => '01', |
||
| 191 | 'Version' => '1.0', |
||
| 192 | 'NumeroInstalacion' => '001', |
||
| 193 | 'TipoUsoPosibleSoloVerifactu' => 'S', |
||
| 194 | 'TipoUsoPosibleMultiOT' => 'N', |
||
| 195 | 'IndicadorMultiplesOT' => 'N', |
||
| 196 | ], |
||
| 197 | 'FechaHoraHusoGenRegistro' => $ts, |
||
| 198 | 'TipoHuella' => '01', |
||
| 199 | 'Huella' => $huella, |
||
| 200 | ]; |
||
| 201 | |||
| 202 | // 8. Mapear destinatarios (opcional, solo si existen) |
||
| 203 | $recipients = $invoice->getRecipients(); |
||
| 204 | if ($recipients->count() > 0) { |
||
| 205 | $destinatarios = []; |
||
| 206 | foreach ($recipients as $recipient) { |
||
| 207 | $r = ['NombreRazon' => $recipient->getName()]; |
||
| 208 | $taxId = $recipient->getTaxId(); |
||
| 209 | if (!empty($taxId)) { |
||
| 210 | $r['NIF'] = $taxId; |
||
| 211 | } |
||
| 212 | $destinatarios[] = $r; |
||
| 213 | } |
||
| 214 | $registroAlta['Destinatarios'] = ['IDDestinatario' => $destinatarios]; |
||
| 215 | } |
||
| 216 | |||
| 217 | $body = [ |
||
| 218 | 'Cabecera' => $cabecera, |
||
| 219 | 'RegistroFactura' => [ |
||
| 220 | ['RegistroAlta' => $registroAlta] |
||
| 221 | ], |
||
| 222 | ]; |
||
| 223 | |||
| 224 | // 9. Configurar SoapClient y enviar |
||
| 225 | $wsdl = $this->production |
||
| 226 | ? 'https://www1.aeat.es/wlpl/TIKE-CONT/ws/SistemaFacturacion/VerifactuSOAP?wsdl' |
||
| 227 | : 'https://prewww2.aeat.es/static_files/common/internet/dep/aplicaciones/es/aeat/tikeV1.0/cont/ws/SistemaFacturacion.wsdl'; |
||
| 228 | $location = $this->production |
||
| 229 | ? 'https://www1.aeat.es/wlpl/TIKE-CONT/ws/SistemaFacturacion/VerifactuSOAP' |
||
| 230 | : 'https://prewww1.aeat.es/wlpl/TIKE-CONT/ws/SistemaFacturacion/VerifactuSOAP'; |
||
| 231 | $options = [ |
||
| 232 | 'local_cert' => $this->certPath, |
||
| 233 | 'passphrase' => $this->certPassword, |
||
| 234 | 'trace' => true, |
||
| 235 | 'exceptions' => true, |
||
| 236 | 'cache_wsdl' => 0, |
||
| 237 | 'soap_version' => SOAP_1_1, |
||
| 238 | 'connection_timeout' => 30, |
||
| 239 | 'stream_context' => stream_context_create([ |
||
| 240 | 'ssl' => [ |
||
| 241 | 'verify_peer' => true, |
||
| 242 | 'verify_peer_name' => true, |
||
| 243 | 'allow_self_signed' => false, |
||
| 244 | 'crypto_method' => STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT, |
||
| 245 | ], |
||
| 246 | 'http' => [ |
||
| 247 | 'user_agent' => 'LaravelVerifactu/1.0', |
||
| 248 | ], |
||
| 249 | ]), |
||
| 250 | ]; |
||
| 251 | |||
| 252 | try { |
||
| 253 | $client = new \SoapClient($wsdl, $options); |
||
| 254 | $client->__setLocation($location); |
||
| 255 | $response = $client->__soapCall('RegFactuSistemaFacturacion', [$body]); |
||
| 256 | return [ |
||
| 257 | 'status' => 'success', |
||
| 258 | 'request' => $client->__getLastRequest(), |
||
| 259 | 'response' => $client->__getLastResponse(), |
||
| 260 | 'aeat_response' => $response, |
||
| 261 | 'hash' => $huella, |
||
| 262 | 'number' => $numSerie, |
||
| 263 | 'date' => $fechaExp, |
||
| 264 | 'timestamp' => $ts, |
||
| 265 | 'first' => $previous ? false : true, |
||
| 266 | ]; |
||
| 267 | } catch (\SoapFault $e) { |
||
| 268 | return [ |
||
| 269 | 'status' => 'error', |
||
| 270 | 'message' => $e->getMessage(), |
||
| 271 | 'request' => isset($client) ? $client->__getLastRequest() : null, |
||
| 272 | 'response' => isset($client) ? $client->__getLastResponse() : null, |
||
| 273 | ]; |
||
| 279 |