private void contabilidad_asientosAjuste(dbContab_Contab_Entities context, int cuentaContableID, int moneda, short ano, int ciaSeleccionada, out bool error, out string message)
        {
            error   = false;
            message = "";

            // asientos
            string queryString =
                $"Select d.NumeroAutomatico as numeroAutomatico, Sum(d.Debe) as sumOfDebe, Sum(d.Haber) as sumOfHaber, " +
                "Sum(d.Debe) - Sum(d.Haber) as diferencia, Max(d.Partida) as ultimaPartida " +
                "From dAsientos d Inner Join Asientos a On d.NumeroAutomatico = a.NumeroAutomatico " +
                "Where a.AnoFiscal = {0} And a.Moneda = {1} And a.Cia = {2} " +
                "Group By d.NumeroAutomatico " +
                "Having Sum(d.Debe) <> Sum(d.Haber) ";

            List <asiento_sumarizacion> query = context.ExecuteStoreQuery <asiento_sumarizacion>(queryString, ano, moneda, ciaSeleccionada).ToList();
            dAsiento partida;

            int asientosContablesAjustados = 0;

            foreach (asiento_sumarizacion p in query)
            {
                partida = new dAsiento();

                partida.NumeroAutomatico = p.numeroAutomatico;
                partida.Partida          = Convert.ToInt16(p.ultimaPartida + 10);
                partida.CuentaContableID = cuentaContableID;
                partida.Descripcion      = "Reconversión (2018) - ajuste al asiento contable";
                partida.Referencia       = null;
                partida.Debe             = p.diferencia <= 0 ? Math.Abs(p.diferencia) : 0;
                partida.Haber            = p.diferencia > 0 ? Math.Abs(p.diferencia) : 0;

                context.dAsientos.AddObject(partida);
                asientosContablesAjustados++;
            }

            try
            {
                context.SaveChanges();
            }
            catch (Exception ex)
            {
                error   = true;
                message = "Ha ocurrido un error al intentar ejecutar una operación de acceso a la base de datos. <br /> " +
                          "El mensaje específico de error es: " + ex.Message + "<br /><br />";

                context.Dispose();

                return;
            }

            context.Dispose();

            message = $"<h3>Asientos contables - ajuste contra cuenta de reconversión</h3><br />" +
                      $"Ok, El proceso ha finalizado en forma satisfactoria.<br /><br />" +
                      $"En total, se han actualizado: {asientosContablesAjustados.ToString()} asientos contables, para ajustarlos y <em>cuadrarlos</em> a cero. ";

            return;
        }
        private void RefreshAndBindInfo()
        {
            if (!User.Identity.IsAuthenticated)
            {
                FormsAuthentication.SignOut();
                return;
            }

            if (Session["filtroForma_consultaAsientosContables"] == null)
            {
                ErrMessage_Span.InnerHtml = "Aparentemente, Ud. no ha indicado un filtro aún.<br />Por favor indique y aplique un filtro " +
                                            "antes de intentar mostrar el resultado de la consulta.";
                ErrMessage_Span.Style["display"] = "block";

                return;
            }

            // --------------------------------------------------------------------------------------------
            // determinamos el mes y año fiscales, para usarlos como criterio para buscar el saldo en la tabla
            // SaldosContables. En esta table, los saldos están para el mes fiscal y no para el mes calendario.
            // Los meses solo varían cuando el año fiscal no es igual al año calendario

            // --------------------------------------------------------------------------------------------
            // eliminamos el contenido de la tabla temporal

            dbContab_Contab_Entities context = new dbContab_Contab_Entities();

            try
            {
                context.ExecuteStoreCommand("Delete From tTempWebReport_ConsultaComprobantesContables Where NombreUsuario = {0}", Membership.GetUser().UserName);
            }
            catch (Exception ex)
            {
                context.Dispose();

                ErrMessage_Span.InnerHtml = "Ha ocurrido un error al intentar ejecutar una operación de acceso a la base de datos. <br /> " +
                                            "El mensaje específico de error es: " + ex.Message + "<br /><br />";
                ErrMessage_Span.Style["display"] = "block";

                return;
            }

            // ----------------------------------------------------------------------------------------------------------------------
            // leemos la tabla de monedas para 'saber' cual es la moneda Bs. Nota: la idea es aplicar las opciones de reconversión
            // *solo* a esta moneda
            var monedaNacional_return = Reconversion.Get_MonedaNacional();

            if (monedaNacional_return.error)
            {
                ErrMessage_Span.InnerHtml        = monedaNacional_return.message;
                ErrMessage_Span.Style["display"] = "block";

                return;
            }

            Monedas monedaNacional = monedaNacional_return.moneda;
            // ----------------------------------------------------------------------------------------------------------------------

            // agregamos este flag luego de la reconversión del 1-Oct-21
            // la idea es que el usuario pueda decidir si reconvertir montos
            bool bReconvertirCifrasAntes_01Oct2021 = (bool)Session["ReconvertirCifrasAntes_01Oct2021"];

            // el usuario puede indicar que desea *solo* asientos con uploads
            string joinTableAsientosUploads = "Left Outer Join ";

            if (Session["SoloAsientosConUploads_CheckBox"] != null && Convert.ToBoolean(Session["SoloAsientosConUploads_CheckBox"]))
            {
                joinTableAsientosUploads = "Join ";
            }

            // usamos el criterio que indico el usuario para leer las cuentas contables y grabarlas a una tabla en la base de datos temporal

            // NOTA: *solo* si el usuario usa algunos criterios en el filtro, usamos un subquery en el select a los asientos contables

            // nótese que el select a la tabla de links (asientos_documentos_links) se hace en un subquery; la idea es evitar que los counts se multipliquen
            // por la cantidad de registros en el primer join. cuando en sql se hacen counts, sum, etc., y hay más de dos tablas en el select, los montos
            // se desvirtúan. La solución es usar subqueries para las tablas que siguen a la segunda ...
            string sSqlQueryString = "";

            if (Session["filtroForma_consultaAsientosContables_subQuery"] == null || Session["filtroForma_consultaAsientosContables_subQuery"].ToString() == "1 = 1")
            {
                sSqlQueryString = "SELECT Asientos.NumeroAutomatico, Asientos.Moneda, Asientos.Fecha, " +
                                  "Count(dAsientos.Partida) As NumPartidas, Sum(dAsientos.Debe) As TotalDebe, Sum(dAsientos.Haber) AS TotalHaber, " +
                                  "iif(links.NumLinks Is Not Null, links.NumLinks, 0) As NumLinks, Lote As Lote " +
                                  "FROM Asientos Left Outer Join dAsientos On Asientos.NumeroAutomatico = dAsientos.NumeroAutomatico " +
                                  joinTableAsientosUploads +    // 'join' o 'left join' dependiendo de si el usuario quiere *solo* asientos con uploads
                                  "(SELECT Asientos_Documentos_Links.NumeroAutomatico, Count(Asientos_Documentos_Links.Id) as NumLinks " +
                                  "FROM Asientos_Documentos_Links " +
                                  "Group By Asientos_Documentos_Links.NumeroAutomatico) as links On Asientos.NumeroAutomatico = links.NumeroAutomatico " +
                                  "Where " + Session["filtroForma_consultaAsientosContables"].ToString() + " " +
                                  "Group By Asientos.NumeroAutomatico, Asientos.Moneda, Asientos.Fecha, Asientos.Lote, links.NumLinks";
            }
            else
            {
                // usamos un subquery para que solo asientos con ciertas cuentas *o* partidas con montos con más de 2 decimales sean seleccionados
                sSqlQueryString = "SELECT Asientos.NumeroAutomatico, Asientos.Moneda, Asientos.Fecha, " +
                                  "Count(dAsientos.Partida) As NumPartidas, Sum(dAsientos.Debe) As TotalDebe, Sum(dAsientos.Haber) AS TotalHaber, " +
                                  "iif(links.NumLinks Is Not Null, links.NumLinks, 0) As NumLinks, Lote As Lote " +
                                  "FROM Asientos " +
                                  "Left Outer Join dAsientos On Asientos.NumeroAutomatico = dAsientos.NumeroAutomatico " +
                                  "Left Outer Join CuentasContables On CuentasContables.ID = dAsientos.CuentaContableID " +

                                  joinTableAsientosUploads +    // 'join' o 'left join' dependiendo de si el usuario quiere *solo* asientos con uploads
                                  "(SELECT Asientos_Documentos_Links.NumeroAutomatico, Count(Asientos_Documentos_Links.Id) as NumLinks " +
                                  "FROM Asientos_Documentos_Links " +
                                  "Group By Asientos_Documentos_Links.NumeroAutomatico) as links On Asientos.NumeroAutomatico = links.NumeroAutomatico " +

                                  "Where " + Session["filtroForma_consultaAsientosContables"].ToString() +
                                  " And (Asientos.NumeroAutomatico In (SELECT Asientos.NumeroAutomatico FROM Asientos " +
                                  "Left Outer Join dAsientos On Asientos.NumeroAutomatico = dAsientos.NumeroAutomatico " +
                                  "Left Outer Join CuentasContables On CuentasContables.ID = dAsientos.CuentaContableID " +
                                  "Where " + Session["filtroForma_consultaAsientosContables"].ToString() + " And " +
                                  Session["filtroForma_consultaAsientosContables_subQuery"].ToString() + "))" +
                                  "Group By Asientos.NumeroAutomatico, Asientos.Moneda, Asientos.Fecha, Asientos.Lote, links.NumLinks";
            }

            if (Session["SoloAsientosDescuadrados"] != null && Convert.ToBoolean(Session["SoloAsientosDescuadrados"]))
            {
                sSqlQueryString = sSqlQueryString + " Having Sum(dAsientos.Debe) <> Sum(dAsientos.Haber)";
            }


            List <ComprobanteContable_Object>            query = context.ExecuteStoreQuery <ComprobanteContable_Object>(sSqlQueryString).ToList();
            tTempWebReport_ConsultaComprobantesContables MyComprobantesContable;

            decimal nTotalDebe        = 0;
            decimal nTotalHaber       = 0;
            int     cantidadRegistros = 0;

            DateTime fechaReconversion2021 = new DateTime(2021, 10, 1);

            foreach (ComprobanteContable_Object a in query)
            {
                decimal totalDebe  = a.TotalDebe != null ? a.TotalDebe.Value : 0;
                decimal totalHaber = a.TotalHaber != null ? a.TotalHaber.Value : 0;
                // -----------------------------------------------------------------------------------------
                // el usuario puede indicar que quiere reconvertir cifras anteriores al 31/Oct/21
                if (bReconvertirCifrasAntes_01Oct2021 && (a.Moneda == monedaNacional.Moneda) && (a.Fecha < fechaReconversion2021))
                {
                    totalDebe /= 1000000;
                    totalDebe  = Math.Round(totalDebe, 2);

                    totalHaber /= 1000000;
                    totalHaber  = Math.Round(totalHaber, 2);
                }

                MyComprobantesContable = new tTempWebReport_ConsultaComprobantesContables();

                MyComprobantesContable.NumeroAutomatico = a.NumeroAutomatico;
                MyComprobantesContable.NumPartidas      = Convert.ToInt16(a.NumPartidas);
                MyComprobantesContable.TotalDebe        = totalDebe;
                MyComprobantesContable.TotalHaber       = totalHaber;
                MyComprobantesContable.NombreUsuario    = Membership.GetUser().UserName;
                MyComprobantesContable.NumUploads       = Convert.ToInt16(a.NumLinks);
                MyComprobantesContable.Lote             = a.Lote;

                context.tTempWebReport_ConsultaComprobantesContables.AddObject(MyComprobantesContable);

                nTotalDebe  += totalDebe;
                nTotalHaber += totalHaber;
                cantidadRegistros++;
            }

            try
            {
                context.SaveChanges();
            }
            catch (Exception ex)
            {
                string errorMessage = ex.Message;
                if (ex.InnerException != null)
                {
                    errorMessage += "<br />" + ex.InnerException.Message;
                }
                ErrMessage_Span.InnerHtml = "Ha ocurrido un error al intentar ejecutar una operación de acceso a la base de datos. <br /> " +
                                            "El mensaje específico de error es: " +
                                            errorMessage;
                ErrMessage_Span.Style["display"] = "block";

                context.Dispose();
                return;
            }

            context.Dispose();

            this.CompaniasFilter_DropDownList.DataBind();
            this.MonedasFilter_DropDownList.DataBind();
            ComprobantesContables_ListView.DataBind();

            // actualizamos los totales en el footer en el ListView
            Label numberOfRecords_label = (Label)ComprobantesContables_ListView.FindControl("numberOfRecords_label");
            Label MySumOfDebe_Label     = (Label)ComprobantesContables_ListView.FindControl("SumOfDebe_Label");
            Label MySumOfHaber_Label    = (Label)ComprobantesContables_ListView.FindControl("SumOfHaber_Label");

            if (numberOfRecords_label != null)
            {
                numberOfRecords_label.Text = cantidadRegistros.ToString();
            }

            if (MySumOfDebe_Label != null)
            {
                MySumOfDebe_Label.Text = nTotalDebe.ToString("#,##0.000");
            }

            if (MySumOfHaber_Label != null)
            {
                MySumOfHaber_Label.Text = nTotalHaber.ToString("#,##0.000");
            }
        }
        private void contabilidad_saldosInicialesCuadrar(dbContab_Contab_Entities context, int cuentaContableID, int moneda, short ano, int ciaSeleccionada, out bool error, out string message)
        {
            error   = false;
            message = "";

            // asientos
            string queryString =
                $"Select s.Moneda as moneda, s.MonedaOriginal as monedaOriginal, s.Ano as ano, s.Cia as cia, Sum(s.Inicial) as sumOfInicial " +
                "From SaldosContables s Inner Join CuentasContables c On s.CuentaContableID = c.ID " +
                "Where s.Ano = {0} And s.Moneda = {1} And s.Cia = {2} " +
                "And c.TotDet = 'D' " +
                "Group By s.Moneda, s.MonedaOriginal, s.Ano, s.Cia";

            List <saldos_sumarizacion> query = context.ExecuteStoreQuery <saldos_sumarizacion>(queryString, ano, moneda, ciaSeleccionada).ToList();
            SaldosContable             saldoContable;

            int saldosContables_ajustados = 0;
            int saldosContables_agregados = 0;

            foreach (saldos_sumarizacion inicial in query)
            {
                if (inicial.sumOfInicial == 0)
                {
                    // si no hay diferencia en el saldo inicial, simplemente continuamos ...
                    continue;
                }


                // leemos un registro de saldos para la cuenta contable, a ver si existe
                saldoContable = context.SaldosContables.Where(s => s.CuentaContableID == cuentaContableID &&
                                                              s.Moneda == inicial.moneda && s.MonedaOriginal == inicial.monedaOriginal &&
                                                              s.Ano == inicial.ano && s.Cia == inicial.cia).FirstOrDefault();

                if (saldoContable != null)
                {
                    // el registro de saldos para la: cuenta, moneda y moneda  original *existe*; lo actualizamos

                    // calculamos el nuevo saldo inicial (ajustado)
                    decimal nuevoInicial = saldoContable.Inicial.HasValue ? saldoContable.Inicial.Value : 0;

                    if (inicial.sumOfInicial >= 0)
                    {
                        nuevoInicial = nuevoInicial - inicial.sumOfInicial;                     // la diferencia es mayor que cero; para ajustar, restamos
                    }
                    else
                    {
                        nuevoInicial = nuevoInicial + Math.Abs(inicial.sumOfInicial);           // la diferencia es menor que cero; para ajustar, sumamos
                    }
                    saldoContable.Inicial = nuevoInicial;
                    saldosContables_ajustados++;
                }
                else
                {
                    // el registro de saldos para la cuenta *no existe*; lo agregamos

                    SaldosContable saldo = new SaldosContable
                    {
                        CuentaContableID = cuentaContableID,
                        Moneda           = inicial.moneda,
                        MonedaOriginal   = inicial.monedaOriginal,
                        Ano = inicial.ano,
                        // agregamos la diferencia, justo con el signo contrario ...
                        Inicial = (inicial.sumOfInicial * -1),
                        Mes01   = 0,
                        Mes02   = 0,
                        Mes03   = 0,
                        Mes04   = 0,
                        Mes05   = 0,
                        Mes06   = 0,
                        Mes07   = 0,
                        Mes08   = 0,
                        Mes09   = 0,
                        Mes10   = 0,
                        Mes11   = 0,
                        Mes12   = 0,
                        Anual   = 0,
                        Cia     = inicial.cia,
                    };

                    saldosContables_agregados++;
                    context.SaldosContables.AddObject(saldo);
                }
            }


            try
            {
                context.SaveChanges();
            }
            catch (Exception ex)
            {
                error   = true;
                message = $"<h3>Saldos iniciales - Cuadre contra cuenta de reconversión</h3><br />" +
                          "Ha ocurrido un error al intentar ejecutar una operación de acceso a la base de datos. <br /> " +
                          "El mensaje específico de error es: " + ex.Message + "<br /><br />";

                context.Dispose();

                return;
            }

            context.Dispose();

            message = $"<h3>Saldos iniciales - ajuste contra cuenta de reconversión</h3><br />" +
                      $"Ok, El proceso ha finalizado en forma satisfactoria.<br /><br />" +
                      $"En total, se han <b>actualizado</b>: {saldosContables_ajustados.ToString()} registros de saldo, para ajustar el saldo inicial del año y <em>cuadrarlo</em> a cero.<br />" +
                      $"Además, se han <b>agregado</b>: {saldosContables_agregados.ToString()} registros de saldo, para ajustar el saldo inicial del año y <em>cuadrarlo</em> a cero.";

            return;
        }
        private void RefreshAndBindInfo()
        {
            if (this.User != null && this.User.Identity != null && !User.Identity.IsAuthenticated)
            {
                FormsAuthentication.SignOut();
                return;
            }

            if (Session["FiltroForma"] == null)
            {
                Session["Thread_ErrorMessage"] = "Aparentemente, Ud. no ha indicado un filtro aún.<br /> " +
                                                 "Por favor indique y aplique un filtro antes de intentar mostrar el resultado de " +
                                                 "la consulta.";
                return;
            }

            Session["Progress_Percentage"] = 0;
            string errorMessage = "";

            // nota importante: esta consulta (y otras?) deben, al menos por ahora, ser pedidas para una CiaContab
            // en particular, pues los años fiscales para las Cias Contab pueden variar entre ellas ...
            if (Session["CiaContabSeleccionada"] == null)
            {
                // el usuario debe seleccionar una cia contab en particular, pues los saldos contables se leen
                // de acuerdo al año fiscal de la misma ...

                Session["Thread_ErrorMessage"] = "Aparentemente, no se ha seleccionado una Cia Contab.<br /> " +
                                                 "Por favor seleccione una Cia Contab al establecer el filtro a usar para esta consulta.";

                Session["Progress_SelectedRecs"] = 0;
                Session["Progress_Completed"]    = 1;
                Session["Progress_Percentage"]   = 0;

                return;
            }

            var ciaContabSeleccionada = Convert.ToInt32(Session["CiaContabSeleccionada"].ToString());

            var dFechaInicialPeriodoIndicado = (System.DateTime)Session["FechaInicialPeriodo"];
            var dFechaFinalPeriodoIndicado   = (System.DateTime)Session["FechaFinalPeriodo"];

            bool bReconvertirCifrasAntes_01Oct2021      = (bool)Session["ReconvertirCifrasAntes_01Oct2021"];
            bool bExcluirAsientosReconversion_01Oct2021 = (bool)Session["ExcluirAsientosReconversion_01Oct2021"];

            bool bExcluirAsientosTipoCierreAnual = (bool)Session["ExcluirAsientosTipoCierreAnual"];

            // ----------------------------------------------------------------------------------------------------------------------
            // leemos la tabla de monedas para 'saber' cual es la moneda Bs. Nota: la idea es aplicar las opciones de reconversión
            // *solo* a esta moneda
            var monedaNacional_return = Reconversion.Get_MonedaNacional();

            if (monedaNacional_return.error)
            {
                ErrMessage_Span.InnerHtml        = monedaNacional_return.message;
                ErrMessage_Span.Style["display"] = "block";

                return;
            }

            Monedas monedaNacional = monedaNacional_return.moneda;

            Session["monedaNacional"] = monedaNacional.Moneda;
            // ----------------------------------------------------------------------------------------------------------------------

            // --------------------------------------------------------------------------------------------
            // eliminamos el contenido de la tabla temporal
            dbContab_Contab_Entities dbContext = new dbContab_Contab_Entities();

            try
            {
                int cantRegistrosEliminados = dbContext.ExecuteStoreCommand("Delete From Contab_BalanceComprobacion Where NombreUsuario = {0}", Membership.GetUser().UserName);
            }
            catch (Exception ex) {
                dbContext.Dispose();

                Session["Thread_ErrorMessage"] = "Ha ocurrido un error al intentar ejecutar una operación de " +
                                                 "acceso a la base de datos. <br /> El mensaje específico de error es: " + ex.Message +
                                                 "<br /><br />";
                return;
            }

            // -----------------------------------------------------------------------------------------------
            // para evitar que el reporte se genere en forma incompleta, intentamos buscar cuentas que tengan
            // asientos, más no registros en SaldosContables. De haberlas, lo notificamos al usuario y le
            // indicamos que ejecute un cierre contable para corregir esta situación

            string filtroFormParaAsientos = Session["FiltroForma"].ToString();

            filtroFormParaAsientos = filtroFormParaAsientos.Replace("SaldosContables", "Asientos");

            string filtroFormaParaSaldosContables = Session["FiltroForma"].ToString();

            string sSqlQueryString = "Select dAsientos.CuentaContableID From dAsientos " +
                                     "Inner Join Asientos On dAsientos.NumeroAutomatico = Asientos.NumeroAutomatico " +
                                     "Inner Join CuentasContables On dAsientos.CuentaContableID = CuentasContables.ID " +
                                     "Where Asientos.Fecha Between '" + dFechaInicialPeriodoIndicado.ToString("yyyy-MM-dd") + "' And '" +
                                     dFechaFinalPeriodoIndicado.ToString("yyyy-MM-dd") + "' And " +
                                     filtroFormParaAsientos +
                                     " And dAsientos.CuentaContableID Not In " +
                                     "(Select CuentaContableID From SaldosContables Inner Join CuentasContables On SaldosContables.CuentaContableID = CuentasContables.ID " +
                                     "Where " + filtroFormaParaSaldosContables + ")";

            int nCuentasSinSaldo = dbContext.ExecuteStoreQuery <int>(sSqlQueryString).Count();

            if (nCuentasSinSaldo > 0)
            {
                // hay cuentas contables en asientos que no tienen un registro en SaldosContables; lo notificamos al
                // usuario para que corrija esta situación con un cierre contable
                Session["Thread_ErrorMessage"] = "Existen cuentas contables usadas en asientos contables que no tienen " +
                                                 "un registro en la tabla de saldos. <br /> " +
                                                 "Por favor ejecute un cierre contable para el mes (o los meses) de esta consulta, " +
                                                 "para corregir esta situación (y agregar un registro de saldos a las cuentas contables que no lo tienen).";

                Session["Progress_SelectedRecs"] = 0;
                Session["Progress_Completed"]    = 1;
                Session["Progress_Percentage"]   = 0;

                return;
            }

            // hacemos una validación similar a la anterior, pero esta vez buscando cuentas contables que hayan cambiado de detalle a total
            // recuérdese que solo cuentas de tipo detalle pueden recibir asientos; notificamos ésto al usuario, para que corrija esta
            // situación antes de continuar ...
            sSqlQueryString = "Select CuentasContables.CuentaEditada + ' ' + CuentasContables.Descripcion From dAsientos " +
                              "Inner Join Asientos On dAsientos.NumeroAutomatico = Asientos.NumeroAutomatico " +
                              "Inner Join CuentasContables On dAsientos.CuentaContableID = CuentasContables.ID " +
                              "Where Asientos.Fecha Between '" + dFechaInicialPeriodoIndicado.ToString("yyyy-MM-dd") + "' And '" +
                              dFechaFinalPeriodoIndicado.ToString("yyyy-MM-dd") + "' And " +
                              filtroFormParaAsientos +
                              " And CuentasContables.TotDet <> 'D'";

            List <string> query0 = dbContext.ExecuteStoreQuery <string>(sSqlQueryString).ToList();

            errorMessage = "";

            foreach (string cuentaTipoTotal in query0)
            {
                if (string.IsNullOrEmpty(errorMessage))
                {
                    errorMessage = cuentaTipoTotal;
                }
                else
                {
                    errorMessage += ", " + cuentaTipoTotal;
                }
            }

            if (errorMessage != "")
            {
                Session["Thread_ErrorMessage"] = "Aparentemente, existen cuentas contables que han recibido asientos contables en el período indicado y que " +
                                                 "no son de tipo 'detalle'; <br />deben serlo, pues solo cuentas de tipo 'detalle' pueden recibir asientos. <br /> " +
                                                 "Es probable que una cuenta de tipo 'detalle' y que recibió asientos en el período indicado, fue cambiada a tipo 'total' y mantuvo sus asientos.<br /> " +
                                                 "A continuación mostramos las cuentas que están en este estado y que ha detectado este proceso: " + errorMessage;

                Session["Progress_SelectedRecs"] = 0;
                Session["Progress_Completed"]    = 1;
                Session["Progress_Percentage"]   = 0;

                return;
            }
            ;

            // para leer los saldos, debemos determinar el año fiscal de la Cia Contab; un año calendario puede ser
            // 2013 y el año fiscal 2012. Los saldos contables se registran para el año fiscal. Por esta razón,
            // debemos obtenerlo *antes* de hacer el select que sigue
            GetMesFiscalContable MyGetMesFiscalContable = new GetMesFiscalContable(dFechaInicialPeriodoIndicado, ciaContabSeleccionada);

            string sErrorMessage = "";

            // antes usabamos Linq to Sql en vez de EF ...
            ContabSysNet_Web.ModelosDatos.dbContabDataContext ContabDB = new ContabSysNet_Web.ModelosDatos.dbContabDataContext();
            MyGetMesFiscalContable.dbContabDataContext = ContabDB;

            if (!MyGetMesFiscalContable.DeterminarMesFiscal(ref sErrorMessage))
            {
                ContabDB.Dispose();

                ErrMessage_Span.InnerHtml        = sErrorMessage;
                ErrMessage_Span.Style["display"] = "block";

                return;
            }

            int anoFiscalCiaSeleccionada = MyGetMesFiscalContable.AnoFiscal;

            // ------------------------------------------------------------------------------------------------------------------------------
            // como vamos a usar esta conexión mientras se ejecute el ciclo que viene, la abrimos aquí y la cerramos al final ...
            SqlConnection sqlConnection = new SqlConnection();

            sqlConnection.ConnectionString = ConfigurationManager.ConnectionStrings["dbContabConnectionString"].ConnectionString;
            sqlConnection.Open();

            // -----------------------------------------------------------------------------------------------------
            // usamos esta clase para leer los movimientos (debe, haber) para cada cuenta contable y moneda
            DeterminarMovimientoCuentaContable determinarMovimientoCuentaContable = new DeterminarMovimientoCuentaContable(sqlConnection,
                                                                                                                           bExcluirAsientosTipoCierreAnual,
                                                                                                                           bReconvertirCifrasAntes_01Oct2021,
                                                                                                                           bExcluirAsientosReconversion_01Oct2021,
                                                                                                                           dFechaInicialPeriodoIndicado,
                                                                                                                           dFechaFinalPeriodoIndicado, monedaNacional.Moneda);

            // -----------------------------------------------------------------------------------------------------
            // primero determinamos la cantidad de registros, para mostrar progreso al usuario

            // NOTA IMPORTANTE: usamos el año para leer en la tabla SaldosContables SOLO cuentas/monedas/cias que
            // corresopondan al año indicado. Recuérdese que una cuenta/moneda/cia puede tener MUCHOS registros
            // en SaldosContables, uno para cada año. SIN EMBARGO, deberíamos usar el año fiscal y no el año de
            // la fecha inicial del período (año calendario). Lo hacemos así, sabiendo que muy pocas veces ésto
            // tendrá un efecto importante en el proceso. Creo que bastaría con hacer un cierre contable anual en
            // los casos en que el efecto exista

            // ----------------------------------------------------------------------
            // ahora ejecutamos el query para que regrese los rows uno a uno

            // leemos las cuentas contables y todos sus datos de compañías, grupos, monedas, etc. Nótese que leemos
            // SOLO las que tienen un registro en SaldosContables y para el año de la fecha de inicio. La idea es
            // solo traernos cuentas que tengan un registro en SaldosContables, no leer su saldo, lo cual hacemos
            // más adelante, para cada una, en el loop que sigue

            // nótese como cambiamos el Select que sigue, para eliminar la tabla SaldosContables de la
            // sección Inner Join. La razón es que, a veces, puede ocurrir que una cuenta tiene asientos
            // más no tiene un registro en SaldosContables. Imaginamos que ésto puede ocurrir cuando una
            // cuenta se usa por 1ra. vez y no se ha corrido el cierre contable aún para el mes del reporte.

            // ordenamos por CiaContab para que la lista que resulta de aquí también lo esté

            sSqlQueryString = "Select Distinct CuentasContables.ID As CuentaContableID, CuentasContables.Cuenta As CuentaContable, " +
                              "CuentasContables.Descripcion As NombreCuentaContable, " +
                              "tGruposContables.Grupo As GrupoContable, " +
                              "tGruposContables.Descripcion As NombreGrupoContable, " +
                              "IsNull(tGruposContables.OrdenBalanceGeneral, 0) As GrupoContable_OrdenBalance, " +
                              "CuentasContables.Cia As CiaContab, Companias.NombreCorto As NombreCiaContab, " +
                              "CuentaEditada As CuentaContableEditada, Monedas.Moneda, Monedas.Descripcion As " +
                              "NombreMoneda, " + "Monedas.Simbolo As SimboloMoneda, " +
                              "Case NumNiveles When 2 Then Nivel1 When 3 Then Nivel1 + Nivel2 When 4 Then Nivel1 + " +
                              "Nivel2 + Nivel3 " + "When 5 Then Nivel1 + Nivel2 + Nivel3 + Nivel4 When 6 Then Nivel1 + " +
                              "Nivel2 + Nivel3 + Nivel4 + Nivel5 " +
                              "When 7 Then Nivel1 + Nivel2 + Nivel3 + Nivel4 + Nivel5 + Nivel6 End AS " +
                              "CuentaContable_NivelPrevio, " +
                              "Nivel1, Nivel2, Nivel3, Nivel4, Nivel5, Nivel6, NumNiveles " +
                              "From CuentasContables " +
                              "Inner Join Companias On CuentasContables.Cia = Companias.Numero " +
                              "Inner Join SaldosContables On CuentasContables.ID = SaldosContables.CuentaContableID " +
                              "Inner Join Monedas On SaldosContables.Moneda = Monedas.Moneda " +
                              "Inner Join tGruposContables On CuentasContables.Grupo = tGruposContables.Grupo " +
                              "Where CuentasContables.TotDet = 'D' And SaldosContables.Ano = " +
                              anoFiscalCiaSeleccionada.ToString() +
                              " And " + Session["FiltroForma"].ToString() + " Order by CuentasContables.Cia";

            List <CuentaContable_Object> query = dbContext.ExecuteStoreQuery <CuentaContable_Object>(sSqlQueryString).ToList();

            if (query.Count() == 0)
            {
                Session["Thread_ErrorMessage"] = "No existen registros que cumplan el criterio de selección " +
                                                 "(filtro) que Ud. ha indicado. <br /> Para regresar registros, " +
                                                 "Ud. puede intentar un filtro diferente al que ha indicado.";

                Session["Progress_SelectedRecs"] = 0;
                Session["Progress_Completed"]    = 1;
                Session["Progress_Percentage"]   = 0;

                return;
            }

            // ---------------------------------------------------------------------------------------------
            // variables usadas para mostrar el meter en la página ...
            int nCantidadRegistros = query.Count();

            int nRegistroActual     = 0;
            int nProgreesPercentaje = 0;

            string nombreUsuario = Membership.GetUser().UserName;

            Session["Progress_SelectedRecs"] = 0;
            // ---------------------------------------------------------------------------------------------

            List <BalanceComprobacion_Item> MyBalanceComprobacion_Lista = new List <BalanceComprobacion_Item>();
            BalanceComprobacion_Item        MyBalanceComprobacion_Record;

            foreach (CuentaContable_Object MyComprobanteContable_Query in query)
            {
                MyBalanceComprobacion_Record = new BalanceComprobacion_Item();

                MyBalanceComprobacion_Record.CuentaContableID          = MyComprobanteContable_Query.CuentaContableID;
                MyBalanceComprobacion_Record.Moneda                    = MyComprobanteContable_Query.Moneda;
                MyBalanceComprobacion_Record.NivelPrevioCuentaContable = MyComprobanteContable_Query.CuentaContable_NivelPrevio;
                MyBalanceComprobacion_Record.Cia = MyComprobanteContable_Query.CiaContab;

                // nótese como agregamos todos los niveles 'previos' de la cuenta, para poder totalizar por éstos en el reporte ...
                switch (MyComprobanteContable_Query.NumNiveles)
                {
                case 2:
                    MyBalanceComprobacion_Record.nivel1 = MyComprobanteContable_Query.Nivel1;
                    break;

                case 3:
                    MyBalanceComprobacion_Record.nivel1 = MyComprobanteContable_Query.Nivel1;
                    MyBalanceComprobacion_Record.nivel2 = MyBalanceComprobacion_Record.nivel1 + MyComprobanteContable_Query.Nivel2;
                    break;

                case 4:
                    MyBalanceComprobacion_Record.nivel1 = MyComprobanteContable_Query.Nivel1;
                    MyBalanceComprobacion_Record.nivel2 = MyBalanceComprobacion_Record.nivel1 + MyComprobanteContable_Query.Nivel2;
                    MyBalanceComprobacion_Record.nivel3 = MyBalanceComprobacion_Record.nivel2 + MyComprobanteContable_Query.Nivel3;
                    break;

                case 5:
                    MyBalanceComprobacion_Record.nivel1 = MyComprobanteContable_Query.Nivel1;
                    MyBalanceComprobacion_Record.nivel2 = MyBalanceComprobacion_Record.nivel1 + MyComprobanteContable_Query.Nivel2;
                    MyBalanceComprobacion_Record.nivel3 = MyBalanceComprobacion_Record.nivel2 + MyComprobanteContable_Query.Nivel3;
                    MyBalanceComprobacion_Record.nivel4 = MyBalanceComprobacion_Record.nivel3 + MyComprobanteContable_Query.Nivel4;
                    break;

                case 6:
                    MyBalanceComprobacion_Record.nivel1 = MyComprobanteContable_Query.Nivel1;
                    MyBalanceComprobacion_Record.nivel2 = MyBalanceComprobacion_Record.nivel1 + MyComprobanteContable_Query.Nivel2;
                    MyBalanceComprobacion_Record.nivel3 = MyBalanceComprobacion_Record.nivel2 + MyComprobanteContable_Query.Nivel3;
                    MyBalanceComprobacion_Record.nivel4 = MyBalanceComprobacion_Record.nivel3 + MyComprobanteContable_Query.Nivel4;
                    MyBalanceComprobacion_Record.nivel5 = MyBalanceComprobacion_Record.nivel4 + MyComprobanteContable_Query.Nivel5;
                    break;

                case 7:
                    MyBalanceComprobacion_Record.nivel1 = MyComprobanteContable_Query.Nivel1;
                    MyBalanceComprobacion_Record.nivel2 = MyBalanceComprobacion_Record.nivel1 + MyComprobanteContable_Query.Nivel2;
                    MyBalanceComprobacion_Record.nivel3 = MyBalanceComprobacion_Record.nivel2 + MyComprobanteContable_Query.Nivel3;
                    MyBalanceComprobacion_Record.nivel4 = MyBalanceComprobacion_Record.nivel3 + MyComprobanteContable_Query.Nivel4;
                    MyBalanceComprobacion_Record.nivel5 = MyBalanceComprobacion_Record.nivel4 + MyComprobanteContable_Query.Nivel5;
                    MyBalanceComprobacion_Record.nivel6 = MyBalanceComprobacion_Record.nivel5 + MyComprobanteContable_Query.Nivel6;
                    break;
                }

                MyBalanceComprobacion_Lista.Add(MyBalanceComprobacion_Record);

                // --------------------------------------------------------------------------------------
                // ... para reportar el progreso al usuario; la página ejecuta un ws que lee el valor de
                // estas session variables
                nRegistroActual    += 1;
                nProgreesPercentaje = nRegistroActual * 100 / nCantidadRegistros;

                Session["Progress_Percentage"]   = nProgreesPercentaje;
                Session["Progress_SelectedRecs"] = (int)Session["Progress_SelectedRecs"] + 1;
            }

            // ---------------------------------------------------------------------------------
            // recorremos la lista para determinar saldo anterior, debe, haber y saldo actual

            // -----------------------------------------------------------------
            // determinamos la cantidad de registros para el progress al usuario
            nCantidadRegistros = MyBalanceComprobacion_Lista.Count();
            // -----------------------------------------------------------------

            nRegistroActual = 0;
            Session["Progress_Percentage"] = 0;

            int nCiaContabAnterior = -999999;

            int    nMesFiscal     = 0;
            int    nAnoFiscal     = 0;
            int    nMesCalendario = 0;
            int    nAnoCalendario = 0;
            string sNombreMes     = "";

            System.DateTime dFechaSaldoInicial;

            Session["Progress_SelectedRecs"] = 0;

            foreach (BalanceComprobacion_Item MyBalanceComprobacion_Record2 in MyBalanceComprobacion_Lista.OrderBy(b => b.Cia))
            {
                if (MyBalanceComprobacion_Record2.Cia != nCiaContabAnterior)
                {
                    // buscamos el mes y año fiscal solo cuando cambian las compañías
                    MyGetMesFiscalContable = new GetMesFiscalContable(dFechaInicialPeriodoIndicado, MyBalanceComprobacion_Record2.Cia);

                    sErrorMessage = "";
                    MyGetMesFiscalContable.dbContabDataContext = ContabDB;
                    if (!MyGetMesFiscalContable.DeterminarMesFiscal(ref sErrorMessage))
                    {
                        ContabDB.Dispose();

                        ErrMessage_Span.InnerHtml        = sErrorMessage;
                        ErrMessage_Span.Style["display"] = "block";

                        return;
                    }

                    nMesFiscal         = MyGetMesFiscalContable.MesFiscal;
                    nAnoFiscal         = MyGetMesFiscalContable.AnoFiscal;
                    nMesCalendario     = MyGetMesFiscalContable.MesCalendario;
                    nAnoCalendario     = MyGetMesFiscalContable.AnoCalendario;
                    sNombreMes         = MyGetMesFiscalContable.NombreMes;
                    dFechaSaldoInicial = MyGetMesFiscalContable.FechaSaldo;

                    nCiaContabAnterior = MyBalanceComprobacion_Record2.Cia;
                }

                // determinamos el saldo anterior de cada cuenta (GetSaldoContable es una clase que está en: old_app_code/Generales2.cs)
                GetSaldoContable MyGetSaldoContable = new GetSaldoContable(MyBalanceComprobacion_Record2.CuentaContableID,
                                                                           nMesFiscal,
                                                                           nAnoFiscal,
                                                                           dFechaInicialPeriodoIndicado,
                                                                           MyBalanceComprobacion_Record2.Moneda,
                                                                           MyBalanceComprobacion_Record2.Cia, bReconvertirCifrasAntes_01Oct2021, monedaNacional.Moneda);

                MyGetSaldoContable.bLeerSaldoCuenta();

                decimal nSaldoAnteriorContable = MyGetSaldoContable.SaldoAnterior;

                MyBalanceComprobacion_Record2.SaldoAnterior = nSaldoAnteriorContable;

                // este método lee los movimientos para la cuenta, la moneda y el período
                var result = determinarMovimientoCuentaContable.leerMovimientoCuentaContable(MyBalanceComprobacion_Record2.CuentaContableID, MyBalanceComprobacion_Record2.Moneda);

                MyBalanceComprobacion_Record2.Debe  = result.sumDebe;
                MyBalanceComprobacion_Record2.Haber = result.sumHaber;
                MyBalanceComprobacion_Record2.CantidadMovimientos = result.recCount;

                // --------------------------------------------------------------------------------------------------------------------
                // por último, leemos la descripción del 'nivel previo' de la cuenta
                string selectDescripcionNivelPrevio = "Select Descripcion From CuentasContables Where Cuenta = {0} And Cia = {1}";

                object[] parameters = { MyBalanceComprobacion_Record2.NivelPrevioCuentaContable, MyBalanceComprobacion_Record2.Cia };
                string   sCuentaContable_NivelPrevio_Descripcion = dbContext.ExecuteStoreQuery <string>(selectDescripcionNivelPrevio, parameters).FirstOrDefault();

                MyBalanceComprobacion_Record2.NivelPrevioCuentaContable_Nombre = sCuentaContable_NivelPrevio_Descripcion;
                MyBalanceComprobacion_Record2.SaldoActual = MyBalanceComprobacion_Record2.SaldoAnterior + MyBalanceComprobacion_Record2.Debe - MyBalanceComprobacion_Record2.Haber;

                // --------------------------------------------------------------------------------------
                // para mostrar el progreso al usuario

                nRegistroActual    += 1;
                nProgreesPercentaje = nRegistroActual * 100 / nCantidadRegistros;

                Session["Progress_Percentage"]   = nProgreesPercentaje;
                Session["Progress_SelectedRecs"] = (int)Session["Progress_SelectedRecs"] + 1;
            }

            // tenemos los niveles 'previos' de cada cuenta; vamos a crear una lista con las descripciones de cada cuenta, para luego
            // leer las mismas y completar los niveles previos en la lista. Para cada nivel, ej: 0101002, quedará así: 0101002 - <su descripción> ...

            var queryCuentasContables = dbContext.CuentasContables.Where(c => c.TotDet == "T" && c.Cia == ciaContabSeleccionada).Select(c => new { c.Cuenta, c.Descripcion });
            List <Cuenta_Nombre> listaCuentasYNombres = new List <Cuenta_Nombre>();

            foreach (var cuenta in queryCuentasContables)
            {
                listaCuentasYNombres.Add(new Cuenta_Nombre {
                    cuenta = cuenta.Cuenta, descripcion = cuenta.Descripcion
                });
            }

            // ahora recorremos la lista y buscamos la descripción de cada cuenta contable
            foreach (BalanceComprobacion_Item item in MyBalanceComprobacion_Lista)
            {
                if (item.nivel1 != null)
                {
                    if (listaCuentasYNombres.Where(c => c.cuenta == item.nivel1).Count() > 0)
                    {
                        item.nivel1 += " - " + listaCuentasYNombres.Where(c => c.cuenta == item.nivel1).First().descripcion;
                    }
                }

                if (item.nivel2 != null)
                {
                    if (listaCuentasYNombres.Where(c => c.cuenta == item.nivel2).Count() > 0)
                    {
                        item.nivel2 += " - " + listaCuentasYNombres.Where(c => c.cuenta == item.nivel2).First().descripcion;
                    }
                }

                if (item.nivel3 != null)
                {
                    if (listaCuentasYNombres.Where(c => c.cuenta == item.nivel3).Count() > 0)
                    {
                        item.nivel3 += " - " + listaCuentasYNombres.Where(c => c.cuenta == item.nivel3).First().descripcion;
                    }
                }

                if (item.nivel4 != null)
                {
                    if (listaCuentasYNombres.Where(c => c.cuenta == item.nivel4).Count() > 0)
                    {
                        item.nivel4 += " - " + listaCuentasYNombres.Where(c => c.cuenta == item.nivel4).First().descripcion;
                    }
                }

                if (item.nivel5 != null)
                {
                    if (listaCuentasYNombres.Where(c => c.cuenta == item.nivel5).Count() > 0)
                    {
                        item.nivel5 += " - " + listaCuentasYNombres.Where(c => c.cuenta == item.nivel5).First().descripcion;
                    }
                }

                if (item.nivel6 != null)
                {
                    if (listaCuentasYNombres.Where(c => c.cuenta == item.nivel6).Count() > 0)
                    {
                        item.nivel6 += " - " + listaCuentasYNombres.Where(c => c.cuenta == item.nivel6).First().descripcion;
                    }
                }
            }

            // agregamos el contenido de la lista a la tabla
            Contab_BalanceComprobacion contabBalanceComprobacionItem;

            foreach (BalanceComprobacion_Item item in MyBalanceComprobacion_Lista)
            {
                contabBalanceComprobacionItem = new Contab_BalanceComprobacion()
                {
                    CuentaContableID                       = item.CuentaContableID,
                    Moneda                                 = item.Moneda,
                    CuentaContable_NivelPrevio             = item.NivelPrevioCuentaContable,
                    CuentaContable_NivelPrevio_Descripcion = item.NivelPrevioCuentaContable_Nombre,
                    nivel1                                 = item.nivel1,
                    nivel2                                 = item.nivel2,
                    nivel3                                 = item.nivel3,
                    nivel4                                 = item.nivel4,
                    nivel5                                 = item.nivel5,
                    nivel6                                 = item.nivel6,
                    SaldoAnterior                          = item.SaldoAnterior,
                    Debe                = item.Debe,
                    Haber               = item.Haber,
                    SaldoActual         = item.SaldoActual,
                    CantidadMovimientos = item.CantidadMovimientos,
                    NombreUsuario       = nombreUsuario
                };

                dbContext.Contab_BalanceComprobacion.AddObject(contabBalanceComprobacionItem);
            }

            try
            {
                dbContext.SaveChanges();
            }
            catch (Exception ex)
            {
                errorMessage = ex.Message;
                if (ex.InnerException != null)
                {
                    errorMessage += "<br />" + ex.InnerException.Message;
                }

                Session["Thread_ErrorMessage"] = "Ha ocurrido un error al intentar ejecutar una operación de " +
                                                 "acceso a la base de datos. <br /> El mensaje específico de error es: " +
                                                 errorMessage + "<br />";

                Session["Progress_Completed"]  = 1;
                Session["Progress_Percentage"] = 0;

                return;
            }

            // ------------------------------------------------------------------------------------------
            // por último, eliminamos las cuentas seleccionadas de acuerdo al criterio indicado en las opciones
            if (!(bool)Session["MostrarCuentasSinSaldoYSinMvtos"])
            {
                dbContext.ExecuteStoreCommand("Delete From Contab_BalanceComprobacion Where SaldoAnterior = 0 And CantidadMovimientos = 0 And NombreUsuario = {0}", nombreUsuario);
            }

            if (!(bool)Session["MostrarCuentasConSaldoYSinMvtos"])
            {
                dbContext.ExecuteStoreCommand("Delete From Contab_BalanceComprobacion Where SaldoAnterior <> 0 And CantidadMovimientos = 0 And NombreUsuario = {0}", nombreUsuario);
            }

            if (!(bool)Session["MostrarCuentasSaldosEnCero"])
            {
                dbContext.ExecuteStoreCommand("Delete From Contab_BalanceComprobacion Where SaldoAnterior = 0 And SaldoActual = 0 And NombreUsuario = {0}", nombreUsuario);
            }

            if (!(bool)Session["MostrarCuentasSaldoFinalEnCero"])
            {
                dbContext.ExecuteStoreCommand("Delete From Contab_BalanceComprobacion Where SaldoActual = 0 And NombreUsuario = {0}", nombreUsuario);
            }

            dbContext.Dispose();

            // -----------------------------------------------------
            // por último, inicializamos las variables que se usan
            // para mostrar el progreso de la tarea
            Session["Progress_Completed"]  = 1;
            Session["Progress_Percentage"] = 0;
            // -----------------------------------------------------
        }