// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
//
//  ExistAlmacen.rpt
//  Nombre del reporte en el sistema: Inventario Actual del Almacén por Producto
//
//  Sistema: AdminPAQ v1.1 
// 
//  Este reporte perteneciente al módulo de inventarios, despliega el 
//  inventario inicial, movimientos de entrada y salida, así como el 
//  inventario final por productos agrupados por almacén tanto en unidades 
//  como en importes. Este reporte sustituye al reporte de existencias por 
//  periodo de Megapaq
//
//	Consideraciones especiales:
//  Este reporte aunque está programado como uno solo, representa en realidad
//  dos reportes en el ámbito del sistema:
//    Inventario Actual del Almacén por Producto (En Unidades)
//    Inventario Actual del Almacén por Producto (En Importes)
//  
//  Elaboró:    Ricardo Paláu Castañeda basado en el reporte RelEntSal
//  Fecha:      10 de Febrero del 2003
//  Modificacion:    HVA
//  Fecha:      09 de Marzo del 2005 ND 3138
//  Modificacion Hva ND 3472 04/10/2005 Cuando se oprime solo con existencias debe mostrar productos 
//  que tengan existencia a la fecha final del reporte
//  Modificacion Hva ND 3527 16/11/2005 Es muy lento el reporte
//  Fecha       N.D.  Autor   Descripción
//  16/11/2005  3010  LAP     Nuevo esquema de búsquedas parciales para agilizar los reportes
//                            - fProcesaPorAlmacen
//  23/02/2006  HVA     N.D.3868    Se duplican los costos cuando el producto tiene unidadades no convertibles.
//  21/05/2007 JAAM     N.C.147     Esquema de adicion de unidades de compra y venta  
//  05/02/2008 JENL     N.D.5062    ERROR TARDA 6 MINUTOS AL MOMENTO DE CONVERTIR UN PEDIDO EN FACTURA
//                                  Se modifico:
//                                  -fProcesaPorAlmacen
//  19/11/2008 ELVS     N.C.224     Se deben considerar los movimientos que están con el 
//                                  campo cAfectadoInventario = 2, se agrega marca y leyenda
//                                  de que esos movimientos están en captura.
//  16/12/2008 ELVS     N.D.5585    REPORTES DE INVENTARIO - CUANDO EL PRODUCTO ESTA EN 
//                                  CAPTURA. En los reportes de capa el texto de explicación
//                                  aparece incompleto y en otros reportes aparece el error
//                                  pero no que se puede hacer. 
//  18/03/2009 ELVS     N.D.5727    REPORTES DE INVENTARIO Inventario Actual del Almacen por
//                                  producto en importes y en unidades - CUANDO EL PRODUCTO ESTA EN
//                                  CAPTURA. Solo indica el error (8) para el primer producto capturado
//                                  en el documento los demas los omite.
//	01/07/2009  MAJU		N.D.4538		ERROR SE MUESTRAN CANTIDADES INCORRECTAS A PARTIR DE UN PRODUCTO QUE NO TIENE 
//  																LA MISMA UNIDAD NO EQUIVALENTE  QUE EL RESTO CUANDO SE PIDE EL REPORTE POR 
//																	UNIDAD NO EQUIVALENTE  AL GENERAR EL REPORTE INVENTARIO ACTUAL DEL ALMACEN 
//																	POR PRODUCTO (EXCEL) Se modifica: Cuerpo del reporte.
// <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

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

Incluye LibAdminPAQ.Rpt
Incluye LibInventarios.Rpt


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

Parametro pFechaInicial = '20030101'    // Fecha inicial del rango
Parametro pFechaFinal = '20031231'      // Fecha final del rango
Parametro pAlmacenInicial = ' '         // Código del almacén inicial del rango (' ' = Todos)
Parametro pAlmacenFinal = ' '           // Código del almacén 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 pImprimirProductos = 0        // Productos a imprimir:
                                        // 0 = Todos
                                        // 1 = Con Movimientos o existencias
Parametro pCosto = 0                    // Método de Costeo:
                                        // 0 = Definido por el producto
                                        // 1 = Costo Promedio (Global)
                                        // 2 = Costo Promedio (Por Almacén)
                                        // 3 = Ultimo Costo (Global)
                                        // 4 = Ultimo Costo (Por Almacén)
Parametro pEstadoProducto = 0           // Incluir productos con estado:
                                        // 0 = Todos
                                        // 1 = Activos
                                        // 2 = Inactivos
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)
Parametro pIdUnidad = 2                 // Id de la unidad equivalente
Parametro pIdClase1 = 0                 // Clasificaciones del produto (0 = Ninguna)
Parametro pIdClase2 = 0                 // Estos seis parámetros corresponden a las seis clasificaciones
Parametro pIdClase3 = 0                 // del producto, y pueden contener una lista de IDs
Parametro pIdClase4 = 0                 // de clasificaciones separados por coma.
Parametro pIdClase5 = 0               
Parametro pIdClase6 = 0               
Parametro pIdMoneda = 1                 // Id de la moneda a expresar el reporte
Parametro pTipoReporte = 0              // Tipo de Reporte (este parámetro es interno a AdminPAQ)
                                        // 0 = Inventario actual en unidades
                                        // 1 = Inventario actual en importes ( costos )

// *****************************************************************************
//  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 tDocumento = tEmpresa['MGW10008']        // Documentos
tDocumento.Usaindice['']                          // Indice primario: cIdDocumento

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

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

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

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

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

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

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

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

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

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

UsaTabla 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

Si pTipoReporte = 0 // En unidades
  // Ancho de columnas = caracteres / 17 * 2.54
  // Se consideran columnas intermedias de un espacio
  // C0  (Código del producto o almacén) = 20 caracteres
  // C2  (Nombre del producto o almacén) = 34 caracteres 
  // C4  (Unidad de medida o peso) = 6 caracteres
  // C6  (Inventario inicial) = 14 caracteres
  // C8  (Entradas) = 14 caracteres
  // C10 (Salidas) = 14 caracteres
  // C12 (Existencia) = 14 caracteres
  // C14 (Rotación inventario) = 10 caracteres
  // C16 (Error de costos) = 5 caracteres
  //N.C.224
  Columnas 18;2.99,0.15,5.08,0.15,0.90,0.15,2.09,0.15,2.09,0.15,2.09,0.15,2.09,0.15,1.49,0.15,0.70,0.70
Sino  // En importes
  // C0  (Código del producto o almacén) = 20 caracteres
  // C2  (Nombre del producto o almacén) = 30 caracteres 
  // C4  (Método de Costeo) = 10 caracteres, no se usa
  // C6  (Inventario inicial) = 17 caracteres
  // C8  (Entradas) = 17 caracteres
  // C10 (Salidas) = 17 caracteres
  // C12 (Existencia) = 17 caracteres
  // C14 (Rotación inventario) = 10 caracteres
  // C16 (Error de costos) = 5 caracteres
  //N.C.224
  Columnas 18;2.99,0.15,4.49,0.15,1.49,0.15,2.09,0.15,2.09,0.15,2.09,0.15,2.09,0.15,1.49,0.15,0.70,0.70
FinSi

Usaletra 'Arial'
Altura 9
[C6,C8,C10,C12,C14].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
gMascarillaProducto = ''  // Mascarilla para el código del producto
gMascarillaAlmacen = ''   // Mascarilla para el código del almacén
gTotalInventario = 0.0
gTotalEntradas = 0.0
gTotalSalidas = 0.0
vImprime = ''
gUnidad = pUnidad
gIdUnidad = pIdUnidad

//N.C.224
gTieneMovtosEnCaptura = 0

// Función área sensible al contexto
// =================================================================================================
DefFunc ContextoDetalleProductoAlm(aNumeroColumna; aValor)
Local lTipoKardex

  // pTipoReporte   0 = Inventario actual en unidades
  //                1 = Inventario actual en importes ( costos )
  // pTipoKardex    2 = Kardex en unidades por almacén
  //                3 = Kardex en importes por almacén  
  lTipoKardex = pTipoReporte + 2
    EJECUTA tEmpresa->SysDirDatos & '\Reportes\AdminPAQ\Kardex.RTW';pFechaInicial,pFechaFinal,@Escoge(aValor;0),@Escoge(aValor;0),@Escoge(aValor;1),@Escoge(aValor;1),0,0,@Escoge(aValor;2),@Escoge(aValor;3),pIdClase1,pIdClase2,pIdClase3,pIdClase4,pIdClase5,pIdClase6,pIdMoneda,lTipoKardex;tEmpresa->Directorio
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ó
    Si pTipoReporte = 0 // En unidades
      [C6,C8,C10,C12].Decimales tParametros(cDecimalesUnidades) // Decimales para existencias
    SiNo  // en importes
      [C6,C8,C10,C12].Decimales tParametros(cDecimalesCostos) // Decimales para costos
    FinSi
    gMascarillaAlmacen =  tParametros(cMascarillaAlmacen)     // Mascarilla para el código del almacen
    gMascarillaProducto = tParametros(cMascarillaProducto)    // Mascarilla para el código del cliente/proveedor
  Sino // Si no encontró el registro de la empresa
    [C6,C8,C10,C12].Decimales 2
  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 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(cCodigoAlmacen)
        tAlmacen.Saltareg[1]
        Si tAlmacen->Encontro
          pAlmacenInicial = tAlmacen(cCodigoAlmacen)
        FinSi
      FinSi  // Si el almacén inicial es el almacén cero
    FinSi  // Si se encontró el primer qalmacén
  FinSi  // Si el almacén inicial esta vacío

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

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


// *****************************************************************************
//  fImprimeDatosAlmacen
//
//  Esta función imprime los datos del almacén para el reporte por almacén.
//
//  Parámetros 
//
//  Salidas 
// *****************************************************************************
DefFunc fImprimeDatosAlmacen(atAlmacen: Tabla)
Local lCodigoEditado

  // Redefine la configuración de las columnas
  // C0 14 caracteres
  // C2 75 caracteres
  Columnas 17;2.09,0.15,11.21,0.15,7.46

  NEGRITAS TRUE

  vImprime = ' '[C0..C16]
  vImprime = 'Almacén: '[C0]
  lCodigoEditado = fMascarilla(atAlmacen(cCodigoAlmacen); gMascarillaAlmacen)
  vImprime = @Col(lCodigoEditado;0)[C2]
  Lista vImprime
  
  vImprime = 'Nombre: '[C0]
  vImprime = atAlmacen(cNombreAlmacen)[C2]
  Lista vImprime
  SaltaLin

  NEGRITAS FALSE

  // Regresa las columnas a su configuracion original
  //N.C.224
  Si pTipoReporte = 0 // En unidades
    Columnas 18;2.99,0.15,5.08,0.15,0.90,0.15,2.09,0.15,2.09,0.15,2.09,0.15,2.09,0.15,1.49,0.15,0.70,0.70
  Sino  // En importes
    Columnas 18;2.99,0.15,4.49,0.15,1.49,0.15,2.09,0.15,2.09,0.15,2.09,0.15,2.09,0.15,1.49,0.15,0.70,0.70
  FinSi
FinFunc


// *****************************************************************************
//  fImprimeProducto
//
//  Esta función imprime los datos y la existencia de un producto 
//
//  Parámetros 
//
//  Salidas 
// *****************************************************************************
DefFunc fImprimeProducto(atProducto: Tabla; aInvInicial; aEntradas; aSalidas)
Local lCodigoEditado, lInvFinal, lPromedio, lRotacion, lFechaSinAfectar

  vImprime = ' '[C0..C16]
  lCodigoEditado = fMascarilla(atProducto(cCodigoProducto); gMascarillaProducto)
  vImprime = @Col(lCodigoEditado;0)[C0]
  
  vImprime = atProducto(cNombreProducto)[C2]

  Si pTipoReporte = 0  // Reporte en unidades
    // 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 = tUnidad(cDespliegue)[C4]
      FinSi
    FinSi

  // Reporte en importes
  Sino 
    Escoge atProducto(cMetodoCosteo)
      Caso 1
        vImprime = 'Prom.'[C4]
      FinCaso
      Caso 2
        vImprime = 'Prom. Alm.'[C4]
      FinCaso
      Caso 3
        vImprime = 'U. Costo'[C4]
      FinCaso
      Caso 4
        vImprime = 'UEPS'[C4]
      FinCaso
      Caso 5
        vImprime = 'PEPS'[C4]
      FinCaso
      Caso 6
        vImprime = 'Específico'[C4]
      FinCaso
      Caso 7
        vImprime = 'Estándar'[C4]
      FinCaso
    FinEscoge
  FinSi  // Si es reporte en unidades o en importes

  vImprime = @Col(aInvInicial;0)[C6]
  vImprime = @Col(aEntradas;0)[C8]
  vImprime = @Col(aSalidas;0)[C10]
  DEFREGION ContextoDetalleProductoAlm
  Si aEntradas != 0
    [C8].REGIONINFO atProducto(cCodigoProducto) & ',' & tAlmacen(cCodigoAlmacen) & ',' & pUnidad & ',' & pIdUnidad
  FinSi
  Si aSalidas != 0
    [C10].REGIONINFO atProducto(cCodigoProducto) & ',' & tAlmacen(cCodigoAlmacen) & ',' & pUnidad & ',' & pIdUnidad
  FinSi
  // calcula el inventario final
  lInvFinal = aInvInicial + aEntradas - aSalidas
  vImprime = @Col(lInvFinal;0)[C12]  
  // Calcular la rotación del inventario
  lPromedio = ( aInvInicial + lInvFinal ) / 2
  Si lPromedio != 0
    lRotacion = aSalidas / lPromedio
  Sino
    lRotacion = 0.0
  FinSi
  vImprime = @Col(lRotacion;0)[C14]

  // Verificar si el producto tiene algún error y sí es así desplegar el error.
  vImprime = ''[C16]

  // Verificar si hay existencias congeladas
  // N.D. 8675 Es necesario enviar como parametro el Id del almacen "tAlmacen(cIdAlmacen)" en ves de el valor de 0, 
  // para que dentro de la función se verifiquen sólo las existencias para ese IdAlmacen
  lExistenciaCongelada = fVerificarExistenciaCongelada(atProducto(cIdProducto); tAlmacen(cIdAlmacen); tParametros(cIdEjercicioActual); tExistencia)

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

  // Si hay error de existencias y no hay movtos sin afectar O
  // el error de existemcias  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)& ')'[C16]
  FinSi // Si hay error de costos
  
  // Si hay movtos sin afectar y no hay errores de existencias O
  // los movtos sin afectar ocurrieron antes del error de existencias
  // 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)'[C16]
  FinSi
  
  //N.C.224
  Si gTieneMovtosEnCaptura = 1
  	vImprime = '(8)'[C17] //N.D.5727
  SiNo  	
  	vImprime = ''[C17]
  FinSi
  Lista vImprime
FinFunc


// *****************************************************************************
//  fImprimeTotales
//
//  Esta función imprime los totales por almacén o generales
//
//  Parámetros 
//
//  Salidas 
// *****************************************************************************
DefFunc fImprimeTotales(aInvInicial; aEntradas; aSalidas)
Local lInvFinal, lPromedio, lRotacion

  lSubRaya = ''
  lSubRaya = '=================='[C6,C8,C10,C12]
  Imprime lSubRaya
  
  vImprime = ' '[C0..C15]
  vImprime = '"Total:'[C4]
  vImprime = @Col(aInvInicial;0)[C6]

  vImprime = @Col(aEntradas;0)[C8]
  vImprime = @Col(aSalidas;0)[C10]
  lInvFinal= aInvInicial + aEntradas - aSalidas
  vImprime = @Col(lInvFinal;0)[C12]

  // Calcular la rotación del inventario
  lPromedio = ( aInvInicial + lInvFinal ) / 2
  Si lPromedio != 0
    lRotacion = aSalidas / lPromedio
  Sino
    lRotacion = 0.0
  FinSi
  vImprime = @Col(lRotacion;0)[C14]

  Lista vImprime
  Saltalin
FinFunc



// *****************************************************************************
//  fCancelado    
//
//  Verifca si el movto es de un docto cancelado, 
//
//  Parámetros 
//
//  Salidas 1 = cancelado, 0 = no cancelado
// *****************************************************************************
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


//--------------------------------------------------------------------------------
// Nombre:  fProcesaPorAlmacen
// Proceso: Esta función procesa el reporte de stock máximo por almacén.

// Entradas:
// Salidas:

// Consideraciones:

//  Historia de Revisiones:
//  Fecha         Autor     ID-Lotus        Descripción
//--------------------------------------------------------------------------------
//  05/02/2008    JENL      N.D.5062        Se cambio el indice iProductoAlmacenFechaAfec por iProductoAlmacenAfecFecha
//--------------------------------------------------------------------------------
DefFunc fProcesaPorAlmacen(atProducto: Tabla)
Local lFecha, lInvInicial, lEntradas, lSalidas, lCostoExistencia
Local lTotalInvInicial, lTotalEntradas, lTotalSalidas
Local lCosto, lTotal, lIdAlmacen, lAlMenosUno


  // Inicia los acumulados por almacén
  lTotalInvInicial = 0.0
  lTotalEntradas   = 0.0
  lTotalSalidas    = 0.0
  
  // Obtiene los productos en el rango especificado en los parámetros del reporte
  Mientras[atProducto.Busca[pProductoInicial & '..' & pProductoFinal]; atProducto->Encontro; atProducto.Busca Siguiente[1]] 
    // Solo busca productos, descarta paquetes y servicios
    Si atProducto(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(atProducto(cStatusProducto) - 2)
      Continua
    FinSi
  
    // Verifica si el producto cumple con las clasificaciones especificadas 
    Si gNumDeClases > 0
      Si !fMiembroDeUnaClase(atProducto; gNumDeClases; gListIdDeClases; 2)
        // Discrimina el producto por no pertenecer a alguna de las clasificaciones
        Continua
      FinSi
    FinSi
  
    // Verifica parámetro pUnidad
    //N.D.4538 (MAJU)
    //Al elegir como filtro “Unidad no equivalente” Si uno de los productos a mostrar 
    //no tiene asignada Unidad No Equivalente no se mostrara en el reporte.
    Si pTipoReporte = 0  // Reporte en unidades
    	Si pUnidad = 1 y atProducto(cIdUnidadNoConvertible) = 0
    		Continua
    	Sino
      	fVerificaUnidad(atProducto)
      FinSi
    FinSi

    // Inicia acumulados por producto
    lEntradas = 0.00
    lSalidas  = 0.00

    lAlMenosUno = False

    // Encuentra el inventario inicial del producto en ese almacén
    // Establece el indice necesario para la rutina de inventario inicial
    tMovimiento.Usaindice['iProductoFechaAfecta'] // cIdProducto+cFecha+cAfectaExistencia

    Si pTipoReporte = 0 // Reporte en unidades
      lInvInicial = fExistenciaInicialProducto(atProducto(cIdProducto); tAlmacen(cIdAlmacen); pFechaInicial; gIdEjercicio; gPeriodo; pUnidad; atProducto(cIdUnidadBase); pIdUnidad; tMovimiento; tExistencia; tEquivalencia)
    Sino // Reporte por almacén en importes
      // Si el costo es por producto y su método es Estándar se calcula en base a las unidades y el valor del costo estándar en el producto
      Si pCosto = 0 Y atProducto(cMetodoCosteo) = 7
        lInvInicial = fExistenciaInicialProducto(atProducto(cIdProducto); tAlmacen(cIdAlmacen); pFechaInicial; gIdEjercicio; gPeriodo; pUnidad; atProducto(cIdUnidadBase); pIdUnidad; tMovimiento; tExistencia; tEquivalencia)
        lInvInicial = lInvInicial * atProducto(cCostoEstandar)
      SiNo
        lInvInicial = fCostoInicialProducto(atProducto(cIdProducto); tAlmacen(cIdAlmacen); pFechaInicial; gIdEjercicio; gPeriodo; atProducto; tMovimiento; tExistencia; tCostoHistorico; tTipoCambio; pIdMoneda)
      FinSi
    Finsi

    // N.D.3010 Inicio
//! // Inicializa la Fecha de inicio del rango de fechas del reporte
//! lFecha = pFechaInicial 
//! // Recorre todas las fechas en el rango de fechas del reporte
//! Mientras lFecha <= pFechaFinal
//!    // 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) & ':' & tAlmacen(cIdAlmacen) & ':' & lFecha]; tMovimiento->Encontro; tMovimiento.Busca Siguiente[3] ]
    // Búsqueda de un movimiento del producto a la fecha inicial del reporte
    gFechaBusqueda = pFechaInicial
    // NOTA: Es obligatorio volver a fijar el índice a fin de que las búsquedas parciales funcionen correctamente
    tMovimiento.Usaindice['iProductoAlmacenFechaAfec']
    tMovimiento.Busca[atProducto(cIdProducto) & ':' & tAlmacen(cIdAlmacen) & ':' & gFechaBusqueda]
    Si !tMovimiento->ENCONTRO
      // Busquedas parciales: Cursor en el registro más cercano que cumple con la llave
      // NOTA: La propiedad Llave de la tabla nos regresa la llave más cercana que cumple con el criterio de la búsqueda
      //       La llave está en formato cadena y no tiene ningún separador entre los campos, por lo que es necesario revisar
      //      en FoxPro la definición del índice para descomponer los elementos de la Llave
      // Revisar si la llave actual corresponde al Producto / Almacén y al Rango de Fechas.
      Si atProducto(cIdProducto) = @Val(@SUBSTR(tMovimiento->Llave;0;11))
        Si tAlmacen(cIdAlmacen) = @Val(@SUBSTR(tMovimiento->Llave;11;11))
          Si @SUBSTR(tMovimiento->Llave;22;8) >= pFechaInicial Y @SUBSTR(tMovimiento->Llave;22;8) <= pFechaFinal
            // Realizar nuevamente la búsqueda a fin de posicionarnos en el registro y activar la propiedad Encontro
            gFechaBusqueda = @SUBSTR(tMovimiento->Llave;22;8)
            tMovimiento.Busca[atProducto(cIdProducto) & ':' & tAlmacen(cIdAlmacen) & ':' & gFechaBusqueda]
          FinSi
        FinSi
      FinSi        
    FinSi

    //N.C.224
    gTieneMovtosEnCaptura = 0 
    lAlmacenActual = tAlmacen(cIdAlmacen)
		
    // N.D.3010 Recorrer los movimientos hasta que sea diferente producto, almacén o se rompa el rango de fechas
    Mientras tMovimiento->Encontro Y (tMovimiento(cFecha) <= pFechaFinal)
      // Discrimina los movimientos que no son entradas ni salidas
      Si tMovimiento(cAfectaExistencia) != 1 y tMovimiento(cAfectaExistencia) != 2
        // N.D.3010 Búsqueda del siguiente registro que cumpla con el producto / almacén
        tMovimiento.BUSCA SIGUIENTE[2]
        Continua
      FinSi
      
      //N.C.224
      //Se deben considerar las unidades que afectaron el inventario 
      //aunque todavía no se afecte el costo, esto sucede cuando todavía se tienen 
      //movimientos en captura.      
      //Se verifica si está o no afectado en inventarios       
      Si tMovimiento(cAfectadoInventario) = 0 
        // N.D.3010 Búsqueda del siguiente registro que cumpla con el producto / almacén
        tMovimiento.BUSCA SIGUIENTE[2]
        Continua
      FinSi
      
      //N.C.224
      //Si detecta que hubo algún movimiento con cAfectadoInventario = 2, se activa la bandera 
      Si tMovimiento(cAfectadoInventario) = 2		
        //N.D.5727 		
        gTieneMovtosEnCaptura = 1
      FinSi
      //Fin N.C.224
  
      // Descarta los movtos de doctos cancelados
      Si fCancelado(tMovimiento)
        // N.D.3010 Búsqueda del siguiente registro que cumpla con el producto / almacén
        tMovimiento.BUSCA SIGUIENTE[2]
        Continua
      Finsi
  
      Si pTipoReporte = 0 // Reporte en unidades
        lCostoExistencia = fUnidades(pUnidad; atProducto(cIdUnidadBase); pIdUnidad; tMovimiento; tEquivalencia)
      Sino // Reporte en importes
        lIdAlmacen = 0
        Si pCosto = 2 o pCosto = 4  // Costo Promedio por almacén o último costo
          lIdAlmacen = tMovimiento(cIdAlmacen)
        FinSi
        Si pCosto = 0 
          lCostoExistencia = fCostoMovimiento(atProducto; tMovimiento; tCostoHistorico; tTipoCambio; pIdMoneda)
        Sino
          Si pCosto = 1 o pCosto = 2 // Costo promedio
            lCosto = fCostoHistorico(atProducto(cIdProducto);lIdAlmacen;lFecha;1;tCostoHistorico;tTipoCambio;pIdMoneda)
          Sino // Ultimo costo
            lCosto = fCostoHistorico(atProducto(cIdProducto);lIdAlmacen;lFecha;3;tCostoHistorico;tTipoCambio;pIdMoneda)
          FinSi
          lCostoExistencia = lCosto * tMovimiento(cUnidades)
        FinSi     
      FinSi
  
      Si tMovimiento(cAfectaExistencia) = 1 // Es una entrada?
        lEntradas = lEntradas + lCostoExistencia
      Sino
        lSalidas = lSalidas + lCostoExistencia
      FinSi
  
      lAlMenosUno = True
      // N.D.3010 Búsqueda del siguiente registro que cumpla con el producto / almacén
      tMovimiento.BUSCA SIGUIENTE[2]
    FinMientras
//!   FinMientras // Hay Movimientos del día
//!   // Incrementa un día a la fecha
//!   lFecha = fFechaSiguiente(lFecha)
//! FinMientras // lFecha <= pFechaFinal
    // N.D.3010 Fin
    
    // Si se imprimen todos los productos
    lEncontrado = 0 // bandera para saber si se cumplio alguna condicion y sumar o no al final
    Si pImprimirProductos = 0
      lEncontrado = 1
      fImprimeProducto(atProducto; lInvInicial; lEntradas; lSalidas)      
    FinSi

    // Si se imprimen sólo productos con existencias o con movimientos 
    Si pImprimirProductos = 1 Y ( @Col(lInvInicial;0) != 0 O lAlMenosUno ) Y !lDatosProducto
      // Imprime los datos del producto
      lEncontrado = 1
      fImprimeProducto(atProducto; lInvInicial; lEntradas; lSalidas)      
    FinSi

    Si pImprimirProductos = 2 Y ( @Col(lInvInicial;0) + lEntradas - lSalidas > 0 ) Y !lDatosProducto //solo con existencias
      // Imprime los datos del producto
      lEncontrado = 1
      fImprimeProducto(atProducto; lInvInicial; lEntradas; lSalidas)      
    FinSi

    Si pImprimirProductos = 3 Y ( lAlMenosUno ) Y !lDatosProducto
      // Imprime los datos del producto
      lEncontrado = 1
      fImprimeProducto(atProducto; lInvInicial; lEntradas; lSalidas)      
    FinSi
    

    Si lEncontrado = 1
      // acumula al total del producto
      lTotalInvInicial = lTotalInvInicial + lInvInicial
      lTotalEntradas   = lTotalEntradas + lEntradas
      lTotalSalidas    = lTotalSalidas + lSalidas
      //N.C.224
      vImprime = ''[C17]
    FinSi
    
  FinMientras // Hay productos

  // Imprime el total del almacén
  fImprimeTotales(lTotalInvInicial; lTotalEntradas; lTotalSalidas)

  // Actualiza totales generales
  gTotalInventario = gTotalInventario + lTotalInvInicial
  gTotalEntradas   = gTotalEntradas   + lTotalEntradas
  gTotalSalidas    = gTotalSalidas    + lTotalSalidas
FinFunc  // fProcesaPorAlmacen


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

Encab
  Texto '@TC{255,0,0}@S{8}AdminPAQ'
  Hoja '"@TC{0,0,0}@S{8}Hoja: '
  Imprime

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

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

  Texto '^@TC{0,0,0}@S{16}INVENTARIO ACTUAL DEL ALMACEN POR PRODUCTO'
  Imprime

  Si pTipoReporte = 0  // En unidades
    Texto '^@TC{0,0,0}@S{16}EN UNIDADES'
  Sino // En importes
    Si pCosto = 0
      Texto '^@TC{0,0,0}@S{16}EN IMPORTES (COSTO DEFINIDO POR EL PRODUCTO)'
    FinSi
    Si pCosto = 1
      Texto '^@TC{0,0,0}@S{16}EN IMPORTES (COSTO PROMEDIO GLOBAL)'
    FinSi
    Si pCosto = 2
      Texto '^@TC{0,0,0}@S{16}EN IMPORTES (COSTO PROMEDIO POR ALMACEN)'
    FinSi
    Si pCosto = 3
      Texto '^@TC{0,0,0}@S{16}EN IMPORTES (ULTIMO COSTO GLOBAL)'
    FinSi
    Si pCosto = 4
      Texto '^@TC{0,0,0}@S{16}EN IMPORTES (ULTIMO COSTO POR ALMACEN)'
    FinSi
  Finsi  
  Imprime

  Texto '^@TC{0,0,0}@S{16}Del: ' & fFormateaFecha(pFechaInicial;gIdioma) & ' Al: ' & fFormateaFecha(pFechaFinal;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

  Si pTipoReporte = 0 // Reporte en unidades
    Texto C6;'"Existencia'
  SiNo
    Texto C6;'"Costo'
    Texto C12;'"Costo' 
  FinSi
  Texto C14;'"Rotación'
  Imprime

  Texto C0;'Código Producto'
  Texto C2;'Nombre Producto'
  Si pTipoReporte = 0 // Reporte en unidades
    Texto C4;''
    Texto C6;'"Inicial'
  SiNo  // En importes
    Texto C4;'Costeo'
    Texto C6;'"Inicial'
  FinSi
  Texto C8;'"Entradas'
  Texto C10;'"Salidas'
  Si pTipoReporte = 0  // En unidades
    Texto C12;'"Existencia'
  Sino  // En importes
    Texto C12;'"Total'
  FinSi
  Texto C14;'"Inventario'
  Texto C16;'"Error' //N.D.5727
  Imprime

  Texto '@TC{0,0,0}@L{-1}'
  Imprime 1

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


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

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

// Configura los decimales del reporte
fConfiguraDecimales()

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

fEjercicioPeriodo(pFechaInicial; &gPeriodo; &gIdEjercicio; tEjercicio) 

// Recorre los almacenes en el rango
Mientras [ tAlmacen.Busca[pAlmacenInicial & '..' & pAlmacenFinal]; tAlmacen->Encontro; tAlmacen.Busca Siguiente[1] ]    
  // Imprime los datos del almacén
  fImprimeDatosAlmacen(tAlmacen)

  // Calcular las existencias de cada producto en el almacén
  fProcesaPorAlmacen(tProducto)

FinMientras

fImprimeTotales(gTotalInventario; gTotalEntradas; gTotalSalidas)

// Imprime al final la leyenda con los errores de existencias o costos
Saltalin
Columnas 17;0.08,100.00,0.0
Lista '', 'Errores de existencias o costos:'
Lista '', ' (1) Captura en desorden de entradas'
Lista '', ' (2) Captura en desorden de salidas'
Lista '', ' (3) Existencias negativas'
Lista '', ' (4) Series sin movimiento de entrada'
Lista '', ' (5) Series sin costo'
Lista '', ' (6) Movimientos sin afectar'
Lista '', ' (7) En proceso de levantamiento de inventario físico'
//N.C.224
Lista '', ' (8) Este producto está incluido en algún movimiento que actualmente está en captura, motivo por el cual dicho movimiento solo está afectado en existencia y no en costo'[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]
//N.C.224
Lista '', 'En este caso se recomienda consultar el reporte en cuanto se termine de capturar el documento'[C1]  
// Fin Reporte

