// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
//	Nombre del reporte en el sistema: Utilidad
//
//  Sistema: AdminPAQ v1.1
// 
//	Este reporte perteneciente al módulo de inventarios, despliega el margen de utilidad de las operaciones de venta realizadas.
//
//	Consideraciones especiales:
//  Este reporte aunque está programado como uno solo, representa en realidad dos reportes en el ámbito del sistema:
//    Utilidad
//    Utilidad detallado
//  
//	Elaboró:    Eduardo Pérez Figueroa
//	Fecha: 	    11 de Septiembre del 2001
//
// <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
// 23/07/2007   JAAM    N.D.4770    Vefiricar que no se calculen movimientos con fechas posteriores al filtro.  
// <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

// *********************************************************************************************************************
// Funciones de librería
// *********************************************************************************************************************
Incluye LibAdminPAQ.Rpt
Incluye LibInventariosO.Rpt


// *********************************************************************************************************************
//	Parámetros del reporte:
// *********************************************************************************************************************
Parametro pFechaInicial 		= '20020101'	// Fecha inicial del rango
Parametro pFechaFinal 			= '20021231'  // Fecha final del rango
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 = Solo productos con movimientos 
Parametro pEstadoProducto 	= 0           // Incluir productos con estado:
                                        	// 0 = Todos
                                        	// 1 = Activos
                                        	// 2 = Inactivos
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 = 1              	// Tipo de Reporte (este parámetro es interno a AdminPAQ)
                                        	// 0 = Utilidad (concentrado)
                                        	// 1 = Utilidad detallado

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

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

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 tConcepto = tEmpresa['MGW10006']         // Conceptos de documentos
tConcepto.Usaindice['']                           // Indice primario: cIdConceptoDocumento

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 tValoresDeClasif = tEmpresa['MGW10020']
tValoresDeClasif.UsaIndice['']                    // Indice primario: cIdValorClasificacion

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

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

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

UsaTabla tPaqComponente = tEmpresa['MGW10005'] Alias 'PaqComponente' // Paquetes y sus componentes
tPaqComponente.Usaindice['']

UsaTabla tMovtoPaquete = tEmpresa['MGW10010'] Alias 'MovtoPaquete'   // Movimientos de paquetes
tMovtoPaquete.Usaindice['iMovtoOwnerMovto']                        // cIdMovtoOwner+cIdMovimiento

UsaTabla tProductoDetalle = tEmpresa['MGW10004']         // Productos
tProductoDetalle.Usaindice['']

UsaTabla tProdComponente = tEmpresa['MGW10005'] Alias 'ProdComponente' 		// Productos paquetes
tProdComponente.Usaindice['']

UsaTabla tMovtoComponente = tEmpresa['MGW10010'] Alias 'MovtoComponente'  // Movimientos de paquetes
tMovtoComponente.Usaindice['iMovtoOwnerMovto']                        // cIdMovtoOwner+cIdMovimiento

UsaTabla tCliente = tEmpresa['MGW10002']					// Clientes / proveedores
tCliente.Usaindice['']														// cIdClienteProveedor


// *********************************************************************************************************************
// Definiciones y asumidos globales para el reporte
// *********************************************************************************************************************
// Ancho de columnas = caracteres / 17 * 2.54
// Se consideran columnas intermedias de un espacio

// *** Encabezado del producto: Reporte concentrado
// C0 (Código del producto) = 14 caracteres
// C2 (Nombre del producto) = 40 caracteres
// C4 (Método de costeo) = 28 caracteres
// C6 (Margen de utilidad) = 15 caracteres
// C7 (%) = 2 caracteres
// Columnas 17;2.09,0.15,5.98,0.15,4.18,0.15,2.24,0.30,2.09,0.30,0.15,0

// *** Costo y utilidad: Reporte concentrado
// C0 (espacios) = 45
// C2 (Costo de venta) = 15 caracteres
// C4 (Ventas) = 15 caracteres
// C6 (Utilidad) = 15 caracteres
// C8 (% de utilidad) = 15 caracteres
// C9 (%) = 2 caracter
// Columnas 17;7.77,0.15,2.24,0.15,2.24,0.15,2.24,0.15,2.24,0.30,0.15,0

// *** Reporte detallado
// C0 (Fecha) = 11 caracteres
// C2 (Serie) = 5 caracteres
// C4 (Folio) = 8 caracteres 
// C6 (Concepto) = 24 caracteres
// C8 (Costo de venta) = 15 caracteres
// C10 (Ventas) = 15 caracteres
// C12 (Utilidad) = 15 caracteres
// C14 (% de utilidad) = 15 caracteres
// C15 (%) = 2 caracteres
// Columnas 17;1.64,0.15,0.75,0.15,1.20,0.15,3.59,0.15,2.24,0.15,2.24,0.15,2.24,0.15,2.24,0.30,0.15

Si pTipoReporte = 1 // Reporte detallado?
  Columnas 17;1.64,0.15,0.75,0.15,1.20,0.15,3.59,0.15,2.24,0.15,2.24,0.15,2.24,0.15,2.24,0.30,0.15
Sino // Reporte concentrado
  Columnas 17;7.77,0.15,2.24,0.15,2.24,0.15,2.24,0.15,2.24,0.30,0.15,0
FinSi
Usaletra 'Arial'
Altura 9


// *********************************************************************************************************************
//	Variables de ámbito global:
// *********************************************************************************************************************
gIdioma = 0               // Idioma del reporte
                          // 0 = Español
                          // 1 = Inglés
gDecimalesCostos = 2      // Número de decimales para costos
gDecimalesMoneda = 2      // Número de decimales para la moneda
gTotalCosto = 0.0         // Acumulado de costos del reporte
gTotalVentas = 0.0        // Acumulado de ventas del reporte
gMascarillaProducto = ''  // Mascarilla para el código del producto


// *********************************************************************************************************************
// FUNCIONES
// *********************************************************************************************************************
// =================================================================================================
// Función área sensible al contexto
// =================================================================================================
DefFunc ContextoDetalleProducto(aNumeroColumna; aValor)
    EJECUTA tEmpresa->SysDirDatos & '\Reportes\AdminPAQ\UTILIDAD.RTW';pFechaInicial,pFechaFinal,aValor,aValor,pImprimirProductos,pEstadoProducto,pIdClase1,pIdClase2,pIdClase3,pIdClase4,pIdClase5,pIdClase6,pIdMoneda,1;tEmpresa->Directorio
FinFunc

// *********************************************************************************************************************
//	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
    gDecimalesCostos = tParametros(cDecimalesCostos)
    gMascarillaProducto = tParametros(cMascarillaProducto)
  Sino
    gDecimalesCostos = 2
  FinSi
  // Busca el registro de la moneda
  tMoneda.Busca[@Str(pIdMoneda;0)]
  Si tMoneda->Encontro
    gDecimalesMoneda = tMoneda(cDecimalesMoneda)
  Sino
    gDecimalesMoneda = 2
  FinSi  
FinFunc


// *********************************************************************************************************************
//	Esta función imprime los datos del reporte de utilidad concentrado.
//  Parámetros 
//  Salidas 
// *********************************************************************************************************************
DefFunc fImprimeConcentrado(aCodigoProducto; aCosto; aVentas)
Local vImprime, lUtilidad, lPorcUtilidad

  vImprime = ' '[C0..C16]

  // Costo de ventas
  vImprime = @Col(aCosto;0)[C2]  
  // Ventas
  vImprime = @Col(aVentas;0)[C4]  
  // Utilidad
  lUtilidad = aVentas - aCosto
  vImprime = @Col(lUtilidad;0)[C6]  
  // % de utilidad
  lPorcUtilidad = lUtilidad / aVentas * 100.0
  vImprime = @Col(lPorcUtilidad;0)[C8]
  vImprime = '%'[C9]
  DEFREGION ContextoDetalleProducto
  Si aCosto != 0 O aVentas != 0
    [C6].REGIONINFO aCodigoProducto
  FinSi
  Lista vImprime
FinFunc


// *********************************************************************************************************************
//	Esta función imprime los datos del movimiento.
//  Parámetros 
//  Salidas 
// *********************************************************************************************************************
DefFunc fImprimeMovimiento(atMovimiento: Tabla; aCosto; aVentas; aImprimePorcentaje)
Local vImprime, lUtilidad, lPorcUtilidad, lIdDocumento
  vImprime = ' '[C0..C16]
  // Fecha del movimiento
  vImprime = fFormateaFecha(atMovimiento(cFecha); gIdioma)[C0]
  
  // 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
  Si tDocumento->Encontro
    // Serie
    vImprime = tDocumento(cSerieDocumento)[C2]
    // Folio
    vImprime = tDocumento(cFolio)[C4]
    // lee el concepto del documento
    tConcepto.Busca[@Str(tDocumento(cIdConceptoDocumento);0)]
    Si tConcepto->Encontro
      vImprime = tConcepto(cNombreConcepto)[C6]
    FinSi
  FinSi
  // Costo de ventas
  vImprime = @Col(aCosto;0)[C8]  
  // Ventas
  vImprime = @Col(aVentas;0)[C10]  
  // Utilidad
  lUtilidad = aVentas - aCosto
  vImprime = @Col(lUtilidad;0)[C12]  
   Si aImprimePorcentaje = 1
    // % de utilidad
    lPorcUtilidad = lUtilidad / aVentas * 100.0
    vImprime = @Col(lPorcUtilidad;0)[C14]
    vImprime = '%'[C15]
  FinSi
  Lista vImprime

FinFunc


// *********************************************************************************************************************
//	Esta función imprime los datos del producto.
//  Parámetros 
//  Salidas 
// *********************************************************************************************************************
DefFunc fImprimeProducto(atProducto: Tabla)
Local vImprime, vImprime2, vImprime3, lFechaEditada
Local lFechaSinAfectar, lExistenciaCongelada

  vImprime = ' '[C0..C16]

  Si pTipoReporte = 1 // Reporte detallado?
    // Redefine el ancho de las columnas
    Columnas 17;2.99,0.15,7.47,0
    NEGRITAS TRUE

    vImprime = 'Producto: '[C0]
    vImprime = @Col(fMascarilla(atProducto(cCodigoProducto); gMascarillaProducto);0)[C2]
    Lista vImprime 
    vImprime = 'Nombre: '[C0]
    vImprime = atProducto(cNombreProducto)[C2]
    Lista vImprime

    vImprime = 'Método de costeo: '[C0]
    Escoge atProducto(cMetodoCosteo)
      Caso 1
        vImprime = 'Costo Promedio'[C2]
      FinCaso
      Caso 2
        vImprime = 'Costo Promedio por Almacén'[C2]
      FinCaso
      Caso 3
        vImprime = 'Ultimo Costo'[C2]
      FinCaso
      Caso 4
        vImprime = 'UEPS'[C2]
      FinCaso
      Caso 5
        vImprime = 'PEPS'[C2]
      FinCaso
      Caso 6
        vImprime = 'Costo Específico'[C2]
      FinCaso
      Caso 7
        vImprime = 'Costo Estándar'[C2]
      FinCaso
    FinEscoge
    Lista vImprime    

    // Columna del margen de utilidad del producto
    // Redefine las columnas
    Columnas 17;2.99,0.15,14.94,0.15,0
    [C2].Decimales 2
    [C2].Coma
    [C2].Blanco '0.00'
    vImprime = 'Margen de utilidad:'[C0]
    vImprime = atProducto(cMargenUtilidad)[C2]
    vImprime = '%' [C3]
    Lista vImprime
  
    vImprime = ''

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

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

    // Si hay error de costos y no hay movtos sin afectar O el error de costos ocurrió antes de los movtos sin afectar
    // entonces se despliega el error de costos
    Si ( atProducto(cErrorCosto) Y !lFechaSinAfectar) O ( lFechaSinAfectar Y atProducto(cErrorCosto) Y atProducto(cFechaErrorCosto) < lFechaSinAfectar )
      Columnas 17; 2.99,0.15,10.97,0
      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 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) )
      Columnas 17; 2.99,0.15,10.97,0
      vImprime = 'Error en costos:'[C0]
      vImprime = '*** Movimientos sin afectar'[C2]
      lFechaEditada = fFormateaFecha(lFechaSinAfectar; gIdioma)
      vImprime = @Col(vImprime;2) & ' a partir del ' & @Col(lFechaEditada;0)[C2]
      vImprime2 = '*** Se recomienda consultar el reporte "Movimientos sin Afectar" para éste producto'[C2]
      vImprime3 = '*** Se recomienda ejecutar el proceso "Recosteo" para éste producto'[C2]
      Lista vImprime
      Lista vImprime2
      Lista vImprime3
    FinSi  // Hay error de movimientos sin afectar
    SaltaLin

    NEGRITAS FALSE

    // Regresa las columnas a su ancho original
    Columnas 17;1.64,0.15,0.75,0.15,1.20,0.15,3.59,0.15,2.24,0.15,2.24,0.15,2.24,0.15,2.24,0.30,0.15
    [C2,C4].Decimales 0
    [C2,C4].Blanco ''
    [C8].Decimales gDecimalesCostos // Decimales para costos
    [C10,C12].Decimales gDecimalesMoneda // Decimales para importes
    [C14].Decimales 2
    [C8,C10,C12,C14].Coma

  // Reporte concentrado
  Sino 
    // Redefine el ancho de las columnas
    Columnas 17;2.09,0.15,5.98,0.15,4.18,0.15,2.24,0.30,0.15,1.64,0.75,0 
    // Columna de margen de utilidad
    [C6].Decimales 2
    [C6].Coma

    // Codigo y nombre del producto
    vImprime = @Col(fMascarilla(atProducto(cCodigoProducto); gMascarillaProducto);0)[C0]
    vImprime = atProducto(cNombreProducto)[C2]

    // Método de costeo
    Escoge atProducto(cMetodoCosteo)
      Caso 1
        vImprime = 'Costo Promedio'[C4]
      FinCaso
      Caso 2
        vImprime = 'Costo Promedio por Almacén'[C4]
      FinCaso
      Caso 3
        vImprime = 'Ultimo Costo'[C4]
      FinCaso
      Caso 4
        vImprime = 'UEPS'[C4]
      FinCaso
      Caso 5
        vImprime = 'PEPS'[C4]
      FinCaso
      Caso 6
        vImprime = 'Costo Específico'[C4]
      FinCaso
      Caso 7
        vImprime = 'Costo Estándar'[C4]
      FinCaso
    FinEscoge

    // Margen de utilidad
    vImprime = atProducto(cMargenUtilidad)[C6]
    vImprime = '%'[C7]

    vImprime = ''[C10]
  
    // Verificar si hay existencias congeladas
    lExistenciaCongelada = fVerificarExistenciaCongelada(atProducto(cIdProducto); 0; tParametros(cIdEjercicioActual); tExistencia)
  
    // Si no hay existencias congeladas verificar si hay movimientos sin afectar.
    // Cuando hay existencias congeladas no se revisan movimientos sin afectar porque pueden deberse a dicha existencia congelada
    Si !lExistenciaCongelada
      // Verificar si hay movimientos sin afectar y en que fecha
      lFechaSinAfectar = fVerificarMovtosSinAfectar(atProducto(cIdProducto); tMovtoSinAfectar; tMovtoOwner; tDocumento)
    SiNo  
      vImprime = '"(7)'[C10]
      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)& ')'[C10]
    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)'[C10]
    FinSi

    Lista vImprime

    // Regresa las columnas a su ancho original
    Columnas 17;7.77,0.15,2.24,0.15,2.24,0.15,2.24,0.15,2.24,0.30,0.15,0
    [C2].Decimales gDecimalesCostos // Decimales para costos
    [C4,C6].Decimales gDecimalesMoneda // Decimales para importes
    [C8].Decimales 2
    [C2,C4,C6,C8].Coma
  FinSi
FinFunc
 

// *********************************************************************************************************************
//	Esta función imprime los totales del reporte.
//  Parámetros 
//  Salidas 
// *********************************************************************************************************************
DefFunc fImprimeTotales(aCosto; aVentas; aGranTotal; aImprimirPorcentaje)
Local vImprime, lUtilidad, lPorcUtilidad

	Si !aGranTotal
	  Si pTipoReporte = 1 // Reporte detallado?
  		Texto C10;'@L{8}'
  		Texto C10;'@L{10}'
		  Texto C12;'@L{12}'
	 		Imprime
	  Sino // Reporte concentrado
  		Texto C10;'@L{2}'
  		Texto C10;'@L{4}'
		  Texto C12;'@L{6}'
	 		Imprime
  	FinSi
	Sino
		lSubRaya = ''
		Si pTipoReporte = 1
		  lSubRaya = '================='[C8,C10,C12]
		SiNo
			lSubRaya = '================='[C2,C4,C6]
		FinSi
   	Imprime lSubRaya
	FinSi
  
  vImprime = ' '[C0..C16]

  // Calcula la utilidad y porcentaje de utilidad
  lUtilidad = aVentas - aCosto
  lPorcUtilidad = lUtilidad / aVentas * 100.0

  Si pTipoReporte = 1 // Reporte detallado?
    // Costo de ventas
    vImprime = @Col(aCosto;0)[C8]  
    // Ventas
    vImprime = @Col(aVentas;0)[C10]  
    // Utilidad
    vImprime = @Col(lUtilidad;0)[C12]  
    Si aImprimirPorcentaje = 1
      // % de utilidad
      vImprime = @Col(lPorcUtilidad;0)[C14]
      vImprime = '%'[C15]
    FinSi
  Sino // Reporte concentrado
    // Costo de ventas
    vImprime = @Col(aCosto;0)[C2]  
    // Ventas
    vImprime = @Col(aVentas;0)[C4]  
    // Utilidad
    vImprime = @Col(lUtilidad;0)[C6]  
    Si aImprimirPorcentaje = 1
       // % de utilidad
       vImprime = @Col(lPorcUtilidad;0)[C8]
       vImprime = '%'[C9]
    FinSi
  FinSi
  Lista vImprime
FinFunc


// *********************************************************************************************************************
//	Esta función obtiene o calcula el costo de un movimiento. 
//	- Sirve para cualquier método de costeo que tenga definido el producto.
//	- Considera el Costo de Movimientos de Paquetes como la suma del costo de los movimientos de sus componentes.
//  Parámetros 
//  Salidas 
// *********************************************************************************************************************
DefFunc fCostoMovtoPaquete(atProdDetalle:Tabla; atProdComponente:Tabla; atMovimiento:Tabla; atMovtoComponente:Tabla; atCostoHistorico:Tabla; atTipoCambio:Tabla; aIdMoneda)
Local	lCosto
Local	lCostoHistorico

	lCosto = 0.0
  // N.D.8654 No se calcula costo espefícifo para paquetes, sólo para sus componentes
  /*Si atMovimiento(cAfectaExistencia) = 1 
		// Movimiento de entrada
		lCosto = fCostoEspecifico(atMovimiento; atTipoCambio; aIdMoneda)
		Retorna lCosto
	Finsi*/

  // Validación del producto del movimiento
	atProdDetalle.Busca[@Str(atMovimiento(cIdProducto);0)]
	Si !atProdDetalle->Encontro
	  Retorna lCosto
	Finsi

	Si atProdDetalle(cTipoProducto)	= 1
		atProdComponente.Busca[@Str(atProdDetalle(cIdProductoPadre);0)]
	Sino
		atProdComponente.Busca[@Str(atProdDetalle(cIdProducto);0)]
	Finsi
	Si !atProdComponente->Encontro
	  Retorna lCosto
	Finsi
 
  // Obtención del tipo de producto, para procesarlo según sea paquete o producto y/o servicio
	Si atProdComponente(cTipoProducto) = 1 
		// Producto normal
		Si atProdComponente(cMetodoCosteo) > 3
			lCosto = fCostoEspecifico(atMovimiento; atTipoCambio; aIdMoneda)
		Sino 
			// Costo promedio y último costo, buscar el costo del movimiento de salida en la tabla de costos historicos
			lCostoHistorico = fCostoHistoricoProducto(atProdComponente(cIdProducto); atProdComponente(cMetodoCosteo) = 2 ? atMovimiento(cIdAlmacen) : 0; atMovimiento(cFecha); atProdComponente(cMetodoCosteo); atCostoHistorico; atTipoCambio; aIdMoneda)
			lCosto = lCostoHistorico * atMovimiento(cUnidades)
		Finsi
	Sino
		Si atProdComponente(cTipoProducto) = 3
			// En caso de ser Servicio el costo unitario se toma del catalogo de Productos
			lCosto = atMovimiento(cUnidades) * atProdComponente(cCostoEstandar)
		Sino
			// Paquete
			// Obtención del costo de sus componentes
			atMovtoComponente.Busca[@Str(atMovimiento(cIdMovtoOwner);0)]
			Si atMovtoComponente->Encontro
				Mientras[atMovtoComponente.Busca[@Str(atMovimiento(cIdMovimiento);0) & '..' & @Str(atMovimiento(cIdMovimiento);0)]; atMovtoComponente->Encontro; atMovtoComponente.Busca Siguiente]
					// El costo de un producto con características se obtendrá al procesar el producto padre y no los detalles
					Si atMovtoComponente(cTipoTraspaso) != 4
						lCosto = lCosto + fCostoMovtoPaquete(atProdDetalle; atProdComponente; atMovtoComponente; atMovtoComponente; atCostoHistorico; atTipoCambio; aIdMoneda)
					FinSi
				FinMientras
			FinSi
		FinSi
	Finsi
  Retorna lCosto
FinFunc


// *********************************************************************************************************************
//  Esta función obtiene el costo de ventas, las ventas y el % de utilidad del producto.
//  Parámetros 
//  Salidas 
// *********************************************************************************************************************
// 23/07/2007   JAAM    N.D.4770    Vefiricar que no se calculen movimientos con fechas posteriores al filtro.  
// *********************************************************************************************************************
DefFunc fProcesaProducto(atProducto: Tabla)
Local lFecha, lCosto, lVentas, lTipoCambioDocumento
Local lTotalCosto, lTotalVentas, lDatosProducto, lAlMenosUno, lIdDocumento
Local lPrecio, lDescuentos, lImpuestos, lTotal
Local lCiclosDoctosDe, lCiclosMax // FBCS POS, para recorrer los nuevos doctosDe el 35 y 36
Local lIdMonedaCliente, lIdMonedaDocumento

  // No se han impreso los datos del producto
  lDatosProducto = 0
  // No existe algun movimiento
  lAlMenosUno = 0
  // Inicia acumulados por producto
  lTotalCosto = 0.0
  lTotalVentas = 0.0
  
  // Bandera para imprimir un total cuando existen notas de venta 
  // N.D.4770
  limprimirTotalGeneral = 0

  lCiclosDoctosDe = 0
  Mientras lCiclosDoctosDe < 2  // agrego este ciclo para barre los doctosDe = 35 y 36 POS
    Si lCiclosDoctosDe = 0
      lCiclosMax = 6
      lDocumentoDe  = 4 
    Sino
      lCiclosMax = 37
      lDocumentoDe = 35
    Finsi
    lCiclosDoctosDe = lCiclosDoctosDe +1  

    Mientras lDocumentoDe < lCiclosMax
      // Se inicializa la fecha con la que se realizaran las busquedas en el rango de fechas
      gFechaBusqueda = pFechaInicial  
      // NOTA: Es obligatorio volver a fijar el índice a fin de que las búsquedas parciales funcionen correctamente
      tMovimiento.Usaindice['iProductoDoctoDeAfecFecha']
      tMovimiento.Busca[atProducto(cIdProducto) & ':' & lDocumentoDe & ':1:' & 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
        Si @Val(@SUBSTR(tMovimiento->Llave;0;11)) = atProducto(cIdProducto)
          Si @Val(@SUBSTR(tMovimiento->Llave;11;11)) = lDocumentoDe
            Si @Val(@SUBSTR(tMovimiento->Llave;22;6)) = 1
              Si @SUBSTR(tMovimiento->Llave;28;8) >= pFechaInicial Y @SUBSTR(tMovimiento->Llave;28;8) <= pFechaFinal
                // Realizar nuevamente la búsqueda a fin de posicionarnos en el registro y activar la propiedad Encontro
                gFechaBusqueda = @SUBSTR(tMovimiento->Llave;28;8)
                tMovimiento.Busca[atProducto(cIdProducto) & ':' & lDocumentoDe & ':1:' & gFechaBusqueda]
              FinSi
            FinSi
          FinSi
        FinSi        
      FinSi

      // Verifica que la fecha del movimiento sea menor a la ingresada por el filtro (tMovimiento(cFecha) <= pFechaFinal)
      // N.D.4770
      Mientras tMovimiento->Encontro y tMovimiento(cFecha) <= pFechaFinal
        // Discrimina los movimientos que no son entradas ni salidas
        Si tMovimiento(cAfectaExistencia) != 1 y tMovimiento(cAfectaExistencia) != 2
          tMovimiento.BUSCA SIGUIENTE[3]
          Continua
        FinSi
			  // En caso de ser un movimiento de paquete no procesar
				Si tMovimiento(cIdDocumento) = 0
				  tMovimiento.BUSCA SIGUIENTE[3]
					Continua
				FinSi
        // Discrimina los movimientos que no son salidas
        Si tMovimiento(cIdDocumentoDe) = 4 O tMovimiento(cIdDocumentoDe) = 35 
          Si tMovimiento(cAfectaExistencia) != 2 
            tMovimiento.BUSCA SIGUIENTE[3]
            Continua 
          FinSi
        Sino
          Si tMovimiento(cAfectaExistencia) != 1 
            tMovimiento.BUSCA SIGUIENTE[3]
            Continua
          FinSi
        FinSi 

        // Existe al menos un movimiento
        lAlMenosUno = 1

        // Convierte importes a la moneda del reporte
        // Lee el documento del movimiento para obtener el tipo de cambio del documento
        Si tMovimiento(cIdDocumento)
          tDocumento.Busca[@Str(tMovimiento(cIdDocumento);0)]
        Sino
          lIdDocumento = fIdDocumentoMovtoOwner(tMovimiento; tMovtoOwner) 
          tDocumento.Busca[@Str(lIdDocumento;0)]
        FinSi
        Si tDocumento->Encontro
          // Si el docuemnto esta cancelado no continuar procesando el movimiento actual
          Si tDocumento(cCancelado) 
             tMovimiento.BUSCA SIGUIENTE[3]
             Continua
          FinSi 
          lTipoCambioDocumento = tDocumento(cTipoCambio)
      	  // Ubica el cliente del documento
	        tCliente.Busca[@Str(tDocumento(cIdClienteProveedor);0)]  
          Si tCliente->Encontro
            lIdMonedaCliente = tCliente(cIdMoneda)
          Sino
            lIdMonedaCliente = tDocumento(cIdMoneda)
          FinSi            
          lIdMonedaDocumento = tDocumento(cIdMoneda)
        Sino
          lTipoCambioDocumento = 1
          lIdMonedaCliente = 1
          lIdMonedaDocumento = 1 
        FinSi
          
        // Obtiene el costo del movimiento
			  Si atProducto(cTipoProducto) = 1
					lCosto = fCostoMovimiento(atProducto; tMovimiento; tCostoHistorico; tTipoCambio; 1)
				Sino
					Si atProducto(cTipoProducto) = 2
						lCosto = fCostoMovtoPaquete(tProductoDetalle; tProdComponente; tMovimiento; tMovtoComponente; tCostoHistorico; tTipoCambio; 1)
          Sino
						// En caso de ser Servicio el costo unitario se toma del catalogo de Productos
						lCosto = tMovimiento(cUnidades) * atProducto(cCostoEstandar)
					Finsi
				Finsi

        // Expresar el costo de la moneda base a la moneda del reporte
        lCosto = fCambiarMonedaImporteLib(tTipoCambio; lCosto; 1; pIdMoneda; lTipoCambioDocumento; tMovimiento(cFecha); lIdMonedaCliente)         
        // Calcula las ventas del movimiento
        lVentas = fNetoMovtoPaquete(tMovimiento; tMovtoOwner; tMovtoPaquete; atProducto; tPaqComponente; &lPrecio; &lDescuentos; &lImpuestos; &lTotal)
        lVentas = fCambiarMonedaImporteLib(tTipoCambio; lVentas; lIdMonedaDocumento; pIdMoneda; lTipoCambioDocumento; tMovimiento(cFecha); lIdMonedaCliente)
         
        // Si el reporte es detallado, imprime el movimiento
        Si pTipoReporte = 1
          Si !lDatosProducto // No se han impreso los datos del producto?
            fImprimeProducto(atProducto)
            lDatosProducto = 1
          FinSi          
          Si tMovimiento(cIdDocumentoDe) = 4 O tMovimiento(cIdDocumentoDe) = 35 
            fImprimeMovimiento(tMovimiento; lCosto; lVentas; 1)
          Sino
            fImprimeMovimiento(tMovimiento; lCosto; lVentas; 0)
          FinSi
        FinSi
        
        Si tMovimiento(cIdDocumentoDe) = 4 O tMovimiento(cIdDocumentoDe) = 35       
          lTotalCosto = lTotalCosto + lCosto
          lTotalVentas = lTotalVentas + lVentas
          gTotalCosto = gTotalCosto + lCosto
          gTotalVentas = gTotalVentas + lVentas     
          lSubTotalCosto = lSubTotalCosto + lCosto
          lSubTotalVentas = lSubTotalVentas + lVentas
        Sino
          lTotalCosto = lTotalCosto - lCosto
          lTotalVentas = lTotalVentas - lVentas
          gTotalCosto = gTotalCosto - lCosto
          gTotalVentas = gTotalVentas - lVentas
          // N.D.8654 El costo se estaba sumando
          lSubTotalCosto = lSubTotalCosto - lCosto
          // N.D.8654 No se decrementaba la venta
          lSubTotalVentas = lSubTotalVentas - lVentas
        FinSi
  
        tMovimiento.BUSCA SIGUIENTE[3]
      FinMientras
       
      Si pTipoReporte = 1 y lAlmenosUno = 1// Reporte detallado?
        // Verifica si imprimir el encabezado del producto
        Si !lDatosProducto y !pImprimirProductos
          fImprimeProducto(atProducto)
          lDatosProducto = 1
        FinSi
        // Imprime totales
        Si lDatosProducto Y (lSubTotalCosto!=0 O lSubTotalVentas!=0)
          Si lDocumentoDe = 4 o tMovimiento(cIdDocumentoDe) = 35 // FBCS POS
            fImprimeTotales(lSubTotalCosto; lSubTotalVentas; 0; 1)
          Sino
            limprimirTotalGeneral = 1
            fImprimeTotales(lSubTotalCosto; lSubTotalVentas; 0; 0)
          FinSi
    			Saltalin
        FinSi
      FinSi
      lSubTotalCosto = 0
      lSubTotalVentas = 0
      lDocumentoDe = lDocumentoDe + 1
    FinMientras 

  FinMientras 

  Si pTipoReporte = 1 // Reporte detallado?
    // Verifica si imprimir el encabezado del producto
    Si !lDatosProducto y !pImprimirProductos
      fImprimeProducto(atProducto)
      lDatosProducto = 1
    FinSi
    // Imprime totales
    // Bandera para imprimir un total cuando existen notas de venta (limprimirTotalGeneral = 1)
    // N.D.4770
    Si lDatosProducto y limprimirTotalGeneral = 1
      fImprimeTotales(lTotalCosto; lTotalVentas; 0; 1)
			Saltalin
    FinSi  
  Sino // Reporte concentrado
    Si !pImprimirProductos O lAlMenosUno
      fImprimeProducto(atProducto)
      lDatosProducto = 1
    FinSi
    // Imprime los datos del reporte acumulado
    Si lDatosProducto
      fImprimeConcentrado(atProducto(cCodigoProducto); lTotalCosto; lTotalVentas)
      Saltalin
    FinSi
  FinSi
FinFunc


// *********************************************************************************************************************
//  Esta función verifica los parámetros pProductoInicial y pProductoFinal.
//  Si están vacíos toma el primer y último producto 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
FinFunc


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

  Si pTipoReporte = 1 // Reporte detallado?
  	Texto '^@TC{0,0,0}@S{16}UTILIDAD DETALLADO'
  Sino // Reporte concentrado
  	Texto '^@TC{0,0,0}@S{16}UTILIDAD'
  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 = 1 // Reporte detallado?
    Texto C0;'Fecha'
    Texto C2;'Serie'
    Texto C4;'^Folio'
    Texto C6;'Concepto'
    Texto C8;'^Costo venta/' 
    Texto C10;'^Ventas/' 
    Texto C12;'^Utilidad'
    Texto C14;'^% Utilidad'
    Imprime
    Texto C8;'^Devolucion'
    Texto C10;'^Devoluciones'
    Imprime
  Sino // Reporte concentrado
    // Redefine el ancho de las columnas
    Columnas 17;2.09,0.15,5.98,0.15,4.18,0.15,2.24,0.30,0.15,1.64,0.75,0 
    Texto C0;'Código'
    Texto C2;'Nombre (Producto)'
    Texto C4;'Método de costeo'
    Texto C6;'^Margen utilidad'
    Texto C10;'"Error'
    Imprime
    // Regresa las columnas a su ancho original
    Columnas 17;7.77,0.15,2.24,0.15,2.24,0.15,2.24,0.15,2.24,0.30,0.15,0
    Texto C2;'^Costo venta/' 
    Texto C4;'^Ventas/' 
    Texto C6;'^Utilidad'
    Texto C8;'^% Utilidad'
    Imprime
    Texto C2;'^Devolucion' 
    Texto C4;'^Devoluciones' 
    Imprime
  FinSi

	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()

// Obtiene los productos en el rango especificado en los parámetros del reporte
Mientras [ tProducto.Busca[pProductoInicial & '..' & pProductoFinal]; tProducto->Encontro; tProducto.Busca Siguiente[1] ]
	
	Si tProducto(cIdProducto) = 0
		Continua
	FinSi
	
	Si tProducto(cSubTipo) = 9 
		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
  
  fProcesaProducto(tProducto)
FinMientras

// Imprime total general
fImprimeTotales(gTotalCosto; gTotalVentas; 1; 1)

Si pTipoReporte = 0 // Reporte concentrado
  Columnas 17;0.15,17.63,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

