private void roudAllValues(ref OverdraftTotalsResult overdraftTotalsResult, RoundUtil roundUtil)
 {
     overdraftTotalsResult.FixAmount = overdraftTotalsResult.FixAmount.ToCustomRound(roundUtil);
     overdraftTotalsResult.Total     = overdraftTotalsResult.Total.ToCustomRound(roundUtil);
     overdraftTotalsResult.TotalDeductionPayments    = overdraftTotalsResult.TotalDeductionPayments.ToCustomRound(roundUtil);
     overdraftTotalsResult.TotalDeductionPaymentsISR = overdraftTotalsResult.TotalDeductionPaymentsISR.ToCustomRound(roundUtil);
     overdraftTotalsResult.TotalExempt                      = overdraftTotalsResult.TotalExempt.ToCustomRound(roundUtil);
     overdraftTotalsResult.TotalLiabilityPayments           = overdraftTotalsResult.TotalLiabilityPayments.ToCustomRound(roundUtil);
     overdraftTotalsResult.TotalOtherDeductions             = overdraftTotalsResult.TotalOtherDeductions.ToCustomRound(roundUtil);
     overdraftTotalsResult.TotalOtherPayments               = overdraftTotalsResult.TotalOtherPayments.ToCustomRound(roundUtil);
     overdraftTotalsResult.TotalRetirementPensionWithdrawal = overdraftTotalsResult.TotalRetirementPensionWithdrawal.ToCustomRound(roundUtil);
     overdraftTotalsResult.TotalSalaryPayments              = overdraftTotalsResult.TotalSalaryPayments.ToCustomRound(roundUtil);
     overdraftTotalsResult.TotalSalaryTotals                = overdraftTotalsResult.TotalSalaryTotals.ToCustomRound(roundUtil);
     overdraftTotalsResult.TotalSeparationCompensation      = overdraftTotalsResult.TotalSeparationCompensation.ToCustomRound(roundUtil);
     overdraftTotalsResult.TotalTaxed = overdraftTotalsResult.TotalTaxed.ToCustomRound(roundUtil);
 }
        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);
        }
        public OverdraftTotalsResult GetTotals(Overdraft overdraft, RoundUtil roundUtil)
        {
            var overdraftTotalsResult = new OverdraftTotalsResult();

            var concepts = overdraft.OverdraftDetails
                           .Where(p =>
                                  p.ConceptPayment.Print && !p.ConceptPayment.Kind)
                           .Select(p => p.ConceptPayment);

            if (overdraft.OverdraftDetails.Any())
            {
                //TotalSalaryPayments By Concepts
                var salaryOverdraftDetails = overdraft.OverdraftDetails
                                             .Where(p =>
                                                    p.ConceptPayment.ConceptType == ConceptType.SalaryPayment);

                var deductionOverdraftDetails = overdraft.OverdraftDetails
                                                .Where(p =>
                                                       p.ConceptPayment.ConceptType == ConceptType.DeductionPayment &&
                                                       p.ConceptPayment.Print &&
                                                       !p.ConceptPayment.Kind);

                //WorkingDays // Días pagados / laborados
                overdraftTotalsResult.WorkingDays = salaryOverdraftDetails
                                                    .Where(p =>
                                                           p.ConceptPayment.SATGroupCode == "P-001" ||
                                                           p.ConceptPayment.SATGroupCode == "P-046"
                                                           )
                                                    .Sum(p => p.Value);

                //TotalGravado (que no sea indemnización / separación
                overdraftTotalsResult.TotalTaxed = salaryOverdraftDetails
                                                   .Sum(p => roundUtil.RoundValue(p.Taxed));

                //TotalGravado Settlement
                overdraftTotalsResult.TotalTaxedSettlement = salaryOverdraftDetails
                                                             .Where(p => p.ConceptPayment.SATGroupCode == "P-022" ||
                                                                    p.ConceptPayment.SATGroupCode == "P-023" ||
                                                                    p.ConceptPayment.SATGroupCode == "P-025")
                                                             .Sum(p => roundUtil.RoundValue(p.Taxed));

                //ajuste al neto
                overdraftTotalsResult.FixAmount = GetOtherPayments(overdraft)
                                                  .Sum(p => p.Amount);

                //TotalExento (que no sea indemnización / separación
                overdraftTotalsResult.TotalExempt = salaryOverdraftDetails
                                                    .Sum(p => roundUtil.RoundValue(p.Exempt));

                //Total Exento Settlement
                overdraftTotalsResult.TotalExemptSettlement = salaryOverdraftDetails
                                                              .Where(p => p.ConceptPayment.SATGroupCode == "P-022" ||
                                                                     p.ConceptPayment.SATGroupCode == "P-023" ||
                                                                     p.ConceptPayment.SATGroupCode == "P-025")
                                                              .Sum(p => roundUtil.RoundValue(p.Exempt));

                //TotalDeductionPayments -Excepts or payments
                overdraftTotalsResult.TotalDeductionPayments = deductionOverdraftDetails
                                                               .Where(p =>
                                                                      !p.ConceptPayment.SATGroupCode.Contains("OP") && p.ConceptPayment.Code != 99)
                                                               .Sum(p => roundUtil.RoundValue(p.Amount));

                //Ajuste al neto
                overdraftTotalsResult.AdjustmentAmount = deductionOverdraftDetails
                                                         .Where(p =>
                                                                p.ConceptPayment.Code == 99 &&
                                                                p.ConceptPayment.ConceptType == ConceptType.DeductionPayment)
                                                         .Sum(p => p.Amount);

                //TotalLiabilityPayments
                var overdraftLiabilities = overdraft.OverdraftDetails.Where(p => p.ConceptPayment.ConceptType == ConceptType.LiabilityPayment);
                overdraftTotalsResult.TotalLiabilityPayments = overdraftLiabilities.Sum(p => roundUtil.RoundValue(p.Amount));

                //Total de impuestos retenidos "002" (ISR)
                var conceptDeductionPaymentISR = concepts
                                                 .Where(p =>
                                                        p.ConceptType == ConceptType.DeductionPayment &&
                                                        p.Code != 99 &&
                                                        p.SATGroupCode == "D-002");
                var conceptDeductionPaymentISRIds = conceptDeductionPaymentISR.Select(p => p.ID);

                if (conceptDeductionPaymentISRIds.Any())
                {
                    overdraftTotalsResult.TotalDeductionPaymentsISR = overdraft.OverdraftDetails.Where(p =>
                                                                                                       conceptDeductionPaymentISRIds.Contains(p.ConceptPaymentID)).Sum(p => roundUtil.RoundValue(p.Amount));
                }

                //Total de Indemnización
                overdraftTotalsResult.TotalSeparationCompensation = salaryOverdraftDetails
                                                                    .Where(p => p.ConceptPayment.SATGroupCode == "P-022" ||
                                                                           p.ConceptPayment.SATGroupCode == "P-023" ||
                                                                           p.ConceptPayment.SATGroupCode == "P-025")
                                                                    .Sum(p => roundUtil.RoundValue(p.Amount));

                overdraftTotalsResult.TotalSalaryPayments =
                    roundUtil.RoundValue(overdraftTotalsResult.TotalExempt)
                    + roundUtil.RoundValue(overdraftTotalsResult.TotalTaxed)
                    + roundUtil.RoundValue(overdraftTotalsResult.TotalSeparationCompensation);

                //TotalSalaryTotals
                //Total de Sueldos
                overdraftTotalsResult.TotalSalaryTotals = salaryOverdraftDetails
                                                          .Where(p => p.ConceptPayment.SATGroupCode != "P-022" &&
                                                                 p.ConceptPayment.SATGroupCode != "P-023" &&
                                                                 p.ConceptPayment.SATGroupCode != "P-025" &&
                                                                 p.ConceptPayment.SATGroupCode != "P-039" &&
                                                                 p.ConceptPayment.SATGroupCode != "P-044"
                                                                 )
                                                          .Sum(p => roundUtil.RoundValue(p.Amount));

                //TotalOtherDeductions
                overdraftTotalsResult.TotalOtherDeductions = roundUtil.RoundValue(overdraftTotalsResult.TotalDeductionPayments)
                                                             - roundUtil.RoundValue(overdraftTotalsResult.TotalDeductionPaymentsISR);

                //Total OtherPayments
                overdraftTotalsResult.TotalOtherPayments = GetOtherPayments(overdraft).DistinctBy(p => p.ConceptPaymentID)
                                                           .Sum(p => p.Amount);

                //Total
                overdraftTotalsResult.Total = roundUtil.RoundValue(overdraftTotalsResult.TotalSalaryPayments) -
                                              roundUtil.RoundValue(overdraftTotalsResult.TotalDeductionPayments);
            }

            roudAllValues(ref overdraftTotalsResult, roundUtil);

            return(overdraftTotalsResult);
        }