jueves, 6 de junio de 2019

Ejecutar script SQL con Postgres & Docker

Muchas veces, al ejecutar un contenedor Docker para Postgres, deseamos inicializar el servidor o la base de datos creada con ciertos scripts previamente creados. En este artículo les mostraré cómo ejecutar un contenedor para Postgres agregando las dos siguientes actividades:

1. Ejecutar un script para inicializar las tablas maestras
Esto normalmente se realiza con la instrucción:
psql -U postgres veronica < veronica.sql
Pero en el caso de Docker, podemos agregar el script al directorio /docker-entrypoint-initdb.d/ y Docker ejecutará automáticamente dicho script luego de iniciar el servicio de base de datos.

2. Agregar una llave para encriptación que será utilizada por la extensión pgcrypto.
Si no utilizamos docker, bastaría con realizar lo siguiente
postgres -c encrypt.key=a_secret_key
Ahora, ambas configuraciones se pueden hacer también directamente con Docker tal como se muestra en el siguiente Dockerfile:
FROM postgres:11.3

# About the author
MAINTAINER Rolando Rodríguez <rolando.roc@gmail.com>

# Environment variables

# Set Postgres environment variables
ENV POSTGRES_PASSWORD=123456 
    POSTGRES_USER=postgres 
    POSTGRES_DB=sample

# Get a sql script and move it to initialization scripts folder
ADD https://raw.githubusercontent.com/rolandopalermo/veronica/master/veronica-app/src/main/postgres/veronica.sql /docker-entrypoint-initdb.d/

# Make sql script executable
RUN chmod 744 /docker-entrypoint-initdb.d/veronica.sql

# Expose the PostgreSQL port
EXPOSE 5432

# Run a command, for example: $ postgres -c encrypt.key=8qxBjzCdQkwdpu
CMD ["postgres", "-c", "encrypt.key=8qxBjzCdQkwdpu"]
Para más información, puedes revisar el siguiente repositorio: https://github.com/rolandopalermo/dockerfiles/tree/master/postgres-run-sql-command

miércoles, 22 de mayo de 2019

Clean out your Docker images, containers and volumes

Cleaning up containers
Kill all running containers
> docker kill $(docker ps -q)
Delete all stopped containers
> docker rm $(docker ps -a -q)
Cleaning up images
Delete an image
> docker rmi <image name>
Delete all 'untagged/dangling' images
> docker rmi $(docker images -q -f dangling=true)
Delete all images
> docker rmi $(docker images -q)
Cleaning up volumnes
Once a volume is not longer associated with a container, it is considered 'dangling'
> docker volume rm $(docker volume ls -f dangling=true -q)

jueves, 16 de mayo de 2019

How to remove namespaces from XML using XSLT

Sometimes, XML documents you receive could contain unneeded or incorrect namespace information. You can use XSLT stylesheets to remove the namespace information in these documents.

XML Input
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ns1:reportRequest xmlns:ns1="http://v1.dto.reports.rolandopalermo.com">
    <ns1:metadata>
        <ns1:account>12345678</ns1:account>
        <ns1:reportName>Current_Payment</ns1:reportName>
    </ns1:metadata>
    <ns1:report>
        <ns1:reportId>1</ns1:reportId>
        <ns1:header>
            <ns1:id>999999999</ns1:id>
            <ns1:name>John Smith</ns1:name>
        </ns1:header>
        <ns1:invoices>
            <ns1:invoice>
                <ns1:invoiceNumber>0000000000</ns1:invoiceNumber>
                <ns1:dueDate>07/01/18</ns1:dueDate>
                <ns1:invoiceAmount>14.00</ns1:invoiceAmount>
                <ns1:amountPaid>14.00</ns1:amountPaid>
            </ns1:invoice>
        </ns1:invoices>
        <ns1:paymentMethodInformation>
            <ns1:cardType>VISA</ns1:cardType>
            <ns1:cardName>************1111</ns1:cardName>
            <ns1:cardHolder>Juan Pérez</ns1:cardHolder>
            <ns1:authorizationCode>dd92c0f864ea9ff1016501e880d03451</ns1:authorizationCode>
        </ns1:paymentMethodInformation>
    </ns1:report>
</ns1:reportRequest>
XSLT
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"
    xmlns:ns1="http://v1.dto.reports.rolandopalermo.com">
    <xsl:template match="/">
        <xsl:apply-templates
            select="ns1:reportRequest/ns1:report" />
    </xsl:template>
    <xsl:template match="*">
        <xsl:element name="{local-name(.)}">
            <xsl:apply-templates />
        </xsl:element>
    </xsl:template>
    <xsl:template match="@*">
        <xsl:copy />
    </xsl:template>
</xsl:stylesheet>
XML Output
<?xml version="1.0" encoding="UTF-8"?>
<report>
    <reportId>1</reportId>
    <header>
        <id>999999999</id>
        <name>John Smith</name>
    </header>
    <invoices>
        <invoice>
            <invoiceNumber>0000000000</invoiceNumber>
            <dueDate>07/01/18</dueDate>
            <invoiceAmount>14.00</invoiceAmount>
            <amountPaid>14.00</amountPaid>
        </invoice>
    </invoices>
    <paymentMethodInformation>
        <cardType>VISA</cardType>
        <cardName>************1111</cardName>
        <cardHolder>Juan Pérez</cardHolder>
        <authorizationCode>dd92c0f864ea9ff1016501e880d03451</authorizationCode>
    </paymentMethodInformation>
</report>

miércoles, 15 de mayo de 2019

Cliente para consumo de servicios Web de la DIAN con Java

Hace ya algunos meses se viene implementando el tema de facturación electrónica en Colombia, lo cual ha conllevado a muchos desarrolladores de software a una intensa búsqueda de recursos en internet que le permitan completar el objetivo de llevar a sus respectivas empresas a la emisión exitosa de comprobantes electrónicos. 

Sin embargo, el camino definitivamente no será fácil debido a la pobre documentación que la DIAN pone a disposición. Este escenario es muy común en países de Latinoamérica; ya hace algunos años tuve la oportunidad de conocer el modelo de facturación electrónica de Ecuador, el cual adolecía de los mismos problemas de documentación. 

Ahora bien, para alegría de nuestros lectores, el día de hoy pongo a su alcance las URL's de los WSDL's de los servicios web de la DIAN, tanto para ambientes de Habilitación como de Operación. En la siguiente tabla muestro un listado de las tres operaciones que la DIAN pone a nuestra disposición: 

Consulta de documentos
Habilitación: https://facturaelectronica.dian.gov.co/habilitacion/B2BIntegrationEngine/FacturaElectronica/consultaDocumentos.wsdl
Operación: https://facturaelectronica.dian.gov.co/operacion/B2BIntegrationEngine/FacturaElectronica/consultaDocumentos.wsdl

Consulta de resoluciones
Habilitación: https://facturaelectronica.dian.gov.co/servicios/B2BIntegrationEngine-servicios/FacturaElectronica/consultaResolucionesFacturacion.wsdl
Operación: https://facturaelectronica.dian.gov.co/servicios/B2BIntegrationEngine-servicios/FacturaElectronica/consultaResolucionesFacturacion.wsdl

Emisión de comprobantes electrónicos
Habilitación: https://facturaelectronica.dian.gov.co/habilitacion/B2BIntegrationEngine/FacturaElectronica/facturaElectronica.wsdl
Operación: https://facturaelectronica.dian.gov.co/operacion/B2BIntegrationEngine/FacturaElectronica/facturaElectronica.wsdl

Y si ya logramos generar los clientes para el consumo de estos servicios Web, ahora es necesario configurar las cabeceras de seguridad que permitirán un apropiado consumo de estos servicios ¡Bastante trabajo el que nos esperar!

Pero no todas son malas noticias - ¿Se imaginan que al ejecutar el siguiente código Java podamos, por ejemplo, consultar un documento electrónico enviado a la DIAN?
public static void main(String[] args) throws MalformedURLException, DatatypeConfigurationException, ParseException {
    String identificadorSoftware = "";
    String password = "";
    SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    EnvioConsultaDocumento request = new EnvioConsultaDocumento();
    //Métodos set omitidos
    DianSoapWSClient client = new DianSoapWSClient(identificadorSoftware, password, TipoAmbiente.HABILITACION);
    DocumentosRecibidos response = client.consultaResultadoValidacionDocumentos(request);
}
Luego de muchas vueltas a la documentación de la DIAN y apoyado en experiencias pasadas, se ha logrado la implementación de un cliente Jar que nos brinda una capa de abstración a toda la complejidad que exige la DIAN como parte del consumo de sus servicios.

Detalles del producto
Versión Java JDK 8
Modo de integración Artefacto Maven
Costo USD 75
Servicios soportados Emisión de facturas, consulta de documentos, consulta de resoluciones
Licenciamiento No posee restricciones de licencia

Datos de contacto
Skype rolandopalermo
Email rolando.roc@gmail.com
Facebook fb.com/rolandopalermo

jueves, 25 de octubre de 2018

How to reset a remote git repository to remove all commits

Delete the .git directory locally and recreate the git repostory:
$ cd (project-directory)
$ git init
$ (add some files)
$ git add .
$ git commit -m 'Initial commit'
Push to remote server, overwriting. Remember you're going to mess everyone else up doing this so you better be the only client.
$ git remote add origin <url>
$ git push --force --set-upstream origin master

domingo, 21 de octubre de 2018

How to Create an Executable JAR with Maven

In this quick article we will focus on packaging a Maven project into an executable Jar file. In order to create an executable jar, we don’t need any additional dependencies. We just need to create Maven Java project, and have at least one class with the main(…) method.

We also need to make sure that our pom.xml contains the the following elements:
<build>
    <finalName>anjus-invoicing-${project.version}</finalName>
    <plugins>
        <plugin>
            <artifactId>maven-assembly-plugin</artifactId>
            <configuration>
                <descriptorRefs>
                    <descriptorRef>jar-with-dependencies</descriptorRef>
                </descriptorRefs>
                <appendAssemblyId>false</appendAssemblyId>
                <archive>
                    <manifest>
                        <mainClass>com.rolandopalermo.example.Main</mainClass>
                    </manifest>
                </archive>
            </configuration>
            <executions>
                <execution>
                    <id>make-assembly</id>
                    <phase>package</phase>
                    <goals>
                        <goal>assembly</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>

viernes, 19 de octubre de 2018

Generación de número CUFE con Java

Siguiendo con la serie de artículos relacionados con Facturación Electrónica en Colombia, les comparto una función para la generación del número CUFE en Java.
public static String calculateInvoiceCUFE(String numFac, String fechaFac, String horaFac, String valFac,
   String valImp1, String valImp2, String valImp3, String valPag, String nitOFE, String tipAdq, String numAdq,
   String clTec) {
  StringBuilder sb = new StringBuilder("");
  sb.append(numFac);
  sb.append(fechaFac);
  sb.append(horaFac);
  sb.append(valFac);
  sb.append(CodImp1);
  if (StringUtils.isEmpty(valImp1)) {
   sb.append("0.00");
  } else {
   sb.append(valImp1);
  }
  sb.append(CodImp2);
  if (StringUtils.isEmpty(valImp2)) {
   sb.append("0.00");
  } else {
   sb.append(valImp2);
  }
  sb.append(CodImp3);
  if (StringUtils.isEmpty(valImp3)) {
   sb.append("0.00");
  } else {
   sb.append(valImp3);
  }
  sb.append(valPag);
  sb.append(nitOFE);
  sb.append(tipAdq);
  sb.append(numAdq);
  sb.append(clTec);
  return DigestUtils.sha1Hex(sb.toString());
 }
Es importante agregar la siguiente dependencia:
<dependency>
 <groupId>commons-codec</groupId>
 <artifactId>commons-codec</artifactId>
 <version>1.11</version>
</dependency>

jueves, 18 de octubre de 2018

Facturacion Electrónica en Colombia

Conocido es por todos que hoy en día la facturación electrónica es ya una realidad en Colombia. Se han realizado planes piloto con diversas empresas y en la actualidad ya es una necesidad acogerse a esta nueva tecnología tributaria.

En este artículo, no voy a entrar en detalle en las resoluciones y leyes emitidas por la entidad regulatoria en Colombia, la DIAN, sin embargo como desarrollador de software, me causa un profundo malestar la pésima documentación que acompaña a las normas que obligan a las empresas a facturar de manera electrónica.

Y es que no es una novedad que instituciones como el SRI en Ecuador, la SUNAT en Perú o la DIAN en Colombia pongan trabas con pésimos canales de resolución de dudas y mala documentación. A veces me planteo la interrogante de cómo es que planifican la puesta en marcha a nivel nacional de proyectos tan grandes con tan poca o nula documentación.

En mi experiencia, leyendo un manual técnico y entendido el esquema funcional del proceso, se debería estar apto para emitir comprobantes electrónicos en no más de un mes, sin embargo nos tapamos con trabas como el tener que leer un PDF que nos habla de un servicio web de tipo SOAP para consultas de facturas y terminar dándonos cuenta que en ningún momento nos indicaron cuál es el WSDL (Si bien son términos muy técnicos, los lectores que desarrollarán software entenderán la gravedad de esto).

Podría disponer de líneas y líneas en mi blog para hablar de la mala experiencia que puede resultar implementar facturación electrónica en Sudamérica pues la diversidad de manuales no versionados sumado a la poca organización de la información y la indisponibilidad de enlaces y URL's puede, a veces, hacernos arrepentir de haber emprendido este tipo de desarrollos. Sin embargo, el propósito de este artículo es clarificar de cierta manera cuales son los pasos que se debe seguir en Colombia.

El siguiente esquema funcional representa el flujo que se sigue para autorizar o en otras palabras, dar validez tributaria, a una factura electrónica en Colombia.


Básicamente, son dos los servicios Web que nos permiten autorizar una factura, el de recepción y el de consulta. Cada uno tiene un WSDL diferente además de estar configurados para ser consumidos mediante WS-Security. Los enlaces los muestro a continuación:

https://facturaelectronica.dian.gov.co/habilitacion/B2BIntegrationEngine/FacturaElectronica/facturaElectronica.wsdl

https://facturaelectronica.dian.gov.co/habilitacion/B2BIntegrationEngine/FacturaElectronica/consultaDocumentos.wsdl

Los pasos a seguir son:

1. Generar XML
Para generar los archivos XML, a diferencia de Ecuador, debemos utilizar el esquema UBL 2.0. En este punto recomiendo no utilizar librerías externas y mejor generar los documentos XML de manera manual utilizando las clases del paquete org.w3c.dom.

2. Firmar XML
Para firmar el documento XML, una excelente librería para Java es xades4j. La dependencia de maven es la siguiente:
<dependency>
 <groupId>com.googlecode.xades4j</groupId>
 <artifactId>xades4j</artifactId>
 <version>1.5.0</version>
</dependency>
En la sección de issues en su página de github podemos encontrar diversas consultas en donde también sugieren cómo implementar la firma electrónica según los estándares de Colombia.

3. Enviar XML firmado a la DIAN
Este paso es nuestra primera interacción con la DIAN. En este servicio se valida la estructura del documento XML así como la firma digital. Es importante recordar que el SOAP request deberá tener como cabecera el objeto de WS-Security. A continuación muestro un ejemplo de request:
<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
    <SOAP-ENV:Header>
        <wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" S:mustUnderstand="1">
            <wsse:UsernameToken>
                <wsse:Username><!--- Proporcionado por la DIAN --></wsse:Username>
                <wsse:Password><!--- Proporcionado por la DIAN --></wsse:Password>
                <wsu:Created xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
                <!--- Hora en que se realiza la petición en formato yyyy-MM-ddTHH:mm:ssZ -->
                </wsu:Created>
                <wsse:Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis200401-wss-soap-message-security1.0#Base64Binary">
                <!--- Secuencia aleatoria codificada en base 64 -->
                </wsse:Nonce>
            </wsse:UsernameToken>
        </wsse:Security>
    </SOAP-ENV:Header>
    <S:Body>
        <EnvioFacturaElectronicaPeticion xmlns="http://www.dian.gov.co/servicios/facturaelectronica/ReportarFactura">
            <NIT><!--- NIT del facturador --></NIT>
            <InvoiceNumber><!--- Número secuencial de la factura --></InvoiceNumber>
            <IssueDate><!--- Fecha de emisión de la factura en formato yyyy-MM-ddTHH:mm:ss --></IssueDate>
            <Document><!--- Archivo ZIP codificado en base 64 --></Document>
        </EnvioFacturaElectronicaPeticion>
    </S:Body>
</S:Envelope>

4. Validar Factura
En caso el código de respuesta del servicio anterior sea 200, el siguiente paso es comprobar que la factura tenga validez tributaria. Al igual que la llamada anterior, la estructura de la petición para este servicio también considera la cabecera de tipo wS-Security.
Este paso es nuestra primera interacción con la DIAN. Es importante recordar que el SOAP request deberá tener como cabecera el objeto de WS-Security. A continuación muestro un ejemplo de request:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:con="http://www.dian.gov.co/servicios/facturaelectronica/ConsultaDocumentos">
   <soapenv:Header>
        <wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
            <wsse:UsernameToken>
                <wsse:Username><!--- Proporcionado por la DIAN --></wsse:Username>
                <wsse:Password><!--- Proporcionado por la DIAN --></wsse:Password>
                <wsu:Created xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
                <!--- Hora en que se realiza la petición en formato yyyy-MM-ddTHH:mm:ssZ -->
                </wsu:Created>
                <wsse:Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis200401-wss-soap-message-security1.0#Base64Binary">
                <!--- Secuencia aleatoria codificada en base 64 -->
                </wsse:Nonce>
            </wsse:UsernameToken>
        </wsse:Security>
    </soapenv:Header>
    <soapenv:Body>
        <con:ConsultaResultadoValidacionDocumentosPeticion>
            <con:TipoDocumento>
            <!--- Parámetros enviados con error, el tipo de documento debe ser 
            1 = Factura, 
            2 = Nota Débito, 
            3 = Nota Crédito -->
            </con:TipoDocumento>
            <con:NumeroDocumento>
            <!--- Número secuencial de la factura -->
            </con:NumeroDocumento>
            <con:NitEmisor><!--- NIT del facturador --></con:NitEmisor>
            <con:FechaGeneracion>
            <!--- Fecha de emisión de la factura en formato yyyy-MM-ddTHH:mm:ss -->
            </con:FechaGeneracion>
            <con:IdentificadorSoftware>
            <!--- Proporcionado por la DIAN -->
            </con:IdentificadorSoftware>
            <con:CUFE>
            <!--- El valor del CUFE es obligatorio cuando el tipo de documento es 1 = 'INVOICE' -->
            </con:CUFE>
        </con:ConsultaResultadoValidacionDocumentosPeticion>
    </soapenv:Body>
</soapenv:Envelope>

5. Comunicar factura
Como paso final, en caso de tener como código de transacción 200, ya podemos enviar la factura en formato ZIP junto con el PDF correspondiente al receptor.

Finalmente les comparto la información de un taller que estamos impartiendo para capacitar a desarrolladores Java en la implementación de este tipo de aplicaciones.

Espero esta información les sea de mucha utilidad y quedo presto a resolver interrogantes adicionales que podrán dejar en la caja de comentarios.

viernes, 20 de abril de 2018

Verónica: Facturación Electrónica de código abierto

En el contexto de la computación, el código fuente se define como un conjunto de líneas de texto, que son las instrucciones que debe seguir la computadora para ejecutar dicho programa; por lo que es en el código fuente, donde se encuentra escrito el funcionamiento de un determinado producto de software. Ahora bien, el código fuente de un programa está escrito en uno o más lenguajes de programación específicos, sin embargo este tipo de lenguajes no pueden ser ejecutado directamente por una computadora sino que debe ser traducidos a otro lenguaje que el ordenador pueda ejecutar más fácilmente.

Acceder al código fuente de un programa, consiste en tener acceso a los algoritmos desarrollados por sus creadores. Es la única forma de transformar de manera eficiente un programa. Al tener acceso a las fuentes, es fácil determinar las falencias de un producto sin embargo, desde un punto de vista más optimista, esto no es tan contraproducente como se pensaría puesto que el encontrar errores de programación podría ser de gran ayuda para poder subsanar de manera rápida dichos fallos o incluso, mejorar el rendimiento de un software.

Y es esto último justamente la razón por la cual, luego de haber trabajado por 6 años con más de 40 empresas en Ecuador, he decidido liberar el código fuente de la plataforma para la emisión de comprobantes electrónicos según la normativa impuesta por el Servicio de Rentas Internas (SRI) desde el año 2012. 

Veronica, que es como bauticé al proyecto, reúne todos los requerimientos para generar, firmar, autorizar y almacenar un comprobante electrónico según las leyes del SRI, la autoridad tributaria Ecuatoriana. La idea es formar una comunidad que permita hacer crecer el proyecto de tal manera que pueda ser utilizado por empresas desde la más pequeña hasta la que posea altos volúmenes de facturación. La arquitectura sobre la que está implementada Veronica justamente fue pensada para eso, la escalabilidad.

Evidentemente el fin no es lucrar con un aspecto tan importante como es la cultura tributaria y la declaración de impuestos, sino es poder apoyar a difundir el uso de la facturación electrónica dados los diversos beneficios que está traería consigo no solo para Ecuador sino también para la región.

El proyecto ha sido liberado en la plataforma de control de versiones Github y se encuentra disponible para su rápida descarga, compilación y despliegue, brindando una potente API Rest (Tendencia en el mercado actual de software) que cubre todas las especificaciones del SRI.

Involúcrate en el proyecto y hagamos que se convierta en un estándar.
https://github.com/rolandopalermo/Veronica

martes, 20 de marzo de 2018

Deserialización JSON de tipos genéricos usando Jackson

Como parte de las buenas prácticas en la reutilización de clases, es común encontrar el uso de tipos genéricos en Java. Sin embargo, cuando estas forman parte de una respuesta JSON, pueden ser un dolor de cabeza. Imaginemos que tenemos como respuestas de un API Rest los dos siguientes escenarios:

PersonaResponse
{
   "codigo":1,
   "mensaje":"ok",
   "data":{
      "nombres":"Rolando Palermo",
      "edad":29
   }
}
ProductoResponse
{
   "codigo":1,
   "mensaje":"ok",
   "data":{
      "codigo":"FX-536",
      "precio":56.98,
      "moneda":"PEN",
   }
}
Ambos Json tienen propiedades comunes, sin embargo sólo el objeto data es diferente. Para deserializar estos jsons sin tipos genéricos, tendríamos que crear 2 envoltorios con 2 clases de datos diferentes. Pero con los tipos genéricos solo necesitamos crear una clase genérica para las propiedades de la raíz y una clase específica para cada objeto en la clave de data.

GenericResponse.java
public class GenericResponse<T> {

    private String codigo;
    private String mensaje;
    T data;

    public String getCodigo() {
        return codigo;
    }

    public void setCodigo(String codigo) {
        this.codigo = codigo;
    }

    public String getMensaje() {
        return mensaje;
    }

    public void setMensaje(String mensaje) {
        this.mensaje = mensaje;
    }

    public T getData() {
        return data;
    }

    public void setData(T data) {
        this.data = data;
    }
    
}
PersonaResponse
public class PersonaResponse {

    private String nombres;
    private int edad;

    public String getNombres() {
        return nombres;
    }

    public void setNombres(String nombres) {
        this.nombres = nombres;
    }

    public int getEdad() {
        return edad;
    }

    public void setEdad(int edad) {
        this.edad = edad;
    }
}
ProductoResponse
public class ProductoResponse {

    private String codigo;
    private double precio;
    private String moneda;

    public String getCodigo() {
        return codigo;
    }

    public void setCodigo(String codigo) {
        this.codigo = codigo;
    }

    public double getPrecio() {
        return precio;
    }

    public void setPrecio(double precio) {
        this.precio = precio;
    }

    public String getMoneda() {
        return moneda;
    }

    public void setMoneda(String moneda) {
        this.moneda = moneda;
    }

}
Ahora, la deserialización JSON nos quedaría de la siguiente manera:
ObjectMapper mapper = new ObjectMapper();
//Para persona
GenericResponse<PersonaResponse> response1 = mapper.readValue(jsonString, new TypeReference<GenericResponse<PersonaResponse>>() {});
//Para producto
GenericResponse<ProductoResponse> response2 = mapper.readValue(jsonString, new TypeReference<GenericResponse<ProductoResponse>>() {});