 // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
//
//  Stock.rpt
//  Nombre del reporte en el sistema: Stock Máximo y Mínimo
//
//  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
//  dos reportes en el ámbito del sistema:
//    Stock Máximo y Mínimo Global
//    Stock Máximo y Mínimo por Almacén
//
//  Elaboró:    Eduardo Pérez Figueroa
//  Fecha:      26 de Agosto del 2001
//
//  Historia de revisiones:
//  Se incluyó el total por reporte
//  Autor: Gabriela Cuevas
//  Fecha: 15 de febrero del 2002
//
//  (N.D. 251) Se modificaron las funciones: fImprimeProducto, fTotalReporte,
//  fImprimeTotales, fImprimeAlmacen, fImprimeCaracteristicas, fProcesaGlobal.
//  Los cambios son en relación al cálculo del punto de reorden. Ahora se toman
//  en cuenta los máximos y mínimos del producto. Además, el acumulado de punto
//  de reorden total del reporte se hace acumulando los valores de punto de
//  reorden de cada producto en base al nuevo cálculo.
//  Autor: Epifanio Gómez Villarreal
//  Fecha: 02 de Abril del 2002
//
//  Historial de Cambios en LN
//  Fecha               Id Lotus    Descripcion
//  05/06/2002          ND 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
// 26/07/2002   RPC     ND 955      Cuando se ejecuta el reporte con rangos de 
//                                  fechas que terminana el mismo día pero 
//                                  inician en días diferentes se producen 
//                                  diferencias en el costo final debido a que 
//                                  el cálculo del costo inicial se hace con un
//                                  día de más 
// 26/07/2002   RPC     SC 150      Imprimir los errores de costeo
// 25/08/2003   EGV     2420        Se modificó: fObtienePendienteSinAfectar. (EGV)
//  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 debajo del mínimo
                                        // 2 = 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 pTipoReporte = 0              // Tipo de Reporte (este parámetro es interno a AdminPAQ)
                                        // 0 = Stock Máximo y Mínimo Global
                                        // 1 = Stock Máximo y Mínimo por Almacén

// *****************************************************************************
//  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'] Alias 'Movto'             // Movimientos
tMovimiento.Usaindice['iProductoDoctoDeAfecFecha']    // cIdProducto+cIdDocumentoDe+cAfectadoInventario+cFecha

UsaTabla tMovimientoOculto = tEmpresa['MGW10010'] Alias 'MovtoOculto' // Movimientos
tMovimientoOculto.Usaindice['iMovtoOwnerAfectaExist']      // cIdMovtoOwner+cAfectaExistencias

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
//tEjercicio.Usaindice['']                          // Indice primario: cIdEjercicio

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 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) = 30 (global) o 34 caracteres (por almacén)
// C4 (Unidad de medida o peso) = 3 caracteres
// C6 (Stock mínimo) = 12 caracteres
// C8 (Stock máximo) = 12 caracteres
// C10 (Existencia) = 14 caracteres
// C12 (Pendiente recibir) = 12 caracteres
// C14 (Pendiente surtir) = 12 caracteres
// C16 (Punto de reorden) = 10 caracteres
// C18 RPC - SC 150: Indica el número de error del producto si tiene
Si pTipoReporte = 0 // global
  Columnas 19;2.09,0.15,4.48,0.15,0.45,0.15,1.79,0.15,1.79,0.15,2.09,0.15,1.79,0.15,1.79,0.15,1.64,0.15,0.75
Sino // por almacén
  Columnas 19;2.09,0.15,5.68,0,0,0.15,1.79,0.15,1.79,0.15,2.09,0.15,1.79,0.15,1.79,0.15,1.79,0.15,0.00
FinSi

Usaletra 'Arial'
Altura 9
[C6,C8,C10,C12,C14,C16].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
gTotalReporte = 0.00
gTotalReorden = 0.00
gTotalReordenDetalleCarac = 0.00


// *****************************************************************************
//  fCalculaReorden
//  Función que calcula el punto de reorden tomando en cuenta los Máximos y
//  Mínimos que tiene el producto.
//
//  Parámetros
//
//  Salidas
// *****************************************************************************
DefFunc fCalculaReorden( aMinimo; aMaximo; atPtrReorden )
  Local lCalculado

  lCalculado = 0

  Si aMinimo > 0 y aMaximo > 0
    // Si la existencia está entre el máximo y el mínimo no hay reorden.
    Si atPtrReorden >= aMinimo y atPtrReorden <= aMaximo
      atPtrReorden = 0.0
      lCalculado = 1
    FinSi
  FinSi

  Si atPtrReorden < aMinimo y lCalculado = 0
    // Ajustar el reorden para que llegue al mínimo ya que falta producto.
    atPtrReorden = aMinimo - atPtrReorden
    lCalculado = 1
  FinSi

  Si atPtrReorden > aMaximo y lCalculado = 0
    // Ajustar el reorden para que llegue al máximo ya que sobra producto.
    atPtrReorden = aMaximo - atPtrReorden
    lCalculado = 1
  FinSi
FinFunc


// *****************************************************************************
//  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,C10,C12,C14,C16].Decimales tParametros(cDecimalesUnidades) // Decimales para existencias
    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,C14,C16].Decimales 2
  FinSi
FinFunc


// *****************************************************************************
//  fImprimeAlmacen
//
//  Esta función imprime los datos del almacén para el reporte por almacén.
//
//  Parámetros 
//
//  Salidas
//
//  Historia de revisiones:
//  01/ABR/2002  N.D. 251  Se calcula el punto de reorden tomando en cuenta los
//                         Máximos y Mínimos.   
// *****************************************************************************
DefFunc fImprimeAlmacen(atAlmacen: Tabla; aMinimo; aMaximo; aExistencia; apRecibir; apSurtir)
Local vImprime, lReorden, lCodigoEditado

  vImprime = ' '[C0..C17]

  // Codigo y nombre del almacén
  lCodigoEditado = fMascarilla(atAlmacen(cCodigoAlmacen); gMascarillaAlmacen)
  vImprime = @Col(lCodigoEditado;0)[C0]
  vImprime = atAlmacen(cNombreAlmacen)[C2]
    
  vImprime = @Col(aMinimo;0)[C6]
  vImprime = @Col(aMaximo;0)[C8]
  vImprime = @Col(aExistencia;0)[C10]
  vImprime = @Col(apRecibir;0)[C12]
  vImprime = @Col(apSurtir;0)[C14]
  // Calcula el punto de reorden
  lReorden = aExistencia + apRecibir - apSurtir

  fCalculaReorden( aMinimo; aMaximo; &lReorden )

  vImprime = @Col(lReorden;0)[C16]

  Lista vImprime
FinFunc


// *****************************************************************************
//  fImprimeCaracteristicas
//
//  Esta función imprime los datos de una combinación de características.
//
//  Parámetros 
//
//  Salidas
//
//  Historia de revisiones:
//  01/ABR/2002  N.D. 251  Se calcula el punto de reorden tomando en cuenta los
//                         Máximos y Mínimos.
// *****************************************************************************
DefFunc fImprimeCaracteristicas(atDetalleProducto: Tabla; aImprimirExistencias; aMinimo; aMaximo; aExistencia; apRecibir; apSurtir; aTextoCaracteristicas)
Local vImprime, lValoresCaracteristicas, lTextoCaracteristicas, lReorden

  // Procesa solo si existen valores de características
  Si atDetalleProducto(cIdValorCaracteristica1) o atDetalleProducto(cIdValorCaracteristica2) o atDetalleProducto(cIdValorCaracteristica3)
    vImprime = ' '[C0..C17]
    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(aMinimo;0)[C6]
      vImprime = @Col(aMaximo;0)[C8]
      vImprime = @Col(aExistencia;0)[C10]
      vImprime = @Col(apRecibir;0)[C12]
      vImprime = @Col(apSurtir;0)[C14]
      // Calcula el punto de reorden
      lReorden = aExistencia + apRecibir - apSurtir

      fCalculaReorden( aMinimo; aMaximo; &lReorden )

      vImprime = @Col(lReorden;0)[C16]
    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 
// *****************************************************************************
// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
// Historia de modificaciones
//  Fecha      Nombre   IDLotus                     Descripcion
// <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
// 26/07/2002   RPC     SC 150      Se agrearon mensajes de error de existencias 
//                                  negativas, costos y movtos sin afectar
// <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
DefFunc fImprimeDatosProducto(atProducto: Tabla)
Local vImprime, vImprime2, vImprime3, lCodigoEditado, lFechaSinAfectar
Local lFechaEditada, lExistenciaCongelada

  // RPC - SC 150 Modificar la defición de columnas para los mensajes de error 
  Columnas 19;2.09,0.15,20.32,0.15

  vImprime = ' '[C0..C17]
  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

  // RPC - SC 150 Verificar si el producto tiene algún error de 
  // existencias o costos y desplegarlo
  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; tMovimientoOculto; 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
  // FIN RPC - SC 150

  // Regesar a la definición original de las Columnas
  Columnas 19;2.09,0.15,5.68,0,0,0.15,1.79,0.15,1.79,0.15,2.09,0.15,1.79,0.15,1.79,0.15,1.79,0.15,0.00

  NEGRITAS FALSE
  SaltaLin

FinFunc


// *****************************************************************************
//  fImprimeProducto
//
//  Esta función imprime los datos del producto para el reporte global.
//
//  Parámetros 
//
//  Salidas
//
//  Historia de revisiones:
//  02/Abr/2002  N.D. 251  Se calcula el punto de reorden tomando en cuenta los
//                         Máximos y Mínimos y en base al resultado, se acumula
//                         el total punto de reorden del reporte. (EGV)
// *****************************************************************************
DefFunc fImprimeProducto(atProducto: Tabla; aImprimirExistencias; aMinimo; aMaximo; aExistencia; apRecibir; apSurtir)
Local vImprime, lReorden, lCodigoEditado, lFechaSinAfectar, lExistenciaCongelada

  vImprime = ' '[C0..C17]

  // 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(aMinimo;0)[C6]
    vImprime = @Col(aMaximo;0)[C8]
    vImprime = @Col(aExistencia;0)[C10]
    vImprime = @Col(apRecibir;0)[C12]
    vImprime = @Col(apSurtir;0)[C14]
    // Calcula el punto de reorden
    lReorden = aExistencia + apRecibir - apSurtir

    fCalculaReorden( aMinimo; aMaximo; &lReorden )
    gTotalReorden = gTotalReorden + @Col(lReorden;0)

    vImprime = @Col(lReorden;0)[C16]
  FinSi

  // RPC - SC 150 Verificar si el producto tiene algún error y sí es así 
  // desplegar en la última columna el número de error
  vImprime = ''[C18]

  // 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; tMovimientoOculto; tDocumento)
  SiNo  
    vImprime = '(7)'[C18]
    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)& ')'[C18]
  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)'[C18]
  FinSi
  // FIN RPC - SC 150

  Lista vImprime
FinFunc

// *****************************************************************************
//  fTotalReporte
//
//  Esta función imprime los totales por reporte
//
//  Parámetros 
//
//  Salidas
//
//  Historia de revisiones:
//  02/Abr/2002  N.D. 251  Se toma el total de punto de reorden en base al
//                         acumulado que se hizo sobre la variable
//                         gTotalReorden. (EGV)
// *****************************************************************************
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]
  vImprime     = @Col(aTotal;14)[C14]
  vImprime[16] = gTotalReorden
  lSubRaya = ''
  lSubRaya = '================='[C6,C8,C10,C12,C14,C16]
  Imprime lSubRaya
  Lista vImprime
  Saltalin
 
FinFunc
// *****************************************************************************
//  fImprimeTotales
//
//  Esta función imprime los totales por producto.
//
//  Parámetros 
//
//  Salidas
//
//  Historia de revisiones:
//  02/Abr/2002  N.D. 251  Se toma el punto de reorden de la variable
//                         gTotalReordenDetalleCarac ya que se hace el cálculo
//                         del mismo en base a cada detalle de características.
//                         Además se acumula dicho punto de reorden al total del
//                         reporte. (EGV)
// *****************************************************************************
DefFunc fImprimeTotales(aMinimo; aMaximo; aExistencia; apRecibir; apSurtir)
Local vImprime, lReorden

  Texto C6;'@L{6}'
  Texto C8;'@L{8}'
  Texto C10;'@L{10}'
  Texto C12;'@L{12}'
  Texto C14;'@L{14}'
  Texto C16;'@L{16}'
  Imprime

  vImprime = ' '[C0..C17]
  vImprime = '^Total producto:'[C2]
  vImprime = @Col(aMinimo;0)[C6]
  vImprime = @Col(aMaximo;0)[C8]
  vImprime = @Col(aExistencia;0)[C10]
  vImprime = @Col(apRecibir;0)[C12]
  vImprime = @Col(apSurtir;0)[C14]
  // Calcula el punto de reorden
  // lReorden = aExistencia + apRecibir - apSurtir

  // Se asigna el total del punto de reorden acumulado de los detalles de
  // caracteristicas.
  vImprime = @Col(gTotalReordenDetalleCarac;0)[C16]

  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)
  gTotalReporte[14] = @Col(gTotalReporte;14) + @Col(vImprime;14)

  // Acumular al total del Reporte el reorden de todos los detalles.
  gTotalReorden = gTotalReorden + gTotalReordenDetalleCarac

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


// *****************************************************************************
// fCancelado
// 
// Verifca si el movto es de un docto cancelado
//
// Salidas: 1 = cancelado, 0 = no cancelado
// RPC - SC 150 
// *****************************************************************************
Deffunc fCancelado(atMovimiento:Tabla)
  local lvalorregreso,lIdDocumento

  // lee el documento del movimiento
  Si atMovimiento(cIdDocumento)
    tDocumento.Busca[@Str(atMovimiento(cIdDocumento);0)]
  Sino // es un movimiento oculto
    // Busca el ID del documento en el movimiento owner
    lIdDocumento = fIdDocumentoMovtoOwner(atMovimiento; tMovtoOwner) 
    tDocumento.Busca[@Str(lIdDocumento;0)]
  FinSi
  lvalorregreso = 1 // cancelado en caso de no encontrar el docto.
  Si tDocumento->Encontro
    lvalorregreso =  tDocumento(cCancelado)    // Regresa el estado del campo
  Finsi

  Retorna lvalorregreso

FinFunc

// *****************************************************************************
//  fObtienePendiente
//
//  Esta función obtiene las unidades pendientes de recibir y de surtir, leyendo
//  las unidades pendientes de los movimientos del producto.
//
//  Parámetros 
//
//  Salidas 
// *****************************************************************************
DefFunc fObtienePendiente(acIdProducto; aIdCaracteristica; acIdAlmacen; aIdUnidadBase; apRecibir; apSurtir)
Local lCiclo, lLlave

  // Indice para recorrido de movimientos
  tMovimiento.Usaindice['iProductoDoctoDeAfecFecha']    // cIdProducto+cIdDocumentoDe+cAfectadoInventario+cFecha

  // Inicia acumulados
  apRecibir = 0.0
  apSurtir = 0.0

  Mientras [ lCiclo = 0; lCiclo < 2; lCiclo = lCiclo + 1 ]
    Si lCiclo = 0 // Si es la primera iteración
      // Busca los pedidos
      lLlave = acIdProducto & ':2'
    Sino
      // Busca las órdenes de compra
      lLlave = acIdProducto & ':17' 
    FinSi
    // Recorre los movimientos del producto para un Documento De
    Mientras [ tMovimiento.Busca[lLlave]; tMovimiento->Encontro; tMovimiento.Busca Siguiente[2] ]
      // RPC - SC 150 Verificar si el movimiento pertenece a un docto cancelado
      Si fCancelado(tMovimiento)
        Continua
      Finsi
      // Si el movimiento excede la fecha de corte, termina el recorrido
      Si tMovimiento(cFecha) > pFechaCorte
        FinCiclo
      FinSi
      // Discrimina movimientos que no pertenecen al almacén solicitado
      Si acIdAlmacen != 0 y tMovimiento(cIdAlmacen) != acIdAlmacen
        Continua
      FinSi   
      // Discrimina movimientos sin unidades pendientes
      Si pUnidad = 1 y tMovimiento(cUnidadesNCPendientes) = 0 // Unidades no convertibles
        Continua
      FinSi
      Si pUnidad != 1 y tMovimiento(cUnidadesPendientes) = 0 // Unidades base o equivalente
        Continua
      FinSi
      // Si el producto tiene características, recorre los movimientos ocultos
      Si aIdCaracteristica
        Mientras [ tMovimientoOculto.Busca[@Str(tMovimiento(cIdMovimiento);0)]; tMovimientoOculto->Encontro; tMovimientoOculto.Busca Siguiente[1] ]
          // Discrimina movimientos que no son de esa característica
          Si tMovimientoOculto(cIdProducto) != aIdCaracteristica
            Continua
          FinSi
          // Si se solicitó en unidades no convertibles
          Si pUnidad = 1
            // Verifica si es pedido u orden de compra
            Si lCiclo = 0 // pedido?
              apSurtir = apSurtir + tMovimientoOculto(cUnidadesNCPendientes)
            Sino // Orden de compra
              apRecibir = apRecibir + tMovimientoOculto(cUnidadesNCPendientes)
            FinSi
          Sino // Unidades base o equivalentes
            // Verifica si es pedido u orden de compra
            Si lCiclo = 0 // pedido?
              apSurtir = apSurtir + tMovimientoOculto(cUnidadesPendientes)
            Sino // Orden de compra
              apRecibir = apRecibir + tMovimientoOculto(cUnidadesPendientes)
            FinSi
          FinSi    
        FinMientras
      Sino // Sino, obtiene las unidades pendientes
        // Si se solicitó en unidades no convertibles
        Si pUnidad = 1
          // Verifica si es pedido u orden de compra
          Si lCiclo = 0 // pedido?
            apSurtir = apSurtir + tMovimiento(cUnidadesNCPendientes)
          Sino // Orden de compra
            apRecibir = apRecibir + tMovimiento(cUnidadesNCPendientes)
          FinSi
        Sino // Unidades base o equivalentes
          // Verifica si es pedido u orden de compra
          Si lCiclo = 0 // pedido?
            apSurtir = apSurtir + tMovimiento(cUnidadesPendientes)
          Sino // Orden de compra
            apRecibir = apRecibir + tMovimiento(cUnidadesPendientes)
          FinSi
        FinSi    
      FinSi
    FinMientras

    // Convierte a unidades equivalentes de ser necesario
    Si pUnidad = 2
      apSurtir = fUnidadesEquivalentes(apSurtir; aIdUnidadBase; pIdUnidad; tEquivalencia)
      apRecibir = fUnidadesEquivalentes(apRecibir; aIdUnidadBase; pIdUnidad; tEquivalencia)
    FinSi
  FinMientras
  
  // Establece el índice para cálculo de existencias
  tMovimiento.Usaindice['iProductoFechaAfecta']     // cIdProducto+cFecha+cAfectaExistencia
FinFunc


// *****************************************************************************
//  fObtienePendienteSinAfectar
//
//  Esta función obtiene las unidades pendientes de recibir y de surtir, leyendo
//  las unidades de los movimientos sin afectar del producto.
//
//  Parámetros 
//
//  Salidas 
// *****************************************************************************
// Historial de revisiones:
// Fecha       Folio    Descricpcion
// *****************************************************************************
// 25/08/2003  2420     El ciclo de Movimientos tomaba en cuenta Doctos. 
//                      cancelados. Esto provocaba el error de que aparecieran
//                      unidades pendientes por recibir o surtir de Movimientos 
//                      sin afectar). (EGV)
// *****************************************************************************
DefFunc fObtienePendienteSinAfectar(acIdProducto; aIdCaracteristica; acIdAlmacen; aIdUnidadBase; apRecibir; apSurtir)
Local lCiclo, lListaIdDocumentoDe, lIdDocumentoDe, lCantidad

  // Indice para recorrido de movimientos
  tMovimiento.Usaindice['iProductoDoctoDeAfecFecha']    // cIdProducto+cIdDocumentoDe+cAfectadoInventario+cFecha

  // Inicia acumulados
  apRecibir = 0.0
  apSurtir = 0.0

  // Lista de documentos a considerar: Remisiones, Facturas, Devoluciones sobre venta,
  // Devoluciones de remisión, Productos en consignación, Compras, Devoluciones al proveedor,
  // Devoluciones de consignación, Entradas, Salidas, Traspasos
  lListaIdDocumentoDe = '3,4,5,6,18,19,20,21,32,33,34,35,36'  // FBCS POS se agrega el 35 y 36

  Mientras [ lCiclo = 0; lCiclo < 13; lCiclo = lCiclo + 1 ]
    lIdDocumentoDe = @Escoge(lListaIdDocumentoDe;lCiclo)
    // Recorre los movimientos sin afectar del producto para un Documento De
    Mientras [ tMovimiento.Busca[acIdProducto & ':' & lIdDocumentoDe & ':0']; tMovimiento->Encontro; tMovimiento.Busca Siguiente[3] ]
      // Si el movimiento excede la fecha de corte, termina el recorrido
      Si tMovimiento(cFecha) > pFechaCorte
        FinCiclo
      FinSi
      // EGV - ND 2420 Verificar si el movimiento pertenece a un docto cancelado.
      Si fCancelado(tMovimiento)
        Continua
      Finsi
      // Discrimina movimientos que no son de entrada ni de salida
      SI tMovimiento(cAfectaExistencia) != 1 y tMovimiento(cAfectaExistencia) != 2
        Continua
      FinSi
      // Discrimina movimientos que no pertenecen al almacén solicitado
      Si acIdAlmacen != 0 y tMovimiento(cIdAlmacen) != acIdAlmacen
        Continua
      FinSi   
      // Discrimina movimiento de entrada en las remisiones para el reporte global
      Si acIdAlmacen = 0 y tMovimiento(cIdDocumentoDe) = 3 y tMovimiento(cAfectaExistencia) = 1
        Continua
      FinSi
      // Discrimina movimiento de salida en las devoluciones de remisión para el reporte global
      Si acIdAlmacen = 0 y tMovimiento(cIdDocumentoDe) = 6 y tMovimiento(cAfectaExistencia) = 2
        Continua
      FinSi
      // Si el producto tiene características, recorre los movimientos ocultos
      Si aIdCaracteristica
        Mientras [ tMovimientoOculto.Busca[@Str(tMovimiento(cIdMovimiento);0)]; tMovimientoOculto->Encontro; tMovimientoOculto.Busca Siguiente[1] ]
          // Discrimina movimientos que no son de esa característica
          Si tMovimientoOculto(cIdProducto) != aIdCaracteristica
            Continua
          FinSi
          // Discrimina movimiento de entrada en las remisiones para el reporte global
          Si acIdAlmacen = 0 y tMovimiento(cIdDocumentoDe) = 3 y tMovimientoOculto(cAfectaExistencia) = 1
            Continua
          FinSi
          // Discrimina movimiento de salida en las devoluciones de remisión para el reporte global
          Si acIdAlmacen = 0 y tMovimiento(cIdDocumentoDe) = 6 y tMovimientoOculto(cAfectaExistencia) = 2
            Continua
          FinSi
          lCantidad = fUnidades(pUnidad; aIdUnidadBase; pIdUnidad; tMovimientoOculto; tEquivalencia)
          // Verifica si es pendiente de surtir o recibir
          Si tMovimientoOculto(cAfectaExistencia) = 2 // salida?
            apSurtir = apSurtir + lCantidad
          Sino // entrada
            apRecibir = apRecibir + lCantidad
          FinSi    
        FinMientras
      Sino // Sino, obtiene las unidades pendientes
        lCantidad = fUnidades(pUnidad; aIdUnidadBase; pIdUnidad; tMovimiento; tEquivalencia)
        // Verifica si es pendiente de surtir o recibir
        Si tMovimiento(cAfectaExistencia) = 2 // salida?
          apSurtir = apSurtir + lCantidad
        Sino // entrada
          apRecibir = apRecibir + lCantidad
        FinSi    
      FinSi
    FinMientras

  FinMientras

  // Establece el índice para cálculo de existencias
  tMovimiento.Usaindice['iProductoFechaAfecta']     // cIdProducto+cFecha+cAfectaExistencia  
FinFunc


// *****************************************************************************
//  fProcesaGlobal
//
//  Esta función procesa el reporte de stock máximo y mínimo global.
//
//  Parámetros 
//
//  Salidas
//
//  Historia de revisiones:
//  02/Abr/2002  N.D. 251  Se calcula el punto de reorden en base a cada detalle
//                         de características encontrado mismo que será
//                         para sacar el total del punto de reorden del
//                         producto. (EGV)
//  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 lMaximo, lMinimo, lExistencia, lpRecibir, lpSurtir, lpRecibirSinAfectar, lpSurtirSinAfectar
Local lTotalMinimo, lTotalMaximo, lTotalExistencia, lTotalpRecibir, lTotalpSurtir
Local lDatosProducto, lTextoCaracteristicas
Local lTmpMinimo, lTmpMaximo
Local lTotalExistenciaDetalle
// 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

  // Establece el índice para cálculo de existencias
  tMovimiento.Usaindice['iProductoFechaAfecta']     // cIdProducto+cFecha+cAfectaExistencia

  // Verifica el stock máximo, mínimo y existencia global del producto
  // 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 lTieneCaracteristicas != 2 // Si el producto no tiene caracteristicas, buscamos el máximo y mínimo en el producto
  // 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 y mínimo en cada detalle de características
    lMinimo = 0
    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)
      lMinimo = lMinimo + lTmpMinimo
      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 >= lMinimo // Existencias por debajo del mínimo
    Retorna
  FinSi
  Si pImprimirProductos = 2 y lExistencia <= lMaximo // Existencias por encima del máximo
    Retorna
  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
    // Encuentra las unidades pendientes de recibir y surtir del producto en el almacén global
    fObtienePendiente(atProducto(cIdProducto); 0; 0; atProducto(cIdUnidadBase); &lpRecibir; &lpSurtir)
    // Ahora encuentra las unidades pendientes de recibir y surtir de documentos sin afectar
    fObtienePendienteSinAfectar(atProducto(cIdProducto); 0; 0; atProducto(cIdUnidadBase); &lpRecibirSinAfectar; &lpSurtirSinAfectar)
    lpRecibir = lpRecibir + lpRecibirSinAfectar
    lpSurtir = lpSurtir + lpSurtirSinAfectar
    // Imprime el producto
    fImprimeProducto(atProducto; 1; lMinimo; lMaximo; lExistencia; lpRecibir; lpSurtir)
    gTotalReporte[6]  = @Col(gTotalReporte;6)  + lMinimo
    gTotalReporte[8]  = @Col(gTotalReporte;8)  + lMaximo
    gTotalReporte[10] = @Col(gTotalReporte;10) + lExistencia
    gTotalReporte[12] = @Col(gTotalReporte;12) + lpRecibir
    gTotalReporte[14] = @Col(gTotalReporte;14) + lpSurtir
  Sino
    lTotalMinimo = 0.0
    lTotalMaximo = 0.0
    lTotalExistencia = 0.0
    lTotalpRecibir = 0.0
    lTotalpSurtir = 0.0
    // No se han impreso los datos del producto
    lDatosProducto = 0
    // No se ha impreso el texto de las caracteristicas
    lTextoCaracteristicas = 0
    // Se inicializa el punto de reorden de todos los detalles del producto.
    gTotalReordenDetalleCarac = 0.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, mínimo 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)
      // Encuentra las unidades pendientes de recibir y surtir del producto en el almacén global
      fObtienePendiente(atProducto(cIdProducto); tDetalleProducto(cIdProducto); 0; atProducto(cIdUnidadBase); &lpRecibir; &lpSurtir)
      // Ahora encuentra las unidades pendientes de recibir y surtir de documentos sin afectar
      fObtienePendienteSinAfectar(atProducto(cIdProducto); tDetalleProducto(cIdProducto); 0; atProducto(cIdUnidadBase); &lpRecibirSinAfectar; &lpSurtirSinAfectar)
      lpRecibir = lpRecibir + lpRecibirSinAfectar
      lpSurtir = lpSurtir + lpSurtirSinAfectar
      // 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; 0)
        lDatosProducto = 1
      FinSi
      // Imprime la combinación de caracterísicas de ese producto
      fImprimeCaracteristicas(tDetalleProducto; 1; lMinimo; lMaximo; lExistencia; lpRecibir; lpSurtir; &lTextoCaracteristicas)
      // acumula al total
      lTotalMinimo = lTotalMinimo + lMinimo
      lTotalMaximo = lTotalMaximo + lMaximo
      lTotalExistencia = lTotalExistencia + lExistencia
      lTotalpRecibir = lTotalpRecibir + lpRecibir
      lTotalpSurtir = lTotalpSurtir + lpSurtir
      lTotalExistenciaDetalle = lExistencia + lpRecibir - lpSurtir
      fCalculaReorden( lMinimo; lMaximo; &lTotalExistenciaDetalle )
      gTotalReordenDetalleCarac = gTotalReordenDetalleCarac + lTotalExistenciaDetalle
    FinMientras
    // Imprime los totales del producto
    fImprimeTotales(lTotalMinimo; lTotalMaximo; lTotalExistencia; lTotalpRecibir; lTotalpSurtir)
    Saltalin // salta una línea para separarlo del producto siguiente
  FinSi   
FinFunc


// *****************************************************************************
//  fProcesaPorAlmacen
//
//  Esta función procesa el reporte de stock máximo y mínimo por almacén.
//
//  Parámetros 
//
//  Salidas
//
//  Historia de revisiones:
//  02/Abr/2002  N.D. 251  Se calcula el punto de reorden en base a cada detalle
//                         de características encontrado mismo que será
//                         para sacar el total del punto de reorden del
//                         producto.
//                         Para el caso de productos que no tienen
//                         Características, también se saca un total del
//                         producto por almacén para el caso en que se tenga que
//                         desplegar el punto de reorden total del producto. (EGV) 
//  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, lpRecibir, lpSurtir, lpRecibirSinAfectar, lpSurtirSinAfectar
Local lTotalMinimo, lTotalMaximo, lTotalExistencia, lTotalpRecibir, lTotalpSurtir
Local lDatosProducto, lDatosCaracteristica, lTextoCaracteristicas
Local lTmpMinimo, lTmpMaximo
Local lTotalExistenciaDetalle
// N.D.600
Local lTieneCaracteristicas

  // Establece el índice para cálculo de existencias
  tMovimiento.Usaindice['iProductoFechaAfecta']     // cIdProducto+cFecha+cAfectaExistencia

  // 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 menor al mínimo o mayor al máximo
  Si pImprimirProductos != 0
    lTotalMinimo = 0
    lTotalMaximo = 0
    lTotalExistencia = 0
    // Calcula el stock máximo, mínimo 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 lTieneCaracteristicas != 2 // Si el producto no tiene caracteristicas, buscamos el máximo y mínimo en el producto
      // Si atProducto(cControlExistencia) != 2 y atProducto(cControlExistencia) != 3 // Si el producto no tiene caracteristicas, buscamos el máximo y mínimo en el producto
      // 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 y mínimo en cada detalle de características
        lMinimo = 0
        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)
          lMinimo = lMinimo + lTmpMinimo
          lMaximo = lMaximo + lTmpMaximo
        FinMientras
      FinSi
      lExistencia = fExistenciaInicialProducto(atProducto(cIdProducto); tAlmacen(cIdAlmacen); gFechaCorteMasUnDia; gIdEjercicio; gPeriodo; pUnidad; atProducto(cIdUnidadBase); pIdUnidad; tMovimiento; tExistencia; tEquivalencia)
      lTotalMinimo = lTotalMinimo + lMinimo
      lTotalMaximo = lTotalMaximo + lMaximo
      lTotalExistencia = lTotalExistencia + lExistencia
    FinMientras

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

  // Inicia acumulados
  lTotalMinimo = 0.0
  lTotalMaximo = 0.0
  lTotalExistencia = 0.0
  lTotalpRecibir = 0.0
  lTotalpSurtir = 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

    // Se inicializa el punto de reorden del producto.
    gTotalReordenDetalleCarac = 0.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 mínimo del producto en el almacén 
      Si atProducto(cControlExistencia) != 2 y atProducto(cControlExistencia) != 3 // Si el producto no tiene caracteristicas, buscamos el máximo y mínimo 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 y mínimo en cada detalle de características
        lMinimo = 0
        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)
          lMinimo = lMinimo + lTmpMinimo
          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)
      // Encuentra las unidades pendientes de recibir y surtir del producto en el almacén 
      fObtienePendiente(atProducto(cIdProducto); 0; tAlmacen(cIdAlmacen); atProducto(cIdUnidadBase); &lpRecibir; &lpSurtir)
      // Ahora encuentra las unidades pendientes de recibir y surtir de documentos sin afectar
      fObtienePendienteSinAfectar(atProducto(cIdProducto); 0; tAlmacen(cIdAlmacen); atProducto(cIdUnidadBase); &lpRecibirSinAfectar; &lpSurtirSinAfectar)
      lpRecibir = lpRecibir + lpRecibirSinAfectar
      lpSurtir = lpSurtir + lpSurtirSinAfectar
      // Si el producto no ha sido impreso, se imprime
      Si !lDatosProducto
        fImprimeDatosProducto(tProducto)    
        lDatosProducto = 1
      FinSi
      // Imprime el almacen
      fImprimeAlmacen(tAlmacen; lMinimo; lMaximo; lExistencia; lpRecibir; lpSurtir)
      // acumula al total
      lTotalMinimo = lTotalMinimo + lMinimo
      lTotalMaximo = lTotalMaximo + lMaximo
      lTotalExistencia = lTotalExistencia + lExistencia
      lTotalpRecibir = lTotalpRecibir + lpRecibir
      lTotalpSurtir = lTotalpSurtir + lpSurtir
      // Truco para hacer que se imprima correctamente el total de punto de
      // reorden del producto.
      lTotalExistenciaDetalle = lExistencia + lpRecibir - lpSurtir
      fCalculaReorden( lMinimo; lMaximo; &lTotalExistenciaDetalle )
      gTotalReordenDetalleCarac = gTotalReordenDetalleCarac + lTotalExistenciaDetalle
    FinMientras
  Sino
    // Se inicializa el punto de reorden de todos los detalles del producto.
    gTotalReordenDetalleCarac = 0.0
    // 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, mínimo 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)
        // Encuentra las unidades pendientes de recibir y surtir del producto en el almacén
        fObtienePendiente(atProducto(cIdProducto); tDetalleProducto(cIdProducto); tAlmacen(cIdAlmacen); atProducto(cIdUnidadBase); &lpRecibir; &lpSurtir)
        // Ahora encuentra las unidades pendientes de recibir y surtir de documentos sin afectar
        fObtienePendienteSinAfectar(atProducto(cIdProducto); tDetalleProducto(cIdProducto); tAlmacen(cIdAlmacen); atProducto(cIdUnidadBase); &lpRecibirSinAfectar; &lpSurtirSinAfectar)
        lpRecibir = lpRecibir + lpRecibirSinAfectar
        lpSurtir = lpSurtir + lpSurtirSinAfectar
        // 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; 0; &lTextoCaracteristicas)
          lDatosCaracteristica = 1
        FinSi
        // Imprime el almacen
        fImprimeAlmacen(tAlmacen; lMinimo; lMaximo; lExistencia; lpRecibir; lpSurtir)
        // acumula al total
        lTotalMinimo = lTotalMinimo + lMinimo
        lTotalMaximo = lTotalMaximo + lMaximo
        lTotalExistencia = lTotalExistencia + lExistencia
        lTotalpRecibir = lTotalpRecibir + lpRecibir
        lTotalpSurtir = lTotalpSurtir + lpSurtir
        lTotalExistenciaDetalle = lExistencia + lpRecibir - lpSurtir
        fCalculaReorden( lMinimo; lMaximo; &lTotalExistenciaDetalle )
        gTotalReordenDetalleCarac = gTotalReordenDetalleCarac + lTotalExistenciaDetalle
      FinMientras
    FinMientras
  FinSi
  // Imprime los totales del producto
  Si lDatosProducto
    fImprimeTotales(lTotalMinimo; lTotalMaximo; lTotalExistencia; lTotalpRecibir; lTotalpSurtir)
  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 // 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
  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 // Global?
    Texto '^@TC{0,0,0}@S{16}STOCK MAXIMO Y MINIMO GLOBAL'
  Sino // Por Almacén
    Texto '^@TC{0,0,0}@S{16}STOCK MAXIMO Y MINIMO POR ALMACEN'
  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'
  Texto C8;'^Stock'
  Texto C12;'^Pendiente'
  Texto C14;'^Pendiente'
  Texto C16;'^Punto de'
  Imprime
  Texto C0;'Código'
  Si pTipoReporte = 0 // Global?
    Texto C2;'Nombre (Producto)'
  Sino // Por Almacén
    Texto C2;'Nombre (Almacén)'
  Finsi  
  Texto C6;'^Mínimo'
  Texto C8;'^Máximo'
  Texto C10;'^Existencia'
  Texto C12;'^de Recibir*'
  Texto C14;'^de Surtir**'
  Texto C16;'^Reorden'
  // RPC - SC 150 En el reporte global se usa una columna extra 
  // para mostrar si hay error de existencias o costos
  Si pTipoReporte = 0 // Reporte global?
    Texto C18;'Error'
  FinSi
  // FIN RPC - SC 150
  Imprime
  Texto '@TC{0,0,0}@L{-1}'
  Imprime 1
  NEGRITAS FALSE

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


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

// 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 // 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)
Saltalin
Texto ' * Nota: La columna de pendiente de recibir incluye órdenes de compra y movimientos de entrada "sin afectar".'
Imprime
Texto '** Nota: La columna de pendiente de surtir incluye pedidos y movimientos de salida "sin afectar".'
Imprime

// RPC - SC 150 En el reporte global ahora se imprime al final la leyenda 
// con los errores de existencias o costos
Si pTipoReporte = 0
  Columnas 19;0.14,19.0,0.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 movimiento 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
// FIN RPC - SC 150
