Ir para conteúdo

POWERED BY:

Williams Duarte

Members
  • Total de itens

    3967
  • Registro em

  • Última visita

  • Dias vencidos

    47

Reputação

431 Incrível

Sobre Williams Duarte

  • Data de Nascimento 05/17/1989

Informações Pessoais

  • Sexo
    Masculino
  • Interesses
    Tecnologia, Engenharia de Software, PHP, Design Pattern, JavaScript, SQL, NoSQL e Empreendedorismo

Últimos Visitantes

8810331 visualizações
  1. Williams Duarte

    PHP+Codeigniter - Implementar o Input CÓDIGO ou ID do Produto

    Primeiro, você não está declarando a variável timeoutId <script type="text/javascript"> $(document).ready(function(){ let timeoutId; // Declaração da variável $('#idProdutos').on('input', function() { clearTimeout(timeoutId); const idprd = $(this).val(); timeoutId = setTimeout(function() { if (idprd) { $.ajax({ url: "<?php echo base_url(); ?>vendas/pdvcaixa/buscarID", type: "POST", data: { idProdutos: idprd }, // Corrigido nome do parâmetro dataType: 'json', // Importante adicionar success: function(response) { if (response.success) { // Como o resultado é um array, pegamos o primeiro item if (response.produto.length > 0) { $("#descricao").val(response.produto[0].descricao); } } }, error: function(xhr, status, error) { console.log('Erro:', error); } }); } }, 1500); }); }); </script> No Controller, ajuste para: public function buscarID() { $idprd = $this->input->post('idProdutos'); $produto = $this->pdvcaixa_model->buscarCodigo($idprd); if ($produto) { echo json_encode([ 'success' => true, 'produto' => $produto ]); } else { echo json_encode([ 'success' => false, 'message' => 'Produto não encontrado' ]); } } Se ainda assim não funcionar, você pode adicionar alguns console.log para debug: $('#idProdutos').on('input', function() { console.log('Input detectado'); clearTimeout(timeoutId); const idprd = $(this).val(); console.log('Valor digitado:', idprd); timeoutId = setTimeout(function() { console.log('Timeout executado'); if (idprd) { // ... resto do código
  2. Williams Duarte

    PHP+Codeigniter - Implementar o Input CÓDIGO ou ID do Produto

    Para implementar a busca automática com delay de 1-2 segundos sem precisar do ENTER, você pode usar jQuery junto com CodeIgniter. Segue um exemplo simples: $('#codigo_produto').on('input', function() { clearTimeout(timeoutId); const codigo = $(this).val(); timeoutId = setTimeout(function() { if (codigo) { $.ajax({ url: '<?= base_url("produtos/buscar") ?>', type: 'POST', data: { codigo: codigo }, success: function(response) { // Exibe o resultado if (response.success) { $('#resultado').html(response.produto.descricao); } } }); } }, 1500); // 1.5 segundos de delay }); No seu controller: public function buscar() { $codigo = $this->input->post('codigo'); $produto = $this->produtos_model->buscar_por_codigo($codigo); echo json_encode([ 'success' => true, 'produto' => $produto ]); } O código acima vai esperar 1.5 segundos após o usuário parar de digitar antes de fazer a busca. Você pode ajustar o delay alterando o valor 1500 para qualquer outro valor em milissegundos.
  3. Williams Duarte

    PHP+Codeigniter - Adicionar Registro Plano de Contas

    Neste caso tem que criar uma configuração dos níveis e seus formatos $nivel_config = [ 1 => ['length' => 2, 'format' => '%02d'], // 1.01 2 => ['length' => 3, 'format' => '%03d'], // 1.01.001 3 => ['length' => 4, 'format' => '%04d'], // 1.01.001.0001 4 => ['length' => 5, 'format' => '%05d'] // 1.01.001.0001.00001 (caso precise e por ai vai) ]; public function getNextCode($parent_code = null) { log_message('debug', '=== INÍCIO getNextCode ==='); log_message('debug', 'Parent Code: ' . ($parent_code ?? 'NULL')); // Configuração dos níveis e seus formatos $nivel_config = [ 1 => ['length' => 2, 'format' => '%02d'], // 1.01 2 => ['length' => 3, 'format' => '%03d'], // 1.01.001 3 => ['length' => 4, 'format' => '%04d'], // 1.01.001.0001 4 => ['length' => 5, 'format' => '%05d'] // 1.01.001.0001.00001 (caso precise e por ai vai) ]; if (!$parent_code) { $query = $this->db->select('MAX(CAST(pl_idconta AS UNSIGNED)) as max_code') ->from('planocontas') ->where('pl_idconta NOT LIKE "%.%"') ->get(); $result = $query->row(); $next_code = ($result->max_code ?? 0) + 1; log_message('debug', 'Gerando código raiz: ' . $next_code); return $next_code; } $parent_parts = explode('.', $parent_code); $nivel = count($parent_parts); log_message('debug', 'Nível hierárquico: ' . $nivel); // Verifica se o nível é suportado if (!isset($nivel_config[$nivel])) { log_message('error', 'Nível não suportado: ' . $nivel); return false; } $query = $this->db->select('pl_idconta') ->from('planocontas') ->like('pl_idconta', $parent_code . '.', 'after') ->order_by('pl_idconta', 'DESC') ->limit(1) ->get(); log_message('debug', 'SQL executado: ' . $this->db->last_query()); if ($query->num_rows() == 0) { // Primeiro subcódigo deste nível $new_code = $parent_code . '.' . sprintf($nivel_config[$nivel]['format'], 1); log_message('debug', 'Primeiro subcódigo deste pai: ' . $new_code); return $new_code; } $ultimo_codigo = $query->row()->pl_idconta; $partes = explode('.', $ultimo_codigo); $ultimo_numero = (int)end($partes); log_message('debug', sprintf( 'Último código: %s | Último número: %s', $ultimo_codigo, $ultimo_numero )); // Gera próximo código usando a configuração do nível $proximo = sprintf($nivel_config[$nivel]['format'], $ultimo_numero + 1); $final_code = $parent_code . '.' . $proximo; log_message('debug', 'Código gerado: ' . $final_code); log_message('debug', '=== FIM getNextCode ==='); return $final_code; } No controller, ajuste a função que busca os códigos pais public function novo() { $this->load->model('planocontas_model'); $max_nivel = 4; // Defina aqui o número máximo de níveis desejado $data['plcta'] = $this->db ->select('*') ->from('planocontas') ->where('pl_status', 1) ->where('CHAR_LENGTH(pl_idconta) - CHAR_LENGTH(REPLACE(pl_idconta, ".", "")) <', $max_nivel) ->order_by('pl_idconta') ->get() ->result(); $this->load->view('estilo/header', $this->data); $this->load->view('faturamento/planocontas/adicionarConta', $data); }
  4. Williams Duarte

    PHP+Codeigniter - Adicionar Registro Plano de Contas

    Aplique log e vai debugando public function getNextCode($parent_code = null) { log_message('debug', '=== INÍCIO getNextCode ==='); log_message('debug', 'Parent Code: ' . ($parent_code ?? 'NULL')); if (!$parent_code) { $query = $this->db->select('MAX(CAST(pl_idconta AS UNSIGNED)) as max_code') ->from('planocontas') ->where('pl_idconta NOT LIKE "%.%"') ->get(); $result = $query->row(); $next_code = ($result->max_code ?? 0) + 1; log_message('debug', 'Gerando código raiz: ' . $next_code); return $next_code; } $parent_parts = explode('.', $parent_code); $nivel = count($parent_parts); log_message('debug', 'Nível hierárquico: ' . $nivel); $query = $this->db->select('pl_idconta') ->from('planocontas') ->like('pl_idconta', $parent_code . '.', 'after') ->order_by('pl_idconta', 'DESC') ->limit(1) ->get(); log_message('debug', 'SQL executado: ' . $this->db->last_query()); if ($query->num_rows() == 0) { $new_code = ($nivel == 1) ? $parent_code . '.01' : $parent_code . '.0001'; log_message('debug', 'Primeiro subcódigo deste pai: ' . $new_code); return $new_code; } $ultimo_codigo = $query->row()->pl_idconta; $partes = explode('.', $ultimo_codigo); $ultimo_numero = end($partes); log_message('debug', sprintf( 'Último código: %s | Último número: %s', $ultimo_codigo, $ultimo_numero )); if ($nivel == 1) { $proximo = str_pad((int)$ultimo_numero + 1, 2, '0', STR_PAD_LEFT); } else { $proximo = str_pad((int)$ultimo_numero + 1, 4, '0', STR_PAD_LEFT); } $final_code = $parent_code . '.' . $proximo; log_message('debug', 'Código gerado: ' . $final_code); log_message('debug', '=== FIM getNextCode ==='); return $final_code; } Os logs serão gravados em application/logs/. Para visualizar, ative debug no config.php: $config['log_threshold'] = 2; // Debug Caso não consiga corrigir o código pelo debug, poste os logs aqui! Mas dei mais um ajuste!
  5. Williams Duarte

    PHP+Codeigniter - Adicionar Registro Plano de Contas

    O problema está na lógica do getNextCode(): Fiz uns ajustes, não tenho como testar, só olhei a doc por cima de como monta selects no CI, que é bem parecida com Query Builder do Laravel public function getNextCode($parent_code = null) { if (!$parent_code) { // Busca próximo código raiz $query = $this->db->select('MAX(CAST(pl_idconta AS UNSIGNED)) as max_code') ->from('planocontas') ->where('pl_idconta NOT LIKE "%.%"') ->get(); $result = $query->row(); return ($result->max_code ?? 0) + 1; } // Busca subcódigos do mesmo nível $parent_parts = explode('.', $parent_code); $nivel = count($parent_parts); $like_pattern = $parent_code . '.%'; $query = $this->db->select('pl_idconta') ->from('planocontas') ->like('pl_idconta', $like_pattern, 'after') ->get(); $codigos_mesmo_nivel = []; foreach ($query->result() as $row) { $parts = explode('.', $row->pl_idconta); if (count($parts) == $nivel + 1) { $codigos_mesmo_nivel[] = (int)end($parts); } } $proximo = empty($codigos_mesmo_nivel) ? 1 : max($codigos_mesmo_nivel) + 1; // Formata o número baseado no nível if ($nivel == 1) { // Nível 1.XX return $parent_code . '.' . str_pad($proximo, 2, '0', STR_PAD_LEFT); } else if ($nivel == 2) { // Nível 1.XX.XXXX return $parent_code . '.' . str_pad($proximo, 4, '0', STR_PAD_LEFT); } else { // Demais níveis return $parent_code . '.' . $proximo; } } Principais correções: Separação por níveis hierárquicos Contagem correta do próximo número Formatação específica para cada nível Exemplo de resultado: 1 - Principal 1.01 - Setor A 1.01.0001 - Subsetor A1 1.02 - Setor B 2 - Secundário 2.01 - Filial 1 Para fins de comparação # Plano de Contas - Correção na geração de códigos hierárquicos ## Alterações no método getNextCode() - Corrigida lógica de incremento dos níveis - Melhorada separação hierárquica - Adicionada formatação específica por nível ```diff public function getNextCode($parent_code = null) { - if (!$parent_code) { - $query = $this->db->select('MAX(SUBSTRING_INDEX(pl_idconta, ".", 1)) as max_code') - ->from('planocontas') - ->where('LENGTH(pl_idconta) = 1') - ->get(); + if (!$parent_code) { + // Busca próximo código raiz usando CAST para comparação numérica + $query = $this->db->select('MAX(CAST(pl_idconta AS UNSIGNED)) as max_code') + ->from('planocontas') + ->where('pl_idconta NOT LIKE "%.%"') + ->get(); $result = $query->row(); return ($result->max_code ?? 0) + 1; } - $like_pattern = $parent_code . '.%'; - $length = strlen($parent_code) + 3; + // Identificação do nível hierárquico atual + $parent_parts = explode('.', $parent_code); + $nivel = count($parent_parts); + // Busca subcódigos existentes no mesmo nível + $like_pattern = $parent_code . '.%'; $query = $this->db->select('MAX(SUBSTRING_INDEX(pl_idconta, ".", -1)) as max_code') ->from('planocontas') ->like('pl_idconta', $like_pattern, 'after') - ->where('LENGTH(pl_idconta) =', $length) ->get(); $result = $query->row(); - $next_number = str_pad(($result->max_code ?? 0) + 1, 2, '0', STR_PAD_LEFT); - return $parent_code . '.' . $next_number; + + // Formatação específica por nível + if ($nivel == 1) { + // Nível 1.XX + return $parent_code . '.' . str_pad($proximo, 2, '0', STR_PAD_LEFT); + } else if ($nivel == 2) { + // Nível 1.XX.XXXX + return $parent_code . '.' . str_pad($proximo, 4, '0', STR_PAD_LEFT); + } else { + // Demais níveis sem padding + return $parent_code . '.' . $proximo; + } } Principais mudanças: Código raiz (nível 0) Uso de CAST para comparação numérica correta Filtro aprimorado para códigos sem pontos Subcódigos Identificação do nível através de explode() Formatação específica para cada nível Remoção da restrição de LENGTH fixa Formatação Nível 1: XX (dois dígitos) Nível 2: XXXX (quatro dígitos) Demais níveis: sem padding
  6. Williams Duarte

    PHP+Codeigniter - Adicionar Registro Plano de Contas

    O campo parent_code é fundamental para criar a hierarquia do plano de contas. Vou mostrar um exemplo prático de formulário HTML e como ele funciona: <form method="post" action="<?php echo base_url('plano_contas/adicionar'); ?>"> <!-- Campo para código pai (parent_code) --> <div class="form-group"> <label>Código Pai:</label> <select name="parent_code" class="form-control"> <option value="">-- Selecione o código pai --</option> <?php foreach($codigos_pais as $codigo): ?> <option value="<?php echo $codigo->codigo; ?>"> <?php echo $codigo->codigo . ' - ' . $codigo->descricao; ?> </option> <?php endforeach; ?> </select> </div> <!-- Campo descrição --> <div class="form-group"> <label>Descrição:</label> <input type="text" name="descricao" class="form-control" required> </div> <button type="submit" class="btn btn-primary">Salvar</button> </form> Para exemplificar o funcionamento: Quando parent_code é vazio (null): Sistema gera: "1" (primeiro registro) Próximo será: "2" Quando parent_code é "1": Sistema gera: "1.01" Próximo será: "1.02" Quando parent_code é "1.01": Sistema gera: "1.01.0001" Próximo será: "1.01.0002" Aqui está o código atualizado do controller para buscar os códigos pais: public function novo() { // Carrega todos os códigos possíveis de serem pai $this->load->model('PlanoContasModel'); $data['codigos_pais'] = $this->db ->select('codigo, descricao') ->from('plano_contas') ->where('LENGTH(codigo) <=', 4) // Limita a 2 níveis (1 ou 1.01) ->order_by('codigo') ->get() ->result(); $this->load->view('plano_contas/formulario', $data); } Assim: Se você deixar o "Código Pai" vazio → Cria código raiz (1, 2, 3...) Se selecionar "1" como pai → Cria subcódigo (1.01, 1.02...) Se selecionar "1.01" como pai → Cria subcódigo (1.01.0001, 1.01.0002...) Vou ler depois sua mensagem!
  7. Williams Duarte

    PHP+Codeigniter - Adicionar Registro Plano de Contas

    Para implementar um sistema de numeração hierárquica automática no CodeIgniter, sugiro a seguinte solução, adapte a sua necessidade: // Model: PlanoContasModel.php class PlanoContasModel extends CI_Model { public function __construct() { parent::__construct(); $this->load->database(); } public function getNextCode($parent_code = null) { if (!$parent_code) { // Busca próximo código raiz (1, 2, 3...) $query = $this->db->select('MAX(SUBSTRING_INDEX(codigo, ".", 1)) as max_code') ->from('plano_contas') ->where('LENGTH(codigo) = 1') ->get(); $result = $query->row(); return ($result->max_code ?? 0) + 1; } else { // Busca próximo subcódigo $like_pattern = $parent_code . '.%'; $length = strlen($parent_code) + 3; $query = $this->db->select('MAX(SUBSTRING_INDEX(codigo, ".", -1)) as max_code') ->from('plano_contas') ->like('codigo', $like_pattern, 'after') ->where('LENGTH(codigo) =', $length) ->get(); $result = $query->row(); $next_number = str_pad(($result->max_code ?? 0) + 1, 2, '0', STR_PAD_LEFT); return $parent_code . '.' . $next_number; } } public function insert($data) { if (empty($data['parent_code'])) { $data['codigo'] = $this->getNextCode(); } else { $data['codigo'] = $this->getNextCode($data['parent_code']); } return $this->db->insert('plano_contas', [ 'codigo' => $data['codigo'], 'descricao' => $data['descricao'] ]); } } // Controller: PlanoContas.php class PlanoContas extends CI_Controller { public function __construct() { parent::__construct(); $this->load->model('PlanoContasModel'); } public function adicionar() { $data = [ 'descricao' => $this->input->post('descricao'), 'parent_code' => $this->input->post('parent_code') ]; $result = $this->PlanoContasModel->insert($data); if ($result) { echo json_encode(['success' => true]); } else { echo json_encode(['success' => false, 'error' => 'Erro ao inserir']); } } } Esta solução: Gera códigos automaticamente seguindo a hierarquia Suporta múltiplos níveis (1, 1.01, 1.01.01, etc.) Mantém o formato padronizado dos códigos Usa zero à esquerda para subcódigos (01, 02, etc.) Exemplo de uso: CREATE TABLE plano_contas ( id INT PRIMARY KEY AUTO_INCREMENT, codigo VARCHAR(20) UNIQUE, descricao VARCHAR(100) ); Para adicionar um registro, faça uma requisição POST com: descricao: Nome da conta parent_code: Código pai (opcional)
  8. Williams Duarte

    Javascript - Passar Parâmetros para uma Função.

    Para passar parâmetros para uma função JS, você pode utilizar atributos de dados (data attributes) nos elementos HTML e, em seguida, acessar esses atributos no JavaScript para obter os valores desejados. Segue o exemplo de como você pode modificar seu código para passar os parâmetros usando JavaScript Modifique os botões "Visualizar" e "Editar" para incluir atributos de dados com os valores necessários: <button class="btn btn-warning" title="Visualizar" style="margin-left:50%; padding: 1px 3px;" data-id-pedido="<?php echo $r->idPedidos; ?>" data-nr-pedido="<?php echo $r->pd_numero; ?>" onclick="visualizarPedido(this)"><i class="fa fa-search icon-white"></i></button> <button class="btn btn-primary" title="Editar" style="margin-left:50%; padding: 1px 3px;" data-id-pedido="<?php echo $r->idPedidos; ?>" data-nr-pedido="<?php echo $r->pd_numero; ?>" onclick="editarPedido(this)"><i class="fa fa-edit icon-white"></i></button> Remova os formulários que envolvem os botões, pois não serão mais necessários. Adicione as seguintes funções JS no seu arquivo de JavaScript ou dentro de uma tag <script> na sua página: function visualizarPedido(button) { const idPedido = button.getAttribute('data-id-pedido'); const nrPedido = button.getAttribute('data-nr-pedido'); // Faça uma requisição Ajax para a URL desejada, passando os parâmetros const url = '<?= base_url() ?>compras/pedidos/visualizar?idPedido=' + idPedido + '&nrPedido=' + nrPedido; // Exemplo usando fetch: fetch(url) .then(response => response.text()) .then(data => { // Faça algo com a resposta recebida console.log(data); }) .catch(error => { console.error('Erro:', error); }); } function editarPedido(button) { const idPedido = button.getAttribute('data-id-pedido'); const nrPedido = button.getAttribute('data-nr-pedido'); // Faça uma requisição Ajax para a URL desejada, passando os parâmetros const url = '<?= base_url() ?>compras/pedidos/editar?idPedido=' + idPedido + '&nrPedido=' + nrPedido; // Exemplo usando fetch: fetch(url) .then(response => response.text()) .then(data => { // Faça algo com a resposta recebida console.log(data); }) .catch(error => { console.error('Erro:', error); }); } No exemplo, adicionamos atributos de dados aos botões para armazenar os valores necessários. No JavaScript, obtemos esses valores usando `getAttribute` e construímos a URL com os parâmetros. Em seguida, fazemos uma requisição Ajax para a URL usando `fetch` e lidamos com a resposta de acordo com as necessidades. Essa abordagem permite passar os parâmetros sem a necessidade de formulários adicionais.
  9. Williams Duarte

    PHP+Codeigniter - Ajuda en NFePhp para Sistema

    O erro Class NFePHP\NFe\Make not found acontece por causa de uma configuração incorreta do autoload do Composer. Par resolver isso: modifique seu composer.json para: { "description": "The CodeIgniter framework", "name": "codeigniter/framework", "type": "project", "homepage": "https://codeigniter.com", "license": "MIT", "require": { "php": ">=7.4", "mpdf/mpdf": "^7.1", "nfephp-org/sped-nfe": "^5.0", "nfephp-org/sped-da": "^1.0" }, "autoload": { "psr-4": { "App\\": "application/" } }, "config": { "vendor-dir": "vendor" } } No seu application/config/config.php, coloque: $config['composer_autoload'] = FCPATH . 'vendor/autoload.php'; Depois, no terminal, execute: composer clear-cache composer update composer dump-autoload -o Seu arquivo libraries/NfeService.php deve começar assim: <?php defined('BASEPATH') OR exit('No direct script access allowed'); use NFePHP\NFe\Make; use NFePHP\NFe\Tools; use NFePHP\Common\Certificate; use NFePHP\NFe\Common\Standardize; class NfeService { private $CI; private $config; private $tools; private $certificate; public function __construct() { $this->CI =& get_instance(); $this->CI->config->load('nfe'); $this->config = $this->CI->config->item('nfe'); $this->initialize(); } // ... resto do código Crie o arquivo application/config/nfe.php: <?php defined('BASEPATH') OR exit('No direct script access allowed'); $config['nfe'] = [ 'atualizacao' => date('Y-m-d H:i:s'), 'tpAmb' => 2, // 1-Produção; 2-Homologação 'razaosocial' => 'Sua Empresa', 'siglaUF' => 'SP', // Seu estado 'cnpj' => '00000000000000', // Seu CNPJ 'schemes' => 'PL_009_V4', 'versao' => '4.00', 'pathNFeFiles' => APPPATH . 'nfe_files/', 'pathCertsFiles' => APPPATH . 'certificates/', 'certFile' => 'certificado.pfx', 'certPassword' => 'senha_certificado' ]; Crie as pastas necessárias: application/ ├── nfe_files/ └── certificates/ PHP tem ser 7.4 ou superior Para testar se está funcionando, crie um arquivo test.php na raiz: <?php require 'vendor/autoload.php'; try { $nfe = new NFePHP\NFe\Make(); echo "NFe class loaded successfully!"; } catch (Exception $e) { echo "Error: " . $e->getMessage(); } Um controlador básico para teste: <?php defined('BASEPATH') OR exit('No direct script access allowed'); class Nfe extends CI_Controller { public function __construct() { parent::__construct(); $this->load->library('NfeService'); } public function teste() { try { $nfe = new \NFePHP\NFe\Make(); echo "NFe class loaded successfully!"; } catch (Exception $e) { echo "Error: " . $e->getMessage(); } } } Algumas verificações adicionais se ainda houver problemas: Verifique se a pasta vendor existe e contém os arquivos do NFePHP Confira as permissões das pastas (755 para diretórios, 644 para arquivos) Certifique-se de que o index.php na raiz tem a linha: require_once __DIR__ . '/vendor/autoload.php'; Se ainda persistir o erro, execute: composer show | grep nfephp E verifique se as bibliotecas estão listadas corretamente. O erro geralmente é resolvido seguindo esses passos.
  10. Williams Duarte

    PHP+Codeigniter - Ajuda en NFePhp para Sistema

    Um passo a passo detalhado para implementar a emissão de NFe usando NFePHP no CodeIgniter. // application/config/nfe.php <?php $config['nfe'] = [ 'atualizacao' => date('Y-m-d H:i:s'), 'tpAmb' => 2, // 1-Produção; 2-Homologação 'razaosocial' => 'Empresa Teste', 'siglaUF' => 'SP', 'cnpj' => '00000000000000', 'schemes' => 'PL_009_V4', 'versao' => '4.00', 'pathNFeFiles' => APPPATH . 'nfe_files/', 'pathCertsFiles' => APPPATH . 'certificates/', 'certFile' => 'certificado.pfx', 'certPassword' => 'senha_certificado' ]; // application/libraries/NfeService.php <?php defined('BASEPATH') OR exit('No direct script access allowed'); use NFePHP\NFe\Make; use NFePHP\NFe\Tools; use NFePHP\Common\Certificate; use NFePHP\NFe\Common\Standardize; class NfeService { private $CI; private $config; private $tools; private $certificate; public function __construct() { $this->CI =& get_instance(); $this->CI->config->load('nfe'); $this->config = $this->CI->config->item('nfe'); $this->initialize(); } private function initialize() { try { // Carrega o certificado $certPath = $this->config['pathCertsFiles'] . $this->config['certFile']; $this->certificate = Certificate::readPfx( file_get_contents($certPath), $this->config['certPassword'] ); // Inicializa as ferramentas $this->tools = new Tools( json_encode($this->config), $this->certificate ); // Configura o ambiente $this->tools->setEnvironment($this->config['tpAmb']); } catch (Exception $e) { log_message('error', 'NFe initialization error: ' . $e->getMessage()); throw new Exception('Erro ao inicializar NFe: ' . $e->getMessage()); } } public function emitirNFe($dados) { try { // Cria o XML $nfe = new Make(); // Informações da Nota $std = new \stdClass(); $std->versao = '4.00'; $nfe->taginfNFe($std); // Identificação da Nota $std = new \stdClass(); $std->cUF = 35; // Código do estado (SP = 35) $std->natOp = 'VENDA'; $std->mod = 55; // Modelo da NFe $std->serie = 1; $std->nNF = $dados['numero_nf']; // Número da nota $std->dhEmi = date('Y-m-d\TH:i:sP'); $std->tpNF = 1; // 1=Saída $std->idDest = 1; // 1=Operação interna $std->cMunFG = '3550308'; // Código do município (São Paulo) $std->tpImp = 1; // 1=DANFE retrato $std->tpEmis = 1; // 1=Emissão normal $std->tpAmb = $this->config['tpAmb']; $std->finNFe = 1; // 1=NFe normal $std->indFinal = 1; // 1=Consumidor final $std->indPres = 1; // 1=Operação presencial $std->procEmi = 0; // 0=Emissão de NF-e com aplicativo do contribuinte $std->verProc = '1.0'; $nfe->tagide($std); // Emitente $std = new \stdClass(); $std->xNome = $this->config['razaosocial']; $std->CNPJ = $this->config['cnpj']; $std->IE = '123456789'; $std->CRT = 3; // 3=Regime normal $std->xLgr = 'Rua Teste'; $std->nro = '123'; $std->xBairro = 'Centro'; $std->cMun = '3550308'; // São Paulo $std->xMun = 'Sao Paulo'; $std->UF = 'SP'; $std->CEP = '01001001'; $std->cPais = '1058'; $std->xPais = 'Brasil'; $nfe->tagemit($std); // Destinatário $std = new \stdClass(); $std->xNome = $dados['cliente']['nome']; $std->CNPJ = $dados['cliente']['cnpj']; $std->IE = $dados['cliente']['ie']; $std->xLgr = $dados['cliente']['endereco']; $std->nro = $dados['cliente']['numero']; $std->xBairro = $dados['cliente']['bairro']; $std->cMun = $dados['cliente']['cod_municipio']; $std->xMun = $dados['cliente']['municipio']; $std->UF = $dados['cliente']['uf']; $std->CEP = $dados['cliente']['cep']; $std->cPais = '1058'; $std->xPais = 'Brasil'; $nfe->tagdest($std); // Produtos foreach ($dados['produtos'] as $i => $produto) { $std = new \stdClass(); $std->item = $i + 1; $std->cProd = $produto['codigo']; $std->xProd = $produto['descricao']; $std->NCM = $produto['ncm']; $std->CFOP = $produto['cfop']; $std->uCom = $produto['unidade']; $std->qCom = $produto['quantidade']; $std->vUnCom = $produto['valor_unitario']; $std->vProd = $produto['valor_total']; $nfe->tagprod($std); // Impostos do produto $std = new \stdClass(); $std->item = $i + 1; $std->vTotTrib = 0.00; $nfe->tagimposto($std); } // Totais $std = new \stdClass(); $std->vBC = $dados['total']['base_calculo']; $std->vICMS = $dados['total']['valor_icms']; $std->vProd = $dados['total']['valor_produtos']; $std->vNF = $dados['total']['valor_nota']; $nfe->tagICMSTot($std); // Transporte $std = new \stdClass(); $std->modFrete = 9; // 9=Sem frete $nfe->tagtransp($std); // Gera o XML $xml = $nfe->getXML(); // Assina o XML $xml = $this->tools->signNFe($xml); // Envia para SEFAZ $response = $this->tools->sefazEnviaLote([$xml]); // Processa resposta $stdResponse = new Standardize($response); $arrayResponse = $stdResponse->toArray(); // Consulta recibo if (isset($arrayResponse['infRec']['nRec'])) { $recibo = $arrayResponse['infRec']['nRec']; $protocolo = $this->tools->sefazConsultaRecibo($recibo); // Guarda XML e protocolo $this->salvarXml($xml, $protocolo, $dados['numero_nf']); } return $arrayResponse; } catch (Exception $e) { log_message('error', 'NFe emission error: ' . $e->getMessage()); throw new Exception('Erro ao emitir NFe: ' . $e->getMessage()); } } private function salvarXml($xml, $protocolo, $numero) { $path = $this->config['pathNFeFiles']; $filename = $numero . '_' . date('Y-m-d_H-i-s') . '.xml'; if (!is_dir($path)) { mkdir($path, 0777, true); } file_put_contents($path . $filename, $xml); file_put_contents($path . 'protocolo_' . $filename, $protocolo); } } // application/controllers/Nfe.php <?php defined('BASEPATH') OR exit('No direct script access allowed'); class Nfe extends CI_Controller { public function __construct() { parent::__construct(); $this->load->library('NfeService'); } public function emitir() { try { // Dados de exemplo - você deve adaptar para seus dados reais $dados = [ 'numero_nf' => '1', 'cliente' => [ 'nome' => 'Cliente Teste', 'cnpj' => '00000000000000', 'ie' => '123456789', 'endereco' => 'Rua Teste', 'numero' => '123', 'bairro' => 'Centro', 'cod_municipio' => '3550308', 'municipio' => 'Sao Paulo', 'uf' => 'SP', 'cep' => '01001001' ], 'produtos' => [ [ 'codigo' => '001', 'descricao' => 'Produto Teste', 'ncm' => '84714900', 'cfop' => '5102', 'unidade' => 'UN', 'quantidade' => 1, 'valor_unitario' => 100.00, 'valor_total' => 100.00 ] ], 'total' => [ 'base_calculo' => 100.00, 'valor_icms' => 18.00, 'valor_produtos' => 100.00, 'valor_nota' => 100.00 ] ]; $response = $this->nfeservice->emitirNFe($dados); // Retorna resposta em JSON $this->output ->set_content_type('application/json') ->set_output(json_encode([ 'status' => 'success', 'data' => $response ])); } catch (Exception $e) { $this->output ->set_content_type('application/json') ->set_output(json_encode([ 'status' => 'error', 'message' => $e->getMessage() ])); } } } Para implementar este código, siga estes passos: 1. Primeiro, instale o NFePHP via Composer: composer require nfephp-org/sped-nfe 2. Crie os diretórios necessários: - application/nfe_files/ - para armazenar os XMLs gerados - application/certificates/ - para armazenar o certificado digital 3. Configure o arquivo application/config/nfe.php com seus dados: - Altere os dados da empresa - Configure o ambiente (homologação/produção) - Defina o caminho e senha do certificado 4. Adapte o modelo de dados no controller para sua estrutura de banco de dados. Pontos importantes: 1. Certificado Digital: - Você precisa ter um certificado digital A1 válido - Configure corretamente o caminho e senha no arquivo de configuração 2. Ambiente: - Comece usando o ambiente de homologação (tpAmb = 2) - Teste extensivamente antes de mudar para produção 3. Validações: - Adicione validações para todos os campos obrigatórios - Verifique as regras de negócio específicas do seu estado 4. Tratamento de Erros: - Implemente logs detalhados - Trate adequadamente as exceções - Armazene os retornos da SEFAZ 5. Melhorias Sugeridas: - Implementar fila de processamento para notas - Adicionar sistema de contingência - Criar rotinas de consulta de status - Implementar cancelamento de notas Você pode chamar a emissão assim: $response = $this->nfeservice->emitirNFe($dados); Este é um exemplo básico que você pode adaptar às suas necessidades. Recomendo: 1. Teste primeiro em homologação 2. Implemente gradualmente 3. Documente todas as alterações 4. Mantenha backup dos XMLs 5. Implemente validações rigorosas
  11. Williams Duarte

    PHP+Codeigniter - Importar Dados de uma Tabela para outra - MySql

    Entendi, porém, sugiro algumas melhorias que podem tornar seu sistema ainda mais robusto, so terá que codificar mais: 1 - Implementar um sistema de comparação automática entre o que foi pedido e o que foi comprado. Assim, quando Compras registrar um item com especificação diferente (como o saco P6 ao invés do P8), o sistema já alertaria automaticamente. 2 - Adicionar um sistema de rastreabilidade onde cada operação (pedido do Almoxarifado e compra do setor de Compras) fique registrada com data, hora e responsável. Isso evita o "disse-me-disse" e deixa claro quem tomou cada decisão. 3 - Criar relatórios que mostrem claramente as divergências entre pedidos e compras, facilitando a gestão e resolução de problemas. 4 - Implementar notificações automáticas quando houver divergências, assim o Almoxarifado seria avisado imediatamente se Compras registrar um produto com especificação diferente da solicitada. Dessa forma, você mantém a separação que deseja (que faz sentido para seu caso), mas adiciona camadas de controle que tornam o processo mais seguro e transparente.
  12. Williams Duarte

    PHP+Codeigniter - Importar Dados de uma Tabela para outra - MySql

    Duplicando dados, nada mais que isto! Correto é como @tetsuo disse, referencie uma chave, que pode ser pedido_id CREATE TABLE lancamentos ( id INT PRIMARY KEY, pedido_id INT, data_lancamento DATETIME, FOREIGN KEY (pedido_id) REFERENCES pedidos(id) ); na model: class LancamentoModel extends CI_Model { public function criarLancamento($pedido_id) { $dados = [ 'pedido_id' => $pedido_id, 'data_lancamento' => date('Y-m-d H:i:s') ]; $this->db->insert('lancamentos', $dados); } } Se precisar visualizar dados consolidados, crie uma view CREATE VIEW view_lancamentos AS SELECT l.id as lancamento_id, p.codigoProduto, p.quantidade, p.valorUnitario, l.data_lancamento FROM lancamentos l JOIN pedidos p ON l.pedido_id = p.id; para obter o id na view public function buscarPorProduto($codigo_produto) { $this->db->from('view_lancamentos'); $this->db->where('codigoProduto', $codigo_produto); return $this->db->get()->result_array(); } E ao inves de fazer todo este processo de importação, faça o automaticamente. class PedidoModel extends CI_Model { public function cadastrar($dados) { // Inicia a transaction $this->db->trans_start(); try { // Insere na tabela de pedidos $this->db->insert('pedidos', [ 'codigoProduto' => $dados['codigoProduto'], 'quantidade' => $dados['quantidade'], 'valorUnitario' => $dados['valorUnitario'], 'status' => 'novo' ]); $pedido_id = $this->db->insert_id(); // Insere na tabela de lançamentos $this->db->insert('lancamentos', [ 'pedido_id' => $pedido_id, 'data_lancamento' => date('Y-m-d H:i:s') ]); // Commit da transaction $this->db->trans_complete(); return $pedido_id; } catch (Exception $e) { // Rollback em caso de erro $this->db->trans_rollback(); log_message('error', 'Erro ao cadastrar pedido: ' . $e->getMessage()); return false; } } }
  13. Williams Duarte

    BelogsTo do laravel

    Certifique-se de que o relacionamento studentRelation está definido corretamente no modelo StudentDisciplineClassroom. Deve ser algo assim: public function studentRelation() { return $this->belongsTo(Student::class, 'ra', 'academic_registration'); } ao usar select('student'), você está limitando as colunas retornadas apenas para 'student'. Isso pode estar causando problemas no carregamento do relacionamento. Tente remover o select ou incluir a coluna 'ra': $all = $sdc::with('studentRelation') ->where('classroom', $request->classroom) ->distinct('student') ->get(); Uso do distinct: O distinct('student') pode estar interferindo no carregamento do relacionamento. Tente remover o distinct ou use groupBy em vez disso: $all = $sdc::with('studentRelation') ->where('classroom', $request->classroom) ->groupBy('student') ->get(); E certifique-se de que existem registros correspondentes na tabela students para os valores de 'ra' na tabela StudentDisciplineClassroom. Se você quiser carregar apenas alguns campos específicos do relacionamento, pode usar o carregamento ávido (eager loading) com restrições: $all = $sdc::with(['studentRelation:id,academic_registration,nome']) ->where('classroom', $request->classroom) ->get(); E por fim, para entender melhor o que está acontecendo, você pode usar o método toSql() para ver a consulta SQL gerada: $query = $sdc::with('studentRelation') ->where('classroom', $request->classroom); dd($query->toSql(), $query->getBindings()); Isso mostrará a consulta SQL e os parâmetros vinculados, o que pode ajudar a identificar problemas na consulta.
×

Informação importante

Ao usar o fórum, você concorda com nossos Termos e condições.