// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
//
// RastreoExisNeg.rpt
// Nombre del reporte en el sistema: Rastreo de Existencia Negativa
//
//  Sistema: AdminPAQ v1.1
// 
//  Este reporte perteneciente al módulo de inventarios, busca si un 
//  producto tiene existencia negativa desde el inicio del ejercicio 
//  vigente hasta el final de su historia de operaciones de entradas
//  y salidas. Si la tiene, entonces despliega el producto y el almacén
//  junto con la cantidad en positivo de la existencia negativa y una
//  fecha como sugerencia para hacer el ajuste en el inventario.
//
//  Consideraciones especiales:
//  Para este reporte se tomó como base el kardex en unidades.
//  
// Elaboró:    Raúl Velasco Gonzáles, Eduardo Nuño Lupercio y Daniel Robles
// Fecha:      14 de Octubre del 2008
//
// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
// Historia de modificaciones
//  Fecha      Nombre   IDLotus                     Descripcion
// <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
// 24/10/2008   DRdA    SC 209      Implementación inicial
// 19/02/2009   DRdA    SC 209      Cambio para solo revisar del final de la historia
//                                  al inicio del ejercicio vigente en lugar de toda la
//                                  historia del producto.
// <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

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

Incluye LibAdminPAQ.Rpt
Incluye LibInventarios.Rpt


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

Parametro pProductoInicial = 'AD10ADM10'        // Código del producto inicial del rango (' ' = Todos)
Parametro pProductoFinal   = 'AD10AVADMAV10'          // Código del producto final del rango (' ' = Todos)
//Parametro pProductoInicial = 'AD10AVADMAV10'        // Código del producto inicial del rango (' ' = Todos)
//Parametro pProductoFinal   = 'AD10NW10'          // Código del producto final del rango (' ' = Todos)
//Parametro pProductoInicial = ' '        // Código del producto inicial del rango (' ' = Todos)
//Parametro pProductoFinal   = ' '          // Código del producto final del rango (' ' = Todos)
Parametro pAlmacenInicial = ' '         // Código inicial del almacen
Parametro pAlmacenFinal = ' '           // Código final del almacén
Parametro pImprimirProductos = 0        // Productos a imprimir:
                                        // 0 = Solo productos con existencia negativa 
                                        // 1 = Todos
                                        
pFechaInicial = '20000101'    // Fecha inicial del rango
pFechaFinal = '20111231'      // Fecha final del rango
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
pIdUnidad = 2                 // Id de la unidad equivalente

// *****************************************************************************
//  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

// N.D.1377 Inicio
// Productos identificadores a fin de saber si el producto es un detalle de
//  caracteristicas o no
UsaTabla tDetalleProducto = tEmpresa['MGW10004']  // Productos
tDetalleProducto.Usaindice['']                    // Indice primario: cIdProducto
// N.D.1377 Fin

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

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

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

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 tAlmacen = tEmpresa['MGW10003']          // Almacenes
tAlmacen.Usaindice['']                            // Indice primario: cIdAlmacen

UsaTabla tAlmacenRecorrido = tEmpresa['MGW10003'] Alias 'AlmacenRecorrido' //Almacenes
tAlmacenRecorrido.Usaindice['cCodigoAlmacen']     // Indice primario: cIdAlmacen

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

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

// *****************************************************************************
//  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 inicio del reporte
gIdEjercicio = 0                  // ID del ejercicio que contiene la fecha de inicio del reporte
gDescUnidad = ''                  // Descripción de la unidad de medida o peso
gMascarillaProducto = ''          // Mascarilla para el código del producto
gMascarillaAlmacen = ''           // Mascarilla para el código del almacén
gExistenciasNegativas = 0
gExistNegGlobal = 0
gIndRen = -1
gFecha = ''
gExNegEjCerr = 0
gProdYaImpreso = 0
//RCVG

// *************************************
// Definicion de constantes de EXCEL
// *************************************
constante xlCentrarEnSeleccion  7
constante xlAutomatico          -4105
constante xlManual              -4135
constante xlInferior            -4107
constante xlSubrayadoDoble      -4119
constante xlIzquierda           -4131
constante xlMediano             -4138
constante xlNinguno             -4142
constante xlDerecha             -4152
constante xlSuperior            -4160
constante xlHojaCalculo         -4167
constante xlLineaSimple         1
constante xlBordeSuperior       8
constante xlBordeInferior       9
constante xlBordeIzquierda      7
constante xlBordeDerecha        10
constante xlCentro              -4108

// *************************************
// Inicializa EXCEL
// *************************************
// Accesa objetos de excel. Crea un objeto de Excel con la funcion @CreaObjeto(). A esta funcion
// se le pasa un identificador de OLE de la aplicacion que se desea crear. En este caso el identificador
// es Excel.Application.5
Objeto Excel = @CreaObjeto('Excel.Application')

// Invoca el metodo de EXCEL Libros y luego el metodo Agregar para crear un libro de hoja de calculo
// representado por la constante de EXCEL (xlHojaCalculo).
// Es importante notar que estos metodos NO LOS EJECUTA EL REPORTEADOR sino que los hace EXCEL
// y estan documentados en el Help de EXCEL
Objeto wb = Excel.WorkBooks.Add(xlHojaCalculo)

// Accesa la hoja de calculo (HojasCalculo) numero 1 del Libro creado anteriormente
// y representado por la variable wb, de la instruccion anterior
Objeto ws = wb.Worksheets(1)

// Hace visible a EXCEL. Para Excel, TRUE o VERDADERO es -1, FALSE o FALSO es 0
Excel.ActiveWindow.DisplayGridlines = FALSE[C0]
Excel.Visible = -1[C0]

RenActual = 1

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

// C0 (Cod. Prod. y Nombre) = 30 caracteres
// C1 (Vacía) = 0 caracteres
// C2 (Separador) = 1 caracteres
// C3 (Unidad) = 6 caracteres
// C4 (Vacía) = 0 caracteres
// C5 (Separador) = 1 caracteres
// C6 (Método de costeo) = 20 caracteres
// C7 (Vacía) = 0 caracteres
// C8 (Separador) = 1 caracteres
// C9 (Cod. Alm. y Nombre) = 22 caracteres
// C10 (Vacía) = 0 caracteres
// C11 (Vacía) = 0 caracteres
// C12 (Fecha para ajuste) = 18 caracteres
// C13 (Separador) = 1 caracteres
// C14 (Existencia Negativa) = 18 caracteres
// C15 (Separador) = 1 caracteres
Columnas 16;4.48,0.0,0.15,0.9,0.0,0.15,2.99,0.0,0.15,3.29,0.0,0.15,2.69,0.15,2.69,0.15

Usaletra 'Arial'
Altura 9
//[C10,C12,C14].Coma
[C14].Coma
[C0,C3,C6,C9,C12].Decimales 0
[C0,C3,C6,C9,C12].Blanco ''

// Pone la longitud de cada columna
// A (Cod. Prod. y Nombre) = 34 caracteres
ws.Columns(1).ColumnWidth = 34[C0]
// B (Unidad) = 6 caracteres
ws.Columns(2).ColumnWidth = 6[C0]
// C (Método de costeo) = 20 caracteres
ws.Columns(3).ColumnWidth = 20[C0]
// D (Cod. Alm. y Nombre) = 34 caracteres
ws.Columns(4).ColumnWidth = 34[C0]
// E (Fecha para ajuste) = 18 caracteres
ws.Columns(5).ColumnWidth = 18[C0]
// F (Existencia Negativa) = 18 caracteres
ws.Columns(6).ColumnWidth = 18[C0]

//ws.Columns(1).ColumnWidth = 1.5[C0] //A
//ws.Columns(2).ColumnWidth = 12[C0]  //B
//ws.Columns(3).ColumnWidth = 8[C0]   //C
//ws.Columns(4).ColumnWidth = 10[C0]  //D
//ws.Columns(5).ColumnWidth = 30[C0]  //E
//ws.Columns(6).ColumnWidth = 12[C0]  //F
//ws.Columns(7).ColumnWidth = 15[C0]  //G
//ws.Columns(8).ColumnWidth = 15[C0]  //H
//ws.Columns(9).ColumnWidth = 15[C0]  //I
//ws.Columns(10).ColumnWidth = 15[C0] //J
//ws.Columns(11).ColumnWidth = 15[C0] //K
//ws.Columns(12).ColumnWidth = 15[C0] //L
//ws.Columns(13).ColumnWidth = 15[C0] //M

// Busca el ejercicio vigente para tomar la fecha inicial y esta será a partir de donde se
// ejecutará este reporte
tParametros.Busca Primero
tEjercicio.BUSCA Ultimo
Mientras tEjercicio->Encontro
  Si tEjercicio(cIdEjercicio) = tParametros(cIdEjercicioActual)
    FinCiclo
  FinSi
  tEjercicio.SaltaReg[-1]
FinMientras
pFechaInicial = tEjercicio(cFecIniPeriodo1)
// Inicializa valores globales para la biblioteca LibAdminPAQ.rpt
fIniFechasHistoria(tEjercicio)
// Toma toda la historia para procesar los productos en el reporte
//pFechaInicial = gFechaIniHistoria
pFechaFinal = gFechaFinHistoria

// *****************************************************************************
//  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()
  LOCAL lDigDecimales, lDecs, lFormato

  lDigsDecimales = '0000000000'

  // Busca el registro de la empresa
  tParametros.Busca Primero
  Si tParametros->Encontro // Si lo encontró
    [C14].Decimales tParametros(cDecimalesUnidades)
    lDecs = @SUBSTR(lDigsDecimales;1;tParametros(cDecimalesUnidades))
    lFormato = '#,##0.' & lDecs & '_);(#,##0.' & lDecs & ')'
    ws.Columns(6).NumberFormat = lFormato[C0]

    gMascarillaProducto = tParametros(cMascarillaProducto)  // mascarilla para el código del producto
    gMascarillaAlmacen = tParametros(cMascarillaAlmacen)    // mascarilla para el código del almacen
  Sino // Si no encontró el registro de la empresa
    [C14].Decimales 2
    ws.Columns(6).NumberFormat = '#,##0.00_);(#,##0.00)'[C0]
  FinSi
FinFunc


// *****************************************************************************
//  fImprimeTotalGeneral
//
//  Esta función imprime los totales generales cuando se trata de los reportes
// por almacén
//
//  Parámetros 
//
//  Salidas 
// *****************************************************************************
DefFunc fImprimeTotalGeneral(aTotal)
//Local vImprime
//
//  vSubraya = '===================='[C14]
//  Imprime vSubraya
//  vImprime = ' '[C0..C15]
//  vImprime = '^Total General: '[C12]
//  vImprime = @Col(aTotal;0)[C14]
//  Lista vImprime
  // Avanza de renglon
  RenActual = RenActual + 1[C0]
  ws.Range('F' & RenActual & ':F' & RenActual).Borders(xlSuperior).LineStyle = xlLineaSimple[C0]
  ws.Range('F' & RenActual & ':F' & RenActual).Borders(xlIzquierda).LineStyle = xlLineaSimple[C0]
  ws.Range('F' & RenActual & ':F' & RenActual).Borders(xlDerecha).LineStyle = xlLineaSimple[C0]
  ws.Range('F' & RenActual & ':F' & RenActual).Borders(xlInferior).LineStyle = xlLineaSimple[C0]
  ws.Range('E' & RenActual & ':F' & RenActual).Font.FontStyle = 'Negrita'[C0]    
  ws.Range('E' & RenActual).HorizontalAlignment = xlCentro[C0]
  ws.Range('E' & RenActual).Value = 'Total General:'[C0]
  ws.Range('F' & RenActual).Value = @Col(aTotal;0)[C0]
  Si @COL(gExNegEjCerr;1) = 1
    // Avanza de renglon
    RenActual = RenActual + 1[C0]
//    ws.Range('A' & RenActual).HorizontalAlignment = xlIzquierda[C0]
    ws.Range('A' & RenActual).Value = '** - La existencia negativa se presenta desde un ejercicio cerrado.'[C0]
  FinSi
FinFunc

// *****************************************************************************
//  fImprimeExisNeg
//
//  Esta función imprime la existencia negativa en la fecha en que se produjo y
//  si no se han impreso los datos del producto también los imprime
//
//  Parámetros 
//
//  Salidas 
// *****************************************************************************
DefFunc fImprimeExisNeg(atProducto: Tabla)
//Local vImprime
Local lCodigoEditado

  // Avanza de renglon
  RenActual = RenActual + 1[C0]
  Si gProdYaImpreso = 0
    gProdYaImpreso = 1
    ws.Range('A' & RenActual & ':F' & RenActual).Borders(xlIzquierda).LineStyle = xlLineaSimple[C0]
    ws.Range('A' & RenActual & ':F' & RenActual).Borders(xlDerecha).LineStyle = xlLineaSimple[C0]
    lCodigoEditado = fMascarilla(atProducto(cCodigoProducto); gMascarillaProducto)
    ws.Range('A' & RenActual).HorizontalAlignment = xlIzquierda[C0]
    ws.Range('A' & RenActual).Value = lCodigoEditado[C0]
    lCodigoEditado = fMascarilla(tAlmacenRecorrido(cCodigoAlmacen); gMascarillaAlmacen)
    ws.Range('D' & RenActual).HorizontalAlignment = xlIzquierda[C0]
    ws.Range('D' & RenActual).Value = lCodigoEditado[C0]
    Si @COL(gExNegEjCerr;0) = 1
      ws.Range('F' & RenActual).HorizontalAlignment = xlDerecha[C0]
      ws.Range('F' & RenActual).Value = '**'[C0]
    FinSi
    RenActual = RenActual + 1[C0]
    ws.Range('A' & RenActual & ':F' & RenActual).Borders(xlIzquierda).LineStyle = xlLineaSimple[C0]
    ws.Range('A' & RenActual & ':F' & RenActual).Borders(xlDerecha).LineStyle = xlLineaSimple[C0]
    ws.Range('A' & RenActual).Value = atProducto(cNombreProducto)[C0]

    // Imprime unidad de medida y peso
    fVerificaUnidad(atProducto)
    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
      // Guarda la descripción de la unidad en una variable global
      // Así se evita tener que leerla nuevamente al desplegar los totales
      gDescUnidad = tUnidad(cDespliegue) 
    Sino
      gDescUnidad = '(N)'
    FinSi
    ws.Range('B' & RenActual).Value = @Col(gDescUnidad;0)[C0]

    Escoge atProducto(cMetodoCosteo)
      Caso 1
        ws.Range('C' & RenActual).Value = 'Promedio'[C0]
      FinCaso
      Caso 2
        ws.Range('C' & RenActual).Value = 'Promedio por Almacén'[C0]
      FinCaso
      Caso 3
        ws.Range('C' & RenActual).Value = 'Ultimo Costo'[C0]
      FinCaso
      Caso 4
        ws.Range('C' & RenActual).Value = 'UEPS'[C0]
      FinCaso
      Caso 5
        ws.Range('C' & RenActual).Value = 'PEPS'[C0]
      FinCaso
      Caso 6
        ws.Range('C' & RenActual).Value = 'Costo Específico'[C0]
      FinCaso
      Caso 7
        ws.Range('C' & RenActual).Value = 'Costo Estándar'[C0]
      FinCaso
    FinEscoge
  
    ws.Range('D' & RenActual).Value = tAlmacenRecorrido(cNombreAlmacen)[C0]
  SiNo
    ws.Range('A' & RenActual & ':F' & RenActual).Borders(xlIzquierda).LineStyle = xlLineaSimple[C0]
    ws.Range('A' & RenActual & ':F' & RenActual).Borders(xlDerecha).LineStyle = xlLineaSimple[C0]
  FinSi
  
  Si gExistenciasNegativas < 0.0
    gFecha = fFormateaFecha(gFecha; gIdioma)
    gFecha = @COL(gFecha;0)
    ws.Range('E' & RenActual).Value = @CHR(39) & @COL(gFecha;1)[C0]
    gExistNegGlobal = gExistNegGlobal + gExistenciasNegativas
  FinSi
  ws.Range('F' & RenActual).Value = @Col(gExistenciasNegativas;0)[C0]
FinFunc

// *****************************************************************************
//  fProcesaPorAlmacen
//
//  Esta función lee y procesa los movimientos de entrada y salida cuando el
//  método de costeo es diferente de UEPS o PEPS, es decir, sin capas. Sólo aplica
// para reportes por almacén
//
//  Parámetros 
//
//  Salidas 
// *****************************************************************************
// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
// Historia de modificaciones
//  Fecha      Nombre   IDLotus   Descripcion
// <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
// 14/10/2008   DRdA              Implementación inicial.
// <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
DefFunc fProcesaPorAlmacen(atProducto: Tabla)
Local lUnidadesMovto, lFechaCiclo
Local lAlmacen, lInventario
Local lHuboMovs
//Local lAuxiliar
 
  // Inicializa la Fecha de inicio del rango de fechas del reporte
  lFechaCiclo = pFechaInicial
 
  Mientras [ tAlmacenRecorrido.Busca[pAlmacenInicial & '..' & pAlmacenFinal]; tAlmacenRecorrido->Encontro; tAlmacenRecorrido.Busca Siguiente[1] ]
    lAlmacen = tAlmacenRecorrido(cIdAlmacen) 
    lFechaCiclo = pFechaInicial
    // Establecer el índice usado por el calculo de costo y existencia inicial

    // calcula el inventario inicial
    lInventario = fExistenciaInicialProducto(atProducto(cIdProducto); lAlmacen; pFechaFinal; gIdEjercicio; gPeriodo; pUnidad; atProducto(cIdUnidadBase); pIdUnidad; tMovimiento; tExistencia; tEquivalencia)
    gExistenciasNegativas = lInventario

    gProdYaImpreso = 0
    gExNegEjCerr[0] = 0
    Si lInventario < 0.0
      gExNegEjCerr = 1
      gFecha = pFechaInicial
      fImprimeExisNeg(atProducto)
    FinSi

    // Regresar al índice usado por el reporte 
    //tMovimiento.Usaindice['iProductoAlmacenFechaAfec']    // cIdProducto+cIdAlmacen+cFecha+cAfectaExistencia
    tMovimiento.Usaindice['iProductoAlmacenAfecFecha'] // cIdProducto+cIdAlmacen+CAFECTADOINVENTARIO+cFecha N.D.5062
    tMovimiento.Busca[@Str(atProducto(cIdProducto);0)]
    // Recorre todas las fechas en el rango de fechas del reporte
    Mientras lFechaCiclo <= pFechaFinal
      lHuboMovs = 0
      // Busca el primer movimiento en esa fecha
      // Si encontro al menos un movimiento, sigue buscando otros movimientos en esa misma fecha
      //Mientras [ tMovimiento.Busca[atProducto(cIdProducto) & ':' & lAlmacen & ':' & lFechaCiclo]; tMovimiento->Encontro; tMovimiento.Busca Siguiente[3] ]
      Mientras [ tMovimiento.Busca[atProducto(cIdProducto) & ':' & lAlmacen & ':' & 1 & ':' &  lFechaCiclo]; tMovimiento->Encontro; tMovimiento.Busca Siguiente[4] ]//N.D.5062
        // Discrimina los movimientos que no son entradas ni salidas
        Si tMovimiento(cAfectaExistencia) != 1 y tMovimiento(cAfectaExistencia) != 2
          Continua
        FinSi

        // N.D.1377 Inicio
        // Discrimina movimientos incompletos de características
        // Revisa si el movimiento corresponde a un detalle de características
        Si tMovimiento(cTipoTraspaso) = 4
          // Busca el producto identificador
          tDetalleProducto.Busca[@Str(tMovimiento(cIdProducto);0)]
          Si tDetalleProducto->Encontro
  	        // Si el producto corresponde a un padre de características, entonces
            //  no hay que procesar este movimiento ya que es un movto
            //  incompleto de características
            Si tDetalleProducto(cTipoProducto) = 0
              Continua
            FinSi
          FinSi
        FinSi
        // N.D.1377 Fin

        lUnidadesMovto = fUnidades(pUnidad; tProducto(cIdUnidadBase); pIdUnidad; tMovimiento; tEquivalencia)
        Si tMovimiento(CAFECTADOINVENTARIO) != 0
          lHuboMovs = 1
          Si tMovimiento(cAfectaExistencia) = 1 // Entradas ================================================
            gExistenciasNegativas = gExistenciasNegativas + lUnidadesMovto //N.D.RCVG
            //Lista gExistenciasNegativas
            //Lista atMovimiento(cAfectaExistencia)& ' TEnt'
          Sino // Salidas =================================================================================
            gExistenciasNegativas = gExistenciasNegativas - lUnidadesMovto //N.D.RCVG
            //Lista lUnidadesMovto & ' US'
            //Lista gExistenciasNegativas & ' ENS'
            //lAuxiliar = @Str(atMovimiento(cAfectaExistencia);0)
            //Lista 'cant ' & lAuxiliar & ' TSal'
          FinSi  // Si el movimiento es entrada o salida
//          gIndRen = gIndRen + 1
//!          {gIndRen}[0] = lUnidadesMovto
//!          {gIndRen}[1] = @Str(tMovimiento(cIdMovimiento);0)
//!          {gIndRen}[2] = tMovimiento(cAfectaExistencia)
//!          {gIndRen}[3] = tMovimiento(cFecha)
//{gIndRen}[4] = tMovimiento(cIdAlmacen)
          //lAuxiliar = @Str(lUnidadesMovto;0)
          //Lista lAuxiliar & ' UNCON'
//Lista tMovimiento(cIdMovimiento)
//Lista tMovimiento(cFecha)
//Lista tMovimiento(cAfectaExistencia)
//Imprime lUnidadesMovto
        FinSi

      FinMientras
      
      Si lHuboMovs = 1 Y (pImprimirProductos O (!pImprimirProductos Y gExistenciasNegativas < 0.0))
        gFecha = lFechaCiclo
        fImprimeExisNeg(atProducto)
      FinSi

      // Incrementa un día a la fecha
      lFechaCiclo = fFechaSiguiente(lFechaCiclo)
    FinMientras
    
    ws.Range('A' & RenActual & ':F' & RenActual).Borders(xlInferior).LineStyle = xlLineaSimple[C0]
  FinMientras
FinFunc


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

// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
// Historia de modificaciones
//  Fecha      Nombre   IDLotus                     Descripcion
// <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
// 19/03/2002   CILA    SC 35     Se agregaron los textos que incluye los movtos
//                                afectados, sin afectar o todos.
// 26/07/20002  RPC     SC 150    Ya no se imprimen el texto y el asterisco de 
//                                movimientos sin afectar
// <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

Encab
  // Pone el nombre del sistema
  ws.Range('A' & RenActual).Font.Color = @RGB(255;0;0)[C0]
  ws.Range('A' & RenActual).Font.Size = 10[C0]
  ws.Range('A' & RenActual).Value = 'AdminPAQ'[C0]
  ws.Range('F' & RenActual).Font.Size = 10[C0]
  ws.Range('F' & RenActual).HorizontalAlignment = xlDerecha[C0]
  ws.Range('F' & RenActual).Value = 'Fecha: ' & fFormateaFecha(@Hoy();gIdioma)[C0]
  // Avanza de renglon
  RenActual = RenActual + 1

  // Pone el nombre de la empresa
  ws.Range('A' & RenActual).Font.Size = 14[C0]
  ws.Range('A' & RenActual).Font.FontStyle = 'Negrita'[C0]
  // Lo centra en las columnas A a la F
  ws.Range('A' & RenActual & ':F' & RenActual).HorizontalAlignment = xlCentrarEnSeleccion[C0]
  ws.Range('A' & RenActual ).Value = tEmpresa->Nombre
  // Avanza de renglon
  RenActual = RenActual + 1[C0]

  // Pone el titulo del reporte en EXCEL
  ws.Range('A' & RenActual).Font.Size = 12[C0]
  ws.Range('A' & RenActual).Font.FontStyle = 'Negrita'[C0]
  ws.Range('A' & RenActual ).Value = 'D E T E C C I Ó N   D E   E X I S T E N C I A   N E G A T I V A'[C0]
  // Lo centra en las columnas A a la F
  ws.Range('A' & RenActual & ':F' & RenActual).HorizontalAlignment = xlCentrarEnSeleccion[C0]
  // Avanza de renglon
  RenActual = RenActual + 1[C0]
  // Pone el rango de fechas del reporte en EXCEL
  ws.Range('A' & RenActual).Font.Size = 12[C0]
  ws.Range('A' & RenActual).Font.FontStyle = 'Negrita'[C0]    
  ws.Range('A' & RenActual ).Value = 'Del: ' & fFormateaFecha(pFechaInicial;gIdioma) & ' Al: ' & fFormateaFecha(pFechaFinal;gIdioma)[C0]
  // Lo centra en las columnas A a la F
  ws.Range('A' & RenActual & ':F' & RenActual).HorizontalAlignment = xlCentrarEnSeleccion[C0]
  // Avanza de renglon
  RenActual = RenActual + 1[C0]

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

  ws.Range('A' & RenActual & ':F' & RenActual).Interior.ColorIndex = 3[C0]    
  // Avanza de renglon
  RenActual = RenActual + 1[C0]

  // Avanza de renglon
  ws.Range('A' & RenActual ).Value = '* - El contenido de este reporte solo es informativo, no genera derechos ni obligaciones.'[C0]
  ws.Range('A' & RenActual & ':F' & RenActual).Borders(xlInferior).LineStyle = xlLineaSimple[C0]
  // Avanza de renglon
  RenActual = RenActual + 1[C0]
  // Pone el titulo de cada columna en EXCEL
  ws.Range('A' & RenActual & ':F' & RenActual).Borders(xlIzquierda).LineStyle = xlLineaSimple[C0]
  ws.Range('A' & RenActual & ':F' & RenActual).Borders(xlDerecha).LineStyle = xlLineaSimple[C0]
  ws.Range('A' & RenActual & ':F' & RenActual).Interior.ColorIndex = 55[C0]    
  ws.Range('A' & RenActual & ':F' & RenActual).Font.FontStyle = 'Negrita'[C0]    
  ws.Range('A' & RenActual & ':F' & RenActual).Font.Color = @RGB(255;255;255)[C0]

  ws.Range('E' & RenActual & ':F' & RenActual).HorizontalAlignment = xlDerecha[C0]
  ws.Range('A' & RenActual & ':F' & RenActual).Value = 'Cod. Producto','','','Cod. Almacén','*',''
  // Avanza de renglon
  RenActual = RenActual + 1[C0]
  ws.Range('A' & RenActual & ':F' & RenActual).Borders(xlIzquierda).LineStyle = xlLineaSimple[C0]
  ws.Range('A' & RenActual & ':F' & RenActual).Borders(xlDerecha).LineStyle = xlLineaSimple[C0]
  ws.Range('A' & RenActual & ':F' & RenActual).Interior.ColorIndex = 55[C0]    
  ws.Range('A' & RenActual & ':F' & RenActual).Font.FontStyle = 'Negrita'[C0]    
  ws.Range('A' & RenActual & ':F' & RenActual).Font.Color = @RGB(255;255;255)[C0]

  ws.Range('F' & RenActual).HorizontalAlignment = xlDerecha[C0]
  ws.Range('A' & RenActual & ':F' & RenActual).Value = 'Nombre','Unidad','Método de costeo','Nombre','Fecha para ajuste','Existencia Negativa'
  ws.Range('A' & RenActual & ':F' & RenActual).Borders(xlInferior).LineStyle = xlLineaSimple[C0]
FinEncab


// *****************************************************************************
// Cuerpo del reporte
// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
// Historia de modificaciones
//  Fecha      Nombre   IDLotus                     Descripcion
// <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
// 14/10/2008   DRdA    SC 209      Implementación inicial.               
// <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

// Configura los decimales del reporte
fConfiguraDecimales()

// A partir de la fecha de inicio del reporte, obtiene el ejercicio y periodo correspondiente
// esto es util para el cálculo del inventario inicial del reporte
fEjercicioPeriodo(pFechaInicial; &gPeriodo; &gIdEjercicio; tEjercicio) 

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

// 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 el rango inicial es el primer almacén, recupera el código del primero
Si pAlmacenInicial = ' '
  tAlmacenRecorrido.Busca Primero
  Si tAlmacenRecorrido->Encontro
    Si tAlmacenRecorrido(cIdAlmacen) = 0
      tAlmacenRecorrido.Saltareg[1]
    FinSi
    Si tAlmacenRecorrido->Encontro
      pAlmacenInicial = tAlmacenRecorrido(cCodigoAlmacen)
    FinSi
  FinSi
FinSi

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

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

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

  // Para este reporte solo se procesan productos activos
  // tProducto(cStatusProducto) = 0 (Inactivo)
  // tProducto(cStatusProducto) = 1 (Activo)
  Si tProducto(cStatusProducto) = 0
    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

  //tMovimiento.Usaindice['iProductoAlmacenFechaAfec']     // cIdProducto+cIdAlmacen+cFecha+cAfectaExistencia
  tMovimiento.Usaindice['iProductoAlmacenAfecFecha'] // cIdProducto+cIdAlmacen+CAFECTADOINVENTARIO+cFecha N.D.5062
  
  fProcesaPorAlmacen(tProducto) 

FinMientras

// Imprimir los totales del reporte
fImprimeTotalGeneral(gExistNegGlobal)
