public IList<ResultVersamento> InsertVersamentoByPersona(int idEsercizio, int idConto, int? idSottoConto, DateTime dataVersamento, string numeroAssegno, string causale, string nota, int? idMovimentoBancario, List<RataPersonaDTO> rate, LogTransazione logTransazione, TipoVersamentoDopoChiusuraEnum? tipoVersamentoDopoChiusura, TipoVersamentoPrimaAperturaEnum? tipoVersamentoPrimaApertura)
        {
            var message = string.Empty;

            TestataMovimentoContabile testata = null;
            MovimentoContabile movimentoPatrimoniale = null;
            var result = new List<ResultVersamento>();
            try
            {
                var esercizio = _daoFactory.GetEsercizioDao().GetById(idEsercizio, false);

                // ------------------------------------------------------
                //  Trovo l'esercizio contabile, se  non è esiste oppure se è chiuso
                //  interrompo l'operazione di versamento
                // ------------------------------------------------------
                var esercizioContabile = esercizio;
                if (esercizio.Gestione == GestioneEsercizioEnum.Ordinario)
                {
                    esercizioContabile = _daoFactory.GetEsercizioDao().GetEsercizioCompetenza(esercizio.CondominioRiferimento, dataVersamento);
                    if (esercizioContabile == null || esercizioContabile.Stato == StatoEsercizioEnum.Chiuso)
                    {
                        var messageEsercizio = esercizioContabile == null ? "l'esercizio di competenza non è ancora stato aperto" : $"l'esercizio '{esercizioContabile.DisplayName}' è chiuso";
                        return new List<ResultVersamento> { new ResultVersamento(null, null, null, null, null, $"Non è possibile registrare il versamento in data {dataVersamento.ToShortDateString()} perchè {messageEsercizio}.")};
                    }
                }

                // La richiesta del versamento può avvenire solo se il versamento è successivo alla data di chiusura dell'esercizio
                if (dataVersamento <= esercizio.DataChiusura.GetValueOrDefault())
                    tipoVersamentoDopoChiusura = null;

                var listaRate = rate.Where(item => item.Selezionabile && item.ImportoVersamento > 0)
                        .GroupBy(item => item.IdSoggetto)
                        .Select(rateSoggetto => rateSoggetto);

                foreach (var ratePerSoggetto in listaRate)
                {
                    var importo = ratePerSoggetto.Sum(item => item.ImportoVersamento.GetValueOrDefault());

                    // ========================================================
                    //  Creo il versamento e i relativi movimenti economici
                    // ========================================================
                    var progressivo = _protocolloService.GetProgressivo(TipoProtocollo.VersamentoCondomino, esercizio);
                    if (progressivo.Progressivo == null)
                    {
                        var resultVersamenti = new List<ResultVersamento> {new ResultVersamento(null, null, null, null, null, progressivo.Message)};
                        return resultVersamenti;
                    }

                    var protocollo = progressivo.Progressivo;
                    var versamento = new VersamentoSoggetto(esercizio, _daoFactory.GetSoggettoCondominioDao().GetById(ratePerSoggetto.Key, false), _daoFactory.GetContoDao().GetById(idConto, false), dataVersamento, importo, protocollo.Value, TipoVersamentoSoggettoEnum.Manuale, logTransazione, tipoVersamentoDopoChiusura, tipoVersamentoPrimaApertura) {NumeroAssegno = numeroAssegno, Causale = causale, Nota = nota};

                    if (idSottoConto != null)
                    {
                        if (idSottoConto > 0)
                            versamento.SottoContoPatrimoniale = _daoFactory.GetSottoContoDao().GetById(idSottoConto.Value, false);
                        else if (idSottoConto < 0)
                            versamento.ContoBancario = _daoFactory.GetDatiBancariCondominiDao().GetById(idSottoConto.Value * -1, false);
                    }

                    var rateDaEmettere = new List<RataSoggetto>();
                    if (rate != null)
                    {
                        foreach (var rataDto in ratePerSoggetto)
                        {
                            if (rataDto.ID > 0)
                            {
                                var rata = _daoFactory.GetRataSoggettoDao().GetById(rataDto.ID, false);
                                var versamentoRata = new VersamentiRate(versamento, rata, rataDto.ImportoVersamento.GetValueOrDefault());

                                // ----------------------------------------------------
                                // Se la rata non è stata emessa devo creare movimento
                                // ----------------------------------------------------
                                if (rata.Stato == StatoRataEnum.Inserita)
                                    rateDaEmettere.Add(rata);
                                rata.SetStato();
                            }
                        }
                    }

                    var testateContabili = _movimentoContabileService.SetMovimentiVersamentoCondomino(testata, movimentoPatrimoniale, esercizioContabile, versamento, versamento.Data, null, null, idMovimentoBancario, null, logTransazione);
                    testata = testateContabili[0];
                    setRataVersamentoDopoChiusura(versamento, esercizioContabile, logTransazione);

                    // Salvataggi
                    _daoFactory.GetVersamentoSoggettoDao().SaveOrUpdate(versamento);

                    var testateMovimenti = testata.Movimenti.Where(item => item.ContoRiferimento.ID == versamento.ContoPatrimoniale.ID).ToList();
                    if (testateMovimenti.Count > 1)
                        _log.ErrorFormat("ATTENZIONE: Trovato più di una testata di movimento - {0} - versamento:{1} - conto:{1} - testate:{2}", Utility.GetMethodDescription(), versamento.ID, versamento.ContoPatrimoniale.ID, testateMovimenti.Aggregate(string.Empty, (current, movimentoContabile) => current + movimentoContabile.ID));

                    movimentoPatrimoniale = testateMovimenti.FirstOrDefault();
                    int? idMovimentoPatrimoniale = null;
                    if (movimentoPatrimoniale != null)
                        idMovimentoPatrimoniale = movimentoPatrimoniale.ID;

                    result.Add(new ResultVersamento(setVersamentoRicevutaDTO(versamento), versamento.ID, protocollo, idMovimentoPatrimoniale, getDescrizioneVersamentoConfermato(versamento), message));
                }
            }
            catch (Exception ex)
            {
                _log.ErrorFormat("Errore inaspettato nel metodo - {0} - esercizio:{1} - conto:{2} - sottoconto:{3} - dataVersamento:{4} - numeroAssegno:{5} - causale:{6}", ex, Utility.GetMethodDescription(), idEsercizio, idConto, idSottoConto.GetValueOrDefault(), dataVersamento, numeroAssegno, causale);
                throw;
            }

            return result;
        }
        public ResultVersamento InsertVersamentoCondomino(int idEsercizio, int idSoggetto, int idConto, int? idSottoConto, DateTime dataVersamento, decimal importo, string numeroAssegno, string causale, string nota, int? idMovimentoBancario, List<RataSoggettoVersataDTO> rate, LogTransazione logTransazione, TipoVersamentoDopoChiusuraEnum? tipoVersamentoDopoChiusura = null, TipoVersamentoPrimaAperturaEnum? tipoVersamentoPrimaApertura = null)
        {
            int? protocollo = null;
            int? idMovimentoContabile = null;
            int? idVersamento = null;
            var message = string.Empty;
            var descrizione = string.Empty;
            VersamentoSoggetto versamento = null;

            try
            {
                var contoPatrimoniale = _daoFactory.GetContoDao().Find(idConto, false);
                if(contoPatrimoniale == null)
                    return new ResultVersamento(null, null, null, null, null, "Non è stato trovato il conto patrimoniale (banca/cassa) selezionato.");

                // ========================================================
                //  Creo il versamento e i relativi movimenti economici
                // ========================================================
                var esercizio = _daoFactory.GetEsercizioDao().GetById(idEsercizio, false);

                var soggetto = _daoFactory.GetSoggettoCondominioDao().Find(idSoggetto, false);
                if(soggetto == null)
                    return new ResultVersamento(null, null, null, null, null, "Il condomino non è più presente in archivio");

                // ------------------------------------------------------
                //  Trovo l'esercizio contabile, se  non è esiste oppure se è chiuso
                //  interrompo l'operazione di versamento
                // ------------------------------------------------------
                var esercizioContabile = esercizio;
                if (esercizio.Gestione == GestioneEsercizioEnum.Ordinario)
                {
                    esercizioContabile = _daoFactory.GetEsercizioDao().GetEsercizioCompetenza(esercizio.CondominioRiferimento, dataVersamento);
                    if (esercizioContabile == null || esercizioContabile.Stato == StatoEsercizioEnum.Chiuso)
                    {
                        var messageEsercizio = esercizioContabile == null ? "l'esercizio di competenza non è ancora stato aperto" : $"l'esercizio '{esercizioContabile.DisplayName}' è chiuso";
                        return new ResultVersamento(null, null, null, null, null, $"Non è possibile registrare il versamento in data {dataVersamento.ToShortDateString()} perchè {messageEsercizio}.");
                    }
                }
                
                var progressivo = _protocolloService.GetProgressivo(TipoProtocollo.VersamentoCondomino, esercizio);

                // La richiesta del versamento può avvenire solo se il versamento è successivo alla data di chiusura dell'esercizio
                if (dataVersamento <= esercizio.DataChiusura.GetValueOrDefault())
                    tipoVersamentoDopoChiusura = null;

                if (progressivo.Progressivo == null)
                    message = progressivo.Message + string.Empty;
                else
                {
                    protocollo = progressivo.Progressivo;

                    versamento = new VersamentoSoggetto(esercizio, soggetto, contoPatrimoniale, dataVersamento, importo, protocollo.Value, TipoVersamentoSoggettoEnum.Manuale, logTransazione, tipoVersamentoDopoChiusura, tipoVersamentoPrimaApertura) {NumeroAssegno = numeroAssegno, Causale = causale, Nota = nota};

                    if (idSottoConto != null)
                    {
                        if (idSottoConto > 0)
                            versamento.SottoContoPatrimoniale = _daoFactory.GetSottoContoDao().GetById(idSottoConto.Value, false);
                        else if (idSottoConto < 0)
                            versamento.ContoBancario = _daoFactory.GetDatiBancariCondominiDao().GetById(idSottoConto.Value * -1, false);
                    }

                    var testateContabili = _movimentoContabileService.SetMovimentiVersamentoCondomino(null, null, esercizioContabile, versamento, versamento.Data, null, null, idMovimentoBancario, null, logTransazione);
                    var versamentiRate = setRataVersamentoDopoChiusura(versamento, esercizioContabile, logTransazione);

                    if (rate != null)
                    {
                        foreach (var rataDto in rate)
                        {
                            if (rataDto.IdRata > 0)
                            {
                                var rata = _daoFactory.GetRataSoggettoDao().Find(rataDto.IdRata, false);
                                if(rata != null)
                                {
                                    var versamentoRata = new VersamentiRate(versamento, rata, rataDto.ImportoVersamento);
                                    rata.SetStato();
                                }
                            }
                        }
                    }

                    _daoFactory.GetVersamentoSoggettoDao().SaveOrUpdate(versamento);

                    idVersamento = versamento.ID;
                    idMovimentoContabile = versamento.MovimentoContabilePatrimoniale.ID;
                    descrizione = getDescrizioneVersamentoConfermato(versamento);
                }
            }
            catch (Exception ex)
            {
                _log.ErrorFormat("Errore inaspettato nel metodo: {0} - esercizio:{1} - conto:{1} - sottoconto:{2} - soggetto:{3} - dataVersamento:{4:d} - importo:{5} - numeroAssegno:{6} - causale:{7}", ex, Utility.GetMethodDescription(), idEsercizio, idConto, idSottoConto.GetValueOrDefault(), idSoggetto, dataVersamento, importo, numeroAssegno, causale);
                throw;
            }

            return new ResultVersamento(setVersamentoRicevutaDTO(versamento), idVersamento, protocollo, idMovimentoContabile, descrizione, message);
        }
        private VersamentiRate setRataVersamentoDopoChiusura(VersamentoSoggetto versamento, Esercizio esercizio, LogTransazione logTransazione)
        {
            VersamentiRate rataVersamento = null;
            LogTransazione logTransazioneRata = null;
            if (esercizio.Gestione == GestioneEsercizioEnum.Ordinario && esercizio.Stato == StatoEsercizioEnum.Aperto && versamento.TipoVersamentoDopoChiusura == TipoVersamentoDopoChiusuraEnum.RicEsSucc)
            {
                var pianoRatealeDettaglio = _daoFactory.GetPianoRatealeDettaglioDao().GetRataVersamentoDopoChiusura(esercizio);
                if (pianoRatealeDettaglio == null)
                {
                    var pianoRateale = _daoFactory.GetPianoRatealeDao().GetByEsercizio(esercizio);
                    LogTransazione logTransazioneDettaglio = null;
                    if (pianoRateale == null)
                        pianoRateale = new PianoRateale(esercizio, TipoAccorpamentoRateEnum.Nessuno, logTransazione);
                    else
                        logTransazioneDettaglio = logTransazione;
                    pianoRatealeDettaglio = new PianoRatealeDettaglio(pianoRateale, esercizio.DataApertura, null, 0, false, logTransazioneDettaglio)
                    {
                        Descrizione = "Versamenti eseguiti dopo la chiusura",
                        IsAcconto = true,
                        VersamentiDopoChiusura = true
                    };
                }
                else
                    logTransazioneRata = logTransazione;

                var rataSoggetto = new RataSoggetto(pianoRatealeDettaglio, versamento.Soggetto, versamento.Data, versamento.Importo, logTransazioneRata);
                rataVersamento = new VersamentiRate(versamento, rataSoggetto, versamento.Importo);
                rataSoggetto.SetStato();
            }

            return rataVersamento;
        }
        public string AssociaVersamentiSenzaRata(int idEsercizio)
        {
            var versamenti = GetVersamentiSenzaRata(idEsercizio, null);

            // ==================================================================
            // Cerco di associare i versamenti alle rate
            // ==================================================================
            foreach (var versamento in versamenti)
            {
                var importoDaAssociare = versamento.GetImportoDaAssociareRata();
                var rate = _daoFactory.GetRataSoggettoDao().GetByEsercizioSoggetto(idEsercizio, versamento.Soggetto.ID);
                foreach (var rata in rate)
                {
                    var residuo = rata.Importo - rata.GetImportoVersato(null);
                    if (residuo > 0)
                    {
                        var importoDaRipartire = residuo;
                        if (importoDaAssociare < importoDaRipartire)
                            importoDaRipartire = importoDaAssociare;

                        var versamentoRata = new VersamentiRate(versamento, rata, importoDaRipartire);
                        _daoFactory.GetVersamentiRateDao().SaveOrUpdate(versamentoRata);

                        rata.SetStato();
                        importoDaAssociare -= importoDaRipartire;
                    }
                    if (importoDaAssociare <= 0)
                    {
                        versamento.Causale = null;
                        break;
                    }
                }
            }

            // ==================================================================
            // Verifico che tutti i versamenti siano associati
            // ==================================================================
            var message = new StringBuilder();
            foreach (var versamento in versamenti)
            {
                var importoDaAssociare = versamento.GetImportoDaAssociareRata();
                if (importoDaAssociare > 0)
                {
                    message.AppendFormat("Per {0} esiste un versamento di {1:c} di cui {2:c} non possono essere associati a nessuna rata", versamento.Soggetto.DisplayName, versamento.Importo, importoDaAssociare);
                    message.AppendLine();
                }
            }

            return message.ToString();
        }
        public ResultVersamento InsertVersamentoCondomino(Esercizio esercizio, RataSoggetto rata, DatiBancariCondomini banca, Conto contoBancario, CausaleContabile causaleVersamento, Conto contoVersamentoCondomini, DateTime dataVersamento, FileCBI file, LogTransazione logTransazione, TipoVersamentoDopoChiusuraEnum? tipoVersamentoDopoChiusura = null, TipoVersamentoPrimaAperturaEnum? tipoVersamentoPrimaApertura = null)
        {
            int? protocollo = null;
            int? idMovimentoContabile = null;
            var message = string.Empty;
            var descrizione = string.Empty;
            VersamentoSoggetto versamento = null;

            // La richiesta del versamento può avvenire solo se il versamento è successivo alla data di chiusura dell'esercizio
            if (dataVersamento <= esercizio.DataChiusura.GetValueOrDefault())
                tipoVersamentoDopoChiusura = null;

            // La richiesta del versamento prima dell'apertura può avvenire solo se il versamento è precedente alla data di apertura dell'esercizio
            if (dataVersamento >= esercizio.DataApertura.GetValueOrDefault())
                tipoVersamentoPrimaApertura = null;
            try
            {
                // ----------------------------------------------------
                //  Creo Versamento
                // ----------------------------------------------------
                if (string.IsNullOrEmpty(message))
                {
                    var progressivo = _protocolloService.GetProgressivo(TipoProtocollo.VersamentoCondomino, esercizio);
                    if (progressivo.Progressivo != null)
                    {
                        protocollo = progressivo.Progressivo;

                        // ------------------------------------------------------
                        //  Trovo l'esercizio contabile, se non esiste oppure se è chiuso
                        //  interrompo l'operazione di versamento
                        // ------------------------------------------------------
                        var esercizioContabile = esercizio;
                        if (esercizioContabile.Gestione == GestioneEsercizioEnum.Ordinario || (esercizioContabile.Gestione == GestioneEsercizioEnum.Straordinario && (esercizioContabile.Stato == StatoEsercizioEnum.Chiuso || esercizioContabile.DataChiusura.GetValueOrDefault() < dataVersamento)))
                        {
                            esercizioContabile = _daoFactory.GetEsercizioDao().GetEsercizioCompetenza(esercizio.CondominioRiferimento, dataVersamento);
                            if (esercizioContabile == null || esercizioContabile.Stato == StatoEsercizioEnum.Chiuso)
                            {
                                var messageEsercizio = esercizioContabile == null ? "l'esercizio di competenza non è ancora stato aperto" : $"l'esercizio '{esercizioContabile.DisplayName}' è chiuso";
                                return new ResultVersamento(null, null, null, null, null, $"Non è possibile registrare il versamento in data {dataVersamento.ToShortDateString()} perchè {messageEsercizio}.");
                            }
                        }

                        try
                        {
                            versamento = new VersamentoSoggetto(esercizio, rata.Soggetto, contoBancario, dataVersamento, rata.Importo, file, banca, logTransazione, tipoVersamentoDopoChiusura, tipoVersamentoPrimaApertura);
                            _movimentoContabileService.SetMovimentiVersamentoCondomino(null, null, esercizioContabile, versamento, dataVersamento, causaleVersamento, contoVersamentoCondomini, null, file, logTransazione);
                        }
                        catch (InvalidDataException exception)
                        {
                            _log.ErrorFormat("Errore inaspettato nella registrazione del versamento del condomino - {0} - esercizio:{1} - rata:{2}", Utility.GetMethodDescription(), esercizioContabile.ID, rata.ID);
                            if (versamento != null)
                            {
                                if (logTransazione != null)
                                {
                                    logTransazione.Versamenti.Remove(versamento);
                                    versamento.LogTransazione = null;
                                }

                                if (file != null)
                                {
                                    file.Versamenti.Remove(versamento);
                                    versamento.File = null;
                                }

                                esercizio.VersamentiCondomini.Remove(versamento);
                                versamento.Esercizio = null;

                                rata.Soggetto.Versamenti.Remove(versamento);
                                versamento.Soggetto = null;

                                versamento = null;
                            }
                            return new ResultVersamento(null, null, null, null, null, $"Non è possibile registrare il versamento in data {dataVersamento.ToShortDateString()} perchè {exception.Message}.");
                        }
                        
                        setRataVersamentoDopoChiusura(versamento, esercizioContabile, logTransazione);

                        // ---------------------------------------------------------------------
                        // Se la rata non è ancora stata pagata associo il versamento alla rata
                        // ---------------------------------------------------------------------
                        // se la rata è già stata pagata, oppure se l'esercizio di riferimento è chiuso viene registrata come rata fuori piano rateale
                        if (rata.Stato != StatoRataEnum.Pagata && rata.Stato != StatoRataEnum.ParzialmentePagata && rata.Esercizio.Stato == StatoEsercizioEnum.Aperto )
                        {
                            var versamentoRata = new VersamentiRate(versamento, rata, rata.Importo);
                            rata.SetStato();
                        }
                        else
                        {
                            versamento.Causale = $"Versamento fuori piano rateale perchè la rata si trova in stato di {rata.Stato}";
                        }

                        idMovimentoContabile = versamento.MovimentoContabilePatrimoniale.ID;
                        descrizione = getDescrizioneVersamentoConfermato(versamento);
                    }
                    else
                        message += progressivo.Message + Environment.NewLine;
                }
            }
            catch (Exception ex)
            {
                _log.ErrorFormat("Errore inaspettato nel metodo: {0} - rata:{1} - dataVersamento:{2}", ex, Utility.GetMethodDescription(), rata.ID, dataVersamento);
                throw;
            }

            return new ResultVersamento(setVersamentoRicevutaDTO(versamento), null, protocollo, idMovimentoContabile, descrizione, message);
        }