Validacion de folios y certificados usando los archivos del SAT

Contenido/contents:
Introducion
Carga de los datos
Programa de Consulta
Uso del programa
Pagina anterior/Previous page

Introduccion
En base a los archivos proporcionados por el SAT en esta pagina ftp://ftp2.sat.gob.mx/agti_servicio_ftp/verifica_comprobante_ftp/ desarrolle un simple procedimiento para cargarlos en dos tablas de mi base de datos y hacer un programa de consulta que se pueda usar localmente para validar las facturas que recibamos en papel.
En la empresa no exigimos el uso de factura electronica ni tampoco el envio en forma electronica de aquellos proveedores que hayan optado por su uso. Por lo cual recibimos "Documentos que son una impresion de un Comprobante Fiscal Digital" y su validacion es a mano.
Si recibieramos las facturas electronicas por medios automatizados la validacion la hariamos en forma diferente.
Carga de los datos en las tablas
A continuacion esta el enunciado SQL para crear las tablas necesarias en la base de datos.
create table cfdcsd (
      no_serie  CHAR(20) PRIMARY KEY, -- Numero de Serie del certificado
      fec_inicial_cert DATETIME YEAR TO SECOND, -- Tiemstamp
      fec_final_cert   DATETIME YEAR TO SECOND, -- Tiemstamp
      RFC              CHAR(13),   -- RFC de la propietario del certificado
      edo_certificado  CHAR(1)     -- Indica si esta:
                                   --    A : Activo
                                   --    R : Revocado
      );

create index cfdcsd_rfc on cfdcsd (RFC, fec_inicial_cert);

create table cfdfolios (
    RFC           CHAR(13),      -- RFC del emisor
    NoAprobacion  INTEGER,       -- Numero de aprobacion
    AnoAprobacion SMALLINT,      -- Anio de aprobacion de los folios
    Serie         CHAR(10),      -- Serie de las facturas
    FolioInicial  DECIMAL(20,0), -- Primer folio autorizado
    FolioFinal    DECIMAL(20,0)  -- Ultimo Folio Autorizado
    );

create index cfdfoli_rfc on cfdfolios (RFC, Serie, FolioInicial);
Esas tablas las cargo periodicamente con un script que ejecuto automaticamente en las noches
#!/bin/bash
TMPDIR=/tmp
CERT=CSD.txt
FOLIOS=FoliosCFD.txt
cd $TMPDIR
rm -f $CERT $FOLIOS
wget ftp://ftp2.sat.gob.mx/agti_servicio_ftp/verifica_comprobante_ftp/$CERT
wget ftp://ftp2.sat.gob.mx/agti_servicio_ftp/verifica_comprobante_ftp/$FOLIOS
chmod 666 $CERT $FOLIOS
php /home/httpd/bin/cargasat.php $TMPDIR $CERT $FOLIOS
Este script a su vez invoca a un programa PHP que carga esos archivos planos en las tablas.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
<?php
//
// +---------------------------------------------------------------------------+
// | cargsat.php : Carga las tablas de la base de datos con los datos del SAT  |
// +---------------------------------------------------------------------------+
// | Copyright (c) 2005  Fabrica de Jabon la Corona, SA de CV                  |
// +---------------------------------------------------------------------------+
// | This program is free software; you can redistribute it and/or             |
// | modify it under the terms of the GNU General Public License               |
// | as published by the Free Software Foundation; either version 2            |
// | of the License, or (at your option) any later version.                    |
// |                                                                           |
// | This program is distributed in the hope that it will be useful,           |
// | but WITHOUT ANY WARRANTY; without even the implied warranty of            |
// | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the             |
// | GNU General Public License for more details.                              |
// |                                                                           |
// | You should have received a copy of the GNU General Public License         |
// | along with this program; if not, write to the Free Software               |
// | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA|
// +---------------------------------------------------------------------------+
// | Autor: Fernando Ortiz <fortiz@lacorona.com.mx>                            |
// +---------------------------------------------------------------------------+
// | 8/feb/2007 original                                                       |
// |                                                                           |
// | 28/dic/2010 Se cambia INSERT a COPY (postgresql) para reducir de 15 min a |
// |             46 segundos de ejecucion!                                     |
// +---------------------------------------------------------------------------+
//
error_reporting(E_ALL);
if ($_SERVER['argc'] != 4) die("Uso: cargsat.php <directorio> <csd> <folios>\n");
require_once('myconn/myconn.inc.php');
$conn = myconn();   // Usando ADODB para replace
$pg=$conn->_connectionID; // Directa a postgresql para COPY rapido
$path = $_SERVER['argv'][1];
$csd = $path."/".$_SERVER['argv'][2];
$folios = $path."/".$_SERVER['argv'][3];
 
$gestor = fopen($csd, "r");
$primero=true; $cant=0;
if ($gestor) {
   while (!feof($gestor)) {
       $bufer = fgets($gestor, 4096);
       if ($cant%10000==0) echo ".";
       if ($primero) {
           # Se ignora el primer registro porque son los encabezados
           # pero ademas sirve para borrar el contenido actual de las tablas
           $conn->Execute("truncate cfdcsd");
           $primero=false;
           pg_query($pg, "copy cfdcsd from stdin with delimiter '|' null as ''");
       } else {
           $bufer=trim($bufer);
           $l=strlen($bufer);
           if ($l>=5) {
               $cant++;
               if (substr($bufer,-1)=="|")
                 $bufer=substr($bufer,0,$l-1);
               pg_put_line($pg, $bufer."\n");
           } // size 5
       } // Primero
   } // While
   pg_put_line($pg, "\\.\n");
   pg_end_copy($pg);
   fclose ($gestor);
} // gestor
echo "\nCantidad de CSD=$cant\n";
$gestor = fopen($folios, "r");
$primero=true; $cant=0;
if ($gestor) {
   while (!feof($gestor)) {
       $bufer = fgets($gestor, 4096);
       if ($cant%10000==0) echo ".";
       if ($primero) {
           # Se ignora el primer registro porque son los encabezados
           # pero ademas sirve para borrar el contenido actual de las tablas
           $conn->Execute("truncate cfdfolios");
           $primero=false;
           pg_query($pg, "copy cfdfolios from stdin with delimiter '|' null as ''");
       } else {
           $bufer=trim($bufer);
           if (strlen($bufer)>=6) {
               $cant++;
               pg_put_line($pg, $bufer."\n");
           } // size 6
       } // primero
   } // while
   pg_put_line($pg, "\\.\n");
   pg_end_copy($pg);
   fclose ($gestor);
}
echo "\nCantidad de Folios=$cant\n";
$refe = "'".date(DATE_RSS)."'";
$conn->replace("document", array('docurefe'=>$refe, 'docucvsi'=>6,'docutire'=>"'CARGASAT'",'docunuli'=>11),
                           array('docucvsi','docutire','docunuli')
            );
exit(0);
?>
Codigo Fuente del programa para consultar las tablas
A continuacion un programa para consultar esas tablas, tiene un formulario interactivo para que un 'humano' teclee los datos y valide en forma manual. Si fuera mucho volumen habria que hacer un programa que valide en forma automatica los CFDs recibidos.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
<?php
//
// +---------------------------------------------------------------------------+
// | satcrfc.php : Consulta en las series y certificados autorizados por RFC  |
// +---------------------------------------------------------------------------+
// | Copyright (c) 2005  Fabrica de Jabon la Corona, SA de CV                  |
// +---------------------------------------------------------------------------+
// | This program is free software; you can redistribute it and/or             |
// | modify it under the terms of the GNU General Public License               |
// | as published by the Free Software Foundation; either version 2            |
// | of the License, or (at your option) any later version.                    |
// |                                                                           |
// | This program is distributed in the hope that it will be useful,           |
// | but WITHOUT ANY WARRANTY; without even the implied warranty of            |
// | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the             |
// | GNU General Public License for more details.                              |
// |                                                                           |
// | You should have received a copy of the GNU General Public License         |
// | along with this program; if not, write to the Free Software               |
// | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA|
// +---------------------------------------------------------------------------+
// | Autor: Fernando Ortiz <fortiz@lacorona.com.mx>                            |
// +---------------------------------------------------------------------------+
// | 8/Feb/2007  Original                                                      |
// +---------------------------------------------------------------------------+
//
?>
<html>
<head>
<title>Consulta Certificados y folios por RFC</title>
<link rel="STYLESHEET" href="/fortiz/fortiz.css" media="screen" type="text/css">
<script src="http://www.google-analytics.com/urchin.js" type="text/javascript">
</script>
<script type="text/javascript">
_uacct = "UA-425709-1";
urchinTracker();
</script>
</head>
<body>
<center>
<h1 align=center>Consulta de Certificados de Sellos y Rango de Folios autorizados por RFC</h1>
<form method=post name=forma>
RFC a consultar 
<input type=text name=rfc size=13 maxlength=13>
<input type=submit value='consultar'>
</form>
<?php
$rfc = trim(strtoupper($_POST['rfc']));
if (strlen($rfc)>10) {
    echo "<hr>";
    require_once('myconn/myconn.inc.php');
    $conn = myconn();
    $rscsd = $conn->Execute("select * from cfdcsd where RFC = '$rfc'");
    echo "<h2>Datos del RFC $rfc</h2>";
    echo "<h4>Certificados</h4>";
    echo "<table border=1>";
    echo "<tr><th>Certificado<th>Fecha de Inicio<th>Fecha de Fin<th>Status";
    while ($row=$rscsd->fetchrow()) {
        $cl = ($i++%2==0) ? "en" : "es";
        echo "<tr>";
        echo "<td align=center class='$cl'>".$row['no_serie'];
        echo "<td align=center class='$cl'>".$row['fec_inicial_cert'];
        echo "<td align=center class='$cl'>".$row['fec_final_cert'];
        echo "<td align=center class='$cl'>".$row['edo_certificado'];
    }
    echo "</table>";
    $rsfoli = $conn->Execute("select * from cfdfolios where RFC = '$rfc'");
    echo "<h4>Series y Folios</h4>";
    echo "<table border=1>";
    echo "<tr><th>Numero<th>A&ntilde;o<th rowspan=2>Serie<th colspan=2>Folio";
    echo "<tr><th colspan=2>Aprobacion<th>Inicial<th>Final";
    while ($row=$rsfoli->fetchrow()) {
        $cl = ($i++%2==0) ? "en" : "es";
        echo "<tr>";
        echo "<td align=center class='$cl'>".$row['noaprobacion'];
        echo "<td align=center class='$cl'>".$row['anoaprobacion'];
        echo "<td align=center class='$cl'>".$row['serie'];
        echo "<td align=right class='$cl'>".number_format($row['folioinicial'],0);
        echo "<td align=right class='$cl'>".number_format($row['foliofinal'],0);
    }
    echo "</table>";
    $refe = $conn->getone("select docurefe from document where docucvsi = 6 and docutire = 'CARGASAT' and docunuli= 11");
    echo "<hr>";
    echo "<small>Con datos del sitio FTP del SAT actualizados al $refe. Informacion proporcionada sin responsabilidad de nuestra parte</small>";
}
?>
</center>
</body>
</html>
Ejemplo de uso del programa
En esta pagina esta el mismo programa mostrado anteriormente en ejecucion.
NO hay mucha diferencia en la ejeucion de mi codigo al mostrado en la pagina oficial del SAT http://www.sat.gob.mx/sitio_internet/sitio_aplicaciones/verifica_comprobante/ pero note que mi programa esta atrazado minimo 24 horas porque el SAT tiene sus archivos en linea y yo los descargo solo periodicamente.
La idea de mi programa es que sirva de ejemplo de utilizacion de las tablas, no que las empresas validen usando mi programa.