jueves, 28 de octubre de 2010

Generar clases java a partir de un XSL para el comprobante fiscal digital

Tuve que entrarle al tema de la factura electrónica y su implementación en java y me encontré con que tenía que generar las clases del famoso Comprobante Fiscal Digital y que éste a su vez compliera con lo establecido por el esquema definido por el SAT.

Para generar las clases de java a partir del XSD solo utiliza el comando XJC que viene con el JDK 1.6.

Ejecuta lo siguiente desde una línea de comandos:

1) Copia el archivo XSD cfdv2.xsd del SAT a una carpeta y dentro de ella crea un folder que se llame src

2) Ejecuta la siguiente instrucción desde una línea de comandos:

xjc cfdv2.xsd -p mx.gob.sat.cfds.beans -d src\

Y listo con eso habrás generado las clases necesarias en java que representan al comprobante fiscal digital.

Para generar el sello y la cadena original puedes leer éste artículo.


PD. en Windows el xjc.exe está en la carpeta BIN del JDK

Espero les sea de utilidad.

lunes, 25 de octubre de 2010

MySQL: quitar el beep cuando hay errores

Seguramente te ha tocado ejecutar un script en MySql paraWindows, si por algun motivo contiene errores o te equivocaste de base de datos, vas a escuchar un beep mutiplicado por cada línea con error.

Bueno, a mi me pasó en la oficina y casi se infartan, pensaron que era la alerta sísmica :D y por mas que le baje el volumen a las bocinas no funcionó.

Simplemente agrega el parámetro -b al comando mysql:

c:\mysql\bin>mysql -b -u tuusuario -p tupassword [Enter]

Otra opción para desactivar el beep es abrir el archivo my.ini que esta en la carpeta BIN de tu instalación de MySql y agrega la siguiente línea en la seccion [client]:

no-beep=TRUE

es decir tu archivo de configuración en la sección debe quedar mas o menos así:

# The following options will be passed to all MySQL clients
[client]
#user = your_username
#password = your_password
host = .
port = 3306
socket = "MySQL"
no-beep=TRUE

Reinicias el servicio de Mysql y listo.

jueves, 5 de agosto de 2010

Mostrar Configuración ODBC de 32 bits en Windows 7 64 bits

Bueno, me tocó modificar una aplicación que accedía a un ODBC de 32 bits y en el panel de control de Windows 7 a 64 bits mostraba solo los drivers para 64 bits, y no aparecía nada de access o excel.

Después de estar buscando información al respecto pude dar con la solución, necesitas abrir una ventana de MSS-DOS y teclar lo siguiente:

C:\>CD \WINDOWS\SYSWOW64 [Enter]
C:\WINDOWS\SYSWOW64>ODBCAD32 [Enter]


Y la configuración de ODBC aparece.

Suerte!

lunes, 2 de agosto de 2010

Agregando estilos al script para generar la documentación MySQL Workbench

En el post anterior http://tmsanchezdev.blogspot.com/2010/07/reporte-en-html-de-un-modelo-en.html generamos una tabla html con la estructura del modelo en MySQL Workbech.

Hice algunas modificaciones al script para agregarle una hoja de estilos, la versión del script la puedes descargar desde aqui.

Este es un ejemplo de la documentación generada:


Saludos.

viernes, 30 de julio de 2010

Reporte en HTML de un modelo en MySQLWorkBench 5.2.25

Por lo regular utilizo MySQL Workbench Standard Edition para diseñar el esquema de mis bases de datos, sin lugar a dudas esta es una gran herramienta, pero ésta versión no tiene un reporteador para generar en HTML la documentación de los campos utilizados.

Me dí a la tarea de buscar como utilizar el lenguaje de scripts y genere el siguiente código que genera una tabla en HTML con los nombres de las tablas y las columnas, con sus descripciones.

Solo tienes que cambiar la siguiente línea para obtener la documentación de tu modelo

local schemata= grtV.getGlobal("/wb/doc/physicalModels/0/catalog/schemata/1/tables")

Para saber la ruta correcta puedes dar clic en Scripting, Scripting Shel y la ventana Working Scripting Shell pulsar en la pestaña Global para seleccionar tu propio esquema.

Para generar el documento en HTML guarda el código de abajo en un documento de texto con la extensión .lua y lo ejecutas desde Scripting, Run Workbench Script File

Espero les sea de utilidad.

Nota importante: Hay una versión más reciente de éste script aquí


local schemata= grtV.getGlobal("/wb/doc/physicalModels/0/catalog/schemata/1/tables")
local i
local j
print("<table border='1' style='width:100%'> \n" )
for i=1, grtV.getn(schemata) do
print("<tr> \n" )
print("<td colspan='5'> TABLE NAME: " .. schemata[i].name .. "</td> \n")
print("</tr> \n" )

print("<tr> \n" )
print("<td>COLUMN_NAME</td> \n")
print("<td>TYPE</td> \n")
print("<td>NULLABLE?</td> \n")
print("<td>AUTO_INCREMENT?</td> \n")
print("<td>COMMENTS</td> \n") print("</tr> \n" )

for j=1, grtV.getn(schemata[i].columns) do
print("<tr> \n" )
print ( " <td>" .. schemata[i].columns[j].name .. "</td> \n" )
print ( " <td>" .. schemata[i].columns[j].formattedType .. "</td> \n" )

if (schemata[i].columns[j].isNotNull == 1) then
print (" <td>" .. "NOT NULL" .. "</td> \n" )
else
print ( " <td>" .. "NULL" .. "</td> \n" )
end

if (schemata[i].columns[j].autoIncrement == 1) then
print (" <td>" .. "AUTO_INCREMENT" .. "</td> \n" )
else
print (" <td>" .. " - " .. "</td> \n" )
end

print ( " <td>" .. schemata[i].columns[j].comment .. "</td> \n" )
print("</tr> \n" ) end

end
print("</table> \n" )
print ( "\n" )

sábado, 19 de septiembre de 2009

TFiltroDinamico: Una forma fácil y sencilla de generar filtros

Cuando desarrollamos aplicaciones es común que se realicen búsquedas por criterios definidos previamente --por lo regular uno, dos o tres campos--, pero...., que pasa si nos piden buscar por una combinación de todos los campos contenidos en nuestra tabla.

Ya me imagino la secuencia de pasos:

Para cada campo de la tabla:
  1. Pulsar Shift + Clic en el componente TLabel
  2. Cambiar los nombre del TLabel con el nombre del campo
  3. Pulsar Shift + Clic en el componente TComboBox para indicar los criterios (menor que, mayor que, etc..)
  4. Poner los cuadros de entrada para cada campo
y así sucesivamente.

Para facilitar ésta tarea, decidí crear la clase TFiltroDinamico, que nos apoya generando los nombres de los campos, combo boxes, cuadros de entrada y algo muy importante, si el campo es de tipo fecha genera un TDateTimePicker.

Un ejemplo de su uso es el siguiente:


// para crear una instancia
filtroDinamico := TFiltroDinamico.Create;
// dataset del cual toma los campos
filtroDinamico.dataSetLocal := Query1;
// scrollbox donde generan los filtros
filtroDinamico.scrlFiltro := scrlCampos;

// libera los filtros que se han creado
filtroDinamico.liberaComponentes;
// genera en el scrollbox los filtros
filtroDinamico.inicializaFiltro;

// obtiene la condición con el valor de cada campo.
filtroDinamico.obtenFiltro;
Este es un ejemplo de una aplicación que utiliza la clase TFiltroDinamico, el recuadro en rojo contiene los campos generados a partir de la información generada por la consulta, el recuadro en verde indica el valor que se asignó a cada campo.

Te invito a que descargues y pruebes TFiltroDinamico. Puedes probar la aplicación de ejemplo y compartirme que te parece.

jueves, 27 de agosto de 2009

Datos tipo Record en Delphi con soporte de métodos

Estaba desarrollando una aplicación que utilizaba coordenadas geográficas y tenía que hacer la conversión de Grados-Minutos-Segundos a su formato en Decimal, así que codifiqué de la siguiente forma:

type
TCoordenadaGeografica = Record
grados : Double;
minutos : Double;
segundos : Double;
End;

TConversion = class
class function gradosADecimales(grados: Double; minutos: Double; Segundos: Double):double; overload;
class function gradosADecimales(coordenadaGeografica: TCoordenadaGeografica):double;overload;
class function decimalesAGrados(valorDecimal :Double):TCoordenadaGeografica;
end;

implementation

{ TConversion }

class function TConversion.decimalesAGrados(
valorDecimal: Double): TCoordenadaGeografica;
var
resultado : TCoordenadaGeografica;
begin
resultado.grados := Int(valorDecimal);
resultado.minutos := Int((valorDecimal - resultado.grados)) * CONV_MINUTOS;
resultado.segundos := (resultado.minutos - Int(resultado.minutos)) * CONV_MINUTOS;
result := Resultado;
end;

class function TConversion.gradosADecimales(grados, minutos,
Segundos: Double): double;
var
resultado : Double;
begin
resultado := grados + (minutos / CONV_MINUTOS ) + (segundos / CONV_SEGUNDOS);
result := resultado;
end;

class function TConversion.gradosADecimales(
coordenadaGeografica: TCoordenadaGeografica): double;
var
resultado : Double;
begin
resultado := coordenadaGeografica.grados +
(coordenadaGeografica.minutos / CONV_MINUTOS ) +
(coordenadaGeografica.segundos / CONV_SEGUNDOS);
result := resultado;
end;


Todo funcionaba correctamente..., pero noté que estaba utilizando muchas líneas de código para hacer las conversiones:


procedure TfrmCaptura.asignaCoordenada(Sender: TObject);
var
latitud : TCoordenadaGeografica;
longitud : TCoordenadaGeografica;
begin
inherited;
latitud.grados := TConversion.toFloat(peaLatG.Text);
latitud.minutos := TConversion.toFloat(peaLatM.Text);
latitud.segundos := TConversion.toFloat(peaLatS.Text);

longitud.grados := TConversion.toFloat(peaLonG.Text);
longitud.minutos := TConversion.toFloat(peaLonM.Text);
longitud.segundos := TConversion.toFloat(peaLonS.Text);

// aquí se guarda en la base
end;
Tomando en cuenta que tenía que hacer ésto en varios lugares encontré la solución haciendo uso de las nuevas características de los Records en Delphi:


type
TCoordenadaGeografica = Record
grados : Double;
minutos : Double;
segundos : Double;
constructor Create(AGrados: Double; AMinutos:Double; ASegundos:Double); overload;
constructor Create(AGrados: String; AMinutos: String; ASegundos:String); overload;
End;

/// más líneas por acá

{ TCoordenadaGeografica }

constructor TCoordenadaGeografica.Create(AGrados, AMinutos, ASegundos: Double);
begin
Self.grados := AGrados;
Self.minutos := AMinutos;
Self.segundos := ASegundos;
end;

constructor TCoordenadaGeografica.Create(AGrados, AMinutos, ASegundos: String);
begin
Self.grados := TConversion.toFloat(AGrados);
Self.minutos := TConversion.toFloat(AMinutos);
Self.segundos := TConversion.toFloat(ASegundos);
end;


Por lo que me ahorré muchas lineas de código al hacer uso de los constructores en los Records:


procedure TfrmCaptura.asignaCoordenada(Sender: TObject);
var
latitud : TCoordenadaGeografica;
longitud : TCoordenadaGeografica;
begin
inherited;
latitud := TCoordenadaGeografica.Create(peaLatG.Text,peaLatM.Text,peaLatS.Text);
longitud := TCoordenadaGeografica.Create(peaLonG.Text,peaLonM.Text,peaLonS.Text);
// aquí se guarda en la base
end;

Otra opción hubiera sido el crear una clase de que se encargara de la conversión (que finalmente así quedó implementado) pero para mí fué una oportunidad de probar esta interesante característica de los Records en Delphi.