domingo, 5 de febrero de 2012

Agilizar la entrada de datos en campos de tipo DATE

Si tenemos un campo de tipo DATE y que tiene la máscara DD/MM/YYYY el usuario puede introducir la fecha 05022012 y automáticamente se transforma en 05/02/2012, pero en muchos casos puede ser útil permitir al usuario lo siguiente:

  • Introducir 1 ó 2 dígitos, por ejemplo, teclear 5 y asumir el mes y año del SYSDATE transformando ese 5 tecleado por el usuario en la fecha 05/02/2012.
  • Introducir 4 dígitos, asumiendo los 2 primeros el día, los 2 dígitos siguientes el mes y el año obtenerlo del SYSDATE.
  • Introducir 6 dígitos y asumiendo los 2 primeros el día, los 2 siguientes el mes y los 2 últimos el año en formato de 2 dígitos, es decir, se le debe de sumar 2000, de forma que si el usuario teclea 050212 se asuma la fecha 05/02/2012.
Para hacerlo lo que haremos será capturar el error de conversión de máscara de formato FRM-50026 en el disparador ON-ERROR de la siguiente forma:

DECLARE
v_errtyp    VARCHAR2(3):= ERROR_TYPE;
v_errnum    NUMBER := ERROR_CODE;
v_errtxt    VARCHAR2(1000) := ERROR_TEXT;
v_fecha       DATE;
v_dia         VARCHAR2(2);
v_mes         VARCHAR2(2);
v_anho        VARCHAR2(4);
v_error       BOOLEAN := TRUE;
v_valor_campo VARCHAR2(30);
v_mascara     VARCHAR2(30);
BEGIN
  IF v_errtyp = 'FRM'
  AND v_errnum = 50026
  AND Get_Item_Property(:system.trigger_item, DATATYPE) = 'DATE'
  AND UPPER(Get_Item_Property(:system.trigger_item, FORMAT_MASK)) = 'DD/MM/YYYY' THEN
  -- Capturamos el FRM-50026 en campos de tipo DATE y con máscara DD/MM/YYYY
  BEGIN
   v_valor_campo := NAME_IN(:system.trigger_item);

      IF LENGTH(v_valor_campo) = 1 THEN
      -- El usuario sólo teclea un dígito
      v_dia := v_valor_campo;
      ELSIF LENGTH(v_valor_campo) IN (2, 4, 6) THEN
     -- Obtenemos el día de lo que ha tecleado el usuario
     v_dia := SUBSTR(v_valor_campo, 1, 2);
    END IF;

      IF LENGTH(v_valor_campo) IN (4, 6) THEN
      -- Obtenemos el mes, sólo si el usuario ha tecleado algo de 4 ó 6 dígitos
      v_mes := SUBSTR(v_valor_campo, 3, 2);
      END IF;

      IF LENGTH(v_valor_campo) = 6 THEN
      -- Obtenemos el año, sólo si el usuario ha tecleado algo 6 dígitos
      v_anho := TO_CHAR(TO_NUMBER(SUBSTR(v_valor_campo, 5, 2)) + 2000);
      END IF;

      -- Obtenemos el mes y el año de SYSDATE en caso de que haber sido tecleado por el usuario
      v_mes := NVL(v_mes, TO_CHAR(SYSDATE, 'MM'));
      v_anho := NVL(v_anho, TO_CHAR(SYSDATE, 'YYYY'));
      v_fecha := TO_DATE(v_dia || '/' || v_mes || '/' || v_anho, 'DD/MM/YYYY');
      -- El comando COPY únicamente está preparado para asignar un valor VARCHAR2,
      -- con variables de tipo DATE debemos de asegurar la máscar de formato
      -- con la que hará conversión.
      v_mascara := Get_Application_Property(BUILTIN_DATE_FORMAT);
      Set_Application_Property(BUILTIN_DATE_FORMAT, 'DD/MM/YYYY');
      COPY(TO_CHAR(v_fecha, 'DD/MM/YYYY'), :system.trigger_item);
      Set_Application_Property(BUILTIN_DATE_FORMAT, v_mascara);
      v_error := FALSE;
    EXCEPTION
      WHEN OTHERS THEN
        -- Ante cualquier error dejamos el mensaje original de error
        NULL;
    END;  
  END IF;

  IF v_error THEN
    -- No se ha podido transformar lo tecleado por el usuario en una fecha válida
    MESSAGE(v_errtyp || '-' || TO_CHAR(v_errnum) || ': ' || v_errtxt); 
    RAISE Form_Trigger_Failure;
  END IF;
END;