willkerms / NFSe

Nota Fiscal de Serviço Eletrônica
4 stars 4 forks source link

Implementação em São Bernardo do Campo SP #1

Open aCidcOde opened 7 years ago

aCidcOde commented 7 years ago

Boa tarde,

Primeiramente parabens pelo code, estou tentando implementar ele na prefeitura de SBC. mas estou enfrentando alguns problemas para roda-lo, vc tem algum exemplo de uso para que eu possa dar uma olhada?

Abs!

willkerms commented 7 years ago

Obrigado, segue modelo de transmissão e consulta.

Transmissão: `<?php use NFSe\ginfes\NFSeGinfesInfRps; use NFSe\ginfes\NFSeGinfes;

    $oRPS = new NFSeGinfesInfRps();

    $oRPS->DataEmissao = preg_replace('/\..*/', "", str_replace(' ', 'T', $oNFSe->getEmissaoRPS()));
    $oRPS->NaturezaOperacao = $oNFSe->getNatOperacao();
    $oRPS->RegimeEspecialTributacao = $oNFSe->getRegimeEspTrib();
    $oRPS->OptanteSimplesNacional = $oNFSe->getSimplesNacional();
    $oRPS->IncentivadorCultural = $oNFSe->getIcentivadorCultural();
    $oRPS->Status = $oNFSe->getStatusRPS();

    $oRPS->IdentificacaoRps->Numero = $oNFSe->getRpsNum();
    $oRPS->IdentificacaoRps->Serie = $oNFSe->getRpsSerie();
    $oRPS->IdentificacaoRps->Tipo = $oNFSe->getRpsTipo();

    if(!is_null($oNFSe->getIdNfseSubstituida())){
        $oRPS->RpsSubstituido->Numero = $oNFSe->getRpsNumSubstituto();
        $oRPS->RpsSubstituido->Serie = $oNFSe->getRpsSerieSubstituto();
        $oRPS->RpsSubstituido->Tipo = $oNFSe->getRpsTipoSubstituto();
    }

    $oRPS->Servico->ItemListaServico = $oNFSe->getCodigoSERVICO();
    $oRPS->Servico->CodigoCnae = $oNFSe->getCodigoCNAE();
    $oRPS->Servico->CodigoTributacaoMunicipio = $oNFSe->getCodTribMunicipio();
    $oRPS->Servico->Discriminacao = utf8_encode(preg_replace("/&([a-z])[a-z]+;/i","$1", strtoupper(htmlentities(str_replace("'", " ", $oNFSe->getDiscriminacao()), ENT_QUOTES, "ISO-8859-1"))));
    $oRPS->Servico->CodigoMunicipio = $oNFSe->getCodIBGEMunicipio();

    $oRPS->Servico->Valores->ValorServicos = $oNFSe->getVlrServico();
    $oRPS->Servico->Valores->ValorDeducoes = $oNFSe->getVlrDeducoes();
    $oRPS->Servico->Valores->ValorPis = $oNFSe->getVlrPis();
    $oRPS->Servico->Valores->ValorCofins = $oNFSe->getVlrConfins();
    $oRPS->Servico->Valores->ValorInss = $oNFSe->getVlrInss();
    $oRPS->Servico->Valores->ValorIr = $oNFSe->getVlrIr();
    $oRPS->Servico->Valores->ValorCsll = $oNFSe->getVlrCsll();
    $oRPS->Servico->Valores->IssRetido = $oNFSe->getIssRetido();//Quando empresa do mesmo município deve reter iss
    $oRPS->Servico->Valores->ValorIss = $oNFSe->getVlrIss();
    $oRPS->Servico->Valores->ValorIssRetido = $oNFSe->getVlrIssRetido();
    $oRPS->Servico->Valores->OutrasRetencoes = $oNFSe->getVlrOutrasRetencoes();
    $oRPS->Servico->Valores->BaseCalculo = $oNFSe->getBaseCalculo();
    $oRPS->Servico->Valores->Aliquota = $oNFSe->getAliquota();
    $oRPS->Servico->Valores->ValorLiquidoNfse = $oNFSe->getVlrLiquidoNfse();//Sistema calcula sozinho
    $oRPS->Servico->Valores->DescontoCondicionado = $oNFSe->getVlrDescCondicionado();
    $oRPS->Servico->Valores->DescontoIncondicionado = $oNFSe->getVlrDescIncondicionado();

    $oRPS->Tomador->IdentificacaoTomador->InscricaoMunicipal = $oNFSe->getInscMunicipalTomador();
    $oRPS->Tomador->IdentificacaoTomador->setCpfCnpj($oNFSe->getCpfCnpjTomador(), $oNFSe->getTpTomador());
    $oRPS->Tomador->RazaoSocial = utf8_encode(preg_replace("/&([a-z])[a-z]+;/i","$1", strtoupper(htmlentities(str_replace("'", " ", $oNFSe->getRazaoSocialTomador()), ENT_QUOTES, "ISO-8859-1"))));
    $oRPS->Tomador->Endereco->Endereco = utf8_encode(preg_replace("/&([a-z])[a-z]+;/i","$1", strtoupper(htmlentities(str_replace("'", " ", $oNFSe->getEnderecoTomador()), ENT_QUOTES, "ISO-8859-1"))));
    $oRPS->Tomador->Endereco->Numero = utf8_encode(preg_replace("/&([a-z])[a-z]+;/i","$1", strtoupper(htmlentities(str_replace("'", " ", $oNFSe->getNumeroEndTomador()), ENT_QUOTES, "ISO-8859-1"))));
    $oRPS->Tomador->Endereco->Complemento = utf8_encode(preg_replace("/&([a-z])[a-z]+;/i","$1", strtoupper(htmlentities(str_replace("'", " ", $oNFSe->getComplementoTomador()), ENT_QUOTES, "ISO-8859-1"))));
    $oRPS->Tomador->Endereco->Bairro = utf8_encode(preg_replace("/&([a-z])[a-z]+;/i","$1", strtoupper(htmlentities(str_replace("'", " ", $oNFSe->getBairroTomador()), ENT_QUOTES, "ISO-8859-1"))));
    $oRPS->Tomador->Endereco->CodigoMunicipio = $oNFSe->getCodIBGEMunTomador();
    $oRPS->Tomador->Endereco->Uf = $oNFSe->getUfTomador();
    $oRPS->Tomador->Endereco->Cep = $oNFSe->getCepTomador();

    $oRPS->Tomador->Contato->Telefone = $oNFSe->getTelefoneTomador();
    $oRPS->Tomador->Contato->Email = $oNFSe->getEmailTomador();

    $aConfig = array();

    $aConfig['cnpj'] = $oFilial->getCpfCnpj();
    $aConfig['inscMunicipal'] = $oFilial->getInscMunicipal();

    $certLocation = "local_certificado";

    $aConfig['privKey'] = $certLocation . '_priKEY.pem';
    $aConfig['pubKey'] = $certLocation . '_pubKEY.pem';
    $aConfig['certKey'] = $certLocation . '_certKEY.pem';

    $aConfig['pathCert'] = "diretorio/para/salvar/xml";

    try{
        $oNFSeGinfes = new NFSeGinfes($aConfig, true);

        $oReturn = $oNFSeGinfes->enviarLoteRps(array($oRPS), new NFSeGinfesLoteRps($numeroLote));

        if(count($oReturn->ListaMensagemRetorno) == 0){
            $oLoteNFSe->setProtocolo($oReturn->Protocolo);
            $oLoteNFSe->setDtaRecebimento(str_replace("T", ' ', $oReturn->DataRecebimento));
            $oLoteNFSe->setStatus(1);

            $oNFSe->setStatus(1);

            if($this->getBOLoteNFSe()->save($oLoteNFSe) && $this->getDAO()->atualizaStatus($oNFSe)){
                $return['ok'] = true;
                $oNFSe = $this->formatNFSeView($oNFSe);
                $return['nfse'] = $oNFSe->toArray();
                $return['msg'] = "Transmitido com sucesso";
            }
            else{
                throw new \Exception("Erro ao salvar lote/atualizar status dá nota!");
            }
        }
        else{
            foreach ($oReturn->ListaMensagemRetorno as $oMensagem)
                $this->getExceptions()->setException(new \Exception($oMensagem->Codigo . " " . $oMensagem->Mensagem . ". " . $oMensagem->Correcao));

            $oNFSe->setStatus(3);
            $this->getDAO()->updateGeneric($oNFSe, array('status'));
        }
    }
    catch (\Exception $e){
        $this->getExceptions()->setException($e);
    }

` Consulta lote rps:

` $oIdfRps = new NFSeGinfesIdentificacaoRps(); $oIdfRps->Numero = "numero"; $oIdfRps->Serie = "serie"; $oIdfRps->Tipo = "tipo";

        $oInfRps = $oNFSeGinfes->consultarNFSePorRps($oIdfRps);

`

aCidcOde commented 7 years ago

Agora que vi a resposta. Obrigado.

aCidcOde commented 7 years ago

Tive alguns problemas:

- A nova versão do openssl no mac nao tinha suporte para certificado digital em transações SOAP e Curl. Solução: Instalar o php com suporte a versão, versão usada foi a 5.5.

- Montar o exemplo de envio da nota.

$oRPS = new \NFSe\ginfes\NFSeGinfesInfRps();
$oRPS->DataEmissao = '2017-08-10T17:00:45';
$oRPS->NaturezaOperacao = 1;
$oRPS->RegimeEspecialTributacao = 2;
$oRPS->OptanteSimplesNacional = 1;
$oRPS->IncentivadorCultural = 2;
$oRPS->Status = 1;
$oRPS->IdentificacaoRps->Numero = 60;
$oRPS->IdentificacaoRps->Serie = "NFSE";
$oRPS->IdentificacaoRps->Tipo = 1;
$oRPS->Servico->ItemListaServico = "1.08";
$oRPS->Servico->CodigoTributacaoMunicipio = "1.08/102329/1234";
$oRPS->Servico->Discriminacao = Canivete::limparStringNFE("Loja Virtual Ecotrend");
$oRPS->Servico->CodigoMunicipio = 3548708;
$oRPS->Servico->Valores->ValorIss = (69.90*0.02);
$oRPS->Servico->Valores->ValorServicos = 69.90;
$oRPS->Servico->Valores->BaseCalculo = 69.90;
$oRPS->Servico->Valores->Aliquota = 0.0200;
$oRPS->Tomador->IdentificacaoTomador->setCpfCnpj('35116014878', 0);
$oRPS->Tomador->RazaoSocial = Canivete::limparStringNFE("André Lima Gomes");
$oRPS->Tomador->Endereco->Endereco = Canivete::limparStringNFE("Av senador Vergueiro");
$oRPS->Tomador->Endereco->Numero = Canivete::limparStringNFE("1350");
$oRPS->Tomador->Endereco->Complemento = Canivete::limparStringNFE("casa");
$oRPS->Tomador->Endereco->Bairro = Canivete::limparStringNFE("Rudge");
$oRPS->Tomador->Endereco->CodigoMunicipio = 3548708;
$oRPS->Tomador->Endereco->Uf = "SP";
$oRPS->Tomador->Endereco->Cep = "09750000";
$oRPS->Tomador->Contato->Telefone = "11948494857";
$oRPS->Tomador->Contato->Email = "acidkp@gmail.com";

$aConfig = array();
$aConfig['cnpj'] = '00599355000149';
$aConfig['inscMunicipal'] = '84301';
$aConfig['passcode'] = '*****';
$aConfig['certKey'] = "/data/ecotrend/nfephp/certs/certificado.pem";
$aConfig['pathCert'] = '/data/ecotrend/arquivos/nfs';

try{
    $oNFSeGinfes = new \NFSe\ginfes\NFSeGinfes($aConfig, true);
    $oReturn = $oNFSeGinfes->enviarLoteRps(array($oRPS), new \NFSe\ginfes\NFSeGinfesLoteRps($oRPS->IdentificacaoRps->Numero));
    if(count($oReturn->ListaMensagemRetorno) == 0){
        var_dump($oReturn);
        exit();
    }
    else{
        foreach ($oReturn->ListaMensagemRetorno as $oMensagem)
                var_dump($oMensagem->Codigo . " " . $oMensagem->Mensagem . ". " . $oMensagem->Correcao);
    }
}
catch (\Exception $e){
    var_dump($e->getMessage());
    exit();
}

- Limpar as string de forma efetiva sem quebrar ou mudar o texto. Solução: Incrementei uma nova classe que eu já tinha feito na NFePHP

public static function limparStringNFE($string) {
    if ( !preg_match('/[\x80-\xff]/', $string) )
        return strtoupper($string);

    $chars = array(
        // Decompositions for Latin-1 Supplement
        chr(195).chr(128) => 'A', chr(195).chr(129) => 'A',
        chr(195).chr(130) => 'A', chr(195).chr(131) => 'A',
        chr(195).chr(132) => 'A', chr(195).chr(133) => 'A',
        chr(195).chr(135) => 'C', chr(195).chr(136) => 'E',
        chr(195).chr(137) => 'E', chr(195).chr(138) => 'E',
        chr(195).chr(139) => 'E', chr(195).chr(140) => 'I',
        chr(195).chr(141) => 'I', chr(195).chr(142) => 'I',
        chr(195).chr(143) => 'I', chr(195).chr(145) => 'N',
        chr(195).chr(146) => 'O', chr(195).chr(147) => 'O',
        chr(195).chr(148) => 'O', chr(195).chr(149) => 'O',
        chr(195).chr(150) => 'O', chr(195).chr(153) => 'U',
        chr(195).chr(154) => 'U', chr(195).chr(155) => 'U',
        chr(195).chr(156) => 'U', chr(195).chr(157) => 'Y',
        chr(195).chr(159) => 's', chr(195).chr(160) => 'a',
        chr(195).chr(161) => 'a', chr(195).chr(162) => 'a',
        chr(195).chr(163) => 'a', chr(195).chr(164) => 'a',
        chr(195).chr(165) => 'a', chr(195).chr(167) => 'c',
        chr(195).chr(168) => 'e', chr(195).chr(169) => 'e',
        chr(195).chr(170) => 'e', chr(195).chr(171) => 'e',
        chr(195).chr(172) => 'i', chr(195).chr(173) => 'i',
        chr(195).chr(174) => 'i', chr(195).chr(175) => 'i',
        chr(195).chr(177) => 'n', chr(195).chr(178) => 'o',
        chr(195).chr(179) => 'o', chr(195).chr(180) => 'o',
        chr(195).chr(181) => 'o', chr(195).chr(182) => 'o',
        chr(195).chr(182) => 'o', chr(195).chr(185) => 'u',
        chr(195).chr(186) => 'u', chr(195).chr(187) => 'u',
        chr(195).chr(188) => 'u', chr(195).chr(189) => 'y',
        chr(195).chr(191) => 'y',
        // Decompositions for Latin Extended-A
        chr(196).chr(128) => 'A', chr(196).chr(129) => 'a',
        chr(196).chr(130) => 'A', chr(196).chr(131) => 'a',
        chr(196).chr(132) => 'A', chr(196).chr(133) => 'a',
        chr(196).chr(134) => 'C', chr(196).chr(135) => 'c',
        chr(196).chr(136) => 'C', chr(196).chr(137) => 'c',
        chr(196).chr(138) => 'C', chr(196).chr(139) => 'c',
        chr(196).chr(140) => 'C', chr(196).chr(141) => 'c',
        chr(196).chr(142) => 'D', chr(196).chr(143) => 'd',
        chr(196).chr(144) => 'D', chr(196).chr(145) => 'd',
        chr(196).chr(146) => 'E', chr(196).chr(147) => 'e',
        chr(196).chr(148) => 'E', chr(196).chr(149) => 'e',
        chr(196).chr(150) => 'E', chr(196).chr(151) => 'e',
        chr(196).chr(152) => 'E', chr(196).chr(153) => 'e',
        chr(196).chr(154) => 'E', chr(196).chr(155) => 'e',
        chr(196).chr(156) => 'G', chr(196).chr(157) => 'g',
        chr(196).chr(158) => 'G', chr(196).chr(159) => 'g',
        chr(196).chr(160) => 'G', chr(196).chr(161) => 'g',
        chr(196).chr(162) => 'G', chr(196).chr(163) => 'g',
        chr(196).chr(164) => 'H', chr(196).chr(165) => 'h',
        chr(196).chr(166) => 'H', chr(196).chr(167) => 'h',
        chr(196).chr(168) => 'I', chr(196).chr(169) => 'i',
        chr(196).chr(170) => 'I', chr(196).chr(171) => 'i',
        chr(196).chr(172) => 'I', chr(196).chr(173) => 'i',
        chr(196).chr(174) => 'I', chr(196).chr(175) => 'i',
        chr(196).chr(176) => 'I', chr(196).chr(177) => 'i',
        chr(196).chr(178) => 'IJ',chr(196).chr(179) => 'ij',
        chr(196).chr(180) => 'J', chr(196).chr(181) => 'j',
        chr(196).chr(182) => 'K', chr(196).chr(183) => 'k',
        chr(196).chr(184) => 'k', chr(196).chr(185) => 'L',
        chr(196).chr(186) => 'l', chr(196).chr(187) => 'L',
        chr(196).chr(188) => 'l', chr(196).chr(189) => 'L',
        chr(196).chr(190) => 'l', chr(196).chr(191) => 'L',
        chr(197).chr(128) => 'l', chr(197).chr(129) => 'L',
        chr(197).chr(130) => 'l', chr(197).chr(131) => 'N',
        chr(197).chr(132) => 'n', chr(197).chr(133) => 'N',
        chr(197).chr(134) => 'n', chr(197).chr(135) => 'N',
        chr(197).chr(136) => 'n', chr(197).chr(137) => 'N',
        chr(197).chr(138) => 'n', chr(197).chr(139) => 'N',
        chr(197).chr(140) => 'O', chr(197).chr(141) => 'o',
        chr(197).chr(142) => 'O', chr(197).chr(143) => 'o',
        chr(197).chr(144) => 'O', chr(197).chr(145) => 'o',
        chr(197).chr(146) => 'OE',chr(197).chr(147) => 'oe',
        chr(197).chr(148) => 'R',chr(197).chr(149) => 'r',
        chr(197).chr(150) => 'R',chr(197).chr(151) => 'r',
        chr(197).chr(152) => 'R',chr(197).chr(153) => 'r',
        chr(197).chr(154) => 'S',chr(197).chr(155) => 's',
        chr(197).chr(156) => 'S',chr(197).chr(157) => 's',
        chr(197).chr(158) => 'S',chr(197).chr(159) => 's',
        chr(197).chr(160) => 'S', chr(197).chr(161) => 's',
        chr(197).chr(162) => 'T', chr(197).chr(163) => 't',
        chr(197).chr(164) => 'T', chr(197).chr(165) => 't',
        chr(197).chr(166) => 'T', chr(197).chr(167) => 't',
        chr(197).chr(168) => 'U', chr(197).chr(169) => 'u',
        chr(197).chr(170) => 'U', chr(197).chr(171) => 'u',
        chr(197).chr(172) => 'U', chr(197).chr(173) => 'u',
        chr(197).chr(174) => 'U', chr(197).chr(175) => 'u',
        chr(197).chr(176) => 'U', chr(197).chr(177) => 'u',
        chr(197).chr(178) => 'U', chr(197).chr(179) => 'u',
        chr(197).chr(180) => 'W', chr(197).chr(181) => 'w',
        chr(197).chr(182) => 'Y', chr(197).chr(183) => 'y',
        chr(197).chr(184) => 'Y', chr(197).chr(185) => 'Z',
        chr(197).chr(186) => 'z', chr(197).chr(187) => 'Z',
        chr(197).chr(188) => 'z', chr(197).chr(189) => 'Z',
        chr(197).chr(190) => 'z', chr(197).chr(191) => 's'
    );

    $string = strtr($string, $chars);

    $string = str_replace("1º", "1", $string);
    $string = str_replace("2º", "2", $string);
    $string = str_replace("3º", "3", $string);
    $string = str_replace("4º", "4", $string);
    $string = str_replace("5º", "5", $string);
    $string = str_replace("6º", "6", $string);
    $string = str_replace("7º", "7", $string);
    $string = str_replace("8º", "8", $string);
    $string = str_replace("9º", "9", $string);

    return strtoupper(trim($string));

}

- A implementação SOAP nao estava aceitando o certificado digital separado da key. Solução: Gerar um novo certificado digital atravez do pfx

openssl pkcs12 -in certificado.pfx -out certificado.pem

Trocar a implementação do SOAP para base na mostrada abaixo

$params = array(
   'local_cert' => "/data/ecotrend/nfephp/certs/certificado.pem",
   'passphrase' => '****',
   'connection_timeout' => 300,
   'encoding' => 'UTF-8',
   'verifypeer'    => false,
   'verifyhost'    => false,
   'soap_version'  => SOAP_1_2,
   'trace'         => true,
   'cache_wsdl' => WSDL_CACHE_NONE
);

return $this->getSoap($wsdl, $params)->__doRequest($data, $url, $action, $version);

- Validar XML com Schema.xsd Solução: Usei o Notepad PLUS e uma extensão XML Doc para validar o xml durante o desenvolvimento. Nao consegui fazer funcionar de uma forma que facilitava pelo MAC.

- Erros no campo Aliquota Solução: A porcentagem deve ser preenchida de forma decimal $oRPS->Servico->Valores->Aliquota = 0.0200; //2%

- Erros no campo ValorISS Solução: Campo deve conter o valor do imposto, e nao a aliquota.

- Campos sem utilidade que davam erro no processamento do Ginfes Solução: Comentei as entradas para elas nao irem para o xml

//$Valores->appendChild($document->createElement("ts:DescontoCondicionado", number_format($oRps->Servico->Valores->DescontoCondicionado, 4, '.', '')));
//$Valores->appendChild($document->createElement("ts:ValorIssRetido", number_format($oRps->Servico->Valores->ValorIssRetido, 2, '.', '')));
//$Valores->appendChild($document->createElement("ts:OutrasRetencoes", number_format($oRps->Servico->Valores->OutrasRetencoes, 2, '.', '')));
//$Servico->appendChild($document->createElement("ts:CodigoCnae", $oRps->Servico->CodigoCnae));