// <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
// Reporte :    FUNCIONES DE USO COMUN EN LOS REPORTES DE ADMINPAQ     LibAdminPAQ.rpt 
//
// Sistema:     ADMINPAQ  Versión v1.0 Windows
//
// Descripcion:En este archivo se guardan todas las funciones de uso común en los reportes 
//             de AdminPAQ, son funciones de edición, de busqueda y de cálculo que se incluyen
//             en los reportes de AdminPAQ
//             
// Creación  :  18/junio/2001
// ==========================================================================================
// Historial de Modificaciones
// 15/11/2004 LAP   N.D.3051    Imprimir 10 valores de clasificación por renglon ya que de 
//                              otra forma se pierden.
//                              - fImprimeClases
// 20/04/2005 LAP   N.D.3219    Adición de función para expresar los importes de los documentos 
//                              en la moneda del reporte. Considerando que los documentos en 
//                              moneda base tienen el tipo de cambio de la moneda del cliente 
//                              a la moneda base.
//                              - fCambiarMonedaDoctoAReporte (Nueva)
// 20/04/2005 LAP               En la busqueda del tipo de cambio considerar la fecha inicial
//                              de la empresa (Anteriormente no lo hacia) 
//                              - fTipoCambio
// 28/04/2005 LAP  N.D.3023     Adición de funciones para expresar un importe en una moneda
//                              - fCambiarMonedaImporteAReporte (Nueva)
// 16/05/2007 JAAM N.C.147      -fVerificaUnidad
//                              Actualizacion del manejo de parametros para considerar la unidad
//                              de compra/venta del producto  
// <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

Columnas 1; 1
// FUNCIONES <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

// N.D.3238 Constante que define el número máximo de decimales de AdminPAQ; similar a la definida en ConstantesGenerales.h
constante kNumDecimalesRedondeo      6

// Variables globales, de uso solamente en este archivo biblioteca. En la Función de fTipoCambio
  
  gFechaIniHistoria = 0
  gFechaFinHistoria = 0

// ==========================================================================================
// fIniFechasHistoria()
// Descripción:  Esta funcion inicializa las variables globales de inicio y fin de historia
//   Se debe mandar llamar siempre que se vaya a utilizar la función fTipoCambio.
// ==========================================================================================
Deffunc fIniFechasHistoria(atPeriodosLib:TABLA)
  // Solo se leen una sola vez en el reporte
  Si (gFechaIniHistoria = 0) O (gFechaFinHistoria = 0)
    // Esta fecha global es usada en la función de fTipoCambio
    atPeriodosLib.BUSCA ULTIMO
    gFechaFinHistoria = atPeriodosLib(cFechaFinal) 

    // Esta fecha global es usada en la función de fTipoCambio  
    atPeriodosLib.BUSCA PRIMERO
    gFechaIniHistoria = atPeriodosLib(cFecIniPeriodo1)
  Finsi
FinFunc 
// **********************************************************************************************
//   fConvJul
// Proceso : Convierte una fecha de la forma AAAAMMDD a un juliano
// Entradas: cFecha - fecha de la forma AAAAMMDD
// Salidas : nJuliano - Fecha en juliano
// Creacion: Ricardo Palau Castañeda
// **********************************************************************************************
DefFunc fConvJul(aFecha)
Local Feb, Mes, Ano, Dia, i, nAuxJul, Aux
Local vRegreso

  // Se guarda un arreglo que contenga los días de cada mes
  Dias = '0,31,28,31,30,31,30,31,31,30,31,30,31'[c0]

  // Fecha :'AAAAMMDD'
  // Se substraen el dia, mes y año respectivamente de la fecha que se recibio
  Dia = @Val(@Substr(aFecha;6;2))
  Mes = @Val(@Substr(aFecha;4;2))
  Ano = @Val(@Substr(aFecha;2;2))

  // Validar año 2000
  Si Ano < 40 
    Ano = Ano + 100
  FinSi

  // Calcula los días de Febrero
  Si Ano % 4 = 0
    Feb = 29[C0]
  SiNo
    Feb = 28[C0]
  FinSi
  nAuxJul[0] = 0
  i[0] = 1
   
  // Suma los días transcurridos en el año
  Mientras i < Mes
    Si i = 2
      nAuxJul = nAuxJul + Feb
    SiNo
      nAuxJul = nAuxJul + @Val(@Escoge(Dias;i))
    Finsi
    i = i + 1
  FinMientras
  // Toma en cuenta los años bisiestos
  Aux = @Int((Ano + 3) / 4 - 3) 
  vRegreso =(@Val(@Col(nAuxJul;0)) + (Ano - 10) * 365 + Aux + Dia)
   
  Retorna vRegreso

FinFunc

// **********************************************************************************************
//	fFechaAnterior
//
//	Esta función recibe una fecha AAAAMMDD y devuelve el dia anterior en AAAAMMDD
// **********************************************************************************************
DefFunc fFechaAnterior(Fecha)
Local vMes, vDia, vAno
Local vRegreso

  // Obtiene el día, mes y año
	vDia = @SubStr(Fecha;6;2)
	vMes = @SubStr(Fecha;4;2)
	vAno = @SubStr(Fecha;0;4)

	Si vDia = 1 // se llego a inicio de mes?
		// retrocede un mes teniendo cuidado con Enero
  	Si vMes = 1 // Enero?
			vMes = 12[C0] // el anterior es diciembtr
			vAno = vAno - 1[C0] // del ano anterior
		SiNo
			vMes = vMes - 1[C0]
		FinSi
		// calcula el dia anterior en funcion del mes		
		Si vMes = 1 o vMes = 3 o vMes = 5 o vMes = 7 o vMes = 8 o vMes = 10 o vMes = 12
			vDia = 31[C0]
		FinSi
		Si vMes = 4 o vMes = 6 o vMes = 9 o vMes = 11
			vDia = 30[C0]
		FinSi
		// verifica el caso especial Febrero
		Si vMes = 2
    		Si vAno % 4 = 0 // Ojo: Esta condición solo funciona hasta el año 2099
				vDia = 29[C0]
			SiNo
				vDia = 28[C0]
	    FinSi
		FinSi
	SiNo // cualquier otro día?
		vDia = vDia - 1[C0]
	FinSi

	// arma la nueva fecha
   vMes = @Val(vMes)[C0]
   vDia = @Val(vDia)[C0]
	vRegreso = vAno & (vMes <= 9? '0' & vMes : vMes) & (vDia <= 9? '0' & vDia : vDia)[C0]
  
  Retorna vRegreso
FinFunc

// **********************************************************************************************
//	fFechaSiguiente
//
//	Esta función recibe una fecha AAAAMMDD y devuelve el dia siguiente en AAAAMMDD
// **********************************************************************************************
DefFunc fFechaSiguiente(Fecha)
Local vMes, vDia, vAno
Local vRegreso

	// Obtiene el día, mes y año
	vDia = @SubStr(Fecha;6;2)
	vMes = @SubStr(Fecha;4;2)
	vAno = @SubStr(Fecha;0;4)

	// verifica los meses de 31 días
	Si vMes = 1 o vMes = 3 o vMes = 5 o vMes = 7 o vMes = 8 o vMes = 10 o vMes = 12
		Si vDia = 31 // se llego a fin de mes
			// entonces el siguiente dia es primero
			vDia = 1[C0]
			// incrementa el mes siempre que no sea diciembre
			Si vMes != 12
				vMes = vMes + 1[C0]
			SiNo 
				// Si es Diciembre incrementa el año y pone el mes en Enero
				vMes = 1[C0]
				vAno = vAno + 1[C0]
			FinSi
		SiNo
			// Si no es fin de mes simplemente incrementa el dia
			vDia = vDia + 1[C0]
		FinSi
   Sino
   	// verifica los meses de 30 días
	   Si vMes = 4 o vMes = 6 o vMes = 9 o vMes = 11
   		Si vDia = 30 // se llego a fin de mes
	   		// entonces el siguiente dia es primero
		   	vDia = 1[C0]
			   vMes = vMes + 1[C0]
   		SiNo
	   		// Si no es fin de mes simplemente incrementa el dia
		   	vDia = vDia + 1[C0]
   		FinSi
	   Sino
   	   // verifica el caso especial de Febrero
      	// Verifica la condición de año bisiesto
    		Si vAno % 4 = 0 // Ojo: Esta condición solo funciona hasta el año 2099
	      	// Si es bisiesto entonces verificar la condicion con 29 dias
     			Si vDia = 29 // se llego a fin de mes?
	      		// entonces el siguiente dia es primero
     				vDia = 1[C0]
	      		vMes = vMes + 1[C0]
     			SiNo
     				// Si no es fin de mes simplemente incrementa el dia
	      		vDia = vDia + 1[C0]
     			FinSi
     		SiNo
	      	// No es bisiesto entonces verificar la condicion con 28 dias
     			Si vDia = 28 // se llego a fin de mes?
	      		// entonces el siguiente dia es primero
     				vDia = 1[C0]
	      		vMes = vMes + 1[C0]
     			SiNo
	      		// Si no es fin de mes simplemente incrementa el dia
     				vDia = vDia + 1[C0]
	      	FinSi
     		FinSi // Año bisiesto
   	FinSi // Condición meses de 30 días
   Finsi // Condición meses de 31 días

	// arma la nueva fecha
   vMes = @Val(vMes)[C0]
   vDia = @Val(vDia)[C0]
	vRegreso = vAno & (vMes <= 9? '0' & vMes : vMes) & (vDia <= 9? '0' & vDia : vDia)[C0]

  Retorna vRegreso
FinFunc


// ==========================================================================================
// fTipoCambio
// Descripción:  Función para obtener el tipo de cambio de un Moneda en una fecha 
//               determinada
// Entrada:  Fecha, Moneda 
//           aTblTiposCambios : Es el handle de la tabla de monedas.
// Regresa:  El tipo de cambio mas cercano a la fecha que recibe como fecha Base
//           en caso de no encontrar alguno, regresa el valor de 1.00
// Indices :iMonedaFecha = Ascendente         
//          iMonedaFechaDesc = Descendente    Nota: Internamente se cambian
// ==========================================================================================
// Historial de Modificaciones
// 20/04/2005 LAP               En la busqueda del tipo de cambio considerar la fecha inicial
//                              de la empresa (Anteriormente no lo hacia) 
// ==========================================================================================
DEFFUNC fTipoCambio(aFechaBase; aIdMoneda; aTblTiposCambio:TABLA)
LOCAL lFechaAnterior, lFechaPosterior
LOCAL lFechaInicial, lFechaFinal, lFechaBusqueda
LOCAL lTipoCambioAnterior, lTipoCambioPosterior, lTipoCambio

	lTipoCambio = 1

  aTblTiposCambio.UsaIndice['iMonedaFecha']  // de inicio se asegura el indice ascendente

   // Busca el tipo de cambio en la fecha exacta
	aTblTiposCambio.BUSCA[aIdMoneda & ':' &  aFechaBase]

  // Si no existe en la fecha exacta, se busca el mas proximo 
  Si !(aTblTiposCambio->Encontro)
    //se recupera la primera ascendente
    lFechaInicial = aFechaBase
    // en caso de no haber ejecutado la fecha
    lFechaFinal = (gFechaFinHistoria = 0?'20200101' : gFechaFinHistoria)// un rango teoricamente no alcanzable
    // este recorrido es valida dado que el primer campo no cambia en caso de 
    // que el primer caso fuera diferente no aplicaria esta instruccion
    lFechaBusqueda = lFechaInicial
    Mientras lFechaBusqueda < lFechaFinal Y !(aTblTiposCambio->Encontro)
      aTblTiposCambio.Busca[aIdMoneda & ':' &  lFechaBusqueda]
      lFechaBusqueda = fFechaSiguiente(lFechaBusqueda)      
    FinMientras

    Si aTblTiposCambio->Encontro
      lFechaPosterior = aTblTiposCambio(cFecha)
      lTipoCambioPosterior = aTblTiposCambio(cImporte)
    Sino
      lFechaPosterior = '20200101'
      lTipoCambioPosterior = 1
    Finsi

    // se cambia a el indice Descendente para recuperar la primera hacia atras
    aTblTiposCambio.UsaIndice['iMonedaFechaDesc']          

    lFechaInicial =  aFechaBase   
    lFechaFinal = (gFechaIniHistoria = 0 ? '19900101': gFechaIniHistoria )// Una fecha anterior teoricamente no alcanzable   

    lFechaBusqueda = lFechaInicial
    Mientras lFechaBusqueda >= lFechaFinal Y !(aTblTiposCambio->Encontro)
      aTblTiposCambio.Busca[aIdMoneda & ':' &  lFechaBusqueda] 
      lFechaBusqueda = fFechaAnterior(lFechaBusqueda)      
    FinMientras
   
    Si aTblTiposCambio->Encontro
      lFechaAnterior = aTblTiposCambio(cFecha)
      lTipoCambioAnterior = aTblTiposCambio(cImporte)
    Sino
      lFechaAnterior = '19900101'
      lTipoCambioAnterior = 1
    Finsi

    // Calcula la fecha mas cercana a la fecha base
    lFechaAnterior = fConvJul(lFechaAnterior)
    lFechaPosterior = fConvJul(lFechaPosterior)
    aFechaBase = fConvJul(aFechaBase)    
    Si (aFechaBase  - lFechaAnterior) < (lFechaPosterior - aFechaBase)
      lTipoCambio = lTipoCambioAnterior
    Sino
      lTipoCambio = lTipoCambioPosterior
    Finsi

  Sino
    lTipoCambio = aTblTiposCambio(cImporte)
  Finsi
  
  retorna lTipoCambio
FINFUNC

// ==========================================================================================
// fEsComando : revisa si el simbolo que recibe es parte de los comandos para la captura de 
//       de un código, si no lo es indica que es el separador de la estructura del código.
// Salida : 1 cuando el simbolo es un comando 
//          0 cuando el simbolo no es un comando
// ==========================================================================================
Deffunc fEsComando(aSimbolo)
Local lComandos, lRegresa, lInd

  lComandos = 't,T,l,L,i,I,C,a,A,0,9,#,!'
  lNumComandos = 13

  lInd = 0
  lRegresa = 0
  Mientras lInd< lNumComandos

    Si @Escoge(lComandos;lInd) = aSimbolo
      lRegresa = 1
      lInd = lNumComandos
    Finsi
    lInd = lInd + 1
  FinMientras

  Retorna lRegresa
FinFunc

// ==========================================================================================
// fMascarilla 
// Descripción: formatea el codigo que recibe de acuerdo a la mascarilla 
// Entrada:     aCodigoSinMascarilla,  aMascarilla ()
// Regresa:     Codigo que recibió, pero formateado según la mascarilla
// Algoritmo :
//    CodigoFormateado = CodigoSinMascarilla (quitándole espacios Derecha)
//    CICLO I = 1  HASTA @LEN(Mascarilla) (quitar espacios a la derecha)
//      SI @SUBSTR(MASCARILLA; I; 1) = '-'
//         CodigoFormateado = Incluye el guión encontrado
//      FINSI  
//      I = I + 1
//    FINCICLO
// ==========================================================================================
DEFFUNC fMascarilla(aCodigoSinMascarilla;aMascarilla)
LOCAL lInd, lLongitud, lCodigoFormateado,lCaracter,lLongMasc

   // Quitar espacios a la derecha del Código sin Formatear
   // Inicializar Codigo Formateado
   lCodigoFormateado = @TRIM(2;aCodigoSinMascarilla)

   Si lCodigoFormateado = '(Ninguno)'
     Retorna lCodigoFormateado
   Finsi

   // Obtener la Longitud del Código Formateado sin espacios a la Derecha
   lLongitud         = @LEN(lCodigoFormateado)
   lLongMasc         = @LEN(@TRIM(2;aMascarilla))
   aMascarilla = @SubStr(aMascarilla;1;lLongMasc-1)
   lLongMasc = lLongMasc -1

   lInd = 0
   // Ciclo que recorre caracter por caracter la Mascarilla
   MIENTRAS lind <= lLongMasc
      // Si el caracter no es un comando, se deberá insertar en el Codigo Formateado
      lCaracter = @SUBSTR(aMascarilla;lind;1)
      SI   !fEsComando(lCaracter)
         lCodigoFormateado = @SUBSTR(lCodigoFormateado;0;lind) & lCaracter & @SUBSTR(lCodigoFormateado;lind; lLongitud - lind)
         // Como se le agrego al código un caracter (el guión), la longitud se actualiza
         lLongitud = lLongitud + 1
      FINSI
      // Siguiente caracter de la mascarilla
      lind = lind + 1
   FINMIENTRAS
   // Regresa el Código ya formateado

   RETORNA lCodigoFormateado
FINFUNC

// ==========================================================================================
// fFormateaFecha  
// Descripción:  La fecha se formatea en DD/MMM/AAAA, para impresión.  Puede hacerse en  en
//               inglés o español
// Entrada:  FechaSinFormato (Formato AAAAMMDD), Idioma  (0 = Español, 1 = Inglés)
// Regresa:  Fecha Formateada
// Algoritmo :
// Incializa Lista en Inglés y en Español
// Mes = Localiza el Mes en la Lista que le corresponda (inglés o español)
// FechaFormateada = DD + '/' + Mes + '/' + AAAA   
// ==========================================================================================
DEFFUNC fFormateaFecha(aFechaSinFormato;aIdioma)
   LOCAL lMesesEsp, lMesesIng, lMes, lRetorno

   // Incializa Listas de Meses en Inglés o en Español
   lMesesEsp = 'ENE,FEB,MAR,ABR,MAY,JUN,JUL,AGO,SEP,OCT,NOV,DIC'[C0]
   lMesesIng = 'JAN,FEB,MAR,APR,MAY,JUN,JUL,AUG,SEP,OCT,NOV,DEC'[C0]

   // Selecciona el Mes de acuerdo a la Lista
   SI aIdioma = 0
      // Español
      lMes = @ESCOGE(lMesesEsp;@SUBSTR(aFechaSinFormato;4;2)-1)
   SINO 
      // Inglés
      lMes = @ESCOGE(lMesesIng;@SUBSTR(aFechaSinFormato;4;2)-1)
   FINSI
   // Regresa la Fecha Formateada
   lRetorno = @SUBSTR(aFechaSinFormato;6;2) & '/' & lMes & '/' & @SUBSTR(aFechaSinFormato;0;4)

   RETORNA lretorno
      
FINFUNC

// ==========================================================================================
// fEjercicioPeriodo
// Descripción:  De acuerdo a un Fecha, regresa el Ejercicio y Periodo al que pertenece
// Entrada:  Fecha  : Para la cual se obtendra el periodo y ejercicio correspodiente.
//           Periodo, Ejercicio : los recibe por referencia ya que se modificarán.
//           aTblPeriodos : recibe el handle de la tabla de periodos.
// Regresa:  Periodo   : Num. de periodo que le corresponde a la fecha
//           Ejercicio : ID  del ejercicio que le corresponde a la fecha
// Consideraciones: Para que funcione se deberan pasar por referencia los argumentos 
//           aPeriodo y aEjercicio
// Algoritmo
// Abre el archivo de Ejercicios y Periodos
// Incializa variables de Periodo y Ejercicio
// MIENTRAS No sea Fin de Archivo de Ejercicios y Periodos
//    SI la Fecha ubicada se encuentra en el rango del ejercicio
//       Ubica el periodo y ejercicio al que pertenece la fecha                
//       FINCICLO
//    FINSI
//    Obten el siguiente registro
// FINMIENTRAS
// ==========================================================================================
DEFFUNC fEjercicioPeriodo(aFecha;aPeriodo;aIdEjercicio;aTblPeriodos:TABLA)
LOCAL lInd, lUltimoRegistro, lFinArchivo
Local lPrimerCampoPeriodo

  Si gFechaFinHistoria = 0
    fIniFechasHistoria(aTblPeriodos)
  FinSi
  
  // hay dos campos antes de la fecha del primer periodo, pero como el arreglo empieza en 1 lOffset 
  //  se inicia en 1; Cuando lInd = 1, tenemos que lOffset + lInd = 2, que corresponde a el campo cFecIniPeriodo1
  lOffset = 1
  aPeriodo   = 0
  aIdEjercicio = 0

  aTblPeriodos.BUSCA ULTIMO
  lUltimoRegistro = aTblPeriodos(cIdEjercicio)
  aTblPeriodos.BUSCA PRIMERO
  lFinArchivo = 1

  Si aFecha > gFechaFinHistoria
    aPeriodo = 12
    aIdEjercicio = lUltimoRegistro
    Retorna
  FinSi
  

  // Empieza la busqueda del registro en el cual caé la fecha buscada aFecha
  Mientras lFinArchivo
    // Busca si la fecha se encuentra en el rango del Ejercicio
    Si aFecha >= aTblPeriodos(cFecIniPeriodo1) Y aFecha <= aTblPeriodos(cFechaFinal)
      aIdEjercicio = aTblPeriodos(cIdEjercicio) // Ejercicio que se regresa
      lInd = 1
      aPeriodo = lInd
      Mientras lInd <= 11        
        Si aFecha >= aTblPeriodos->CampoVal[lOffset + lInd] Y aFecha < aTblPeriodos->CampoVal[lOffset+lInd+1]
          Finciclo
        Finsi
        lInd = lInd + 1
        aPeriodo = lInd  // periodo que se regresa
      FinMientras
      lFinArchivo = 0
    Finsi 
    // La fecha no encanja con los periodos existentes, regresa y no se toma ningun valor 
    Si aTblPeriodos(cIdEjercicio) = lUltimoRegistro 
      Finciclo
    Finsi
    aTblPeriodos.SALTAREG[1]
  FinMientras
FinFunc

// ===========================================================================================
// fConvierteTipoCambio
// Descripcion: Esta función convierte el aImporte que se encuentra en la moneda aIDMonedaDe a 
//              a un importe representado en la moneda aIDMonedaA.
// Entradas: aImporte = Importe que se convertira a otra moneda
//           aIDMonedaDe = El ID de la moneda del importe de entrada
//           aIDMonedaA = El ID de la moneda a la que se quiere convertir
//           aFechaBase = es la fecha que se concidera para obtener el tipo de cambio.
// Salida :  Regresa el importe aImporte representado en la aIDMonedaA
// ===========================================================================================
Deffunc fConvierteTipoCambio(aImporte;aIDMonedaDe;aIDMonedaA;aFechaBase;aTblTiposCambio:TABLA)
      
      // X = Tipo cambio moneda del cliente
      // Y = Tipo cambio moneda del rpt
      // resultado = importe * (X/Y)
  Local lTipoCambioDe, lTipoCambioA
  Local lRegreso

  lRegreso = aImporte

  Si aIDMonedaDe != aIDMonedaA 

    // Se convierte a la moneda base primero, solo si es diferente a la moneda base
    Si aIDMonedaDe != 1
      lTipoCambioDe = fTipoCambio(aFechaBase;aIDMonedaDe;aTblTiposCambio)
      lRegreso = aImporte * lTipoCambioDe
    Finsi
    // Si el reporte es requerido en una Moneda distinta a la Moneda Base
    SI aIDMonedaA != 1
   	  // Convierte de la Moneda Base a la Moneda requerida en el Reporte
      lTipoCambioA  = fTipoCambio(aFechaBase;aIdMonedaA;aTblTiposCambio)
      lRegreso = lRegreso / lTipoCambioA            
    FINSI

  Finsi

  Retorna lRegreso
FinFunc

//// FUNCIONES PARA APLICAR CLASIFICACIONES

   // Variables de uso global para aplicar clasificaciones
  gNumDeClases = 0
  gListNombreDeClases = ''
  gListIdDeClases = ''
  gClasesRecibidas = ''
  gNumClasesPorTipo = ''

// ==========================================================================================
// Esta Función solo es para no repetir código para cada una de las lista que recibe fCuentaClases
//  Regresa en un string el número de elementos en la lista, cadena vacia en lugar de cero
Deffunc fCuenta(aLista)
  Local lNum,lCont

  lCont = 0
  lNum = 0

  Si aLista = 0
    Retorna ''  // Cadena vacia
  FinSi

  Mientras @Escoge(aLista;lNum)
    lNum = lNum +1
  FinMientras
  
  Retorna @Str(lNum;0)
FinFunc
// ==========================================================================================
//    fCuentaClases (fProcedimientos) 
//  Modifica aNumClases, le asigna una lista que contiene el número de elementos de cada 
//  clasificación
//  Regresa : Una lista con todas las clasificaciones.
// ==========================================================================================
Deffunc fCuentaClases(aNumClases;aLista1;aLista2;aLista3;aLista4;aLista5;aLista6)
  Local lClasesRecibidas  

  aNumClases = fCuenta(aLista1)
  aNumClases = aNumClases &','& fCuenta(aLista2)
  aNumClases = aNumClases &','& fCuenta(aLista3)
  aNumClases = aNumClases &','& fCuenta(aLista4)
  aNumClases = aNumClases &','& fCuenta(aLista5)
  aNumClases = aNumClases &','& fCuenta(aLista6)
  lClasesRecibidas = aLista1&','&aLista2&','&aLista3&','&aLista4&','&aLista5&','&aLista6

  Retorna lClasesRecibidas
FinFunc

// ==========================================================================================
// fCargaClases  (Procedimiento)
// Descripcion : Recupera los nombres de las Clasificaciones que corresponden a los ID que se 
//         recibieron en los parametros, descarta los ID que son cero, pues indica que no se 
//         recibió ningún parámetros
//         aClases = es la lista de Id de clases de la que se desea obtener los validos
// Modifica : se modifican las siguientes varibles que recibe por referencia
//          gListaIdClases  =  Guarda la lista de ID validos
//          gListaNombreClases = Guarda los nombres de correspondiente a los ID validos
//Regresa : gNumClases, el número de clases validas que se cargaron,sumados por todas las 
//          clasificaciones. 
//          Una clase valida es la que corresponde a un ID diferente de 0
// ==========================================================================================
Deffunc fCargaClases(aTblClasif:TABLA; aListaNombreClases; aListaIdClases;aNumClases)
  Local lCont, lNumClases
    
  lCont = 0

  aListaNombreClases  = ''
  aListaIdClases  = ''
  lNumClases  = 0
  // se barren la lista de seis clasificaciones, y se crea una nueva lista con los ID validos
  // y con los nombres de esas clasificaciones validas
  Mientras @Escoge(aNumClases;lCont) != ''

    // en caso de ser > 0, entonces Se trata de un ID valido
    Si @Val(@Escoge(aNumClases;lCont)) > 0

      aTblClasif.Busca[@Escoge(aNumClases;lCont)]
      Si aTblClasif->Encontro

        Si lNumClases = 0
          aListaIdClases = @Escoge(aNumClases;lCont)
          aListaNombreClases = aTblClasif(cValorClasificacion)
        Sino
          aListaIdClases = aListaIdClases & ',' & @Escoge(aNumClases;lCont)
          aListaNombreClases = aListaNombreClases &',' & aTblClasif(cValorClasificacion)
        Finsi
        
        lNumClases = lNumClases + 1
      Finsi

    Finsi
    lCont = lCont +1

  FinMientras  // Escoge

  Retorna lNumClases

FinFunc

// ==========================================================================================
// fImprimeClases  (Procedimiento)
// Descripcion : Imprime los nombres de las clases justificados al centro
//   aNumClases = El número total de clases seleccionadas,contando todas las clasificaciones
//   aNumPorClase = Lista que tiene como elementos el numero de clases seleccionadas para cada
//            clasificación.
// ******************************************************************************************
// Historial de Modificaciones
// 15/11/2004 LAP   N.D.3051    Imprimir 10 valores de clasificación por renglon ya que de 
//                              otra forma se pierden.
// ==========================================================================================
Deffunc fImprimeClases(aListaNombreClases; aNumPorClase)
Local lMaxNoClasificaciones
Local lNoValoresClasificaciones
Local lLeyenda
Local lOffset
Local lContador
Local lNumeroValorClas
Local lValoresPorRenglon

  lValoresPorRenglon = 10
  
  // Se barren las seis listas de ID´s
  lOffset = 0
  lMaxNoClasificaciones = 0
  Mientras lMaxNoClasificaciones < 6
    // Se imprimen los que si tuvieron al menos un ID 
    Si @Escoge(aNumPorClase; lMaxNoClasificaciones) = ''
      lMaxNoClasificaciones = lMaxNoClasificaciones + 1
      Continua
    FinSi
      
    // Obtengo el número de elementos para la clase
    lNoValoresClasificaciones = @Val(@Escoge(aNumPorClase; lMaxNoClasificaciones))
    // Barre todos los elementos de esta clase      
    lNumeroValorClas = 0
    // Imprimir 10 valores de clasificación por renglón
    Mientras lNumeroValorClas < lNoValoresClasificaciones
      lContador = 0
      lLeyenda = ''
      Mientras lContador < lValoresPorRenglon Y lNumeroValorClas < lNoValoresClasificaciones
        lLeyenda = lLeyenda & ', '& @Escoge(aListaNombreClases; lOffset+lNumeroValorClas)
        lContador = lContador +1
        lNumeroValorClas = lNumeroValorClas + 1
      FinMientras 
      // Impresión de la clasificación y sus valores seleccionados
      Si lNumeroValorClas <= lValoresPorRenglon
        Texto '^@TC{0,0,0}@S{9}'& 'CLASIFICACIÓN No.'& @Str(lMaxNoClasificaciones+1; 0) & ' : '& @SubStr(lLeyenda; 1; @Len(lLeyenda)-1)
      Sino
        Texto '^@TC{0,0,0}@S{9}'& @SubStr(lLeyenda; 1; @Len(lLeyenda)-1)
      FinSi
      Imprime         
    FinMientras
    lOffset = lOffset + lNoValoresClasificaciones    
    lMaxNoClasificaciones = lMaxNoClasificaciones + 1
  FinMientras
FinFunc

// ==========================================================================================
// fMiembroDeUnaClase  
//    Esta función que determina si el elemento que recibe pertenece a alguna de las clases 
// que seleccionadas para cada clasificación. Todas las clases, de todas las clasificaciones 
// estan en una lista aListaIDs, y el número de clases por cada clasificación está en aNum.
// gNumClasesPorTipo.
// Entrada:
//   atTablaComparada =  el registro del cual se quiere saber si pertenece o no a alguna Clases
//   aNum   = el número de clases contra las que se va a comparar
//   aListaIDs = Es una lista con los ID de las clases.
//   aTipo = Determina el tipo de elemento que se recibe en atTablaComparada
//           0 =  Un registro de un Cliente
//           1 =  Un registro de un Proveedor
//           2 =  Un registro de un Producto
//           3 =  Un registro de un Agente
//           4 =  Un registro de un Almacen
// Salida : En todos los casos, regresa un :
//      0 = cuando el elemento atTablaComparada NO pertenece a una de las clases seleccionadas
//          por cada clasificación.
//      1 = cuando el elemento atTablaComparada SI pertenece a una clase seleccionada por cada 
//          una de las clasificaciones.
// ==========================================================================================
Deffunc fMiembroDeUnaClase(atTablaComparada:TABLA;aNum;aListaIDs;aTipo)
  Local lMaxClases, lOffsetCampo, lCampo, lClase, lPertenece,lOffsetClase

  lPertenece = 0
  lCampo = 0   // para barrer los seis campos de clasificaciones
  lClases = 0   // para barre los Id de clases
  lClasif = 0  // lleva el orden de la clasificación que se está comparando.
   
  // se asigna el offset apropiado según el tipo de tabla a comparar
  Escoge aTipo
    Caso 0
      lOffsetCampo = 13      
    FinCaso 
    Caso 1
      lOffsetCampo = 43
    FinCaso 
    Caso 2
      lOffsetCampo = 26
    FinCaso
    Caso 3
      lOffsetCampo = 9
    FinCaso
    Caso 4
      lOffsetCampo = 4
    FinCaso
  FinEscoge
  lOffsetClase = 0
  // lCampo empieza en cero y va hasta 5
  // La primera vez lOffsetCampo + lCampo =    campo  13          ó  campo  43
  //       Respectivamente indican   = cIdValorClasifCliente1, cIdValorClasifProv1
  //                                      campo  18          ó  campo  48
  // con lCampo = 5                  = cIdValorClasifCliente6, cIdValorClasifProv6

  // Se barren las 6 posibles listas de ID´s
  Mientras lClasif < 6

    Si @Escoge(gNumClasesPorTipo;lClasif) != ''
      
      aNum = @Val(@Escoge(gNumClasesPorTipo;lClasif))
      lClases = 0      

      Mientras lClases < aNum
  
        Mientras lCampo < 6  // asume que todas las tablas tienen max. 6 clasificaciones

          Si atTablaComparada->CampoVal[lOffsetCampo + lCampo]  = @Val(@Escoge(aListaIDs;lOffsetClase+lClases))
            lPertenece = lPertenece + aNum
            // se alteran las variables para forzar la salida del ciclo
            lCampo = 6
            lClases = aNum
          Finsi
  
          lCampo = lCampo + 1
  
        FinMientras
        // Si encontro alguno, al salir del ciclo lPertenece es igual a lPertence + aNum
        lClases = lClases + 1
        lCampo = 0
    
      FinMientras
      
      lOffsetClase = lOffsetClase + aNum

    Finsi
        
    lClasif = lClasif +1

  FinMientras  

  // Si lPertence es igual a el número total de clases, indica que entro en todos los casos a la
  // condición, por lo tanto en cada clasificación hubo una clase con la que coicidió el registro
  lPertenece = lPertenece >= gNumDeClases ? 1 : 0

  Retorna lPertenece
FinFunc

// ==========================================================================================
// fRevisaClases
// ========================================================================================== 
Deffunc fRevisaClases(aValoresDeClases:TABLA)
    // ===== se arman los ID en una LIsta para poder recorrerlos en una iteración
    gClasesRecibidas = fCuentaClases(&gNumClasesPorTipo;pIdClase1;pIdClase2;pIdClase3;pIdClase4;pIdClase5;pIdClase6)
    // se cargan las clasificaciones validas y se obtiene el número de clases validas
    gNumDeClases = fCargaClases(aValoresDeClases;&gListNombreDeClases; &gListIdDeClases; gClasesRecibidas)
    Si gNumDeClases > 0    // solo si hubo al menos una clase seleccionada
      fImprimeClases(gListNombreDeClases;gNumClasesPorTipo ) 
    Finsi
FinFunc

// ==========================================================================================
// fNetoMovtoPaquete  
//    Esta función calcula el neto de un movimiento de un componete de paquete en base a una 
//    proporción del precio del paquete.
// 
// Entrada:
//   atMovimiento    = Registro de movimiento donde se obtienen los valore de total y descuentos.
//   atMovtoOwner    = Registro del movimiento Owner del movimiento
//   atMovtoPaquete  = Registro del movimiento con el producto del paquete
//   atProducto      = Registro del producto en turno
//   atPaqComponente = Registro del paquete en turno
//   aPrecio         = Precio del Paquete
//   aDescuentos     = Total de Descuentos del Paquete
//   aImpuestos      = Total de Impuestos del Paquete
//   aTotal          = Total del Paquete
//
// Salida : Importe Neto del movimiento del paquete
//      
// ==========================================================================================
Deffunc fNetoMovtoPaquete(atMovimiento:Tabla; atMovtoOwner:Tabla; atMovtoPaquete:Tabla; atProducto:Tabla; atPaqComponente:Tabla; aPrecio; aDescuentos; aImpuestos; aTotal)
  Local lPrecioPaquete
  Local lNetoMovimiento
  Local lVenta
  Local lProporcion
  Local lTemporal

  lPrecioPaquete = 0
  lNetoMovimiento = 0
  lVenta = atMovimiento(cNeto) - atMovimiento(cDescuento1) - atMovimiento(cDescuento2) - atMovimiento(cDescuento3) - atMovimiento(cDescuento4) - atMovimiento(cDescuento5)

  aPrecio     = atMovimiento(cPrecio)
  aDescuentos = atMovimiento(cDescuento1) + atMovimiento(cDescuento2) + atMovimiento(cDescuento3) + atMovimiento(cDescuento4) + atMovimiento(cDescuento5)
  aImpuestos  = atMovimiento(cImpuesto1) + atMovimiento(cImpuesto2) + atMovimiento(cImpuesto3) - atMovimiento(cRetencion1) - atMovimiento(cRetencion2)
  aTotal      = atMovimiento(cTotal)

  // Si el  movimiento no es oculto no puede ser componente de paquete
  Si atMovimiento(cMovtoOculto) = 0
    Retorna lVenta
  // Si el movimiento es oculto 
  SiNo
    // Buscar el movimiento dueño del movimiento oculto
    atMovtoOwner.Busca[ @Str(atMovimiento(cIdMovtoOwner);0) ]
    Si !atMovtoOwner->Encontro  
      Retorna lVenta
    FinSi

    // Buscar el producto del movimiento dueño
    atPaqComponente.Busca[ @Str(atMovtoOwner(cIdProducto);0) ]
    Si !atPaqComponente->Encontro 
      Retorna lVenta
    FinSi

    // Si el producto del movimiento dueño no es paquete
    Si atPaqComponente(cTipoProducto) != 2
      lProporcion = atMovimiento(cUnidades) / atMovtoOwner(cUnidades)
      lVenta = atMovtoOwner(cNeto) - atMovtoOwner(cDescuento1) - atMovtoOwner(cDescuento2) - atMovtoOwner(cDescuento3) - atMovtoOwner(cDescuento4) - atMovtoOwner(cDescuento5)
      lNetoMovimiento = lProporcion * lVenta
      aPrecio = atMovtoOwner(cPrecio)
      aDescuentos = lProporcion * ( atMovtoOwner(cDescuento1) + atMovtoOwner(cDescuento2) + atMovtoOwner(cDescuento3) + atMovtoOwner(cDescuento4) + atMovtoOwner(cDescuento5) )
      aImpuestos = lProporcion * ( atMovtoOwner(cImpuesto1) + atMovtoOwner(cImpuesto2) + atMovtoOwner(cImpuesto3) - atMovtoOwner(cRetencion1) - atMovtoOwner(cRetencion2) )
      aTotal = lProporcion * atMovtoOwner(cTotal)
      Retorna lNetoMovimiento
    FinSi

    // Calcula el precio total de un paquete en base a la suma de los precios de sus componentes
    lVenta = atMovtoOwner(cNeto) - atMovtoOwner(cDescuento1) - atMovtoOwner(cDescuento2) - atMovtoOwner(cDescuento3) - atMovtoOwner(cDescuento4) - atMovtoOwner(cDescuento5)
    aPrecio = atMovtoOwner(cPrecio)
    aDescuentos = atMovtoOwner(cDescuento1) + atMovtoOwner(cDescuento2) + atMovtoOwner(cDescuento3) + atMovtoOwner(cDescuento4) + atMovtoOwner(cDescuento5)
    aImpuestos = atMovtoOwner(cImpuesto1) + atMovtoOwner(cImpuesto2) + atMovtoOwner(cImpuesto3) - atMovtoOwner(cRetencion1) - atMovtoOwner(cRetencion2)
    aTotal = atMovtoOwner(cTotal)

    Mientras[ atMovtoPaquete.Busca[ @Str(atMovtoOwner(cIdMovimiento);0) & '..' & @Str(atMovtoOwner(cIdMovimiento);0) ]; atMovtoPaquete->Encontro; atMovtoPaquete.Busca Siguiente ]

      // Si es movto de Remisión y es entrada no tomarlo en cuenta ( se toma la salida )
      Si atMovtoPaquete(cIdDocumentoDe) = 3 Y atMovtoPaquete(cAfectaExistencia) = 1
        Continua
      FinSi

      // Si es movto de Dev de Remisión y es salida no tomarlo en cuenta ( se toma la entrada )
      Si atMovtoPaquete(cIdDocumentoDe) = 6 Y atMovtoPaquete(cAfectaExistencia) = 2 
        Continua
      FinSi

      // Si es movto de Traspaso y es entrada no tomarlo en cuenta ( se toma la salida )
      Si atMovtoPaquete(cIdDocumentoDe) = 34 Y atMovtoPaquete(cAfectaExistencia) = 1
        Continua
      FinSi

      // Si es movto de detalle de características no tomarlo en cuenta
      Si atMovtoPaquete(cTipoTraspaso) = 4
        Continua
      FinSi

      // Buscar el producto componente del movimeinto
      atPaqComponente.Busca[ @Str(atMovtoPaquete(cIdProducto);0) ] 

      // Sumar el precio del componente al precio calculado del paquete
      lPrecioPaquete = lPrecioPaquete + ( atPaqComponente(cPrecio1) * atMovtoPaquete(cUnidades) )

    FinMientras  // Hay movimientos del paquete

    // Calular el precio del movimiento oculto en base a una proporción del precio 
    // del componente contra el precio calculado del paquete
    lProporcion = atProducto(cPrecio1) * atMovimiento(cUnidades) / lPrecioPaquete
    lNetoMovimiento = lVenta * lProporcion
    aPrecio = aPrecio * lProporcion
    aDescuentos = aDescuentos * lProporcion
    aImpuestos = aImpuestos * lProporcion
    aTotal = aTotal * lProporcion

  FinSi  // Si es movimiento oculto 

  Retorna lNetoMovimiento
FinFunc


// ==========================================================================================
// fCambiarMonedaDoctoAReporte()
// Descripcion : Esta función convierte el importe expresado en la moneda del documento a un 
//        importe expresado en la moneda del reporte.
// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
// Historia de modificaciones
// Fecha       Nombre   IDLotus     Descripcion
// 20/04/2005  LAP      N.D.3219    Expresar el importe recibido de la moneda del documento a
//                                  la moneda del reporte. Considerando que los documentos en 
//                                  moneda base tienen el tipo de cambio de la moneda del 
//                                  cliente a la moneda base.
// ==========================================================================================
DefFunc fCambiarMonedaDoctoAReporte(aTblDocto:TABLA; atTiposCambioLib:TABLA; aImporte; aIdMoneda; aIdMonedaCliente)
Local lImporteMonedaRpt, lImporteMonedaBase
Local lTipoCambioRpt

  // Por omisión el resultado es el saldo del documento
  lImporteMonedaRpt = aImporte
  
	// La moneda del reporte es distinta a la moneda del documento
  Si aTblDocto(cIdMoneda) != aIdMoneda
    // Expresar el saldo en la moneda base
    lImporteMonedaBase = aImporte
    Si aTblDocto(cIdMoneda) != 1
      lImporteMonedaBase = lImporteMonedaBase * aTblDocto(cTipoCambio)
    FinSi
    
    // Por omisión el tipo de cambio de la moneda base al reporte es 1.00
    lTipoCambioRpt = 1.00
    
     // Obtención del tipo de cambio de la moneda base a la moneda del reporte
    Si aIdMoneda != 1
      Si aIdMonedaCliente = aIdMoneda Y aTblDocto(cTipoCambio) > 1.00
        lTipoCambioRpt = aTblDocto(cTipoCambio)
      Sino
        lTipoCambioRpt = fTipoCambio(aTblDocto(cFecha);aIdMoneda;atTiposCambioLib)
      FinSi
    Finsi
    
    lImporteMonedaRpt = lImporteMonedaBase / lTipoCambioRpt
  Finsi
  
  // Regresar el saldo del documento en la moneda del reporte
  Retorna lImporteMonedaRpt
FinFunc

// ==========================================================================================
// fCambiarMonedaImporteAReporte()
// Descripcion : Esta función convierte el importe expresado en la moneda indicada a un 
//               importe expresado en la moneda del reporte.
// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
// Historia de modificaciones
// Fecha       Nombre   IDLotus     Descripcion
// 28/04/2005  LAP      N.D.3023    Expresar el importe recibido de la moneda indicada a la
//                                  moneda del reporte. Considerando que los documentos en 
//                                  moneda base tienen el tipo de cambio de la moneda del 
//                                  cliente a la moneda base.
// ==========================================================================================
DefFunc fCambiarMonedaImporteAReporte(atDocto:TABLA; atTiposCambioLib:TABLA; aImporte; aIdMonedaOrigen; aIdMonedaDestino; aIdMonedaCliente)
Local lImporteMonedaRpt, lImporteMonedaBase
Local lTipoCambioRpt

  // Por omisión el resultado es el saldo del documento
  lImporteMonedaRpt = aImporte
  
	// La moneda del reporte es distinta a la moneda del documento
  Si aIdMonedaOrigen != aIdMonedaDestino
    // Expresar el saldo en la moneda base
    lImporteMonedaBase = aImporte
    Si aIdMonedaOrigen != 1
      lImporteMonedaBase = lImporteMonedaBase * atDocto(cTipoCambio)
    FinSi
    
    // Por omisión el tipo de cambio de la moneda base al reporte es 1.00
    lTipoCambioRpt = 1.00
    
     // Obtención del tipo de cambio de la moneda base a la moneda del reporte
    Si aIdMonedaDestino != 1
      Si aIdMonedaCliente = aIdMonedaDestino Y atDocto(cTipoCambio) > 1.00
        lTipoCambioRpt = atDocto(cTipoCambio)
      Sino
        lTipoCambioRpt = fTipoCambio(atDocto(cFecha); aIdMonedaDestino; atTiposCambioLib)
      FinSi
    Finsi
    
    lImporteMonedaRpt = lImporteMonedaBase / lTipoCambioRpt
  Finsi
  
  // Regresar el saldo del documento en la moneda del reporte
  Retorna lImporteMonedaRpt
FinFunc


// ==========================================================================================
// fCambiarMonedaDoctoAReporte()
// Descripcion : Esta función convierte el importe expresado en la moneda del documento a un 
//        importe expresado en la moneda del reporte.
// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
// Historia de modificaciones
// Fecha       Nombre   IDLotus     Descripcion
// 20/04/2005  LAP      N.D.3219    Expresar el importe recibido de la moneda del documento a
//                                  la moneda del reporte. Considerando que los documentos en 
//                                  moneda base tienen el tipo de cambio de la moneda del 
//                                  cliente a la moneda base.
// ==========================================================================================
DefFunc fCambiarMonedaImporteLib(atTiposCambioLib:TABLA; aImporte; aIdMonedaImporte; aIdMonedaDestino; aTipoCambioDocto; aFecha; aIdMonedaCliente)
Local lImporteMonedaRpt, lImporteMonedaBase
Local lTipoCambioRpt

  // Por omisión el resultado es el saldo del documento
  lImporteMonedaRpt = aImporte
  
	// La moneda del reporte es distinta a la moneda del documento
  Si aIdMonedaImporte != aIdMonedaDestino
    // Expresar el saldo en la moneda base
    lImporteMonedaBase = aImporte
    Si aIdMonedaImporte != 1
      lImporteMonedaBase = lImporteMonedaBase * aTipoCambioDocto
    FinSi
    
    // Por omisión el tipo de cambio de la moneda base al reporte es 1.00
    lTipoCambioRpt = 1.00
    
     // Obtención del tipo de cambio de la moneda base a la moneda del reporte
    Si aIdMonedaDestino != 1
      Si aIdMonedaCliente = aIdMonedaDestino Y aTipoCambioDocto > 1.00
        lTipoCambioRpt = aTipoCambioDocto
      Sino
        lTipoCambioRpt = fTipoCambio(aFecha; aIdMonedaDestino; atTiposCambioLib)
      FinSi
    Finsi
    
    lImporteMonedaRpt = lImporteMonedaBase / lTipoCambioRpt
  Finsi
  
  // Regresar el saldo del documento en la moneda del reporte
  Retorna lImporteMonedaRpt
FinFunc

// ==========================================================================================
// fImporteMonedaReporte
// Descripcion : El importe lo expresa en la moneda del Reporte
// Entrada : 		aImporte 					: 	Importe a cambiar
//							aIdMonedaDocto		:   Id de la moneda en la que esta el importe
//          		aTipoCambioDocto	: 	Tipo de cambio a la moneda base
//							aFecha						:		Fecha para buscar el tipo de cambio de la moneda base
//																			a la moneda del reporte
// Salida  :		Importe expresado en la moneda del reporte
// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
// Historia de modificaciones
//  Fecha      Nombre   IDLotus                     Descripcion
// ==========================================================================================
Deffunc fImporteMonedaReporteLib(atTiposCambio:TABLA; aImporte; aIdMonedaImporte; aIdMonedaReporte; aTipoCambioMonedaBase; aFecha)
Local lImporteMonedaRpt

  lImporteMonedaRpt = aImporte

	Si aIdMonedaImporte != aIdMonedaReporte	 
	  // Si la moneda del importe es diferente a la del reporte
		// Transforma el importe a la moneda base tomando el tipo de cambio del documento
		lImporteMonedaRpt	= aImporte * aTipoCambioMonedaBase
		
		// Si la moneda del reporte es diferente a la base convierte tomando el tipo de cambio
		Si aIdMonedaReporte != 1				
			lImporteMonedaRpt = lImporteMonedaRpt/ fTipoCambio(aFecha; aIdMonedaReporte; atTiposCambio)
		FinSi				
	FinSi

	Retorna lImporteMonedaRpt
FinFunc


// ==========================================================================================
// fRedondeaImporte
// Descripcion : El importe lo redondea a los decimales indicados.
//               Utiliza el criterio "Según valor" para redondear
// Entrada : 		aImporte 					: Importe
//							aDecimales				: Numero de decimales
// Salida  :		Importe redondeado
// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
// Historia de modificaciones
//  Fecha      Nombre   IDLotus                     Descripcion
// ==========================================================================================
DefFunc fRedondeaImporteLib(aImporte; aDecimales)
Local lImporte, lImporteCadena, lIndice

  lImporte = aImporte
  lIndice = 1
  Mientras lIndice <= aDecimales 
    lImporte = lImporte * 10
    lIndice = lIndice + 1
  FinMientras
  lImporteCadena = @Str(lImporte;0)

  lImporte = @Val(lImporteCadena)
  lIndice = 1
  Mientras lIndice <= aDecimales 
    lImporte = lImporte / 10
    lIndice = lIndice + 1
  FinMientras

  Retorna lImporte
FinFunc

// *************************************************************************************************
//	Se aplica redondeo tal y como lo hace AdminPAQ.
//  Parámetros: aNumero    = Valor doble.
//              aDecimales = Decimales a los que se desea redondear.
//  Salidas : Regresa el valor redondeado.
// *************************************************************************************************
DEFFUNC fFormateaNumero(aNumero;aDecimales)
Local lNumeroComoCadena,lPos, lCont, lCadenaTmp
Local lImporteBase10             // Importe para la base 10.
Local lImporte                   // Importe con la base aplicada.
Local lEntero      // Parte entera del Importe.
Local lFraccion    // Parte fraccionaria del importe.
Local lEnteroTmp                 // Parte entera del importe de la fracción.
Local lFraccionTmp // Parte fraccionara del importe de la fracción.
Local lPotencia  // Para sacar la potencia en base 10 de acuerdo a los decimales.
Local lCiclo, lBuffer

  // Obtiene el importe de la base, 10X:                      Ejemplo: pow10(3) = 1000
  lImporteBase10 = 1
  lCiclo = 1
  Mientras lCiclo <= aDecimales
    lImporteBase10 = lImporteBase10 * 10
    lCiclo = lCiclo + 1
  FinMientras 

  // lImporte = aImporte * 10 * 10
  lImporte = aNumero
  lCiclo = 1
  Mientras lCiclo <= aDecimales
    lImporte = lImporte * 10
    lCiclo = lCiclo + 1 
  FinMientras

  // Obtiene la parte entera y la fracción del importe.       lFraccion = .4 lEntero   = 10017  
  lEntero = @int(lImporte)
  lBuffer = @val(lEntero)
  lFraccion = lImporte - lBuffer

  // Obtiene el valor de la fraccion como entero para saber si se incrementa en 1 a la parte entera del importe.
  // Ejemplo: .4 * 10 = 4 
  lFraccionTmp = lFraccion * 10
  lEnteroTmp = @int(lFraccionTmp)
  
  // Si la fracción es mayor a 5 incrementa uno para que no haya pérdida en la fracción o valor anterior
  Si @abs(lEnteroTmp) >= 5 
    lEntero = lEntero + @abs(lEnteroTmp) / lEnteroTmp // Considera el signo
  FinSi
  
  // Obtiene el nuevo importe redondead Ejemplo: 10017 / 1000 = 10.017 
  // Si hizo esta validación por la posible pérdida que genera el proceso y esto se hace por que en 
  //  los redondeos nunca el resultado final deber ser menor al inicial
  lBuffer = lEntero / lImporteBase10
  Retorna lBuffer
FINFUNC

// ********************************************************************************************************************
//  fVerificaUnidad
//  Esta función verifica y corrige cualquier inconsistencia del parámetro del reporte "pUnidad" almacenado en la 
//  variable global "lUnidad", en función del método de costeo del producto y del valor del campo "cIdUnidadNoConvertible".
//
//  Parámetros
//  Salidas
// ********************************************************************************************************************
DefFunc fVerificaUnidad(atProducto: Tabla)
  pUnidad = gUnidad
  pIdUnidad = gIdUnidad

  // Si se solicita el reporte en unidades no convertibles y el producto no tiene unidad no convertible
  Si pUnidad = 1 y atProducto(cIdUnidadNoConvertible) = 0
    pUnidad = 0 // Se toma la unidad base del producto
  FinSi
  Si pUnidad = 3 
    Si atProducto(cIdUnidadCompra) = 0
      pUnidad = 0
    Sino
      pUnidad = 2
      pIdUnidad = atProducto(cIdUnidadCompra)
    FinSi
  FinSi
  Si pUnidad = 4 
    Si atProducto(cIdUnidadVenta) = 0
      pUnidad = 0
    Sino
      pUnidad = 2
      pIdUnidad = atProducto(cIdUnidadVenta)
    FinSi
  FinSi
FinFunc
