Passed
Push — master ( 43458c...ac6f10 )
by Francimar
03:09
created

SEFAZ::fromArray()   B

Complexity

Conditions 5
Paths 5

Size

Total Lines 15
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 10
CRAP Score 5

Importance

Changes 0
Metric Value
dl 0
loc 15
ccs 10
cts 10
cp 1
rs 8.8571
c 0
b 0
f 0
cc 5
eloc 11
nc 5
nop 1
crap 5
1
<?php
2
/**
3
 * MIT License
4
 *
5
 * Copyright (c) 2016 MZ Desenvolvimento de Sistemas LTDA
6
 *
7
 * @author Francimar Alves <[email protected]>
8
 *
9
 * Permission is hereby granted, free of charge, to any person obtaining a copy
10
 * of this software and associated documentation files (the "Software"), to deal
11
 * in the Software without restriction, including without limitation the rights
12
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13
 * copies of the Software, and to permit persons to whom the Software is
14
 * furnished to do so, subject to the following conditions:
15
 *
16
 * The above copyright notice and this permission notice shall be included in all
17
 * copies or substantial portions of the Software.
18
 *
19
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
25
 * SOFTWARE.
26
 *
27
 */
28
namespace NFe\Core;
29
30
use NFe\Logger\Log;
31
use NFe\Task\Tarefa;
32
use NFe\Task\Autorizacao;
33
use NFe\Common\Ajuste;
34
35
/**
36
 * Classe que envia uma ou mais notas fiscais para os servidores da sefaz
37
 */
38
class SEFAZ
39
{
40
41
    private $notas;
42
    private $configuracao;
43
    private static $instance;
44
45 100
    public function __construct($sefaz = [])
46
    {
47 100
        $this->fromArray($sefaz);
48 100
    }
49
50 104
    public static function getInstance($new = false)
51
    {
52 104
        if (is_null(self::$instance) || $new) {
53 99
            self::$instance = new self();
54
        }
55 104
        return self::$instance;
56
    }
57
58 4
    public function getNotas()
59
    {
60 4
        return $this->notas;
61
    }
62
63 100
    public function setNotas($notas)
64
    {
65 100
        $this->notas = $notas;
66 100
        return $this;
67
    }
68
69 3
    public function addNota($nota)
70
    {
71 3
        $this->notas[] = $nota;
72 3
        return $this;
73
    }
74
75 94
    public function getConfiguracao()
76
    {
77 94
        return $this->configuracao;
78
    }
79
80 100
    public function setConfiguracao($configuracao)
81
    {
82 100
        $this->configuracao = $configuracao;
83 100
        return $this;
84
    }
85
86 1
    public function toArray($recursive = false)
87
    {
88 1
        $sefaz = [];
89 1
        if ($recursive) {
90
            $notas = [];
91
            $_notas = $this->getNotas();
92
            foreach ($_notas as $_nota) {
93
                $notas[] = $_nota->toArray($recursive);
94
            }
95
            $sefaz['notas'] = $notas;
96
        } else {
97 1
            $sefaz['notas'] = $this->getNotas();
98
        }
99 1
        if (!is_null($this->getConfiguracao()) && $recursive) {
100
            $sefaz['configuracao'] = $this->getConfiguracao()->toArray($recursive);
101
        } else {
102 1
            $sefaz['configuracao'] = $this->getConfiguracao();
103
        }
104 1
        return $sefaz;
105
    }
106
107 100
    public function fromArray($sefaz = [])
108
    {
109 100
        if ($sefaz instanceof SEFAZ) {
110 1
            $sefaz = $sefaz->toArray();
111 100
        } elseif (!is_array($sefaz)) {
112 1
            return $this;
113
        }
114 100
        if (!isset($sefaz['notas'])) {
115 100
            $this->setNotas([]);
116
        } else {
117 1
            $this->setNotas($sefaz['notas']);
118
        }
119 100
        $this->setConfiguracao(new Ajuste(isset($sefaz['configuracao']) ? $sefaz['configuracao'] : []));
120 100
        return $this;
121
    }
122
123 1
    private function despacha($nota, &$dom, $retorno)
124
    {
125 1
        $evento = $this->getConfiguracao()->getEvento();
126 1
        if ($retorno->isRecebido()) {
127
            Log::debug('SEFAZ.despacha - Recibo: '.$retorno->getNumero().' da '.$nota->getID(true));
128
            $evento->onNotaProcessando($nota, $dom, $retorno);
129 1
        } elseif ($retorno->isAutorizado()) {
130 1
            $dom = $nota->addProtocolo($dom);
131 1
            Log::debug('SEFAZ.despacha('.$retorno->getStatus().') - '.$retorno->getMotivo().
132 1
                ', Protocolo: '.$retorno->getNumero().' - '.$nota->getID(true));
133 1
            $evento->onNotaAutorizada($nota, $dom, $retorno);
134
        } elseif ($retorno->isDenegada()) {
135
            $evento->onNotaDenegada($nota, $dom, $retorno);
136
        } elseif ($retorno->isCancelado()) {
137
            $evento->onNotaCancelada($nota, $dom, $retorno);
138
        } else {
139
            $evento->onNotaRejeitada($nota, $dom, $retorno);
140
            throw new \Exception($retorno->getMotivo(), $retorno->getStatus());
141
        }
142 1
    }
143
144
    /**
145
     * Envia as notas adicionadas para a SEFAZ, caso não consiga, torna-as em contingência
146
     * todos os status são informados no evento da configuração, caso não possua evento,
147
     * uma \Exception será lançada na primeira falha
148
     */
149 3
    public function autoriza()
150
    {
151 3
        $i = 0;
152 3
        $evento = $this->getConfiguracao()->getEvento();
153 3
        foreach ($this->getNotas() as $nota) {
154
            try {
155 2
                $envia = true;
156
                do {
157 2
                    $dom = $nota->getNode()->ownerDocument;
158 2
                    $evento->onNotaGerada($nota, $dom);
159 2
                    $dom = $nota->assinar($dom);
160 2
                    $evento->onNotaAssinada($nota, $dom);
161 2
                    $dom = $nota->validar($dom); // valida o XML da nota
162 2
                    $evento->onNotaValidada($nota, $dom);
163 2
                    if (!$envia) {
164 1
                        break;
165
                    }
166 2
                    $evento->onNotaEnviando($nota, $dom);
167 2
                    $autorizacao = new Autorizacao();
168
                    try {
169 2
                        $retorno = $autorizacao->envia($nota, $dom);
170 1
                    } catch (\Exception $e) {
171 1
                        $partial_response = $e instanceof \NFe\Exception\IncompleteRequestException;
172 1
                        if ($partial_response) {
173 1
                            $evento->onNotaPendente($nota, $dom, $e);
174
                        }
175 1
                        if ($nota->getEmissao() == Nota::EMISSAO_CONTINGENCIA) {
176
                            throw $e;
177
                        }
178 1
                        Log::debug('SEFAZ.autoriza('.$e->getCode().') - Mudando emissão para contingência: '.
179 1
                            $e->getMessage().' - '.$nota->getID(true));
180 1
                        $msg = substr('Falha no envio da nota: '.$e->getMessage(), 0, 256);
181 1
                        $nota->setEmissao(Nota::EMISSAO_CONTINGENCIA);
182 1
                        $nota->setDataContingencia(time());
183 1
                        $nota->setJustificativa($msg);
184 1
                        $evento->onNotaContingencia($nota, !$partial_response, $e);
185 1
                        $envia = false;
186 1
                        continue;
187
                    }
188 1
                    Log::debug('SEFAZ.autoriza('.$retorno->getStatus().') - '.
189 1
                        $retorno->getMotivo().' - '.$nota->getID(true));
190 1
                    $this->despacha($nota, $dom, $retorno);
191 1
                    break;
192 1
                } while (true);
193 2
                $evento->onNotaCompleto($nota, $dom);
194 2
                $i++;
195
            } catch (\Exception $e) {
196
                Log::error('SEFAZ.autoriza('.$e->getCode().') - '.$e->getMessage());
197 2
                $evento->onNotaErro($nota, $e);
198
            }
199
        }
200 3
        return $i;
201
    }
202
203
    /**
204
     * Consulta o status das notas em processamento
205
     */
206 2
    public function consulta($pendencias)
207
    {
208 2
        $i = 0;
209 2
        $evento = $this->getConfiguracao()->getEvento();
210 2
        foreach ($pendencias as $pendencia) {
211
            $nota = $pendencia->getNota();
212
            try {
213
                $retorno = $pendencia->executa();
214
                $dom = $pendencia->getDocumento();
215
                Log::debug('SEFAZ.consulta('.$retorno->getStatus().') - '.
216
                    $retorno->getMotivo().' - '.$nota->getID(true));
217
                $this->despacha($nota, $dom, $retorno);
218
                $evento->onNotaCompleto($nota, $dom);
219
                $pendencia->setDocumento($dom);
220
                $evento->onTarefaExecutada($pendencia, $retorno);
221
                $i++;
222
            } catch (\Exception $e) {
223
                Log::error('SEFAZ.consulta('.$e->getCode().') - '.$e->getMessage());
224
                $evento->onNotaErro($nota, $e);
225
            }
226
        }
227 2
        return $i;
228
    }
229
230
    /* Consulta se as notas existem e cancela ou inutiliza seus números
231
     * Também processa pedido de inutilização e cancelamento de notas
232
     */
233 4
    public function executa($tarefas)
234
    {
235 4
        $i = 0;
236 4
        $evento = $this->getConfiguracao()->getEvento();
237 4
        foreach ($tarefas as $tarefa) {
238
            try {
239 2
                $save_dom = $tarefa->getDocumento();
240 2
                $retorno = $tarefa->executa();
241 1
                $dom = $tarefa->getDocumento();
242 1
                Log::debug('SEFAZ.executa('.$retorno->getStatus().') - '.$retorno->getMotivo().
243 1
                    ' - Tarefa: '.$tarefa->getID());
244 1
                switch ($tarefa->getAcao()) {
245 1
                    case Tarefa::ACAO_INUTILIZAR:
246 1
                        $inutilizacao = $tarefa->getAgente();
247 1
                        Log::debug('SEFAZ.executa[inutiliza]('.$inutilizacao->getStatus().') - '.
248 1
                            $inutilizacao->getMotivo().' - '.$inutilizacao->getID(true));
249 1
                        $evento->onInutilizado($inutilizacao, $dom);
250 1
                        break;
251
                    default:
252
                        $nota = $tarefa->getNota();
253
                        $this->despacha($nota, $save_dom, $retorno);
254
                        $evento->onNotaCompleto($nota, $save_dom);
255
                }
256 1
                $evento->onTarefaExecutada($tarefa, $retorno);
257 1
                $i++;
258 1
            } catch (\Exception $e) {
259 1
                Log::error('SEFAZ.executa('.$e->getCode().') - '.$e->getMessage());
260 2
                $evento->onTarefaErro($tarefa, $e);
261
            }
262
        }
263 3
        return $i;
264
    }
265
266
    /* *
267
     * Inutiliza um intervalo de números de notas fiscais e insere o resultado no
268
     * próprio objeto de inutilização
269
     */
270 2
    public function inutiliza($inutilizacao)
271
    {
272 2
        $tarefa = new Tarefa();
273 2
        $tarefa->setAcao(Tarefa::ACAO_INUTILIZAR);
274 2
        $tarefa->setAgente($inutilizacao);
275
        try {
276 2
            $this->executa([$tarefa]);
277 1
        } catch (\Exception $e) {
278 1
            Log::error('SEFAZ.inutiliza('.$e->getCode().') - '.$e->getMessage());
279 1
            return false;
280
        }
281 1
        return true;
282
    }
283
284
    /**
285
     * Obtém as notas pendentes de autorização e envia para a SEFAZ
286
     */
287 1
    public function processa()
288
    {
289 1
        $i = 0;
290
        /* Envia as notas não enviadas, rejeitadas e em contingência */
291
        try {
292 1
            $db = $this->getConfiguracao()->getBanco();
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $db. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
293 1
            $notas = $db->getNotasAbertas();
294 1
            $this->setNotas($notas);
295 1
            $i += $this->autoriza();
296
        } catch (\Exception $e) {
297
            Log::error('SEFAZ.processa[autoriza]('.$e->getCode().') - '.$e->getMessage());
298
        }
299
        /* Consulta o status das notas em processamento */
300
        try {
301 1
            $db = $this->getConfiguracao()->getBanco();
302 1
            $pendencias = $db->getNotasPendentes();
303 1
            $i += $this->consulta($pendencias);
304
        } catch (\Exception $e) {
305
            Log::error('SEFAZ.processa[pendentes]('.$e->getCode().') - '.$e->getMessage());
306
        }
307
        /* Consulta se as notas existem e cancela ou inutiliza seus números
308
         * Também processa pedido de inutilização e cancelamento de notas
309
         */
310
        try {
311 1
            $db = $this->getConfiguracao()->getBanco();
312 1
            $tarefas = $db->getNotasTarefas();
313 1
            $i += $this->executa($tarefas);
314
        } catch (\Exception $e) {
315
            Log::error('SEFAZ.processa[tarefas]('.$e->getCode().') - '.$e->getMessage());
316
        }
317 1
        return $i;
318
    }
319
}
320