private async Task <PayrollStampingResult> payrollStampingCoreAsync(PayrollStampingParams payrollStampingParams,
                                                                            List <Overdraft> historicOverdraftsToStamp,
                                                                            List <Incident> incidents,
                                                                            List <Inhability> inhabilities,
                                                                            List <EmployerFiscalInformation> employerFiscalInformations,
                                                                            List <PayrollCompanyConfiguration> payrollConfigurations)
        {
            var payrollStampingResult = new PayrollStampingResult();

            List <string> zipCodesToFind =
                historicOverdraftsToStamp.Select(p => p.HistoricEmployee.EmployerRegistrationZipCode).ToList();

            zipCodesToFind.AddRange(payrollConfigurations.Select(p => p.Address?.ZipCode));

            //Obtener los zipCodes
            var zipCodeMiddlewareManager = new MiddlewareManager <catCFDI_CodigoPostal>(
                new BaseRecordManager <catCFDI_CodigoPostal>(),
                new catCFDI_CodigoPostalValidator());
            var zipCodes = await zipCodeMiddlewareManager.FindByExpressionAsync(p =>
                                                                                zipCodesToFind.Contains(p.c_CodigoPostal)
                                                                                , payrollStampingParams.IdentityWorkID);

            //Round for currency
            var roundUtil = new RoundUtil(payrollStampingParams.Currency.ToString());

            //Zip Code manager
            var zipCodeManager = new ZipCodeManager(zipCodes);

            //Blob Storage Util
            var blobStorageUtil = new BlobStorageUtil(payrollStampingParams.InstanceID);
            await blobStorageUtil.InitializeAsync();

            ISendMailProvider sendMailProvider = FactoryMailProvider.CreateInstance(SendMailProvider.SendGrid);

            var tasks = new List <Task <List <PayrollStampingResultDetail> > >();

            foreach (var overdraftToStamp in historicOverdraftsToStamp)
            {
                tasks.Add(doWorkAsync(overdraftToStamp, roundUtil, zipCodeManager,
                                      payrollStampingParams, blobStorageUtil, sendMailProvider, incidents,
                                      inhabilities, employerFiscalInformations, payrollConfigurations));
            }

            ConcurrentBag <PayrollStampingResultDetail> payrollStampingDetails = new ConcurrentBag <PayrollStampingResultDetail>();

            foreach (var task in await Task.WhenAll(tasks))
            {
                foreach (var insideTask in task)
                {
                    payrollStampingDetails.Add(insideTask);
                }
            }
            payrollStampingResult.PayrollStampingResultDetails = payrollStampingDetails.ToList();

            //Update DB indicate that Overdraft was stamped correctly / PeriodDetail
            await saveOverdraftStampedAsync(payrollStampingParams, payrollStampingResult);

            //Errors preparation
            if (payrollStampingResult.PayrollStampingResultDetails.Any(p => p.PayrollStampingResultStatus == PayrollStampingResultStatus.Fail))
            {
                var errorMessages = payrollStampingResult.PayrollStampingResultDetails.Select(p => p.Message);
                throw new CotorraException(109, "109", string.Join("\n", errorMessages), null);
            }

            return(payrollStampingResult);
        }
        private async Task <List <PayrollStampingResultDetail> > doWorkAsync(Overdraft overdraftToStamp, RoundUtil roundUtil,
                                                                             ZipCodeManager zipCodeManager, PayrollStampingParams payrollStampingParams,
                                                                             BlobStorageUtil blobStorageUtil,
                                                                             ISendMailProvider sendMailProvider,
                                                                             List <Incident> incidents,
                                                                             List <Inhability> inhabilities,
                                                                             List <EmployerFiscalInformation> employerFiscalInformations,
                                                                             List <PayrollCompanyConfiguration> payrollConfigurations)
        {
            var payrollStampingResultDetail  = new PayrollStampingResultDetail();
            var payrollStampingResultDetails = new List <PayrollStampingResultDetail>();

            //Obtiene los xmls de los comprobantes segun la version de CFDI especificada
            IFiscalStamping fiscalStamping = FiscalStampingFactory.CreateInstance(payrollStampingParams.FiscalStampingVersion);

            //1. Get totals
            var overdraftResults = new OverdraftManager().GetTotals(overdraftToStamp, roundUtil);

            //1.1 Datetime for zipCode
            (var zipcode, var datetimeFromZipCode) = await zipCodeManager.GetZipCode(overdraftToStamp, payrollConfigurations.FirstOrDefault());

            //2. Get XML - Creates comprobante
            var payrolllStampingDetail = payrollStampingParams.Detail
                                         .FirstOrDefault(detail => detail.OverdraftID == overdraftToStamp.ID);
            ICFDINomProvider cfdi = null;

            try
            {
                cfdi = fiscalStamping.CreateComprobante(new CreateComprobanteParams()
                {
                    PayrollStampingDetail       = payrolllStampingDetail,
                    PayrollStampingParams       = payrollStampingParams,
                    Overdraft                   = overdraftToStamp,
                    OverdraftResults            = overdraftResults,
                    PayrollCompanyConfiguration = payrollConfigurations.FirstOrDefault(),
                    CFDIDateTimeStamp           = datetimeFromZipCode,
                    ZipCode      = zipcode,
                    RoundUtil    = roundUtil,
                    Incidents    = incidents,
                    Inhabilities = inhabilities
                });
            }
            catch (Exception ex)
            {
                //Errores en validaciónes de armado / fiscales
                payrollStampingResultDetail.Message = ex.Message;
                payrollStampingResultDetail.PayrollStampingResultStatus = PayrollStampingResultStatus.Fail;
                payrollStampingResultDetail.HistoricEmployeeID          = overdraftToStamp.HistoricEmployeeID.Value;
                payrollStampingResultDetail.EmployeeID     = overdraftToStamp.HistoricEmployee.EmployeeID;
                payrollStampingResultDetail.OverdraftID    = overdraftToStamp.ID;
                payrollStampingResultDetail.Overdraft      = overdraftToStamp;
                payrollStampingResultDetail.PeriodDetailID = overdraftToStamp.PeriodDetailID;
                payrollStampingResultDetail.PeriodDetail   = overdraftToStamp.PeriodDetail;

                payrollStampingResultDetails.Add(payrollStampingResultDetail);
                return(payrollStampingResultDetails);
            }

            //3. Sign XML
            var certificateCER = employerFiscalInformations.FirstOrDefault().CertificateCER;
            var certificateKey = employerFiscalInformations.FirstOrDefault().CertificateKEY;
            var certPassword   = employerFiscalInformations.FirstOrDefault().CertificatePwd;

            //Decrypt
            (var certificatebytesCER, var certificatebytesKEY, var certPasswordResult) = Crypto(payrollStampingParams, certificateCER, certificateKey, certPassword);

            var stampingResult = fiscalStamping.SignDocument(cfdi, certificatebytesCER, certificatebytesKEY, certPasswordResult);

            //Set the employer
            stampingResult.EmployerRFC = payrollConfigurations.FirstOrDefault().RFC;

            //4. Stamp XML
            stampingResult = await fiscalStamping.StampDocumetAsync(stampingResult);

            if (stampingResult.WithErrors)
            {
                //error en el timbrado
                var errrorMessage = $"\nPara el empleado <strong>'{overdraftToStamp.HistoricEmployee.FullName}'</strong> encontramos los siguientes errores de timbrado: '{stampingResult.Details}'";
                payrollStampingResultDetail.Message = errrorMessage;
                payrollStampingResultDetail.PayrollStampingResultStatus = PayrollStampingResultStatus.Fail;
            }
            else
            {
                //5. Return the complete XML
                stampingResult.XML = fiscalStamping.CreateXml <ICFDINomProvider>(stampingResult.CFDI, true);

                //5.5 Fill the result data
                payrollStampingResultDetail.Message = String.Empty;
                payrollStampingResultDetail.UUID    = stampingResult.UUID;
                payrollStampingResultDetail.XML     = stampingResult.XML;
                payrollStampingResultDetail.PayrollStampingResultStatus = PayrollStampingResultStatus.Success;

                //6. Fill the result data
                overdraftToStamp.UUID            = stampingResult.UUID;
                overdraftToStamp.OverdraftStatus = OverdraftStatus.Stamped;

                //Fire and forget convertion and sending email
                fireAndForgetAsync(payrollStampingParams, overdraftToStamp, payrollConfigurations,
                                   blobStorageUtil, sendMailProvider, payrollStampingResultDetail.UUID, payrollStampingResultDetail.XML);

                //Fill the result object
                payrollStampingResultDetail.HistoricEmployeeID = overdraftToStamp.HistoricEmployeeID.Value;
                payrollStampingResultDetail.EmployeeID         = overdraftToStamp.HistoricEmployee.EmployeeID;
                payrollStampingResultDetail.OverdraftID        = overdraftToStamp.ID;
                payrollStampingResultDetail.Overdraft          = overdraftToStamp;
                payrollStampingResultDetail.PeriodDetailID     = overdraftToStamp.PeriodDetailID;
                payrollStampingResultDetail.PeriodDetail       = overdraftToStamp.PeriodDetail;
            }


            payrollStampingResultDetails.Add(payrollStampingResultDetail);

            return(payrollStampingResultDetails);
        }