Subversion Repositories Integrator Subversion

Rev

Rev 182 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
1 espaco 1
package br.com.ec.domain.model;
2
 
3
import java.io.ByteArrayInputStream;
4
import java.io.ByteArrayOutputStream;
5
import java.io.IOException;
6
import java.io.Serializable;
7
import java.io.StringReader;
8
import java.util.Date;
9
import java.util.List;
10
 
11
import javax.persistence.Column;
12
import javax.persistence.Entity;
13
import javax.persistence.GeneratedValue;
14
import javax.persistence.GenerationType;
15
import javax.persistence.Id;
16
import javax.persistence.JoinColumn;
17
import javax.persistence.ManyToOne;
18
import javax.persistence.Table;
19
import javax.persistence.Transient;
20
import javax.validation.constraints.NotNull;
21
import javax.validation.constraints.Size;
22
import javax.xml.bind.JAXBContext;
23
import javax.xml.bind.JAXBException;
24
import javax.xml.bind.Unmarshaller;
25
import javax.xml.parsers.DocumentBuilder;
26
import javax.xml.parsers.DocumentBuilderFactory;
27
import javax.xml.transform.Transformer;
28
import javax.xml.transform.TransformerFactory;
29
import javax.xml.transform.dom.DOMSource;
30
import javax.xml.transform.stream.StreamResult;
31
import javax.xml.transform.stream.StreamSource;
32
 
33
import org.apache.commons.io.IOUtils;
34
import org.hibernate.annotations.ForeignKey;
35
import org.primefaces.model.DefaultStreamedContent;
36
import org.w3c.dom.Document;
37
 
38
import br.com.ec.domain.model.tipos.TipoModeloNotaFiscal;
39
import br.com.ec.domain.model.tipos.TipoNotaFiscal;
40
import br.edu.cesmac.core.exception.NegocioException;
41
import br.edu.cesmac.core.generic.identidade.Identidade;
42
import br.edu.cesmac.core.interfaces.Alterar;
43
import br.edu.cesmac.core.interfaces.Cadastrar;
44
import br.edu.cesmac.core.util.StringUtil;
45
import br.edu.cesmac.core.util.VerificadorUtil;
46
import br.edu.cesmac.relatorio.util.RelatorioUtils;
47
import br.edu.cesmac.web.util.DataUtil;
48
import nfce.java.TNfeProc;
49
 
50
@Entity
51
@Table(name="sec_nota_fiscal", schema="sc_sec")
52
public class NotaFiscal implements Serializable, Identidade {
53
 
54
        private static final long serialVersionUID = 1L;
55
 
56
        private Long sequencial;
57
        private Long numeroNotaFiscal;
58
        private String chave;
59
        private String serie;
60
        private Venda venda;
61
        private Pessoa pessoaJuridicaEmitente;
62
        private Pessoa pessoaJuridicaDestinatario;
63
        private String observacaoVenda;
64
//      private Transporte transporte;
65
        private String descricaoComplementares;
66
        private Date dataHoraEmissao;
67
        private String textoXml;
68
        private String tipoNotaFiscal;
69
        private String tipoModeloNotaFiscal;
70
        private String protocoloAutorizacao;
71
        private String caminhoQrcode;
72
 
73
        private String statusRetorno;
74
        private String motivoRetorno;
75
        private Date dataHoraEvento;
76
        private String textoXmlEvento;
77
 
78
        private List<NotaFiscalProduto> listaNFRemessaProdutos;
79
 
80
        private byte[] arquivoXml;
81
 
82
        private List<Parcela> listaParcelas;
83
        private List<CompraProduto> listaCompraProduto;
84
        private List<Lancamento> listaLancamento;
85
        private List<VendaFormaPagamento> listaFormaPagamento;
86
 
87
        private Boolean notaFiscalPendente;
88
 
89
        @Override
90
        @Transient
91
        public Object getId() {
92
                return this.getSequencial();
93
        }
94
        @Override
95
        public void setId(Object id) {
96
                this.sequencial = (Long) id;
97
        }
98
 
99
        @Id
100
        @GeneratedValue(strategy = GenerationType.IDENTITY)
101
        @Column(name="seq_nota_fiscal", nullable=false)
102
        public Long getSequencial() {
103
                return sequencial;
104
        }
105
        public void setSequencial(Long sequencial) {
106
                this.sequencial = sequencial;
107
        }
108
 
109
        @Column(name="cod_nota_fiscal")
110
        public String getChave() {
111
                return chave;
112
        }
113
        public void setChave(String chave) {
114
                this.chave = chave;
115
        }
116
 
117
        @Column(name="dsc_serie")
118
        public String getSerie() {
119
                return serie;
120
        }
121
        public void setSerie(String serie) {
122
                this.serie = serie;
123
        }
124
 
125
        @Column(name="num_nota_fiscal", nullable=false)
126
        public Long getNumeroNotaFiscal() {
127
                return numeroNotaFiscal;
128
        }
129
        public void setNumeroNotaFiscal(Long numeroNotaFiscal) {
130
                this.numeroNotaFiscal = numeroNotaFiscal;
131
        }
132
 
133
        @ManyToOne
134
        @ForeignKey(name = "fk_notafiscal_venda")
135
        @JoinColumn(name="seq_venda", referencedColumnName="seq_venda", insertable=true, updatable=false)
136
        public Venda getVenda() {
137
                return venda;
138
        }
139
        public void setVenda(Venda venda) {
140
                this.venda = venda;
141
        }
142
 
143
        @ManyToOne
144
        @ForeignKey(name = "fk_notafiscal_emitente")
145
        @JoinColumn(name="seq_pessoa_emitente", referencedColumnName="seq_pessoa", insertable=true, updatable=true)
146
        public Pessoa getPessoaJuridicaEmitente() {
147
                return pessoaJuridicaEmitente;
148
        }
149
        public void setPessoaJuridicaEmitente(Pessoa pessoaJuridicaEmitente) {
150
                this.pessoaJuridicaEmitente = pessoaJuridicaEmitente;
151
        }
152
 
153
        @ManyToOne
154
        @ForeignKey(name = "fk_notafiscal_destinatario")
155
        @JoinColumn(name="seq_pessoa_destinatario", referencedColumnName="seq_pessoa", insertable=true, updatable=true)
156
        public Pessoa getPessoaJuridicaDestinatario() {
157
                return pessoaJuridicaDestinatario;
158
        }
159
        public void setPessoaJuridicaDestinatario(Pessoa pessoaJuridicaDestinatario) {
160
                this.pessoaJuridicaDestinatario = pessoaJuridicaDestinatario;
161
        }
162
 
163
        @Column(name="dsc_obs_venda")
164
        @Size(max = 100, message = "Limite de caracteres ultrapassado: Observação da Venda")
165
        public String getObservacaoVenda() {
166
                return observacaoVenda;
167
        }
168
        public void setObservacaoVenda(String observacaoVenda) {
169
                this.observacaoVenda = observacaoVenda;
170
        }
171
 
172
        @Column(name="dsc_complementares")
173
        @Size(max = 1500, message = "Limite de caracteres ultrapassado: Descrição Complementares")
174
        public String getDescricaoComplementares() {
175
                return descricaoComplementares;
176
        }
177
        public void setDescricaoComplementares(String descricaoComplementares) {
178
                this.descricaoComplementares = descricaoComplementares;
179
        }
180
 
181
        @Column(name="dth_emissao", nullable=false)
182
        public Date getDataHoraEmissao() {
183
                return dataHoraEmissao;
184
        }
185
        public void setDataHoraEmissao(Date dataHoraEmissao) {
186
                this.dataHoraEmissao = dataHoraEmissao;
187
        }
188
 
189
        @Column(name="txt_xml")
190
        public String getTextoXml() {
191
                return textoXml;
192
        }
193
        public void setTextoXml(String textoXml) {
194
                this.textoXml = textoXml;
195
        }
196
 
197
        @Column(name="tip_nota_fiscal", nullable=false)
198
        @NotNull(message="Obrigatório informar o tipo da nota fiscal", groups={Cadastrar.class, Alterar.class})
199
        public String getTipoNotaFiscal() {
200
                return tipoNotaFiscal;
201
        }
202
        public void setTipoNotaFiscal(String tipoNotaFiscal) {
203
                this.tipoNotaFiscal = tipoNotaFiscal;
204
        }
205
        @Transient
206
        public String getDescricaoDoTipoNotaFiscal() {
207
                return VerificadorUtil.naoEstaNuloOuVazio(getTipoNotaFiscal())? TipoNotaFiscal.parse(getTipoNotaFiscal()).getDescricao() : null;
208
        }
209
 
210
        @Column(name="tip_modelo_notafiscal", nullable=false)
211
        @NotNull(message="Obrigatório informar o tipo do modelo da nota fiscal", groups={Cadastrar.class, Alterar.class})
212
        public String getTipoModeloNotaFiscal() {
213
                return tipoModeloNotaFiscal;
214
        }
215
        public void setTipoModeloNotaFiscal(String tipoModeloNotaFiscal) {
216
                this.tipoModeloNotaFiscal = tipoModeloNotaFiscal;
217
        }
218
        @Transient
219
        public String getDescricaoDoTipoModeloNotaFiscal() {
220
                return VerificadorUtil.naoEstaNuloOuVazio(getTipoModeloNotaFiscal())? TipoModeloNotaFiscal.parse(getTipoModeloNotaFiscal()).getDescricao() : null;
221
        }
222
 
223
        @Column(name="dsc_protocolo_autorizacao")
224
        @Size(max = 100, message = "Limite de caracteres ultrapassado: Protocolo da Autorização")
225
        public String getProtocoloAutorizacao() {
226
                return protocoloAutorizacao;
227
        }
228
        public void setProtocoloAutorizacao(String protocoloAutorizacao) {
229
                this.protocoloAutorizacao = protocoloAutorizacao;
230
        }
231
 
232
        @Column(name="dsc_caminho_qrcode")
233
        @Size(max = 500, message = "Limite de caracteres ultrapassado: Caminho QRCode")
234
        public String getCaminhoQrcode() {
235
                return caminhoQrcode;
236
        }
237
        public void setCaminhoQrcode(String caminhoQrcode) {
238
                this.caminhoQrcode = caminhoQrcode;
239
        }
240
 
241
        @Column(name="dsc_status_retorno")
242
        @Size(max = 100, message = "Limite de caracteres ultrapassado: Status do Retorno")
243
        public String getStatusRetorno() {
244
                return statusRetorno;
245
        }
246
        public void setStatusRetorno(String statusRetorno) {
247
                this.statusRetorno = statusRetorno;
248
        }
249
 
250
        @Column(name="dsc_motivo_retorno")
251
        @Size(max = 100, message = "Limite de caracteres ultrapassado: Motivo do Retorno")
252
        public String getMotivoRetorno() {
253
                return motivoRetorno;
254
        }
255
        public void setMotivoRetorno(String motivoRetorno) {
256
                this.motivoRetorno = motivoRetorno;
257
        }
258
 
259
        @Column(name="dth_evento")
260
        public Date getDataHoraEvento() {
261
                return dataHoraEvento;
262
        }
263
        public void setDataHoraEvento(Date dataHoraEvento) {
264
                this.dataHoraEvento = dataHoraEvento;
265
        }
266
 
267
        @Column(name="txt_xml_evento", nullable=false)
268
        public String getTextoXmlEvento() {
269
                return textoXmlEvento;
270
        }
271
        public void setTextoXmlEvento(String textoXmlEvento) {
272
                this.textoXmlEvento = textoXmlEvento;
273
        }
274
 
275
//      @OneToMany(mappedBy="notaFiscal", cascade=CascadeType.ALL, orphanRemoval=true)
276
        @Transient
277
        public List<NotaFiscalProduto> getListaNFRemessaProdutos() {
278
                return listaNFRemessaProdutos;
279
        }
280
        public void setListaNFRemessaProdutos(List<NotaFiscalProduto> listaNFRemessaProdutos) {
281
                this.listaNFRemessaProdutos = listaNFRemessaProdutos;
282
        }
283
 
284
        @Transient
285
        public Boolean temPessoaJuridica() {
286
                if (VerificadorUtil.estaNulo(getVenda())) return false;
287
                if (VerificadorUtil.estaNulo(getVenda().getLoja())) return false;
288
                return VerificadorUtil.naoEstaNulo(getVenda().getLoja().getPessoaJuridica());
289
        }
290
 
291
        @Transient
292
        public String getRazaoSocialDaLoja() {
293
                return temPessoaJuridica()? getVenda().getLoja().getPessoaJuridica().getRazaoSocial() : "";
294
        }
295
 
296
        @Transient
297
        public String getCnpjDaLoja() {
298
                return temPessoaJuridica()? StringUtil.formatarCnpj(getVenda().getLoja().getPessoaJuridica().getCpfCnpj()) : "";
299
        }
300
 
301
        @Transient
302
        public byte[] getArquivoXml() {
303
                return arquivoXml;
304
        }
305
        public void setArquivoXml(byte[] arquivoXml) {
306
                this.arquivoXml = arquivoXml;
307
        }
308
 
309
        @Transient
310
        public List<Parcela> getListaParcelas() {
311
                return listaParcelas;
312
        }
313
        public void setListaParcelas(List<Parcela> listaParcelas) {
314
                this.listaParcelas = listaParcelas;
315
        }
316
 
317
        @Transient
318
        public List<CompraProduto> getListaCompraProduto() {
319
                return listaCompraProduto;
320
        }
321
        public void setListaCompraProduto(List<CompraProduto> listaCompraProduto) {
322
                this.listaCompraProduto = listaCompraProduto;
323
        }
324
 
325
        @Transient
326
        public List<Lancamento> getListaLancamento() {
327
                return listaLancamento;
328
        }
329
        public void setListaLancamento(List<Lancamento> listaLancamento) {
330
                this.listaLancamento = listaLancamento;
331
        }
332
 
333
        @Transient
334
        public List<VendaFormaPagamento> getListaFormaPagamento() {
335
                return listaFormaPagamento;
336
        }
337
        public void setListaFormaPagamento(List<VendaFormaPagamento> listaFormaPagamento) {
338
                this.listaFormaPagamento = listaFormaPagamento;
339
        }
340
 
341
        @Transient
342
        public Boolean getNotaFiscalPendente() {
343
                return notaFiscalPendente;
344
        }
345
        public void setNotaFiscalPendente(Boolean notaFiscalPendente) {
346
                this.notaFiscalPendente = notaFiscalPendente;
347
        }
348
 
349
        @Override
350
        public int hashCode() {
351
                final int prime = 31;
352
                int result = 1;
353
                result = prime * result + ((chave == null) ? 0 : chave.hashCode());
354
                return result;
355
        }
356
 
357
        @Override
358
        public boolean equals(Object obj) {
359
                if (this == obj)
360
                        return true;
361
                if (obj == null)
362
                        return false;
363
                if (getClass() != obj.getClass())
364
                        return false;
365
                NotaFiscal other = (NotaFiscal) obj;
366
                if (chave == null) {
367
                        if (other.chave != null)
368
                                return false;
369
                } else if (!chave.equals(other.chave))
370
                        return false;
371
                return true;
372
        }
373
 
374
        @Transient
375
        public Double getTotalLancamentos() {
376
                Double total = 0.0;
377
                if (!VerificadorUtil.isListaNulaOuVazia(getListaLancamento())) {
378
                        for (Lancamento lancamento : getListaLancamento()) {
379
                                total = total + lancamento.getValorVenda();
380
                        }
381
                }
382
                return total;  
383
        }
384
 
385
        @Transient
386
        public Double getTotalPago() {
387
                Double total = 0.0;
388
                if (!VerificadorUtil.isListaNulaOuVazia(getListaFormaPagamento())) {
389
                        for (VendaFormaPagamento vendaFormaPagamento : getListaFormaPagamento()) {
390
                                total = total + vendaFormaPagamento.getValorPagamento();
391
                        }
392
                }
393
                return total;  
394
        }
395
 
396
        @Transient
397
        public Double getValorNotaFiscal() {
398
                if (VerificadorUtil.naoEstaNuloOuVazio(getTextoXml())) {
399
                        if (getTextoXml().indexOf("<vNF>") > 0) {
400
                                String valorLocalizado = getTextoXml().substring(getTextoXml().indexOf("<vNF>"), getTextoXml().indexOf("</vNF>"))
401
                                                                                        .replace("<vNF>", "").replace("</vNF>", "");
402
                                return new Double(valorLocalizado);
403
                        }
404
                }
405
                return 0.0;
406
        }
407
 
408
        @Transient
409
        public Double getTotalValorProdutosRemessa() {
410
                Double total = new Double(0.0);
411
                if (VerificadorUtil.naoEstaNuloOuVazio(getListaNFRemessaProdutos())) {
412
                        for (NotaFiscalProduto notaFiscalProduto : getListaNFRemessaProdutos()) {
413
                                total = total + notaFiscalProduto.getProduto().getValorCompra() * notaFiscalProduto.getQuantidade();
414
                        }
415
                }
416
                return total;
417
        }
418
 
419
        @Transient
420
        public Boolean ehNotaFiscalDeRemessa() {
421
                return VerificadorUtil.naoEstaNulo(getTipoNotaFiscal()) ? getTipoNotaFiscal().equals(TipoNotaFiscal.NFE_REMESSA.getValor()) : false;
422
        }
423
 
424
        @Transient
425
        public byte[] criarArquivoXml() {
426
                ByteArrayInputStream in = null;
427
                ByteArrayOutputStream bos = null;
428
                try {
429
                        in = new ByteArrayInputStream(getTextoXml().getBytes());
430
                        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
431
                        DocumentBuilder builder = dbf.newDocumentBuilder();
432
                        Document xml = builder.parse(in);
433
 
434
                        bos = new ByteArrayOutputStream();
435
                        TransformerFactory transformerFactory = TransformerFactory.newInstance();
436
                        Transformer transformer = transformerFactory.newTransformer();
437
                        DOMSource source = new DOMSource(xml);
438
                        StreamResult result = new StreamResult(bos);
439
                        transformer.transform(source, result);
440
 
441
                        DefaultStreamedContent arquivo = (DefaultStreamedContent) RelatorioUtils.gerarArquivo(bos.toByteArray(), "ArquivoXML", "xml");
442
                        return IOUtils.toByteArray(arquivo.getStream());
443
                } catch (Exception e) {
444
                        e.printStackTrace();
445
                } finally {
446
                        try {
447
                                in.close();
448
                                bos.close();
449
                        } catch (IOException e) {
450
                                e.printStackTrace();
451
                        }
452
                }
453
                return null;
454
        }
455
 
456
        @Transient
457
        public byte[] getRetornarArquivoXml() {
458
                setArquivoXml(criarArquivoXml());
459
                return getArquivoXml();
460
        }
461
 
462
        @Transient
463
        public void gerarChaveAcessoNfe(String codigoUFEmitente, String dataAAMM, String cnpjCpfEmitente, String modeloNf,
464
                        String serieNF, String tpEmissao, String numeroNF) {
465
                try {
466
            StringBuilder chave = new StringBuilder();
467
            chave.append(StringUtil.lpadTo(codigoUFEmitente, 2, '0'));
468
            chave.append(StringUtil.lpadTo(dataAAMM, 4, '0'));
469
            chave.append(StringUtil.lpadTo(cnpjCpfEmitente.replaceAll("\\D",""), 14, '0'));
470
            chave.append(StringUtil.lpadTo(modeloNf, 2, '0'));
471
            chave.append(StringUtil.lpadTo(serieNF, 3, '0'));
472
            chave.append(StringUtil.lpadTo(String.valueOf(numeroNF), 9, '0'));
473
            chave.append(StringUtil.lpadTo(tpEmissao, 1, '0'));
474
            chave.append(StringUtil.lpadTo(numeroNF, 8, '0'));
475
            chave.append(StringUtil.modulo11(chave.toString()));
476
            chave.insert(0, "NFe");
477
            setChave(chave.toString());
478
        } catch (Exception e) {
479
                System.out.println(e.toString());
480
                throw new NegocioException(e.getMessage());
481
        }
482
        }
483
 
484
        @Transient
485
        public Long getSequencialDaVenda() {
486
                return VerificadorUtil.naoEstaNulo(getVenda())? getVenda().getSequencial() : null;
487
        }
488
 
489
        @Transient
490
        public void preencherNumeroPelaChave() {
491
                if (VerificadorUtil.naoEstaNuloOuVazio(getChave())) {
492
                        setNumeroNotaFiscal(new Long(getChave().substring(25, 34)));
493
                }
494
        }
495
 
496
        @Transient
497
        public void preencherMotivoPadrao() {
498
                setMotivoRetorno("Autorizado o uso da NF-e");
499
        }
500
 
501
        @Transient
502
        public void preencherSeriePadrao() {
503
                setSerie("1");
504
        }
505
 
506
        @Transient
507
        public void preencherStatusPadrao() {
508
                setStatusRetorno("100");
509
        }
510
 
511
        @Transient
512
        public void preencherQRCodePadrao() {
513
                setCaminhoQrcode("http://nfce.sefaz.al.gov.br/QRCode/consultarNFCe.jsp?p=" + getChave() + "|2|1|1|437A52AD9DE9BDD04D6BFF55ED997C5199ECC64D");
514
        }
515
 
516
        @Transient
517
        public String retornarCodigoNumerico() {
518
                if (VerificadorUtil.naoEstaNuloOuVazio(getChave())) {
519
                        if (getChave().length() > 42) {
520
                                return getChave().substring(35, 43);
521
                        }
522
                }
523
                return "";
524
        }
525
 
526
        @Transient
527
        public void preencherComXML() {
528
                if (VerificadorUtil.naoEstaNuloOuVazio(this.getTextoXml())) {
529
                        TNfeProc tNfeProc = unmarshal(this.getTextoXml());
530
                        preencher(tNfeProc);
531
                }
532
 
533
        }
534
 
535
        @Transient
536
        public void preencher(TNfeProc nfeProc) {
537
                if (VerificadorUtil.naoEstaNulo(nfeProc)) {
538
                        if (VerificadorUtil.naoEstaNulo(nfeProc.getNFe())) {
539
                                if (VerificadorUtil.naoEstaNulo(nfeProc.getNFe().getInfNFe())) {
540
                                        if (VerificadorUtil.naoEstaNulo(nfeProc.getNFe().getInfNFe().getIde())) {
541
                                                //this.setCodigoNotaFiscal(new Long(nfeProc.getNFe().getInfNFe().getIde().getCNF()));
542
                                                this.setNumeroNotaFiscal(new Long(nfeProc.getNFe().getInfNFe().getIde().getNNF()));
543
                                                this.setSerie(nfeProc.getNFe().getInfNFe().getIde().getSerie());
544
                                                this.setDataHoraEmissao(DataUtil.retornarDataApartirString("yyyy-MM-dd'T'HH:mm:ss", nfeProc.getNFe().getInfNFe().getIde().getDhEmi()));
545
                                        }
546
                                        if (VerificadorUtil.naoEstaNulo(nfeProc.getNFe().getInfNFe().getInfAdic())) {
547
                                                this.setDescricaoComplementares(nfeProc.getNFe().getInfNFe().getInfAdic().getInfCpl());
548
                                        }
549
                                }
550
                                if (VerificadorUtil.naoEstaNulo(nfeProc.getNFe().getInfNFeSupl())) {}
551
                                if (VerificadorUtil.naoEstaNulo(nfeProc.getNFe().getSignature())) {}
552
                        }
553
                        if (VerificadorUtil.naoEstaNulo(nfeProc.getProtNFe())) {
554
                                if (VerificadorUtil.naoEstaNulo(nfeProc.getProtNFe().getInfProt())) {
555
                                        this.setChave(nfeProc.getProtNFe().getInfProt().getChNFe());
556
                                        this.setDataHoraEvento(DataUtil.retornarDataApartirString("yyyy-MM-dd'T'HH:mm:ss", nfeProc.getProtNFe().getInfProt().getDhRecbto()));
557
                                        this.setProtocoloAutorizacao(nfeProc.getProtNFe().getInfProt().getNProt());
558
                                        this.setStatusRetorno(nfeProc.getProtNFe().getInfProt().getCStat());
559
                                        this.setMotivoRetorno(nfeProc.getProtNFe().getInfProt().getXMotivo());
560
                                }
561
                        }
562
                }
563
        }
564
 
565
        @Transient
566
        public TNfeProc unmarshal(String stringXml) {
567
        JAXBContext context = null;
568
        try {
569
            context = JAXBContext.newInstance(nfce.java.TNfeProc.class);
570
            Unmarshaller unmarshaller = context.createUnmarshaller();
571
            return (TNfeProc) unmarshaller.unmarshal(new StreamSource(new StringReader(stringXml)));
572
        } catch (JAXBException e) {
573
            e.printStackTrace();
574
        }
575
        return null;
576
    }
577
 
578
}