// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
//
//	StockExcedente.rpt
//	Nombre del reporte en el sistema: Stock Máximo (Excedente de acuerdo 
//  al costo)
//
//  Sistema: AdminPAQ v1.1
// 
//	Este reporte perteneciente al módulo de inventarios, despliega el stock
//  máximo, mínimo y la existencia de productos en un almacén o global.
//
//	Consideraciones especiales:
//  Este reporte aunque está programado como uno solo, representa en realidad
//  cuatro reportes en el ámbito del sistema:
//    Stock Máximo Global (Excedente de acuerdo al último costo)
//    Stock Máximo Global (Excedente de acuerdo al costo promedio)
//    Stock Máximo por Almacén (Excedente de acuerdo al último costo)
//    Stock Máximo por Almacén (Excedente de acuerdo al costo promedio)
//  
//	Elaboró:    Eduardo Pérez Figueroa
//	Fecha: 	    03 de Septiembre del 2001
//
//  Historia de revisiones:
//	Se incluyeron totales por reporte
//	Autor: Gabriela Cuevas
//	Fecha: 15 de febrero del 2002
//
//	Historial de Cambios en LN
//	Fecha				Id LotusNotes		Descripcion
//  05/06/2002	N.D.600					Los productos que manejan características deben 
//															de presentar sus valores de características 
//															desglozados considerando la combinación de
//															caracteristicas con series y/o capas (LAP).
//															- fProcesaGlobal 		-> Stock máximo global
//															- fProcesaPorAlmacen-> Stock máximo por Almacen
//	Se incluyo la solicitud de cambio 150 (existencias)
//	Autor: Hector Valencia
//	Fecha: 29 de julio del 2002
//  22/05/2007 JAAM     N.C.147     Esquema de adicion de unidades de compra y venta  
// <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<


// *****************************************************************************
// Funciones de librería

Incluye LibAdminPAQ.Rpt
Incluye LibInventarios.Rpt


// *****************************************************************************
//	Parámetros del reporte:

Parametro pFechaCorte = '20021231'      // Fecha de corte o final del reporte
Parametro pProductoInicial = ' '        // Código del producto inicial del rango (' ' = Todos)
Parametro pProductoFinal = ' '          // Código del producto final del rango (' ' = Todos)
Parametro pAlmacenInicial = ' '         // Código del almacén inicial del rango (' ' = Todos)
Parametro pAlmacenFinal = ' '           // Código del almacén final del rango (' ' = Todos)
Parametro pImprimirProductos = 0        // Productos a imprimir:
                                        // 0 = Todos
                                        // 1 = Existencias por encima del máximo
Parametro pEstadoProducto = 0           // Incluir productos con estado:
                                        // 0 = Todos
                                        // 1 = Activos
                                        // 2 = Inactivos
Parametro pCaracteristicas = 1          // Imprimir características de productos
                                        // 0 = No imprimir características
                                        // 1 = Imprimir características
Parametro pUnidad = 0                   // Unidad de medida y peso para expresar el reporte
                                        // 0 = Unidad base del producto
                                        // 1 = Unidad no equivalente del producto
                                        // 2 = Unidad equivalente (ver parámetro pIdUnidad)
                                        // 3 = Unidad de Compra
                                        // 4 = Unidad de Venta
Parametro pIdUnidad = 2                 // Id de la unidad equivalente
Parametro pIdClase1 = 0                 // Clasificaciones del produto (0 = Ninguna)
Parametro pIdClase2 = 0                 // Estos seis parámetros corresponden a las seis clasificaciones
Parametro pIdClase3 = 0                 // del producto, y pueden contener una lista de IDs
Parametro pIdClase4 = 0                 // de clasificaciones separados por coma.
Parametro pIdClase5 = 0               
Parametro pIdClase6 = 0               
Parametro pIdMoneda = 1                 // Id de la moneda a expresar el reporte
Parametro pTipoReporte = 0              // Tipo de Reporte (este parámetro es interno a AdminPAQ)
                                        // 0 = Stock Máximo Global (Excedente de acuerdo al costo promedio)
                                        // 1 = Stock Máximo por almacén (Excedente de acuerdo al costo promedio)
                                        // 2 = Stock Máximo Global (Excedente de acuerdo al último costo)
                                        // 3 = Stock Máximo por almacén (Excedente de acuerdo al último costo)

// *****************************************************************************
//	Tablas a utilizar e Indices de búsqueda por omisión

UsaEmpresa tEmpresa;'ADMINPAQW'                   // Parámetros

UsaTabla tProducto = tEmpresa['MGW10005']         // Productos
tProducto.Usaindice['iCodigoTipo']                // cCodigoProducto+cTipoProducto

UsaTabla tAlmacen = tEmpresa['MGW10003']          // Almacenes
tAlmacen.Usaindice['cCodigoAlmacen']              // cCodigoAlmacen

UsaTabla tDetalleProducto = tEmpresa['MGW10004']  // Productos
tDetalleProducto.Usaindice['iProdPadreValoresCarac']  // cIdProductoPadre+cIdValorCaracteristica1+cIdValorCaracteristica2+cIdValorCaracteristica3

UsaTabla tDocumento = tEmpresa['MGW10008']        // Documentos
tDocumento.Usaindice['']                          // Indice primario: cIdDocumento

UsaTabla tMovimiento = tEmpresa['MGW10010']       // Movimientos
tMovimiento.Usaindice['iProductoFechaAfecta']     // cIdProducto+cFecha+cAfectaExistencia

UsaTabla tMovtoOwner = tEmpresa['MGW10010'] Alias 'MovtoOwner'  // Movimientos
tMovtoOwner.Usaindice['']                         // Indice primario: cIdMovimiento

UsaTabla tMovtoSinAfectar = tEmpresa['MGW10010'] Alias 'MovtoSinAfectar'  // Movimientos sin afectar
tMovtoSinAfectar.Usaindice['iProductoDoctoDeAfecFecha']    // cIdProducto+cIdDocumentoDe+cAfectadoInventario+cFecha

UsaTabla tEjercicio = tEmpresa['MGW10031']        // Ejercicios
tEjercicio.Usaindice['cNumeroEjercicio']          // cNumeroEjercicio

UsaTabla tExistencia = tEmpresa['MGW10030']       // Existencia
tExistencia.Usaindice['iProdEjerAlmacenTipo']     // cIdProducto+cIdEjercicio+cIdAlmacen+cTipoExistencia

UsaTabla tExistenciaMinMax = tEmpresa['MGW10016'] // Existencia mínima y máxima
tExistenciaMinMax.Usaindice['iAlmacenProducto']   // cIdAlmacen+cIdProducto

UsaTabla tCostoHistorico = tEmpresa['MGW10017']   // Costo histórico
tCostoHistorico.Usaindice['iProdAlmacenFecha']    // cIdProducto+cIdAlmacen+cFechaCostoH+cTipoCosto

UsaTabla tMoneda = tEmpresa['MGW10034']           // Monedas
tMoneda.Usaindice['']                             // Indice primario: cIdMoneda

UsaTabla tTipoCambio = tEmpresa['MGW10035']       // Tipo de cambio
tTipoCambio.Usaindice['iMonedaFecha']             // cIdMoneda+cFecha

UsaTabla tUnidad = tEmpresa['MGW10026']           // Unidades de peso y medida
tUnidad.Usaindice['']                             // Indice primario: cIdUnidad

UsaTabla tEquivalencia = tEmpresa['MGW10027']     // Equivalencia de unidades de peso y medida
tEquivalencia.Usaindice['']                       // Indice primario: cIdUnidad1+cIdUnidad2

UsaTabla tCaracteristica = tEmpresa['MGW10021']       // Características
tCaracteristica.Usaindice['']                         // Indice primario: cIdPadreCaracteristica

UsaTabla tValorCaracteristica = tEmpresa['MGW10022']  // Valores de características
tValorCaracteristica.Usaindice['']                    // Indice primario: cIdValorCaracteristica

UsaTabla tValoresDeClasif = tEmpresa['MGW10020']
tValoresDeClasif.UsaIndice['']                    // Indice primario: cIdValorClasificacion

UsaTabla tParametros = tEmpresa['MGW10000']       // Parámetros de la empresa
tParametros.Usaindice['']                         // Indice primario: cIdEmpresa


// *****************************************************************************
// Definiciones y asumidos globales para el reporte

// Ancho de columnas = caracteres / 17 * 2.54
// Se consideran columnas intermedias de un espacio
// C0 (Código del producto o almacén) = 14 caracteres
// C2 (Nombre del producto o almacén) = 39 (global) o 46 caracteres (por almacén)
// C4 (Unidad de medida o peso) = 3 caracteres
// C6 (Stock mínimo) = 14 caracteres
// C8 (Existencia) = 14 caracteres
// C10 (Costo promedio o último costo) = 14 caracteres
// C12 (Costo excedente) = 14 caracteres
// C14 (Observaciones) = 4 caracteres
Si pTipoReporte = 0 o pTipoReporte = 2 // global
  Columnas 16;2.09,0.15,5.83,0.15,0.45,0.15,2.09,0.15,2.09,0.15,2.09,0.15,2.09,0.15,0.70,0.15 
Sino // por almacén
  Columnas 16;2.09,0.15,6.87,0,0,0.15,2.09,0.15,2.09,0.15,2.09,0.15,2.09,0.15,0
FinSi

Usaletra 'Arial'
Altura 9
[C6,C8,C10,C12].Coma


// *****************************************************************************
//	Variables de ámbito global:

gIdioma = 0             // Idioma del reporte
                        // 0 = Español
                        // 1 = Inglés
// Las siguientes variables se utilizan en el cuerpo principal del reporte
gPeriodo = 0            // Numero de período que corresponde a la fecha de corte del reporte
gIdEjercicio = 0        // ID del ejercicio que contiene la fecha de corte del reporte
gFechaCorteMasUnDia = 0 // Fecha de corte más un día
gUnidad = pUnidad         // Almacenamiento del parámetro pUnidad
gIdUnidad = pIdUnidad         // Almacenamiento del parámetro pIdUnidad
gMascarillaProducto = ''  // Mascarilla para el código del producto
gMascarillaAlmacen = ''   // Mascarilla para el código del almacén
gObservaciones = 0        // Imprimir notas de las observaciones de errores en costos
gTotalReporte = 0.00

// *****************************************************************************
//  fConfiguraDecimales
//
//	Configura el número de decimales de las columnas de importes, basado en
//  en los parámetros de la empresa.
//
//  Parámetros 
//
//  Salidas 
// *****************************************************************************
DefFunc fConfiguraDecimales()

  // Busca el registro de la empresa
  tParametros.Busca Primero
  Si tParametros->Encontro // Si lo encontró
    [C6,C8].Decimales tParametros(cDecimalesUnidades) // Decimales para existencias
    [C10,C12].Decimales tParametros(cDecimalesCostos) // Decimales para costos
    gMascarillaAlmacen = tParametros(cMascarillaAlmacen) // mascarilla para el código del almacen
    gMascarillaProducto = tParametros(cMascarillaProducto)  // mascarilla para el código del producto
  Sino // Si no encontró el registro de la empresa
    [C6,C8,C10,C12].Decimales 2
  FinSi
FinFunc


// *****************************************************************************
//  fImprimeAlmacen
//
//	Esta función imprime los datos del almacén para el reporte por almacén.
//
//  Parámetros 
//
//  Salidas 
// *****************************************************************************
DefFunc fImprimeAlmacen(atAlmacen: Tabla; aMaximo; aExistencia; aCostoTotal; aCostoExcedente)
Local vImprime, lCodigoEditado

  vImprime = ' '[C0..C15]

  // Codigo y nombre del almacén
  lCodigoEditado = fMascarilla(atAlmacen(cCodigoAlmacen); gMascarillaAlmacen)
  vImprime = @Col(lCodigoEditado;0)[C0]
  vImprime = atAlmacen(cNombreAlmacen)[C2]
    
  vImprime = @Col(aMaximo;0)[C6]
  vImprime = @Col(aExistencia;0)[C8]
  vImprime = @Col(aCostoTotal;0)[C10]
  vImprime = @Col(aCostoExcedente;0)[C12]

  Lista vImprime
FinFunc


// *****************************************************************************
//  fImprimeCaracteristicas
//
//	Esta función imprime los datos de una combinación de características.
//
//  Parámetros 
//
//  Salidas 
// *****************************************************************************
DefFunc fImprimeCaracteristicas(atDetalleProducto: Tabla; aImprimirExistencias; aMaximo; aExistencia; aCostoTotal; aCostoExcedente; aTextoCaracteristicas)
Local vImprime, lValoresCaracteristicas, lTextoCaracteristicas

  // Procesa solo si existen valores de características
  Si atDetalleProducto(cIdValorCaracteristica1) o atDetalleProducto(cIdValorCaracteristica2) o atDetalleProducto(cIdValorCaracteristica3)
    vImprime = ' '[C0..C15]
    lValoresCaracteristicas = ' '
    lTextoCaracteristicas = ' '

    // Lee los valores de las características
    // Caracteristica 1
    Si atDetalleProducto(cIdValorCaracteristica1)
      tValorCaracteristica.Busca[@Str(atDetalleProducto(cIdValorCaracteristica1);0)]
      Si tValorCaracteristica->Encontro
        lValoresCaracteristicas = tValorCaracteristica(cValorCaracteristica)
        // Verifica si leer el nombre de la caracteristica
        Si !aTextoCaracteristicas
          tCaracteristica.Busca[@Str(tValorCaracteristica(cIdPadreCaracteristica);0)]
          Si tCaracteristica->Encontro
            lTextoCaracteristicas = tCaracteristica(cNombreCaracteristica)
          FinSi
        FinSi
      FinSi
    FinSi

    // Caracteristica 2
    Si atDetalleProducto(cIdValorCaracteristica2)
      tValorCaracteristica.Busca[@Str(atDetalleProducto(cIdValorCaracteristica2);0)]
      Si tValorCaracteristica->Encontro
        lValoresCaracteristicas = lValoresCaracteristicas & '-' & tValorCaracteristica(cValorCaracteristica)
        // Verifica si leer el nombre de la caracteristica
        Si !aTextoCaracteristicas
          tCaracteristica.Busca[@Str(tValorCaracteristica(cIdPadreCaracteristica);0)]
          Si tCaracteristica->Encontro
            lTextoCaracteristicas = lTextoCaracteristicas & '-' & tCaracteristica(cNombreCaracteristica)
          FinSi
        FinSi
      FinSi
    FinSi

    // Caracteristica 3
    Si atDetalleProducto(cIdValorCaracteristica3)
      tValorCaracteristica.Busca[@Str(atDetalleProducto(cIdValorCaracteristica3);0)]
      Si tValorCaracteristica->Encontro
        lValoresCaracteristicas = lValoresCaracteristicas & '-' & tValorCaracteristica(cValorCaracteristica)
        // Verifica si leer el nombre de la caracteristica
        Si !aTextoCaracteristicas
          tCaracteristica.Busca[@Str(tValorCaracteristica(cIdPadreCaracteristica);0)]
          Si tCaracteristica->Encontro
            lTextoCaracteristicas = lTextoCaracteristicas & '-' & tCaracteristica(cNombreCaracteristica)
          FinSi
        FinSi
      FinSi
    FinSi

    Si !aImprimirExistencias
      Negritas TRUE
    FinSi     
    // Imprime características y valores
    // Verifica si se imprime la leyenda 
    Si !aTextoCaracteristicas
      vImprime = @Substr(lTextoCaracteristicas;0;14)[C0]
      // Enciende bandera de que el texto ya fue impreso
      aTextoCaracteristicas = 1
    FinSi
    vImprime = @Col(lValoresCaracteristicas;0)[C2]

    // Verifica si imprimir los datos de existencias
    Si aImprimirExistencias 
      vImprime = @Col(aMaximo;0)[C6]
      vImprime = @Col(aExistencia;0)[C8]
      vImprime = @Col(aCostoTotal;0)[C10]
      vImprime = @Col(aCostoExcedente;0)[C12]
    FinSi
  
    Lista vImprime
    Negritas FALSE

  FinSi

FinFunc


// *****************************************************************************
//  fImprimeDatosProducto
//
//	Esta función imprime los datos del producto para el reporte por Almacén.
//
//  Parámetros 
//
//  Salidas 
// *****************************************************************************
DefFunc fImprimeDatosProducto(atProducto: Tabla)
Local vImprime, vImprime2, vImprime3, lCodigoEditado
Local lFechaSinAfectar, lExistenciaCongelada

  // Redefine el ancho de las columnas
  Columnas 16;2.70,0.15,10.46,0.15

  vImprime = ' '[C0..C15]

  NEGRITAS TRUE

  vImprime = 'Producto: '[C0]
  lCodigoEditado = fMascarilla(atProducto(cCodigoProducto); gMascarillaProducto)
  vImprime = @Col(lCodigoEditado;0)[C2]
  Lista vImprime
  
  vImprime = 'Nombre: '[C0]
  vImprime = atProducto(cNombreProducto)[C2]
  Lista vImprime
  
  // Imprime unidad de medida y peso
  Escoge pUnidad
    Caso 0 // Unidad base
      tUnidad.Busca[@Str(atProducto(cIdUnidadBase);0)]      
    FinCaso
    Caso 1 // Unidad no equivalente
      tUnidad.Busca[@Str(atProducto(cIdUnidadNoConvertible);0)]        
    FinCaso
    Caso 2 // Unidad equivalente
      tEquivalencia.Busca[atProducto(cIdUnidadBase) & ':' & pIdUnidad]
      Si tEquivalencia->Encontro // Si encontró una equivalencia con la unidad base
        tUnidad.Busca[@Str(pIdUnidad;0)] // Busca la unidad equivalente
      Sino // Si no encontró la unidad equivalente, Busca la unidad base
        tUnidad.Busca[@Str(atProducto(cIdUnidadBase);0)]
      FinSi
    FInCaso
  FinEscoge
  Si tUnidad->Encontro // Encontró el registro de la unidad de medida y peso
    Si tUnidad(cIdUnidad) y tUnidad(cDespliegue) != ''
      vImprime = 'Unidad:'[C0]
      vImprime = tUnidad(cDespliegue)[C2]
      Lista vImprime
    FinSi
  FinSi
 
  // HVA - SC 150 Verificar si el producto tiene algún error y sí es así 
  // desplegar el error.
  vImprime = ''

  // Verificar si hay existencias congeladas
  lExistenciaCongelada = fVerificarExistenciaCongelada(atProducto(cIdProducto); 0; tParametros(cIdEjercicioActual); tExistencia)

  // Si no hay existencias congeladas verificar si hay movimientos sin afectar.
  // Cuando hay existencias congeladas no se revisan movimientos sin afectar 
  // porque pueden deberse a dicha existencia congelada
  Si !lExistenciaCongelada
    // Verificar si hay movimientos sin afectar y en que fecha
    lFechaSinAfectar = fVerificarMovtosSinAfectar(atProducto(cIdProducto); tMovtoSinAfectar; tMovtoOwner; tDocumento)
  SiNo  
    vImprime =  'Advertencia:'[C0]
    vImprime =  '*** En proceso de levantamiento de inventario físico'[C2]
    vImprime2 = '*** Se recomienda aplicar el inventario físico'[C2]
    Lista vImprime
    Lista vImprime2
    lFechaSinAfectar = ''
  FinSi

  // Si hay error de costos y no hay movtos sin afectar O
  // el error de costos ocurrió antes de los movtos sin afectar
  // entonces se despliega el error de costos
  Si ( atProducto(cErrorCosto) Y !lFechaSinAfectar) O ( lFechaSinAfectar Y atProducto(cErrorCosto) Y atProducto(cFechaErrorCosto) < lFechaSinAfectar )
    vImprime = 'Error en costos:'[C0]
    Escoge atProducto(cErrorCosto)
      Caso 1
        vImprime =  '*** Captura en desorden de entradas'[C2]
        vImprime2 = '*** Se recomienda ejecutar el proceso "Recosteo" para éste producto'[C2]
        vImprime3 = ''
      FinCaso
      Caso 2
        vImprime =  '*** Captura en desorden de salidas'[C2]
        vImprime2 = '*** Se recomienda ejecutar el proceso "Recosteo" para éste producto'[C2]
        vImprime3 = ''
      FinCaso
      Caso 3
        vImprime =  '*** Existencias negativas'[C2]
        vImprime2 = '*** Se recomienda registrar las entradas necesarias'[C2]
        vImprime3 = '*** y ejecutar el proceso "Recosteo" para éste producto'[C2]
      FinCaso
      Caso 4
        vImprime =  '*** Series sin movimiento de entrada'[C2]
        vImprime2 = '*** Se recomienda registrar la entrada de las series pendientes'[C2]
        vImprime3 = '*** y ejecutar el proceso "Recosteo" para éste producto'[C2]
      FinCaso
      Caso 5
        vImprime =  '*** Series sin costo'[C2]
        vImprime2 = '*** Se recomienda ejecutar el proceso "Recosteo" para éste producto'[C2]
        vImprime3 = ''
      FinCaso
    FinEscoge
    lFechaEditada = fFormateaFecha(atProducto(cFechaErrorCosto); gIdioma)
    vImprime = @Col(vImprime;2) & ' a partir del ' & @Col(lFechaEditada;0)[C2]
    Lista vImprime
    Lista vImprime2
    Si vImprime3 
      Lista vImprime3
    FinSi
  FinSi  // Si hay error de costos

  // Si hay movtos sin afectar y no hay errores de costos O
  // los movtos sin afectar ocurrieron antes del error de costos
  // entonces se despliega el error de movtos sin afectar
  Si ( lFechaSinAfectar Y !atProducto(cErrorCosto) ) O ( lFechaSinAfectar Y atProducto(cErrorCosto) Y lFechaSinAfectar <= atProducto(cFechaErrorCosto) )
    vImprime = 'Error en costos:'[C0]
    vImprime = '*** Movimientos sin afectar'[C2]
    lFechaEditada = fFormateaFecha(lFechaSinAfectar; gIdioma)
    vImprime = @Col(vImprime;2) & ' a partir del ' & @Col(lFechaEditada;0)[C2]
    vImprime2 = '*** Se recomienda consultar el reporte "Movimientos sin Afectar" para éste producto'[C2]
    vImprime3 = '*** Se recomienda ejecutar el proceso "Recosteo" para éste producto'[C2]
    Lista vImprime
    Lista vImprime2
    Lista vImprime3
  FinSi  // Hay error de movimientos sin afectar
  SaltaLin
  // FIN HVA - SC 150

  NEGRITAS FALSE

  // Regresa las columnas a su configuración original
  Columnas 16;2.09,0.15,6.87,0,0,0.15,2.09,0.15,2.09,0.15,2.09,0.15,2.09,0.15,0
FinFunc


// *****************************************************************************
//  fImprimeProducto
//
//	Esta función imprime los datos del producto para el reporte global.
//
//  Parámetros 
//
//  Salidas 
// *****************************************************************************
DefFunc fImprimeProducto(atProducto: Tabla; aImprimirExistencias; aMaximo; aExistencia; aCostoTotal; aCostoExcedente)
Local vImprime, lCodigoEditado, lFechaSinAfectar, lExistenciaCongelada

  vImprime = ' '[C0..C15]

  // Codigo y nombre del producto
  lCodigoEditado = fMascarilla(atProducto(cCodigoProducto); gMascarillaProducto)
  vImprime = @Col(lCodigoEditado;0)[C0]
  vImprime = atProducto(cNombreProducto)[C2]
    
  // Imprime unidad de medida y peso
  Escoge pUnidad
    Caso 0 // Unidad base
      tUnidad.Busca[@Str(atProducto(cIdUnidadBase);0)]      
    FinCaso
    Caso 1 // Unidad no equivalente
      tUnidad.Busca[@Str(atProducto(cIdUnidadNoConvertible);0)]        
    FinCaso
    Caso 2 // Unidad equivalente
      tEquivalencia.Busca[atProducto(cIdUnidadBase) & ':' & pIdUnidad]
      Si tEquivalencia->Encontro // Si encontró una equivalencia con la unidad base
        tUnidad.Busca[@Str(pIdUnidad;0)] // Busca la unidad equivalente
      Sino // Si no encontró la unidad equialente, Busca la unidad base
        tUnidad.Busca[@Str(atProducto(cIdUnidadBase);0)]
      FinSi
    FInCaso
  FinEscoge 
  Si tUnidad->Encontro y tUnidad(cIdUnidad) != 0 // Encontró el registro de la unidad de medida y peso
    vImprime = tUnidad(cDespliegue)[C4]
  FinSi

  // Verifica si imprimir los datos de existencias
  Si aImprimirExistencias 
    vImprime = @Col(aMaximo;0)[C6]
    vImprime = @Col(aExistencia;0)[C8]
    vImprime = @Col(aCostoTotal;0)[C10]
    vImprime = @Col(aCostoExcedente;0)[C12]
  FinSi

  // Observaciones
//  Si atProducto(cErrorCosto) // Verifica si el producto tiene errores en costos
//    vImprime = '(' & atProducto(cErrorCosto)& ')' [C14] 
//    gObservaciones = 1
//  FinSi

  // HVA - SC 150 Verificar si el producto tiene algún error y sí es así 
  // desplegar el error.
  vImprime = '' [C14] 

  // Verificar si hay existencias congeladas
  lExistenciaCongelada = fVerificarExistenciaCongelada(atProducto(cIdProducto); 0; tParametros(cIdEjercicioActual); tExistencia)

  // Si no hay existencias congeladas verificar si hay movimientos sin afectar.
  // Cuando hay existencias congeladas no se revisan movimientos sin afectar 
  // porque pueden deberse a dicha existencia congelada
  Si !lExistenciaCongelada
    // Verificar si hay movimientos sin afectar y en que fecha
    lFechaSinAfectar = fVerificarMovtosSinAfectar(atProducto(cIdProducto); tMovtoSinAfectar; tMovtoOwner; tDocumento)
  SiNo  
    vImprime = '(7)' [C14] 
    gObservaciones = 1
    lFechaSinAfectar = ''
  FinSi

  // Si hay error de costos y no hay movtos sin afectar O
  // el error de costos ocurrió antes de los movtos sin afectar
  // entonces se despliega el error de costos
  Si ( atProducto(cErrorCosto) Y !lFechaSinAfectar) O ( lFechaSinAfectar Y atProducto(cErrorCosto) Y atProducto(cFechaErrorCosto) < lFechaSinAfectar )
    vImprime = '(' & atProducto(cErrorCosto)& ')' [C14] 
    gObservaciones = 1
  FinSi  // Si hay error de costos

  // Si hay movtos sin afectar y no hay errores de costos O
  // los movtos sin afectar ocurrieron antes del error de costos
  // entonces se despliega el error de movtos sin afectar
  Si ( lFechaSinAfectar Y !atProducto(cErrorCosto) ) O ( lFechaSinAfectar Y atProducto(cErrorCosto) Y lFechaSinAfectar <= atProducto(cFechaErrorCosto) )
    vImprime = '(6)' [C14] 
    gObservaciones = 1
  FinSi  // Hay error de movimientos sin afectar
  // Fin HVA - SC 150 

  Lista vImprime
FinFunc

// *****************************************************************************
//  fTotalReporte
//
//	Esta función imprime los totales por reporte
//
//  Parámetros 
//
//  Salidas 
// *****************************************************************************
DefFunc fTotalReporte(aTotal)
Local vImprime, lSubRaya


  vImprime     = ' '[C0..C12]
  vImprime     = @Col(aTotal;6)[C6]
  vImprime     = @Col(aTotal;8)[C8]
  vImprime     = @Col(aTotal;10)[C10]
  vImprime     = @Col(aTotal;12)[C12]
  lSubRaya = ''
 	lSubRaya = '================='[C6,C8,C10,C12]
  Imprime lSubRaya
  Lista vImprime
  Saltalin
 
FinFunc

// *****************************************************************************
//  fImprimeTotales
//
//	Esta función imprime los totales por producto.
//
//  Parámetros 
//
//  Salidas 
// *****************************************************************************
DefFunc fImprimeTotales(aMaximo; aExistencia; aCostoTotal; aCostoExcedente)
Local vImprime

	Texto C6;'@L{6}'
	Texto C8;'@L{8}'
	Texto C10;'@L{10}'
	Texto C12;'@L{12}'
	Imprime
  
  vImprime = ' '[C0..C15]
  vImprime = '^Total producto:'[C2]
  vImprime = @Col(aMaximo;0)[C6]
  vImprime = @Col(aExistencia;0)[C8]
	vImprime = @Col(aCostoTotal;0)[C10]
	vImprime = @Col(aCostoExcedente;0)[C12]

  Lista vImprime

	gTotalReporte[6]  = @Col(gTotalReporte;6)  + @Col(vImprime;6)
	gTotalReporte[8]  = @Col(gTotalReporte;8)  + @Col(vImprime;8)
	gTotalReporte[10] = @Col(gTotalReporte;10) + @Col(vImprime;10)
	gTotalReporte[12] = @Col(gTotalReporte;12) + @Col(vImprime;12)

  // Si el reporte es por almacén, brinca un renglón al totalizar el producto
  Si pTipoReporte = 1 o pTipoReporte = 3
    Saltalin
  FinSi
FinFunc


// *****************************************************************************
//  fProcesaGlobal
//
//  Esta función procesa el reporte de stock máximo global.
//
//  Parámetros 
//
//  Salidas 
//
//  05/06/2002	N.D.600					Los productos que manejan características deben 
//															de presentar sus valores de características 
//															desglozados considerando la combinación de
//															caracteristicas con series y/o capas (LAP).
// *****************************************************************************
DefFunc fProcesaGlobal(atProducto: Tabla)
Local lMinimo, lMaximo, lExistencia, lExistenciaBase, lCosto, lCostoExcedente
Local lTotalMaximo, lTotalExistencia, lTotalCostoTotal, lTotalCostoExcedente
Local lDatosProducto, lTextoCaracteristicas
Local lTmpMinimo, lTmpMaximo, lCostoTotal
// N.D.600
Local lTieneCaracteristicas

	// N.D.600 Inicio
	// Determina si el producto utiliza características a través de restar los 
	//	demas controles de existencia
	lTienecaracteristicas = atProducto(cControlExistencia)
	// Eliminación del control de existencia por: Unidades de entrada
	//																						kProdControlExistUnidadesEntrada 32
	Si ( lTieneCaracteristicas > 31 )
		lTieneCaracteristicas = lTieneCaracteristicas - 32
	FinSi
	// Eliminación del control de existencia por: Lotes
	//																						kProdControlExistLotes           16
	Si ( lTieneCaracteristicas > 15 )
		lTieneCaracteristicas = lTieneCaracteristicas - 16
	FinSi
	// Eliminación del control de existencia por: Pedimentos
	//																						kProdControlExistPedimentos       8
	Si ( lTieneCaracteristicas > 7 )
		lTieneCaracteristicas = lTieneCaracteristicas - 8
	FinSi
	// Eliminación del control de existencia por: Series
	//																						kProdControlExistSeries           4
	Si ( lTieneCaracteristicas > 3 )
		lTieneCaracteristicas = lTieneCaracteristicas - 4
	FinSi
	// Eliminación del control de existencia por: Unidades
	//																						kProdControlExistUnidades         1
	Si ( lTieneCaracteristicas > 2 )
		lTieneCaracteristicas = lTieneCaracteristicas - 1
	FinSi
	// N.D.600 Fin

  // Verifica el stock máximo y existencia global del producto
  // Con éste dato discriminamos el producto conforme al parámetro pImprimirProductos
	// N.D.600 Inicio
	// Si el producto no tiene caracteristicas, buscamos el máximo en el producto
	Si lTieneCaracteristicas != 2
  // Si atProducto(cControlExistencia) != 2 y atProducto(cControlExistencia) != 3 
	// N.D.600 Fin
    fStockMinMax(atProducto(cIdProducto); 0; pUnidad; atProducto(cIdUnidadBase); pIdUnidad; &lMinimo; &lMaximo; tExistenciaMinMax; tEquivalencia)
  Sino // Si el producto tiene características, buscamos el máximo en cada detalle de características
    lMaximo = 0
    Mientras [ tDetalleProducto.Busca[@Str(atProducto(cIdProducto);0)]; tDetalleProducto->Encontro; tDetalleProducto.Busca Siguiente[1] ]
      fStockMinMax(tDetalleProducto(cIdProducto); 0; pUnidad; atProducto(cIdUnidadBase); pIdUnidad; &lTmpMinimo; &lTmpMaximo; tExistenciaMinMax; tEquivalencia)
      lMaximo = lMaximo + lTmpMaximo
    FinMientras
  FinSi
  lExistencia = fExistenciaInicialProducto(atProducto(cIdProducto); 0; gFechaCorteMasUnDia; gIdEjercicio; gPeriodo; pUnidad; atProducto(cIdUnidadBase); pIdUnidad; tMovimiento; tExistencia; tEquivalencia)
  // Verifica si se imprime el producto según parámetro del reporte
  Si pImprimirProductos = 1 y lExistencia <= lMaximo // Existencias por encima del máximo
    Retorna
  FinSi

  // calcula el costo del producto
  // El primer parámetro de la siguiente función es el método de costeo y los valores permitidos son:
  // 1 = Costo promedio
  // 3 = Ultimo costo
  lCosto = fCostoHistorico(atProducto(cIdProducto); 0; pFechaCorte; pTipoReporte = 0 o pTipoReporte = 1? 1 : 3; tCostoHistorico; tTipoCambio; pIdMoneda)
  // Si el reporte se pidio en unidades no convertibles o equivalentes
  Si pUnidad != 0
    // calcula la existencia en la unidad base
    lExistenciaBase = fExistenciaInicialProducto(atProducto(cIdProducto); 0; gFechaCorteMasUnDia; gIdEjercicio; gPeriodo; 0; atProducto(cIdUnidadBase); pIdUnidad; tMovimiento; tExistencia; tEquivalencia)
    // El costo unitario deberá expresarse en términos de la relación que existe entre la unidad base y la unidad equivalente o no convertible
    lCosto = lCosto * lExistenciaBase / lExistencia
  FinSi

  // Si no se imprimen características o el producto no tiene caracteristicas
  // cControlExistencias es un campo de bits. El segundo bit menos significativo indica que 
  // el producto tiene características
	// N.D.600 Inicio
  Si !pCaracteristicas o lTieneCaracteristicas != 2
	// Si !pCaracteristicas o (atProducto(cControlExistencia) != 2 y atProducto(cControlExistencia) != 3)
	// N.D.600 Fin
    // Imprime el producto
    lCostoTotal = lExistencia * lCosto
    // Si la existencia es mayor que el máximo, calcula costo excedente
    Si lExistencia > lMaximo
      lCostoExcedente = lCostoTotal - (lMaximo * lCosto)
    Sino // De lo contrario el costo excedente es igual a cero
      lCostoExcedente = 0.0
    FinSi
    fImprimeProducto(atProducto; 1; lMaximo; lExistencia; lCostoTotal; lCostoExcedente)
		gTotalReporte[6]  = @Col(gTotalReporte;6)  + lMaximo
		gTotalReporte[8]  = @Col(gTotalReporte;8)  + lExistencia
		gTotalReporte[10] = @Col(gTotalReporte;10) + lCostoTotal
		gTotalReporte[12] = @Col(gTotalReporte;12) + lCostoExcedente
  Sino
    lTotalMaximo = 0.0
    lTotalExistencia = 0.0
		lTotalCostoTotal = 0.0
		lTotalCostoExcedente = 0.0
    // No se han impreso los datos del producto
    lDatosProducto = 0
    // No se ha impreso el texto de las caracteristicas
    lTextoCaracteristicas = 0

    // Recorre los detalles de características
    Mientras [ tDetalleProducto.Busca[@Str(atProducto(cIdProducto);0)]; tDetalleProducto->Encontro; tDetalleProducto.Busca Siguiente[1] ]
      // Encuentra el stock máximo y existencia del producto en el almacén global
      fStockMinMax(tDetalleProducto(cIdProducto); 0; pUnidad; atProducto(cIdUnidadBase); pIdUnidad; &lMinimo; &lMaximo; tExistenciaMinMax; tEquivalencia)
      lExistencia = fExistenciaInicialProducto(tDetalleProducto(cIdProducto); 0; gFechaCorteMasUnDia; gIdEjercicio; gPeriodo; pUnidad; atProducto(cIdUnidadBase); pIdUnidad; tMovimiento; tExistencia; tEquivalencia)
      // Si el producto no ha sido impreso, se imprime
      Si !lDatosProducto
        Saltalin // salta una línea para separarlo del producto anterior
        fImprimeProducto(atProducto; 0; 0; 0; 0; 0)
        lDatosProducto = 1
      FinSi
      // Imprime la combinación de caracterísicas de ese producto
      lCostoTotal = lExistencia * lCosto
      // Si la existencia es mayor que el máximo, calcula costo excedente
      Si lExistencia > lMaximo
        lCostoExcedente = lCostoTotal - (lMaximo * lCosto)
      Sino // De lo contrario el costo excedente es igual a cero
        lCostoExcedente = 0.0
      FinSi
      fImprimeCaracteristicas(tDetalleProducto; 1; lMaximo; lExistencia; lCostoTotal; lCostoExcedente; &lTextoCaracteristicas)
      // acumula al total
      lTotalMaximo = lTotalMaximo + lMaximo
      lTotalExistencia     = lTotalExistencia + lExistencia
			lTotalCostoTotal     = lTotalCostoTotal + lCostoTotal
			lTotalCostoExcedente = lTotalCostoExcedente + lCostoExcedente
    FinMientras
    // Imprime los totales del producto
    fImprimeTotales(lTotalMaximo; lTotalExistencia; lTotalCostoTotal; lTotalCostoExcedente)
    Saltalin // salta una línea para separarlo del producto siguiente
  FinSi   
FinFunc


// *****************************************************************************
//  fProcesaPorAlmacen
//
//  Esta función procesa el reporte de stock máximo por almacén.
//
//  Parámetros 
//
//  Salidas 
//
//  05/06/2002	N.D.600					Los productos que manejan características deben 
//															de presentar sus valores de características 
//															desglozados considerando la combinación de
//															caracteristicas con series y/o capas (LAP).
// *****************************************************************************
DefFunc fProcesaPorAlmacen(atProducto: Tabla)
Local lMaximo, lMinimo, lExistencia, lExistenciaBase, lCosto, lCostoExcedente
Local lTotalMaximo, lTotalExistencia, lTotalCostoTotal, lTotalCostoExcedente
Local lDatosProducto, lDatosCaracteristica, lTextoCaracteristicas
Local lTmpMinimo, lTmpMaximo, lCostoTotal
// N.D.600
Local lTieneCaracteristicas

	// N.D.600 Inicio
	// Determina si el producto utiliza características a través de restar los 
	//	demas controles de existencia
	lTienecaracteristicas = atProducto(cControlExistencia)
	// Eliminación del control de existencia por: Unidades de entrada
	//																						kProdControlExistUnidadesEntrada 32
	Si ( lTieneCaracteristicas > 31 )
		lTieneCaracteristicas = lTieneCaracteristicas - 32
	FinSi
	// Eliminación del control de existencia por: Lotes
	//																						kProdControlExistLotes           16
	Si ( lTieneCaracteristicas > 15 )
		lTieneCaracteristicas = lTieneCaracteristicas - 16
	FinSi
	// Eliminación del control de existencia por: Pedimentos
	//																						kProdControlExistPedimentos       8
	Si ( lTieneCaracteristicas > 7 )
		lTieneCaracteristicas = lTieneCaracteristicas - 8
	FinSi
	// Eliminación del control de existencia por: Series
	//																						kProdControlExistSeries           4
	Si ( lTieneCaracteristicas > 3 )
		lTieneCaracteristicas = lTieneCaracteristicas - 4
	FinSi
	// Eliminación del control de existencia por: Unidades
	//																						kProdControlExistUnidades         1
	Si ( lTieneCaracteristicas > 2 )
		lTieneCaracteristicas = lTieneCaracteristicas - 1
	FinSi
	// N.D.600 Fin

  // Verifica si discriminar productos con existencia mayor al máximo
  Si pImprimirProductos != 0
    lTotalMaximo = 0
    lTotalExistencia = 0
		lTotalCostoTotal = 0
		lTotalCostoExcedente = 0
    // Calcula el stock máximo y existencia del producto en todos los almacenes
    Mientras [ tAlmacen.Busca[pAlmacenInicial & '..' & pAlmacenFinal]; tAlmacen->Encontro; tAlmacen.Busca Siguiente[1] ]
      // Con éste dato discriminamos el producto conforme al parámetro pImprimirProductos
			// N.D.600 Inicio
      // Si atProducto(cControlExistencia) != 2 y atProducto(cControlExistencia) != 3 // Si el producto no tiene caracteristicas, buscamos el máximo y mínimo en el producto
			// Si el producto no tiene caracteristicas, buscamos el máximo y mínimo en el producto
			Si lTieneCaracteristicas != 2
			// N.D.600 Fin
        fStockMinMax(atProducto(cIdProducto); tAlmacen(cIdAlmacen); pUnidad; atProducto(cIdUnidadBase); pIdUnidad; &lMinimo; &lMaximo; tExistenciaMinMax; tEquivalencia)
      Sino // Si el producto tiene características, buscamos el máximo en cada detalle de características
        lMaximo = 0
        Mientras [ tDetalleProducto.Busca[@Str(atProducto(cIdProducto);0)]; tDetalleProducto->Encontro; tDetalleProducto.Busca Siguiente[1] ]
          fStockMinMax(tDetalleProducto(cIdProducto); tAlmacen(cIdAlmacen); pUnidad; atProducto(cIdUnidadBase); pIdUnidad; &lTmpMinimo; &lTmpMaximo; tExistenciaMinMax; tEquivalencia)
          lMaximo = lMaximo + lTmpMaximo
        FinMientras
      FinSi
      lExistencia = fExistenciaInicialProducto(atProducto(cIdProducto); tAlmacen(cIdAlmacen); gFechaCorteMasUnDia; gIdEjercicio; gPeriodo; pUnidad; atProducto(cIdUnidadBase); pIdUnidad; tMovimiento; tExistencia; tEquivalencia)
      lTotalMaximo = lTotalMaximo + lMaximo
      lTotalExistencia = lTotalExistencia + lExistencia
    FinMientras

    // Verifica si se imprime el producto según parámetro del reporte
    Si pImprimirProductos = 1 y lTotalExistencia <= lTotalMaximo // Existencias por encima del máximo
      Retorna
    FinSi
  FinSi

  // Inicia acumulados
  lTotalMaximo = 0.0
  lTotalExistencia = 0.0

  // No se han impreso los datos del producto
  lDatosProducto = 0

  // Si no se imprimen características o el producto no tiene caracteristicas
  // cControlExistencias es un campo de bits. El segundo bit menos significativo indica que 
  // el producto tiene características
	// N.D.600 Inicio
  Si !pCaracteristicas o lTieneCaracteristicas != 2
	// Si !pCaracteristicas o (atProducto(cControlExistencia) != 2 y atProducto(cControlExistencia) != 3)
	// N.D.600 Fin
    // Recorre los almacenes según el rango
    Mientras [ tAlmacen.Busca[pAlmacenInicial & '..' & pAlmacenFinal]; tAlmacen->Encontro; tAlmacen.Busca Siguiente[1] ]
      // Encuentra el stock máximo del producto en el almacén 
      Si atProducto(cControlExistencia) != 2 y atProducto(cControlExistencia) != 3 // Si el producto no tiene caracteristicas, buscamos el máximo en el producto
        fStockMinMax(atProducto(cIdProducto); tAlmacen(cIdAlmacen); pUnidad; atProducto(cIdUnidadBase); pIdUnidad; &lMinimo; &lMaximo; tExistenciaMinMax; tEquivalencia)
      Sino // Si el producto tiene características, buscamos el máximo en cada detalle de características
        lMaximo = 0
        Mientras [ tDetalleProducto.Busca[@Str(atProducto(cIdProducto);0)]; tDetalleProducto->Encontro; tDetalleProducto.Busca Siguiente[1] ]
          fStockMinMax(tDetalleProducto(cIdProducto); tAlmacen(cIdAlmacen); pUnidad; atProducto(cIdUnidadBase); pIdUnidad; &lTmpMinimo; &lTmpMaximo; tExistenciaMinMax; tEquivalencia)
          lMaximo = lMaximo + lTmpMaximo
        FinMientras
      FinSi
      // Encuentra la existencia del producto en el almacén
      lExistencia = fExistenciaInicialProducto(atProducto(cIdProducto); tAlmacen(cIdAlmacen); gFechaCorteMasUnDia; gIdEjercicio; gPeriodo; pUnidad; atProducto(cIdUnidadBase); pIdUnidad; tMovimiento; tExistencia; tEquivalencia)
      // calcula el costo del producto
      // El primer parámetro de la siguiente función es el método de costeo y los valores permitidos son:
      // 1 = Costo promedio
      // 3 = Ultimo costo
      lCosto = fCostoHistorico(atProducto(cIdProducto); 0; pFechaCorte; pTipoReporte = 0 o pTipoReporte = 1? 1 : 3; tCostoHistorico; tTipoCambio; pIdMoneda)
      // Si el reporte se pidio en unidades no convertibles o equivalentes
      Si pUnidad != 0
        // calcula la existencia en la unidad base
        lExistenciaBase = fExistenciaInicialProducto(atProducto(cIdProducto); tAlmacen(cIdAlmacen); gFechaCorteMasUnDia; gIdEjercicio; gPeriodo; 0; atProducto(cIdUnidadBase); pIdUnidad; tMovimiento; tExistencia; tEquivalencia)
        // El costo unitario deberá expresarse en términos de la relación que existe entre la unidad base y la unidad equivalente o no convertible
        lCosto = lCosto * lExistenciaBase / lExistencia
      FinSi
      // Si el producto no ha sido impreso, se imprime
      Si !lDatosProducto
        fImprimeDatosProducto(tProducto)    
        lDatosProducto = 1
      FinSi
      // Imprime el almacen
      lCostoTotal = lExistencia * lCosto
      // Si la existencia es mayor que el máximo, calcula costo excedente
      Si lExistencia > lMaximo
        lCostoExcedente = lCostoTotal - (lMaximo * lCosto)
      Sino // De lo contrario el costo excedente es igual a cero
        lCostoExcedente = 0.0
      FinSi
      fImprimeAlmacen(tAlmacen; lMaximo; lExistencia; lCostoTotal; lCostoExcedente)
      // acumula al total
      lTotalMaximo = lTotalMaximo + lMaximo
      lTotalExistencia = lTotalExistencia + lExistencia
			lTotalCostoTotal = lTotalCostoTotal + lCostoTotal
			lTotalCostoExcedente = lTotalCostoExcedente + lCostoExcedente
    FinMientras
  Sino
    // Recorre los detalles de características
    Mientras [ tDetalleProducto.Busca[@Str(atProducto(cIdProducto);0)]; tDetalleProducto->Encontro; tDetalleProducto.Busca Siguiente[1] ]
      // No se han impreso los datos de la característica
      lDatosCaracteristica = 0
      // Recorre los almacenes según el rango
      Mientras [ tAlmacen.Busca[pAlmacenInicial & '..' & pAlmacenFinal]; tAlmacen->Encontro; tAlmacen.Busca Siguiente[1] ]
        // Encuentra el stock máximo y existencia del producto en el almacén 
        fStockMinMax(tDetalleProducto(cIdProducto); tAlmacen(cIdAlmacen); pUnidad; atProducto(cIdUnidadBase); pIdUnidad; &lMinimo; &lMaximo; tExistenciaMinMax; tEquivalencia)
        lExistencia = fExistenciaInicialProducto(tDetalleProducto(cIdProducto); tAlmacen(cIdAlmacen); gFechaCorteMasUnDia; gIdEjercicio; gPeriodo; pUnidad; atProducto(cIdUnidadBase); pIdUnidad; tMovimiento; tExistencia; tEquivalencia)
        // calcula el costo del producto, solo si no ha sido calculado antes
        // El primer parámetro de la siguiente función es el método de costeo y los valores permitidos son:
        // 1 = Costo promedio
        // 3 = Ultimo costo
        lCosto = fCostoHistorico(atProducto(cIdProducto); 0; pFechaCorte; pTipoReporte = 0 o pTipoReporte = 1? 1 : 3; tCostoHistorico; tTipoCambio; pIdMoneda)
        // Si el reporte se pidio en unidades no convertibles o equivalentes
        Si pUnidad != 0
          // calcula la existencia en la unidad base
          lExistenciaBase = fExistenciaInicialProducto(tDetalleProducto(cIdProducto); tAlmacen(cIdAlmacen); gFechaCorteMasUnDia; gIdEjercicio; gPeriodo; 0; atProducto(cIdUnidadBase); pIdUnidad; tMovimiento; tExistencia; tEquivalencia)
          // El costo unitario deberá expresarse en términos de la relación que existe entre la unidad base y la unidad equivalente o no convertible
          lCosto = lCosto * lExistenciaBase / lExistencia
        FinSi
        // Si el producto no ha sido impreso, se imprime
        Si !lDatosProducto
          fImprimeDatosProducto(tProducto)    
          lDatosProducto = 1
        FinSi
        // Si la característica no ha sido impresa, se imprime
        Si !lDatosCaracteristica
          // No se ha impreso el texto de las caracteristicas
          lTextoCaracteristicas = 0
          fImprimeCaracteristicas(tDetalleProducto; 0; 0; 0; 0; 0; &lTextoCaracteristicas)
          lDatosCaracteristica = 1
        FinSi
        // Imprime el almacen
        lCostoTotal = lExistencia * lCosto
        // Si la existencia es mayor que el máximo, calcula costo excedente
        Si lExistencia > lMaximo
          lCostoExcedente = lCostoTotal - (lMaximo * lCosto)
        Sino // De lo contrario el costo excedente es igual a cero
          lCostoExcedente = 0.0
        FinSI
        fImprimeAlmacen(tAlmacen; lMaximo; lExistencia; lCostoTotal; lCostoExcedente)
        // acumula al total
        lTotalMaximo = lTotalMaximo + lMaximo
        lTotalExistencia = lTotalExistencia + lExistencia
				lTotalCostoTotal = lTotalCostoTotal + lCostoTotal
				lTotalCostoExcedente = lTotalCostoExcedente + lCostoExcedente
      FinMientras
    FinMientras
  FinSi
  // Imprime los totales del producto
  Si lDatosProducto
    fImprimeTotales(lTotalMaximo; lTotalExistencia; lTotalCostoTotal; lTotalCostoExcedente)
  FinSi

FinFunc


// *****************************************************************************
//  fVerificaParametros
//
//  Esta función verifica los parámetros pProductoInicial, pProductoFinal,
//  pAlmacenInicial y pAlmacenFinal.
//  Si están vacíos toma el primer y último registro de la tabla.
//
//  Parámetros 
//
//  Salidas 
// *****************************************************************************
DefFunc fVerificaParametros()

  // Si el rango inicial es el primer producto, recupera el código del primero
  Si pProductoInicial = ' '
    tProducto.Busca Primero
    Si tProducto->Encontro
      pProductoInicial = tProducto(cCodigoProducto)
    FinSi
  FinSi

  // Si el rango final es el último producto, recupera el código del último
  Si pProductoFinal = ' '
    tProducto.Busca Ultimo
    Si tProducto->Encontro
      pProductoFinal = tProducto(cCodigoProducto)
    FinSi
  FinSi

  Si pTipoReporte = 1 o pTipoReporte = 3 // Si el reporte es por almacén
    // Si el rango inicial es el primer almacén, recupera el código del primero
    Si pAlmacenInicial = ' '
      tAlmacen.Busca Primero
      Si tAlmacen->Encontro
        pAlmacenInicial = tAlmacen(cCodigoAlmacen)
        // Si el almacén inicial es el almacén cero, lee el siguiente
        Si !tAlmacen(cIdAlmacen)
          tAlmacen.Saltareg[1]
          Si tAlmacen->Encontro
            pAlmacenInicial = tAlmacen(cCodigoAlmacen)
          FinSi
        FinSI
      FinSi
    FinSi  

    // Si el rango final es el último almacén, recupera el código del último
    Si pAlmacenFinal = ' '
      tAlmacen.Busca Ultimo
      Si tAlmacen->Encontro
        pAlmacenFinal = tAlmacen(cCodigoAlmacen)
      FinSi
    FinSi
  FinSi
FinFunc


// *****************************************************************************
// Encabezado del reporte

Encab
	Texto '@TC{255,0,0}@S{8}AdminPAQ'
	Hoja '"@TC{0,0,0}@S{8}Hoja: '
	Imprime
  // lee la moneda del reporte
  tMoneda.Busca[@Str(pIdMoneda;0)]
  Si tMoneda->Encontro
    Texto '@TC{0,0,0}@S{8}Moneda: ' & tMoneda(cNombreMoneda)
  FinSi
	Texto '"@TC{0,0,0}@S{8}Fecha: ' & fFormateaFecha(@Hoy();gIdioma)
  Imprime

	Texto '^@TC{0,0,0}@S{22}' & tEmpresa->Nombre
	Imprime

  Si pTipoReporte = 0 o pTipoReporte = 2 // Global?
  	Texto '^@TC{0,0,0}@S{16}STOCK MAXIMO GLOBAL'
  Sino // Por Almacén
  	Texto '^@TC{0,0,0}@S{16}STOCK MAXIMO POR ALMACEN'
  Finsi  
	Imprime
  Si pTipoReporte = 0 o pTipoReporte = 1 // Costo promedio?
  	Texto '^@TC{0,0,0}@S{16}EXCEDENTE DE ACUERDO AL COSTO PROMEDIO'
  Sino // Ultimo costo
  	Texto '^@TC{0,0,0}@S{16}EXCEDENTE DE ACUERDO AL ULTIMO COSTO'
  Finsi  
	Imprime
	Texto '^@TC{0,0,0}@S{16}Al: ' & fFormateaFecha(pFechaCorte;gIdioma)
	Imprime

  // Se Revisan las clasificaciones, para recuperarlas e imprimirlas en el encabezado
  fRevisaClases(tValoresDeClasif)

  Saltalin

	NEGRITAS TRUE
	Texto '@TC{0,0,0}@L{-1}'
  Imprime
  Texto C6;'^Stock'
  Si pTipoReporte = 0 o pTipoReporte = 1 // Costo promedio?
    Texto C10;'^Costo'
  Sino // Ultimo costo
    Texto C10;'^Ultimo'
  FinSi
  Texto C12;'^Costo'
  Imprime
	Texto C0;'Código'
  Si pTipoReporte = 0 o pTipoReporte = 2 // Global?
  	Texto C2;'Nombre (Producto)'
  Sino // Por Almacén
  	Texto C2;'Nombre (Almacén)'
  Finsi  
	Texto C6;'^Máximo'
	Texto C8;'^Existencia'
  Si pTipoReporte = 0 o pTipoReporte = 1 // Costo promedio?
  	Texto C10;'^Promedio'
  Sino // Ultimo costo
	  Texto C10;'^Costo'
  FinSi
	Texto C12;'^Excedente'
  Si pTipoReporte = 0 o pTipoReporte = 2 // Global?
    Texto C14;'Error'  // HVA
  FinSi
  Imprime
	Texto '@TC{0,0,0}@L{-1}'
	Imprime 1
	NEGRITAS FALSE

	Color @RGB(0;0;0)
	Altura 9
FinEncab


// *****************************************************************************
// Cuerpo del reporte

// Inicializa valores globales para la biblioteca LibAdminPAQ.rpt
fIniFechasHistoria(tEjercicio)

// Configura los decimales del reporte
fConfiguraDecimales()

// Verifica el rango de productos del reporte
fVerificaParametros()

// A partir de la fecha de corte del reporte, obtiene el ejercicio y periodo correspondiente
// esto es util para el cálculo de la existencia del reporte
// Debido a que se utilizará la función de librería para cálculo de existencia inicial,
// se suma un día a la fecha de corte
gFechaCorteMasUnDia = fFechaSiguiente(pFechaCorte)
fEjercicioPeriodo(gFechaCorteMasUnDia; &gPeriodo; &gIdEjercicio; tEjercicio) 
// Si no se encontró el ejercico, puede ser porque la fecha cae en dos ejercicios futuros,
// Por tanto obtiene el ejercicio y período de la fecha de corte
Si !gIdEjercicio
  fEjercicioPeriodo(pFechaCorte; &gPeriodo; &gIdEjercicio; tEjercicio) 
  // Suma uno al período debido a que se manda llamar funciones de existencia o costo inicial
  gPeriodo = gPeriodo + 1
FinSi

// Obtiene los productos en el rango especificado en los parámetros del reporte

Mientras [ tProducto.Busca[pProductoInicial & '..' & pProductoFinal]; tProducto->Encontro; tProducto.Busca Siguiente[1] ]

  // Solo busca productos, descarta paquetes y servicios
  Si tProducto(cTipoProducto) != 1
    Continua
  FinSi

  // Verifica si el producto es candidato a imprimirse en función de su estado
  // tProducto(cStatusProducto) = 0 (Inactivo)
  // tProducto(cStatusProducto) = 1 (Activo)
  Si pEstadoProducto y pEstadoProducto != @Abs(tProducto(cStatusProducto) - 2)
    Continua
  FinSi

  // Verifica si el producto cumple con las clasificaciones especificadas 
  Si gNumDeClases > 0
    Si !fMiembroDeUnaClase(tProducto; gNumDeClases; gListIdDeClases; 2)
      // Discrimina el producto por no pertenecer a alguna de las clasificaciones
      Continua
    FinSi
  FinSi

  // Verifica parámetro pUnidad
  fVerificaUnidad(tProducto)
  Si pTipoReporte = 0 o pTipoReporte = 2 // Reporte global?
    fProcesaGlobal(tProducto)
  Sino // por almacén
    fProcesaPorAlmacen(tProducto)
  FinSi

  // Regresa el valor original de pUnidad guardado en la variable global gUnidad
  //pUnidad = gUnidad

FinMientras

fTotalReporte(gTotalReporte)

Si pTipoReporte = 0 o pTipoReporte = 2 // Reporte global?
  Columnas 16;0.15,18.94,0.15,0 
  Saltalin
  Lista '', 'Errores de existencias o costos:'[C1]
  Lista '', ' (1) Captura en desorden de entradas'[C1]
  Lista '', ' (2) Captura en desorden de salidas'[C1]
  Lista '', ' (3) Existencias negativas'[C1]
  Lista '', ' (4) Series sin movimeinto de entrada'[C1]
  Lista '', ' (5) Series sin costo'[C1]
  Lista '', ' (6) Movimientos sin afectar'[C1]
  Lista '', ' (7) En proceso de levantamiento de inventario físico'[C1]
  Saltalin
  Lista '', 'En caso de captura en desorden de entradas se recomienda ejecutar el proceso "Recosteo" para los productos con errores'[C1]
  Lista '', 'En caso de captura en desorden de salidas se recomienda ejecutar el proceso "Recosteo" para los productos con errores'[C1]
  Lista '', 'En caso de existencias Negativas se recomienda registrar las entradas necesarias y ejecutar el proceso "Recosteo" para los productos con errores'[C1]
  Lista '', 'En caso de series sin movimiento de entrada la existencia puede no coincidir con las series desplegadas, se recomienda registrar la entrada de las series pendientes'[C1]
  Lista '', 'En caso de series sin costo se recomienda ejecutar el proceso "Recosteo" para los productos con errores'[C1]
  Lista '', 'En caso de movimientos sin afectar se recomienda consultar el reporte "Movimientos sin Afectar"'[C1]
  Lista '', 'En caso de inventario físico se recomienda aplicar el inventario físico'[C1]
Finsi

