Passed
Pull Request — master (#921)
by
unknown
08:01
created

Tools   F

Complexity

Total Complexity 142

Size/Duplication

Total Lines 1189
Duplicated Lines 0 %

Test Coverage

Coverage 9.73%

Importance

Changes 31
Bugs 0 Features 0
Metric Value
wmc 142
eloc 760
dl 0
loc 1189
ccs 71
cts 730
cp 0.0973
rs 1.84
c 31
b 0
f 0

23 Methods

Rating   Name   Duplication   Size   Complexity  
A sefazStatus() 0 23 3
A sefazCancela() 0 10 4
A sefazDownload() 0 36 3
A sefazEPP() 0 28 3
A sefazCCe() 0 21 3
B sefazCancelaPorSubstituicao() 0 32 9
B sefazCsc() 0 36 6
B sefazEventoLote() 0 67 7
B sefazValidate() 0 44 8
B sefazInutiliza() 0 84 9
C sefazEPEC() 0 81 12
A sefazConsultaRecibo() 0 26 4
A sefazECPP() 0 20 4
A sefazAtorInteressado() 0 26 4
B sefazCadastro() 0 40 8
A sefazConsultaChave() 0 27 5
C tpEv() 0 71 16
B sefazComprovanteEntrega() 0 36 7
B sefazEnviaLote() 0 51 8
A sefazDistDFe() 0 38 3
B sefazManifestaLote() 0 33 7
B sefazEvento() 0 89 5
A sefazManifesta() 0 11 4

How to fix   Complexity   

Complex Class

Complex classes like Tools often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Tools, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
/**
4
 * Class responsible for communication with SEFAZ extends
5
 * NFePHP\NFe\Common\Tools
6
 *
7
 * @category  NFePHP
8
 * @package   NFePHP\NFe\Tools
9
 * @copyright NFePHP Copyright (c) 2008-2020
10
 * @license   http://www.gnu.org/licenses/lgpl.txt LGPLv3+
11
 * @license   https://opensource.org/licenses/MIT MIT
12
 * @license   http://www.gnu.org/licenses/gpl.txt GPLv3+
13
 * @author    Roberto L. Machado <linux.rlm at gmail dot com>
14
 * @link      http://github.com/nfephp-org/sped-nfe for the canonical source repository
15
 */
16
17
namespace NFePHP\NFe;
18
19
use NFePHP\Common\Strings;
20
use NFePHP\Common\Signer;
21
use NFePHP\Common\UFList;
22
use NFePHP\NFe\Common\Tools as ToolsCommon;
23
use RuntimeException;
24
use InvalidArgumentException;
25
26
class Tools extends ToolsCommon
27
{
28
    const EVT_CONFIRMACAO = 210200; //only one per nfe seq=n
29
    const EVT_CIENCIA = 210210; //only one per nfe seq=1
30
    const EVT_DESCONHECIMENTO = 210220; //only one per nfe seq=n
31
    const EVT_NAO_REALIZADA = 210240; //only one per nfe but seq=n
32
    const EVT_CCE = 110110; //many seq=n
33
    const EVT_CANCELA = 110111; //only seq=1
34
    const EVT_CANCELASUBSTITUICAO = 110112;
35
    const EVT_EPEC = 110140; //only seq=1
36
    const EVT_ATORINTERESSADO = 110150; //many seq=n
37
    const EVT_COMPROVANTE_ENTREGA = 110130; //many seq=n
38
    const EVT_CANCELAMENTO_COMPROVANTE_ENTREGA = 110131; ///many seq=n
39
    const EVT_PRORROGACAO_1 = 111500;
40
    const EVT_PRORROGACAO_2 = 111501;
41
    const EVT_CANCELA_PRORROGACAO_1 = 111502;
42
    const EVT_CANCELA_PRORROGACAO_2 = 111503;
43
44
    /**
45
     * Request authorization to issue NFe in batch with one or more documents
46
     * @param array $aXml array of nfe's xml
47
     * @param string $idLote lote number
48
     * @param int $indSinc flag to use synchronous communication
49
     * @param bool $compactar flag to compress data with gzip
50
     * @param array $xmls array with xmls substitutes if contigency is on
51
     * @return string soap response xml
52
     * @throws InvalidArgumentException
53
     */
54 5
    public function sefazEnviaLote(
55
        $aXml,
56
        $idLote = '',
57
        $indSinc = 0,
58
        $compactar = false,
59
        &$xmls = []
60
    ) {
61 5
        if (!is_array($aXml)) {
0 ignored issues
show
introduced by
The condition is_array($aXml) is always true.
Loading history...
62 1
            throw new InvalidArgumentException('Envia Lote: XMLs de NF-e deve ser um array!');
63
        }
64 4
        if ($indSinc == 1 && count($aXml) > 1) {
65 1
            throw new InvalidArgumentException('Envio sincrono deve ser usado para enviar '
66 1
                . 'uma UNICA nota por vez. Você está tentando enviar varias.');
67
        }
68 3
        $servico = 'NfeAutorizacao';
69 3
        $this->checkContingencyForWebServices($servico);
70 3
        if ($this->contingency->type != '') {
71
            // Em modo de contingencia esses XMLs deverão ser modificados e re-assinados e retornados
72
            // no parametro $xmls para serem armazenados pelo aplicativo pois serão alterados.
73
            foreach ($aXml as $doc) {
74
                //corrigir o xml para o tipo de contigência setado
75
                $xmls[] = $this->correctNFeForContingencyMode($doc);
76
            }
77
            $aXml = $xmls;
78
        }
79 3
        $ax = [];
80 3
        foreach ($aXml as $xml) {
81
            //verifica se o modelo do XML é o mesmo setado na classe
82
            //gera um exception se não for
83 3
            $this->checkModelFromXml($xml);
84 3
            $ax[] = trim(preg_replace("/<\?xml.*?\?>/", "", $xml));
85
        }
86 3
        $sxml = trim(implode("", $ax));
87 3
        $this->servico($servico, $this->config->siglaUF, $this->tpAmb);
88 3
        $request = "<enviNFe xmlns=\"$this->urlPortal\" versao=\"$this->urlVersion\">"
89 3
            . "<idLote>$idLote</idLote>"
90 3
            . "<indSinc>$indSinc</indSinc>"
91 3
            . "$sxml"
92 3
            . "</enviNFe>";
93 3
        $this->isValid($this->urlVersion, $request, 'enviNFe');
94 3
        $this->lastRequest = $request;
95
        //montagem dos dados da mensagem SOAP
96 3
        $parameters = ['nfeDadosMsg' => $request];
97 3
        $body = "<nfeDadosMsg xmlns=\"$this->urlNamespace\">$request</nfeDadosMsg>";
98 3
        if ($compactar) {
99 1
            $gzdata = base64_encode(gzencode($request, 9, FORCE_GZIP));
100 1
            $parameters = ['nfeDadosMsgZip' => $gzdata];
101 1
            $body = "<nfeDadosMsgZip xmlns=\"$this->urlNamespace\">$gzdata</nfeDadosMsgZip>";
102
        }
103 3
        $this->lastResponse = $this->sendRequest($body, $parameters);
104 3
        return $this->lastResponse;
105
    }
106
107
    /**
108
     * Check status of Batch of NFe sent by receipt of this shipment
109
     * @param string $recibo
110
     * @param int $tpAmb
111
     * @return string
112
     * @throws InvalidArgumentException
113
     */
114 2
    public function sefazConsultaRecibo($recibo, $tpAmb = null)
115
    {
116 2
        if (empty($recibo)) {
117 1
            throw new InvalidArgumentException('Consulta Recibo: numero do recibo vazio!');
118
        }
119 1
        if (empty($tpAmb)) {
120 1
            $tpAmb = $this->tpAmb;
121
        }
122
        //carrega serviço
123 1
        $servico = 'NfeRetAutorizacao';
124 1
        $this->checkContingencyForWebServices($servico);
125 1
        $this->servico($servico, $this->config->siglaUF, $tpAmb);
126 1
        if ($this->urlService == '') {
127
            $msg = "A consulta de NFe nao esta disponivel na SEFAZ {$this->config->siglaUF}!";
128
            throw new RuntimeException($msg);
129
        }
130 1
        $request = "<consReciNFe xmlns=\"$this->urlPortal\" versao=\"$this->urlVersion\">"
131 1
            . "<tpAmb>$tpAmb</tpAmb>"
132 1
            . "<nRec>$recibo</nRec>"
133 1
            . "</consReciNFe>";
134 1
        $this->isValid($this->urlVersion, $request, 'consReciNFe');
135 1
        $this->lastRequest = $request;
136 1
        $parameters = ['nfeDadosMsg' => $request];
137 1
        $body = "<nfeDadosMsg xmlns=\"$this->urlNamespace\">$request</nfeDadosMsg>";
138 1
        $this->lastResponse = $this->sendRequest($body, $parameters);
139 1
        return $this->lastResponse;
140
    }
141
142
    /**
143
     * Check the NFe status for the 44-digit key and retrieve the protocol
144
     * @param string $chave
145
     * @param int $tpAmb
146
     * @return string
147
     * @throws InvalidArgumentException
148
     */
149 4
    public function sefazConsultaChave($chave, $tpAmb = null)
150
    {
151 4
        if (empty($chave)) {
152 1
            throw new InvalidArgumentException('Consulta chave: a chave esta vazia!');
153
        }
154 3
        if (strlen($chave) != 44 || !is_numeric($chave)) {
155 2
            throw new InvalidArgumentException("Consulta chave: chave \"$chave\" invalida!");
156
        }
157 1
        $uf = UFList::getUFByCode((int)substr($chave, 0, 2));
158 1
        if (empty($tpAmb)) {
159 1
            $tpAmb = $this->tpAmb;
160
        }
161
        //carrega serviço
162 1
        $servico = 'NfeConsultaProtocolo';
163 1
        $this->checkContingencyForWebServices($servico);
164 1
        $this->servico($servico, $uf, $tpAmb);
165 1
        $request = "<consSitNFe xmlns=\"$this->urlPortal\" versao=\"$this->urlVersion\">"
166 1
            . "<tpAmb>$tpAmb</tpAmb>"
167 1
            . "<xServ>CONSULTAR</xServ>"
168 1
            . "<chNFe>$chave</chNFe>"
169 1
            . "</consSitNFe>";
170 1
        $this->isValid($this->urlVersion, $request, 'consSitNFe');
171 1
        $this->lastRequest = $request;
172 1
        $parameters = ['nfeDadosMsg' => $request];
173 1
        $body = "<nfeDadosMsg xmlns=\"$this->urlNamespace\">$request</nfeDadosMsg>";
174 1
        $this->lastResponse = $this->sendRequest($body, $parameters);
175 1
        return $this->lastResponse;
176
    }
177
178
    /**
179
     * Request to disable one or an NFe sequence of a given series
180
     * @param int $nSerie
181
     * @param int $nIni
182
     * @param int $nFin
183
     * @param string $xJust
184
     * @param int $tpAmb
185
     * @param string $ano
186
     * @return string
187
     * @throws InvalidArgumentException
188
     */
189
    public function sefazInutiliza($nSerie, $nIni, $nFin, $xJust, $tpAmb = null, $ano = null)
190
    {
191
        if (empty($nIni) || empty($nFin) || empty($xJust)) {
192
            throw new InvalidArgumentException('Inutilizacao: parametros incompletos!');
193
        }
194
        if (empty($tpAmb)) {
195
            $tpAmb = $this->tpAmb;
196
        }
197
        $xJust = Strings::replaceUnacceptableCharacters($xJust);
198
        $servico = 'NfeInutilizacao';
199
        $this->checkContingencyForWebServices($servico);
200
        //carrega serviço
201
        $this->servico($servico, $this->config->siglaUF, $tpAmb);
202
        $cnpj = $this->config->cnpj;
203
        $strAno = $ano;
204
        if (empty($ano)) {
205
            $strAno = (string) date('y');
206
        }
207
        $strSerie = str_pad((string)$nSerie, 3, '0', STR_PAD_LEFT);
208
        $strInicio = str_pad((string)$nIni, 9, '0', STR_PAD_LEFT);
209
        $strFinal = str_pad((string)$nFin, 9, '0', STR_PAD_LEFT);
210
        $idInut = "ID"
211
            . $this->urlcUF
212
            . $strAno
213
            . str_pad($cnpj, 14, '0', STR_PAD_LEFT)
214
            . $this->modelo
215
            . $strSerie
216
            . $strInicio
217
            . $strFinal;
218
        //limpa os caracteres indesejados da justificativa
219
        $xJust = Strings::replaceUnacceptableCharacters($xJust);
220
        //montagem do corpo da mensagem
221
        $msg = "<inutNFe xmlns=\"$this->urlPortal\" versao=\"$this->urlVersion\">" .
222
            "<infInut Id=\"$idInut\">" .
223
            "<tpAmb>$tpAmb</tpAmb>" .
224
            "<xServ>INUTILIZAR</xServ>" .
225
            "<cUF>$this->urlcUF</cUF>" .
226
            "<ano>$strAno</ano>" .
227
            "<CNPJ>$cnpj</CNPJ>" .
228
            "<mod>$this->modelo</mod>" .
229
            "<serie>$nSerie</serie>" .
230
            "<nNFIni>$nIni</nNFIni>" .
231
            "<nNFFin>$nFin</nNFFin>" .
232
            "<xJust>$xJust</xJust>" .
233
            "</infInut></inutNFe>";
234
        //inutizaçao para produtor rural com CPF em MT
235
        $flag = false;
236
        if ($this->config->siglaUF == 'MT' && strlen($cnpj) == 11) {
237
            //montagem do corpo da mensagem
238
            $msg = "<inutNFe xmlns=\"$this->urlPortal\" versao=\"$this->urlVersion\">" .
239
                "<infInut Id=\"$idInut\">" .
240
                "<tpAmb>$tpAmb</tpAmb>" .
241
                "<xServ>INUTILIZAR</xServ>" .
242
                "<cUF>$this->urlcUF</cUF>" .
243
                "<ano>$strAno</ano>" .
244
                "<CPF>$cnpj</CPF>" .
245
                "<mod>$this->modelo</mod>" .
246
                "<serie>$nSerie</serie>" .
247
                "<nNFIni>$nIni</nNFIni>" .
248
                "<nNFFin>$nFin</nNFFin>" .
249
                "<xJust>$xJust</xJust>" .
250
                "</infInut></inutNFe>";
251
            $flag = true;
252
        }
253
        //assina a solicitação
254
        $request = Signer::sign(
255
            $this->certificate,
256
            $msg,
257
            'infInut',
258
            'Id',
259
            $this->algorithm,
260
            $this->canonical
261
        );
262
        $request = Strings::clearXmlString($request, true);
263
        if (!$flag) {
264
            $this->isValid($this->urlVersion, $request, 'inutNFe');
265
        } else {
266
            $this->isValid($this->urlVersion, $request, 'MT_inutNFe');
267
        }
268
        $this->lastRequest = $request;
269
        $parameters = ['nfeDadosMsg' => $request];
270
        $body = "<nfeDadosMsg xmlns=\"$this->urlNamespace\">$request</nfeDadosMsg>";
271
        $this->lastResponse = $this->sendRequest($body, $parameters);
272
        return $this->lastResponse;
273
    }
274
275
    /**
276
     * Search for the registration data of an NFe issuer,
277
     * if in contingency mode this service will cause a
278
     * Exception and remember not all Sefaz have this service available,
279
     * so it will not work in some cases.
280
     * @param string $uf federation unit (abbreviation)
281
     * @param string $cnpj CNPJ number (optional)
282
     * @param string $iest IE number (optional)
283
     * @param string $cpf CPF number (optional)
284
     * @return string xml soap response
285
     * @throws InvalidArgumentException
286
     */
287
    public function sefazCadastro($uf, $cnpj = '', $iest = '', $cpf = '')
288
    {
289
        $filter = '';
290
        if (!empty($cnpj)) {
291
            $filter = "<CNPJ>$cnpj</CNPJ>";
292
        } elseif (!empty($iest)) {
293
            $filter = "<IE>$iest</IE>";
294
        } elseif (!empty($cpf)) {
295
            $filter = "<CPF>$cpf</CPF>";
296
        }
297
        if (empty($uf) || empty($filter)) {
298
            throw new InvalidArgumentException('Sigla UF esta vazia ou CNPJ+IE+CPF vazios!');
299
        }
300
        //carrega serviço
301
        $servico = 'NfeConsultaCadastro';
302
        $this->checkContingencyForWebServices($servico);
303
        $this->servico($servico, $uf, $this->tpAmb, true);
304
        $request = "<ConsCad xmlns=\"$this->urlPortal\" versao=\"$this->urlVersion\">"
305
            . "<infCons>"
306
            . "<xServ>CONS-CAD</xServ>"
307
            . "<UF>$uf</UF>"
308
            . "$filter"
309
            . "</infCons>"
310
            . "</ConsCad>";
311
        if (strtoupper($uf) == 'MT') {
312
            $request = "<nfeDadosMsg>$request</nfeDadosMsg>" ;
313
        }
314
        $this->isValid($this->urlVersion, $request, 'consCad');
315
        $this->lastRequest = $request;
316
        $parameters = ['nfeDadosMsg' => $request];
317
        if ($this->urlVersion === '2.00') {
318
            $this->objHeader = new \SoapHeader(
319
                $this->urlNamespace,
320
                'nfeCabecMsg',
321
                ['cUF' => $this->urlcUF, 'versaoDados' => $this->urlVersion]
322
            );
323
        }
324
        $body = "<nfeDadosMsg xmlns=\"$this->urlNamespace\">$request</nfeDadosMsg>";
325
        $this->lastResponse = $this->sendRequest($body, $parameters);
326
        return $this->lastResponse;
327
    }
328
329
    /**
330
     * Check services status SEFAZ/SVC
331
     * If $uf is empty use normal check with contingency
332
     * If $uf is NOT empty ignore contingency mode
333
     * @param string $uf  initials of federation unit
334
     * @param int $tpAmb
335
     * @param bool $ignoreContingency
336
     * @return string xml soap response
337
     */
338
    public function sefazStatus($uf = '', $tpAmb = null, $ignoreContingency = true)
339
    {
340
        if (empty($tpAmb)) {
341
            $tpAmb = $this->tpAmb;
342
        }
343
        if (empty($uf)) {
344
            $uf = $this->config->siglaUF;
345
            $ignoreContingency = false;
346
        }
347
        $servico = 'NfeStatusServico';
348
        $this->checkContingencyForWebServices($servico);
349
        $this->servico($servico, $uf, $tpAmb, $ignoreContingency);
350
        $request = "<consStatServ xmlns=\"$this->urlPortal\" versao=\"$this->urlVersion\">"
351
            . "<tpAmb>$tpAmb</tpAmb>"
352
            . "<cUF>$this->urlcUF</cUF>"
353
            . "<xServ>STATUS</xServ>"
354
            . "</consStatServ>";
355
        $this->isValid($this->urlVersion, $request, 'consStatServ');
356
        $this->lastRequest = $request;
357
        $parameters = ['nfeDadosMsg' => $request];
358
        $body = "<nfeDadosMsg xmlns=\"$this->urlNamespace\">$request</nfeDadosMsg>";
359
        $this->lastResponse = $this->sendRequest($body, $parameters);
360
        return $this->lastResponse;
361
    }
362
363
    /**
364
     * Service for the distribution of summary information and
365
     * electronic tax documents of interest to an actor.
366
     * @param integer $ultNSU  last NSU number recived
367
     * @param integer $numNSU  NSU number you wish to consult
368
     * @param string $fonte data source 'AN' and for some cases it may be 'RS'
369
     * @return string
370
     */
371
    public function sefazDistDFe($ultNSU = 0, $numNSU = 0, $fonte = 'AN')
372
    {
373
        //carrega serviço
374
        $servico = 'NfeDistribuicaoDFe';
375
        $this->checkContingencyForWebServices($servico);
376
        $this->servico($servico, $fonte, $this->tpAmb, true);
377
        $cUF = UFList::getCodeByUF($this->config->siglaUF);
378
        $cnpj = $this->config->cnpj;
379
        $ultNSU = str_pad((string)$ultNSU, 15, '0', STR_PAD_LEFT);
380
        $tagNSU = "<distNSU><ultNSU>$ultNSU</ultNSU></distNSU>";
381
        if ($numNSU != 0) {
382
            $numNSU = str_pad((string)$numNSU, 15, '0', STR_PAD_LEFT);
383
            $tagNSU = "<consNSU><NSU>$numNSU</NSU></consNSU>";
384
        }
385
        //monta a consulta
386
        $consulta = "<distDFeInt xmlns=\"$this->urlPortal\" versao=\"$this->urlVersion\">"
387
            . "<tpAmb>" . $this->tpAmb . "</tpAmb>"
388
            . "<cUFAutor>$cUF</cUFAutor>";
389
        if ($this->typePerson === 'J') {
390
            $consulta .= "<CNPJ>$cnpj</CNPJ>";
391
        } else {
392
            $consulta .= "<CPF>$cnpj</CPF>";
393
        }
394
        $consulta .= "$tagNSU"
395
            . "</distDFeInt>";
396
        //valida o xml da requisição
397
        $this->isValid($this->urlVersion, $consulta, 'distDFeInt');
398
        $this->lastRequest = $consulta;
399
        //montagem dos dados da mensagem SOAP
400
        $request = "<nfeDadosMsg xmlns=\"$this->urlNamespace\">$consulta</nfeDadosMsg>";
401
        $parameters = ['nfeDistDFeInteresse' => $request];
402
        $body = "<nfeDistDFeInteresse xmlns=\"$this->urlNamespace\">"
403
            . $request
404
            . "</nfeDistDFeInteresse>";
405
        //este webservice não requer cabeçalho
406
        $this->objHeader = null;
407
        $this->lastResponse = $this->sendRequest($body, $parameters);
408
        return $this->lastResponse;
409
    }
410
411
    /**
412
     * Request authorization for Letter of Correction
413
     * @param string $chave
414
     * @param string $xCorrecao
415
     * @param int $nSeqEvento
416
     * @return string
417
     * @throws InvalidArgumentException
418
     */
419
    public function sefazCCe($chave, $xCorrecao, $nSeqEvento = 1)
420
    {
421
        if (empty($chave) || empty($xCorrecao)) {
422
            throw new InvalidArgumentException('CC-e: chave ou motivo da correcao vazio!');
423
        }
424
        $uf = $this->validKeyByUF($chave);
425
        $xCorrecao = Strings::replaceUnacceptableCharacters(substr(trim($xCorrecao), 0, 1000));
426
        $xCondUso = 'A Carta de Correcao e disciplinada pelo paragrafo '
427
            . '1o-A do art. 7o do Convenio S/N, de 15 de dezembro de 1970 '
428
            . 'e pode ser utilizada para regularizacao de erro ocorrido '
429
            . 'na emissao de documento fiscal, desde que o erro nao esteja '
430
            . 'relacionado com: I - as variaveis que determinam o valor '
431
            . 'do imposto tais como: base de calculo, aliquota, '
432
            . 'diferenca de preco, quantidade, valor da operacao ou da '
433
            . 'prestacao; II - a correcao de dados cadastrais que implique '
434
            . 'mudanca do remetente ou do destinatario; III - a data de '
435
            . 'emissao ou de saida.';
436
        $tagAdic = "<xCorrecao>"
437
            . $xCorrecao
438
            . "</xCorrecao><xCondUso>$xCondUso</xCondUso>";
439
        return $this->sefazEvento($uf, $chave, self::EVT_CCE, $nSeqEvento, $tagAdic);
440
    }
441
442
    /**
443
     * Evento do Ator Interessado
444
     * NOTA: NT2020.007_v1.00a
445
     * @param \stdClass $std
446
     * @return string
447
     */
448
    public function sefazAtorInteressado(\stdClass $std)
449
    {
450
        $xCondUso = 'O emitente ou destinatário da NF-e, declara que permite o '
451
            . 'transportador declarado no campo CNPJ/CPF deste evento a '
452
            . 'autorizar os transportadores subcontratados ou redespachados a '
453
            . 'terem acesso ao download da NF-e';
454
        if (empty($std->verAplic) && !empty($this->verAplic)) {
455
            $std->verAplic = $this->verAplic;
456
        }
457
        $cUF = UFList::getCodeByUF($this->config->siglaUF);
458
        $tagAdic = "<cOrgaoAutor>{$cUF}</cOrgaoAutor>"
459
            . "<tpAutor>{$std->tpAutor}</tpAutor>"
460
            . "<verAplic>{$std->verAplic}</verAplic>"
461
            . "<autXML>";
462
        $tagAdic .=  !empty($std->CNPJ)
463
            ? "<CNPJ>{$std->CNPJ}</CNPJ>"
464
            : "<CPF>{$std->CPF}</CPF>";
465
        $tagAdic .= "</autXML>"
466
            . "<tpAutorizacao>{$std->tpAutorizacao}</tpAutorizacao>"
467
            . "<xCondUso>$xCondUso</xCondUso>";
468
        return $this->sefazEvento(
469
            'AN',
470
            $std->chNFe,
471
            self::EVT_ATORINTERESSADO,
472
            $std->nSeqEvento,
473
            $tagAdic
474
        );
475
    }
476
477
    /**
478
     * Request extension of the term of return of products of an NF-e of
479
     * consignment for industrialization to order with suspension of ICMS
480
     * in interstate operations
481
     * @param string $chNFe
482
     * @param string $nProt
483
     * @param integer $tipo 1-primerio prazo, 2-segundo prazo
484
     * @param array $itens
485
     * @param integer $nSeqEvento
486
     * @return string
487
     */
488
    public function sefazEPP(
489
        $chNFe,
490
        $nProt,
491
        $itens = [],
492
        $tipo = 1,
493
        $nSeqEvento = 1
494
    ) {
495
        $uf = UFList::getUFByCode((int)substr($chNFe, 0, 2));
496
        //pedido de prorrogação primero prazo
497
        $tpEvento = 111500;
498
        if ($tipo == 2) {
499
            //pedido de prorrogação segundo prazo
500
            $tpEvento = 111501;
501
        }
502
        $tagAdic = "<nProt>$nProt</nProt>";
503
        foreach ($itens as $item) {
504
            $tagAdic .= "<itemPedido numItem=\""
505
                . $item[0]
506
                . "\"><qtdeItem>"
507
                . $item[1]
508
                . "</qtdeItem></itemPedido>";
509
        }
510
        return $this->sefazEvento(
511
            $uf,
512
            $chNFe,
513
            $tpEvento,
514
            $nSeqEvento,
515
            $tagAdic
516
        );
517
    }
518
519
    /**
520
     * Request the cancellation of the request for an extension of the term
521
     * of return of products of an NF-e of consignment for industrialization
522
     * by order with suspension of ICMS in interstate operations
523
     * @param string $chave
524
     * @param string $nProt
525
     * @param integer $tipo 1-primerio prazo, 2-segundo prazo
526
     * @param integer $nSeqEvento
527
     * @return string
528
     * @throws InvalidArgumentException
529
     */
530
    public function sefazECPP($chave, $nProt, $tipo, $nSeqEvento = 1)
531
    {
532
        if (empty($chave) || empty($nProt)) {
533
            throw new InvalidArgumentException('A chave ou o numero do protocolo estão vazios!');
534
        }
535
        $uf = UFList::getUFByCode((int)substr($chave, 0, 2));
536
        $tpEvento = self::EVT_CANCELA_PRORROGACAO_1; //111502;
537
        $origEvent = self::EVT_PRORROGACAO_1; //111500;
538
        if ($tipo == 2) {
539
            //pedido de cancelamento do segundo prazo
540
            $tpEvento = self::EVT_CANCELA_PRORROGACAO_2; //111503;
541
            $origEvent = self::EVT_PRORROGACAO_2; //111501;
542
        }
543
        $sSeqEvento = str_pad((string)$nSeqEvento, 2, "0", STR_PAD_LEFT);
544
        $idPedidoCancelado = "ID{$origEvent}{$chave}{$sSeqEvento}";
545
        $tagAdic = "<idPedidoCancelado>"
546
                . "$idPedidoCancelado"
547
                . "</idPedidoCancelado>"
548
                . "<nProt>$nProt</nProt>";
549
        return $this->sefazEvento($uf, $chave, $tpEvento, $nSeqEvento, $tagAdic);
550
    }
551
552
    /**
553
     * Requires nfe cancellation
554
     * @param  string $chave key of NFe
555
     * @param  string $xJust justificative 255 characters max
556
     * @param  string $nProt protocol number
557
     * @return string
558
     * @throws InvalidArgumentException
559
     */
560
    public function sefazCancela($chave, $xJust, $nProt)
561
    {
562
        if (empty($chave) || empty($xJust) || empty($nProt)) {
563
            throw new InvalidArgumentException('Cancelamento: chave, just ou numprot vazio!');
564
        }
565
        $uf = $this->validKeyByUF($chave);
566
        $xJust = Strings::replaceUnacceptableCharacters(substr(trim($xJust), 0, 255));
567
        $nSeqEvento = 1;
568
        $tagAdic = "<nProt>$nProt</nProt><xJust>$xJust</xJust>";
569
        return $this->sefazEvento($uf, $chave, self::EVT_CANCELA, $nSeqEvento, $tagAdic);
570
    }
571
572
    /**
573
     * Requires nfe cancellation by substitution
574
     * @param string $chave key of NFe
575
     * @param string $xJust justificative 255 characters max
576
     * @param string $nProt protocol number
577
     * @param string $chNFeRef key of New NFe
578
     * @param string $verAplic version of applicative
579
     * @return string
580
     * @throws InvalidArgumentException
581
     */
582
    public function sefazCancelaPorSubstituicao($chave, $xJust, $nProt, $chNFeRef, $verAplic = null)
583
    {
584
        if ($this->modelo != 65) {
585
            throw new InvalidArgumentException(
586
                'Cancelamento pro Substituição deve ser usado apenas para '
587
                . 'operações com modelo 65 NFCe'
588
            );
589
        }
590
        if (empty($verAplic) && !empty($this->verAplic)) {
591
            $verAplic = $this->verAplic;
592
        }
593
        if (
594
            empty($chave) || empty($xJust) || empty($nProt)
595
            || empty($chNFeRef) || empty($verAplic)
596
        ) {
597
            throw new InvalidArgumentException(
598
                'CancelamentoPorSubs: chave da NFCe cancelada, justificativa, '
599
                . 'protocolo, chave da NFCe substituta, ou versão do aplicativo '
600
                . 'emissor não podem ser vazios!'
601
            );
602
        }
603
        $uf = $this->validKeyByUF($chave);
604
        $xJust = Strings::replaceUnacceptableCharacters(substr(trim($xJust), 0, 255));
605
        $nSeqEvento = 1;
606
        $cOrgao = substr($chave, 0, 2);
607
        $tagAdic = "<cOrgaoAutor>$cOrgao</cOrgaoAutor>"
608
            . "<tpAutor>1</tpAutor>"
609
            . "<verAplic>$verAplic</verAplic>"
610
            . "<nProt>$nProt</nProt>"
611
            . "<xJust>$xJust</xJust>"
612
            . "<chNFeRef>$chNFeRef</chNFeRef>";
613
        return $this->sefazEvento($uf, $chave, self::EVT_CANCELASUBSTITUICAO, $nSeqEvento, $tagAdic);
614
    }
615
616
    /**
617
     * Request the registration of the manifestation of recipient
618
     * @param string $chave
619
     * @param int $tpEvento
620
     * @param string $xJust Justification for not carrying out the operation
621
     * @param int $nSeqEvento
622
     * @return string
623
     * @throws InvalidArgumentException
624
     */
625
    public function sefazManifesta($chave, $tpEvento, $xJust = '', $nSeqEvento = 1)
626
    {
627
        if (empty($chave) || empty($tpEvento)) {
628
            throw new InvalidArgumentException('Manifestacao: chave ou tipo de evento vazio!');
629
        }
630
        $tagAdic = '';
631
        if ($tpEvento == self::EVT_NAO_REALIZADA) {
632
            $xJust = Strings::replaceUnacceptableCharacters(substr(trim($xJust), 0, 255));
633
            $tagAdic = "<xJust>$xJust</xJust>";
634
        }
635
        return $this->sefazEvento('AN', $chave, $tpEvento, $nSeqEvento, $tagAdic);
636
    }
637
638
    /**
639
     * Request the registration of the manifestation of recipient in batch
640
     * @param \stdClass $std
641
     * @return string
642
     * @throws InvalidArgumentException
643
     * @throws RuntimeException
644
     */
645
    public function sefazManifestaLote(?\stdClass $std)
646
    {
647
        $allowed = [
648
            self::EVT_CONFIRMACAO,
649
            self::EVT_CIENCIA,
650
            self::EVT_DESCONHECIMENTO,
651
            self::EVT_NAO_REALIZADA,
652
        ];
653
        if (!$std || empty($std->evento)) {
654
            throw new InvalidArgumentException('Manifestacao: parametro "std" ou evento estao vazios!');
655
        }
656
        if (count($std->evento) > 20) {
657
            throw new RuntimeException('Manifestacao: o lote de eventos esta limitado a 20!');
658
        }
659
        $evt = new \stdClass();
660
        $i = 0;
661
        foreach ($std->evento as $s) {
662
            if (!in_array($s->tpEvento, $allowed)) { // se o evento não estiver entre os permitidos ignore
663
                continue;
664
            }
665
            $tagAdic = '';
666
            if ($s->tpEvento == self::EVT_NAO_REALIZADA) {
667
                $xJust = Strings::replaceUnacceptableCharacters(substr(trim($s->xJust), 0, 255));
668
                $tagAdic = "<xJust>$xJust</xJust>";
669
            }
670
            $evt->evento[$i] = new \stdClass();
671
            $evt->evento[$i]->chave = $s->chNFe;
672
            $evt->evento[$i]->tpEvento = $s->tpEvento;
673
            $evt->evento[$i]->nSeqEvento = $s->nSeqEvento;
674
            $evt->evento[$i]->tagAdic = $tagAdic;
675
            $i++;
676
        }
677
        return $this->sefazEventoLote('AN', $evt);
678
    }
679
680
    /**
681
     * Send event for delivery receipt
682
     * @param \stdClass $std
683
     * @return string
684
     */
685
    public function sefazComprovanteEntrega(\stdClass $std)
686
    {
687
        if (empty($std->verAplic) && !empty($this->verAplic)) {
688
            $std->verAplic = $this->verAplic;
689
        }
690
        $hash = base64_encode(sha1($std->chNFe . $std->imagem, true));
691
        $datahash = date('Y-m-d\TH:i:sP');
692
        $cod = UFList::getCodeByUF($this->config->siglaUF);
693
        $cancelar = !empty($std->cancelar) ? $std->cancelar : false;
694
        if (!$cancelar) {
695
            $tagAdic = "<cOrgaoAutor>{$cod}</cOrgaoAutor>"
696
                . "<tpAutor>1</tpAutor>"
697
                . "<verAplic>{$std->verAplic}</verAplic>"
698
                . "<dhEntrega>{$std->data_recebimento}</dhEntrega>"
699
                . "<nDoc>{$std->documento_recebedor}</nDoc>"
700
                . "<xNome>{$std->nome_recebedor}</xNome>";
701
            if (!empty($std->latitude) && !empty($std->longitude)) {
702
                $tagAdic .= "<latGPS>{$std->latitude}</latGPS>"
703
                    . "<longGPS>{$std->longitude}</longGPS>";
704
            }
705
            $tagAdic .= "<hashComprovante>{$hash}</hashComprovante>"
706
                . "<dhHashComprovante>{$datahash}</dhHashComprovante>";
707
            $tpEvento = self::EVT_COMPROVANTE_ENTREGA;
708
        } else {
709
            $tpEvento = self::EVT_CANCELAMENTO_COMPROVANTE_ENTREGA;
710
            $tagAdic = "<cOrgaoAutor>{$cod}</cOrgaoAutor>"
711
                . "<tpAutor>1</tpAutor>"
712
                . "<verAplic>{$std->verAplic}</verAplic>"
713
                . "<nProtEvento>{$std->nProcEvento}</nProtEvento>";
714
        }
715
        return $this->sefazEvento(
716
            'AN',
717
            $std->chNFe,
718
            $tpEvento,
719
            $std->nSeqEvento,
720
            $tagAdic
721
        );
722
    }
723
724
    /**
725
     * Send event to SEFAZ in batch
726
     * @param string $uf
727
     * @param \stdClass $std
728
     * @return string
729
     * @throws RuntimeException
730
     * @throws InvalidArgumentException
731
     */
732
    public function sefazEventoLote($uf, ?\stdClass $std)
733
    {
734
        if (empty($uf) || !$std) {
735
            throw new InvalidArgumentException('Evento Lote: UF ou parametro "std" vazio!');
736
        }
737
        if (count($std->evento) > 20) {
738
            throw new RuntimeException('Evento Lote: o lote de eventos esta limitado a 20!');
739
        }
740
        $servico = 'RecepcaoEvento';
741
        $this->checkContingencyForWebServices($servico);
742
        $this->servico($servico, $uf, $this->tpAmb, false);
743
        $batchRequest = '';
744
        foreach ($std->evento as $evt) {
745
            if ($evt->tpEvento == self::EVT_EPEC) {
746
                continue; //não é possivel enviar EPEC com outros eventos
747
            }
748
            $ev = $this->tpEv($evt->tpEvento);
749
            $descEvento = $ev->desc;
750
            $cnpj = $this->config->cnpj;
751
            $dt = new \DateTime('now', new \DateTimeZone($this->timezone));
752
            $dhEvento = $dt->format('Y-m-d\TH:i:sP');
753
            $sSeqEvento = str_pad($evt->nSeqEvento, 2, "0", STR_PAD_LEFT);
754
            $eventId = "ID" . $evt->tpEvento . $evt->chave . $sSeqEvento;
755
            $cOrgao = UFList::getCodeByUF($uf);
756
            $request = "<evento xmlns=\"$this->urlPortal\" versao=\"$this->urlVersion\">"
757
                . "<infEvento Id=\"$eventId\">"
758
                . "<cOrgao>$cOrgao</cOrgao>"
759
                . "<tpAmb>$this->tpAmb</tpAmb>";
760
            if ($this->typePerson === 'J') {
761
                $request .= "<CNPJ>$cnpj</CNPJ>";
762
            } else {
763
                $request .= "<CPF>$cnpj</CPF>";
764
            }
765
            $request .= "<chNFe>$evt->chave</chNFe>"
766
                . "<dhEvento>$dhEvento</dhEvento>"
767
                . "<tpEvento>$evt->tpEvento</tpEvento>"
768
                . "<nSeqEvento>$evt->nSeqEvento</nSeqEvento>"
769
                . "<verEvento>$this->urlVersion</verEvento>"
770
                . "<detEvento versao=\"$this->urlVersion\">"
771
                . "<descEvento>$descEvento</descEvento>"
772
                . "$evt->tagAdic"
773
                . "</detEvento>"
774
                . "</infEvento>"
775
                . "</evento>";
776
            //assinatura dos dados
777
            $request = Signer::sign(
778
                $this->certificate,
779
                $request,
780
                'infEvento',
781
                'Id',
782
                $this->algorithm,
783
                $this->canonical
784
            );
785
            $batchRequest .= Strings::clearXmlString($request, true);
786
        }
787
        $dt = new \DateTime('now', new \DateTimeZone($this->timezone));
788
        $lote = $dt->format('YmdHis') . rand(0, 9);
789
        $request = "<envEvento xmlns=\"$this->urlPortal\" versao=\"$this->urlVersion\">"
790
            . "<idLote>$lote</idLote>"
791
            . $batchRequest
792
            . "</envEvento>";
793
        $this->isValid($this->urlVersion, $request, 'envEvento');
794
        $this->lastRequest = $request;
795
        $parameters = ['nfeDadosMsg' => $request];
796
        $body = "<nfeDadosMsg xmlns=\"$this->urlNamespace\">$request</nfeDadosMsg>";
797
        $this->lastResponse = $this->sendRequest($body, $parameters);
798
        return $this->lastResponse;
799
    }
800
801
    /**
802
     * Request authorization for issuance in contingency EPEC
803
     * @param string $xml
804
     * @param string $verAplic
805
     * @return string
806
     * @throws InvalidArgumentException
807
     * @throws RuntimeException
808
     */
809
    public function sefazEPEC(&$xml, $verAplic = null)
810
    {
811
        if (empty($xml)) {
812
            throw new InvalidArgumentException('EPEC: parâmetro xml esta vazio!');
813
        }
814
        $nSeqEvento = 1;
815
        if ($this->contingency->type !== 'EPEC') {
816
            throw new RuntimeException('A contingencia EPEC deve estar ativada!');
817
        }
818
        //ajusta a NFe para a contingência definida e assina novamente
819
        $xml = $this->correctNFeForContingencyMode($xml);
820
        //extrai os dados da NFe
821
        $dom = new \DOMDocument('1.0', 'UTF-8');
822
        $dom->preserveWhiteSpace = false;
823
        $dom->formatOutput = false;
824
        $dom->loadXML($xml);
825
        $infNFe = $dom->getElementsByTagName('infNFe')->item(0);
826
        $emit = $dom->getElementsByTagName('emit')->item(0);
827
        $dest = $dom->getElementsByTagName('dest')->item(0);
828
        $cOrgaoAutor = UFList::getCodeByUF($this->config->siglaUF);
829
        $chNFe = substr($infNFe->getAttribute('Id'), 3, 44);
830
        $ufchave = substr($chNFe, 0, 2);
831
        if ($cOrgaoAutor != $ufchave) {
832
            throw new RuntimeException("O autor [{$cOrgaoAutor}] não é da mesma UF que a NFe [{$ufchave}]");
833
        }
834
        // EPEC
835
        $verProc = $dom->getElementsByTagName('verProc')->item(0)->nodeValue;
836
        $dhEmi = $dom->getElementsByTagName('dhEmi')->item(0)->nodeValue;
837
        $tpNF = $dom->getElementsByTagName('tpNF')->item(0)->nodeValue;
838
        $emitIE = $emit->getElementsByTagName('IE')->item(0)->nodeValue;
839
        $destUF = $dest->getElementsByTagName('UF')->item(0)->nodeValue;
840
        $total = $dom->getElementsByTagName('total')->item(0);
841
        $vNF = $total->getElementsByTagName('vNF')->item(0)->nodeValue;
842
        $vICMS = $total->getElementsByTagName('vICMS')->item(0)->nodeValue;
843
        $vST = $total->getElementsByTagName('vST')->item(0)->nodeValue;
844
        $dID = !empty($dest->getElementsByTagName('CNPJ')->item(0))
845
            ? $dest->getElementsByTagName('CNPJ')->item(0)->nodeValue
846
            : null;
847
        if (!empty($dID)) {
848
            $destID = "<CNPJ>$dID</CNPJ>";
849
        } else {
850
            $dID = !empty($dest->getElementsByTagName('CPF')->item(0)->nodeValue)
851
                ? $dest->getElementsByTagName('CPF')->item(0)->nodeValue : null;
852
            if (!empty($dID)) {
853
                $destID = "<CPF>$dID</CPF>";
854
            } else {
855
                $dID = $dest->getElementsByTagName('idEstrangeiro')
856
                    ->item(0)
857
                    ->nodeValue;
858
                $destID = "<idEstrangeiro>$dID</idEstrangeiro>";
859
            }
860
        }
861
        $dIE = !empty($dest->getElementsByTagName('IE')->item(0)->nodeValue)
862
            ? $dest->getElementsByTagName('IE')->item(0)->nodeValue
863
            : '';
864
        $destIE = '';
865
        if (!empty($dIE)) {
866
            $destIE = "<IE>{$dIE}</IE>";
867
        }
868
        if (empty($verAplic)) {
869
            if (!empty($this->verAplic)) {
870
                $verAplic = $this->verAplic;
871
            } else {
872
                $verAplic = $verProc;
873
            }
874
        }
875
        $tagAdic = "<cOrgaoAutor>$cOrgaoAutor</cOrgaoAutor>"
876
            . "<tpAutor>1</tpAutor>"
877
            . "<verAplic>{$verAplic}</verAplic>"
878
            . "<dhEmi>{$dhEmi}</dhEmi>"
879
            . "<tpNF>{$tpNF}</tpNF>"
880
            . "<IE>{$emitIE}</IE>"
881
            . "<dest>"
882
            . "<UF>{$destUF}</UF>"
883
            . $destID
884
            . $destIE
885
            . "<vNF>{$vNF}</vNF>"
886
            . "<vICMS>{$vICMS}</vICMS>"
887
            . "<vST>{$vST}</vST>"
888
            . "</dest>";
889
        return $this->sefazEvento('AN', $chNFe, self::EVT_EPEC, $nSeqEvento, $tagAdic);
890
    }
891
892
    /**
893
     * Send event to SEFAZ
894
     * @param string $uf
895
     * @param string $chave
896
     * @param int $tpEvento
897
     * @param int $nSeqEvento
898
     * @param string $tagAdic
899
     * @return string
900
     */
901
    public function sefazEvento(
902
        $uf,
903
        $chave,
904
        $tpEvento,
905
        $nSeqEvento = 1,
906
        $tagAdic = ''
907
    ) {
908
        $eventos = [
909
            self::EVT_CCE => ['versao' => '1.00', 'nome' => 'envCCe'],
910
            self::EVT_CANCELA => ['versao' => '1.00', 'nome' => 'envEventoCancNFe'],
911
            self::EVT_CANCELASUBSTITUICAO => ['versao' => '1.00', 'nome' => 'envEventoCancSubst'],
912
            self::EVT_ATORINTERESSADO => ['versao' => '1.00', 'nome' => 'envEventoAtorInteressado'],
913
            self::EVT_COMPROVANTE_ENTREGA => ['versao' => '1.00', 'nome' => 'envEventoEntregaNFe'],
914
            self::EVT_CANCELAMENTO_COMPROVANTE_ENTREGA => ['versao' => '1.00', 'nome' => 'envEventoCancEntregaNFe'],
915
            self::EVT_CIENCIA => ['versao' => '1.00', 'nome' => 'envConfRecebto'],
916
            self::EVT_CONFIRMACAO => ['versao' => '1.00', 'nome' => 'envConfRecebto'],
917
            self::EVT_DESCONHECIMENTO => ['versao' => '1.00', 'nome' => 'envConfRecebto'],
918
            self::EVT_NAO_REALIZADA => ['versao' => '1.00', 'nome' => 'envConfRecebto'],
919
            self::EVT_PRORROGACAO_1 => ['versao' => '1.00', 'nome' => 'envRemIndus'],
920
            self::EVT_PRORROGACAO_2 => ['versao' => '1.00', 'nome' => 'envRemIndus'],
921
            self::EVT_CANCELA_PRORROGACAO_1 => ['versao' => '1.00', 'nome' => 'envRemIndus'],
922
            self::EVT_CANCELA_PRORROGACAO_2 => ['versao' => '1.00', 'nome' => 'envRemIndus'],
923
            self::EVT_EPEC => ['versao' => '1.00', 'nome' => 'envEPEC'],
924
        ];
925
        $verEvento = $this->urlVersion;
926
        if (!empty($eventos[$tpEvento])) {
927
            $evt = $eventos[$tpEvento];
928
            $verEvento = $evt['versao'];
929
        }
930
        $ignore = $tpEvento == self::EVT_EPEC;
931
        $servico = 'RecepcaoEvento';
932
        $this->checkContingencyForWebServices($servico);
933
        $this->servico($servico, $uf, $this->tpAmb, $ignore);
934
        $ev = $this->tpEv($tpEvento);
935
        $descEvento = $ev->desc;
936
        $cnpj = isset($this->config->cnpj) ? $this->config->cnpj : '';
937
        $dt = new \DateTime(date("Y-m-d H:i:sP"), new \DateTimeZone($this->timezone));
938
        $dhEvento = $dt->format('Y-m-d\TH:i:sP');
939
        $sSeqEvento = str_pad((string)$nSeqEvento, 2, "0", STR_PAD_LEFT);
940
        $eventId = "ID" . $tpEvento . $chave . $sSeqEvento;
941
        $cOrgao = UFList::getCodeByUF($uf);
942
        $request = "<evento xmlns=\"$this->urlPortal\" versao=\"$this->urlVersion\">"
943
            . "<infEvento Id=\"$eventId\">"
944
            . "<cOrgao>$cOrgao</cOrgao>"
945
            . "<tpAmb>$this->tpAmb</tpAmb>";
946
        if ($this->typePerson === 'J') {
947
            $request .= "<CNPJ>$cnpj</CNPJ>";
948
        } else {
949
            $request .= "<CPF>$cnpj</CPF>";
950
        }
951
        $request .= "<chNFe>$chave</chNFe>"
952
            . "<dhEvento>$dhEvento</dhEvento>"
953
            . "<tpEvento>$tpEvento</tpEvento>"
954
            . "<nSeqEvento>$nSeqEvento</nSeqEvento>"
955
            . "<verEvento>$verEvento</verEvento>"
956
            . "<detEvento versao=\"$verEvento\">"
957
            . "<descEvento>$descEvento</descEvento>"
958
            . "$tagAdic"
959
            . "</detEvento>"
960
            . "</infEvento>"
961
            . "</evento>";
962
        //assinatura dos dados
963
        $request = Signer::sign(
964
            $this->certificate,
965
            $request,
966
            'infEvento',
967
            'Id',
968
            $this->algorithm,
969
            $this->canonical
970
        );
971
        $request = Strings::clearXmlString($request, true);
972
        $lote = $dt->format('YmdHis') . rand(0, 9);
973
        $request = "<envEvento xmlns=\"$this->urlPortal\" versao=\"$this->urlVersion\">"
974
            . "<idLote>$lote</idLote>"
975
            . $request
976
            . "</envEvento>";
977
        if (!empty($eventos[$tpEvento])) {
978
            $evt = $eventos[$tpEvento];
979
            $this->isValid($evt['versao'], $request, $evt['nome']);
980
        } else {
981
            $this->isValid($this->urlVersion, $request, 'envEvento');
982
        }
983
        $this->lastRequest = $request;
984
        //return $request;
985
986
        $parameters = ['nfeDadosMsg' => $request];
987
        $body = "<nfeDadosMsg xmlns=\"$this->urlNamespace\">$request</nfeDadosMsg>";
988
        $this->lastResponse = $this->sendRequest($body, $parameters);
989
        return $this->lastResponse;
990
    }
991
992
    /**
993
     * Request the NFe download already manifested by its recipient, by the key
994
     * using new service in NfeDistribuicaoDFe
995
     * NOTA: NfeDownloadNF is deactivated
996
     * @param  string $chave
997
     * @return string
998
     * @throws InvalidArgumentException
999
     */
1000
    public function sefazDownload($chave)
1001
    {
1002
        if (empty($chave)) {
1003
            throw new InvalidArgumentException('Download: chave esta vazia!');
1004
        }
1005
        //carrega serviço
1006
        $servico = 'NfeDistribuicaoDFe';
1007
        $this->checkContingencyForWebServices($servico);
1008
        $this->servico($servico, 'AN', $this->tpAmb, true);
1009
        $cUF = UFList::getCodeByUF($this->config->siglaUF);
1010
        $tagChave = "<consChNFe><chNFe>$chave</chNFe></consChNFe>";
1011
        $cnpj = $this->config->cnpj;
1012
        //monta a consulta
1013
        $request = "<distDFeInt xmlns=\"$this->urlPortal\" versao=\"$this->urlVersion\">"
1014
            . "<tpAmb>" . $this->tpAmb . "</tpAmb>"
1015
            . "<cUFAutor>$cUF</cUFAutor>";
1016
        if ($this->typePerson === 'J') {
1017
            $request .= "<CNPJ>$cnpj</CNPJ>";
1018
        } else {
1019
            $request .= "<CPF>$cnpj</CPF>";
1020
        }
1021
        $request .= "$tagChave"
1022
            . "</distDFeInt>";
1023
        //valida o xml da requisição
1024
        $this->isValid($this->urlVersion, $request, 'distDFeInt');
1025
        $this->lastRequest = $request;
1026
        //montagem dos dados da mensagem SOAP
1027
        $request = "<nfeDadosMsg xmlns=\"$this->urlNamespace\">$request</nfeDadosMsg>";
1028
        $parameters = ['nfeDistDFeInteresse' => $request];
1029
        $body = "<nfeDistDFeInteresse xmlns=\"$this->urlNamespace\">"
1030
            . $request
1031
            . "</nfeDistDFeInteresse>";
1032
        //este webservice não requer cabeçalho
1033
        $this->objHeader = null;
1034
        $this->lastResponse = $this->sendRequest($body, $parameters);
1035
        return $this->lastResponse;
1036
    }
1037
1038
    /**
1039
     * Maintenance of the Taxpayer Security Code - CSC (Old Token)
1040
     * @param int $indOp Identificador do tipo de operação:
1041
     *                   1 - Consulta CSC Ativos;
1042
     *                   2 - Solicita novo CSC;
1043
     *                   3 - Revoga CSC Ativo
1044
     * @return string
1045
     * @throws InvalidArgumentException
1046
     * @throws RuntimeException
1047
     */
1048
    public function sefazCsc($indOp)
1049
    {
1050
        if (empty($indOp) || $indOp < 1 || $indOp > 3) {
1051
            throw new InvalidArgumentException('CSC: identificador operacao invalido!');
1052
        }
1053
        if ($this->modelo != 65) {
1054
            throw new RuntimeException('CSC: modelo diferente de 65!');
1055
        }
1056
        $raizCNPJ = substr($this->config->cnpj, 0, -6);
1057
        //carrega serviço
1058
        $servico = 'CscNFCe';
1059
        $this->checkContingencyForWebServices($servico);
1060
        $this->servico($servico, $this->config->siglaUF, $this->tpAmb);
1061
        $request = "<admCscNFCe versao=\"$this->urlVersion\" xmlns=\"$this->urlPortal\">"
1062
            . "<tpAmb>$this->tpAmb</tpAmb>"
1063
            . "<indOp>$indOp</indOp>"
1064
            . "<raizCNPJ>$raizCNPJ</raizCNPJ>"
1065
            . "</admCscNFCe>";
1066
        if ($indOp == 3) {
1067
            $request = "<admCscNFCe versao=\"$this->urlVersion\" xmlns=\"$this->urlPortal\">"
1068
            . "<tpAmb>$this->tpAmb</tpAmb>"
1069
            . "<indOp>$indOp</indOp>"
1070
            . "<raizCNPJ>$raizCNPJ</raizCNPJ>"
1071
            . "<dadosCsc>"
1072
            . "<idCsc>" . $this->config->CSCid . "</idCsc>"
1073
            . "<codigoCsc>" . $this->config->CSC . "</codigoCsc>"
1074
            . "</dadosCsc>"
1075
            . "</admCscNFCe>";
1076
        }
1077
        //o xsd não está disponivel
1078
        $this->isValid($this->urlVersion, $request, 'admCscNFCe');
1079
        $this->lastRequest = $request;
1080
        $parameters = ['nfeDadosMsg' => $request];
1081
        $body = "<nfeDadosMsg xmlns=\"$this->urlNamespace\">$request</nfeDadosMsg>";
1082
        $this->lastResponse = $this->sendRequest($body, $parameters);
1083
        return $this->lastResponse;
1084
    }
1085
1086
    /**
1087
     * Checks the validity of an NFe, normally used for received NFe
1088
     * @param string $nfe
1089
     * @return bool
1090
     * @throws InvalidArgumentException
1091
     */
1092
    public function sefazValidate($nfe)
1093
    {
1094
        if (empty($nfe)) {
1095
            throw new InvalidArgumentException('Validacao NF-e: a string da NF-e esta vazia!');
1096
        }
1097
        //verifica a assinatura da NFe, exception caso de falha
1098
        Signer::isSigned($nfe);
1099
        $dom = new \DOMDocument('1.0', 'utf-8');
1100
        $dom->formatOutput = false;
1101
        $dom->preserveWhiteSpace = false;
1102
        $dom->loadXML($nfe);
1103
        //verifica a validade no webservice da SEFAZ
1104
        $tpAmb = $dom->getElementsByTagName('tpAmb')->item(0)->nodeValue;
1105
        $infNFe  = $dom->getElementsByTagName('infNFe')->item(0);
1106
        $chNFe = preg_replace('/[^0-9]/', '', $infNFe->getAttribute("Id"));
1107
        $protocol = $dom->getElementsByTagName('nProt')->item(0)->nodeValue;
1108
        $digval = $dom->getElementsByTagName('DigestValue')->item(0)->nodeValue;
1109
        //consulta a NFe
1110
        $response = $this->sefazConsultaChave($chNFe, (int)$tpAmb);
1111
        $ret = new \DOMDocument('1.0', 'UTF-8');
1112
        $ret->preserveWhiteSpace = false;
1113
        $ret->formatOutput = false;
1114
        $ret->loadXML($response);
1115
        $retProt = $ret->getElementsByTagName('protNFe')->item(0);
1116
        if (!isset($retProt)) {
1117
            $xMotivo = $ret->getElementsByTagName('xMotivo')->item(0);
1118
            if (isset($xMotivo)) {
1119
                throw new InvalidArgumentException('Validacao NF-e: ' . $xMotivo->nodeValue);
1120
            } else {
1121
                throw new InvalidArgumentException('O documento de resposta nao contem o node "protNFe".');
1122
            }
1123
        }
1124
        $infProt = $ret->getElementsByTagName('infProt')->item(0);
1125
        $dig = $infProt->getElementsByTagName("digVal")->item(0);
1126
        $digProt = '000';
1127
        if (isset($dig)) {
1128
            $digProt = $dig->nodeValue;
1129
        }
1130
        $chProt = $infProt->getElementsByTagName("chNFe")->item(0)->nodeValue;
1131
        $nProt = $infProt->getElementsByTagName("nProt")->item(0)->nodeValue;
1132
        if ($protocol == $nProt && $digval == $digProt && $chNFe == $chProt) {
1133
            return true;
1134
        }
1135
        return false;
1136
    }
1137
1138
    /**
1139
     * Returns alias and description event from event code.
1140
     * @param  int $tpEvento
1141
     * @return \stdClass
1142
     * @throws \RuntimeException
1143
     */
1144
    private function tpEv($tpEvento)
1145
    {
1146
        $std = new \stdClass();
1147
        $std->alias = '';
1148
        $std->desc = '';
1149
        switch ($tpEvento) {
1150
            case self::EVT_CCE:
1151
                $std->alias = 'CCe';
1152
                $std->desc = 'Carta de Correcao';
1153
                break;
1154
            case self::EVT_CANCELA:
1155
                $std->alias = 'CancNFe';
1156
                $std->desc = 'Cancelamento';
1157
                break;
1158
            case self::EVT_CANCELASUBSTITUICAO:
1159
                $std->alias = 'CancNFe';
1160
                $std->desc = 'Cancelamento por substituicao';
1161
                break;
1162
            case self::EVT_EPEC: // Emissão em contingência EPEC
1163
                $std->alias = 'EPEC';
1164
                $std->desc = 'EPEC';
1165
                break;
1166
            case self::EVT_COMPROVANTE_ENTREGA:
1167
                $std->alias = 'CompEntrega';
1168
                $std->desc = 'Comprovante de Entrega da NF-e';
1169
                break;
1170
            case self::EVT_CANCELAMENTO_COMPROVANTE_ENTREGA:
1171
                $std->alias = 'CancCompEntrega';
1172
                $std->desc = 'Cancelamento Comprovante de Entrega da NF-e';
1173
                break;
1174
            case 111500:
1175
            case 111501:
1176
                //EPP
1177
                //Pedido de prorrogação
1178
                $std->alias = 'EPP';
1179
                $std->desc = 'Pedido de Prorrogacao';
1180
                break;
1181
            case 111502:
1182
            case 111503:
1183
                //ECPP
1184
                //Cancelamento do Pedido de prorrogação
1185
                $std->alias = 'ECPP';
1186
                $std->desc = 'Cancelamento de Pedido de Prorrogacao';
1187
                break;
1188
            case self::EVT_CONFIRMACAO: // Manifestação Confirmacao da Operação
1189
                $std->alias = 'EvConfirma';
1190
                $std->desc = 'Confirmacao da Operacao';
1191
                break;
1192
            case self::EVT_CIENCIA: // Manifestação Ciencia da Operação
1193
                $std->alias = 'EvCiencia';
1194
                $std->desc = 'Ciencia da Operacao';
1195
                $std->tpAutor = 2;
1196
                break;
1197
            case self::EVT_DESCONHECIMENTO: // Manifestação Desconhecimento da Operação
1198
                $std->alias = 'EvDesconh';
1199
                $std->desc = 'Desconhecimento da Operacao';
1200
                break;
1201
            case self::EVT_NAO_REALIZADA: // Manifestação Operacao não Realizada
1202
                $std->alias = 'EvNaoRealizada';
1203
                $std->desc = 'Operacao nao Realizada';
1204
                break;
1205
            case self::EVT_ATORINTERESSADO: //ator interessado
1206
                $std->alias = 'EvAtorInteressado';
1207
                $std->desc = 'Ator interessado na NF-e';
1208
                break;
1209
            default:
1210
                $msg = "O código do tipo de evento informado não corresponde a "
1211
                . "nenhum evento estabelecido.";
1212
                throw new RuntimeException($msg);
1213
        }
1214
        return $std;
1215
    }
1216
}
1217