public SaveEmpMonthlyEnteredRecordRequestValidation()
        {
            this.CascadeMode = CascadeMode.Stop;

            RuleFor(x => x.YearMonth).NotEmpty().WithMessage(ValiationErrors.YEAR_MONTH_NULL_ERROR)
            .Must(x => YearMonth.TryParse(x)).WithMessage(ValiationErrors.YEAR_MONTH_INVALID_FORMAT_ERROR);
            RuleFor(x => x.Key).NotEmpty().WithMessage(ValiationErrors.KEY_EMPTY_ERROR);
            RuleFor(x => x.StandardWorkingDays).GreaterThanOrEqualTo(1).WithMessage(ValiationErrors.STANDARD_WORKING_DAY_MIN_ERROR)
            .LessThanOrEqualTo(23).WithMessage(ValiationErrors.STANDARD_WORKING_DAY_MAX_ERROR);
            RuleFor(x => x.Records).Must(x => !x.GroupBy(y => y.Email).Any(g => g.Count() > 1))
            .WithMessage(ValiationErrors.RECORD_DUPLICATED_ERROR);
            RuleForEach(x => x.Records).ChildRules(record =>
            {
                record.CascadeMode = CascadeMode.Stop;
                record.RuleFor(y => y.Email).EmailAddress().WithMessage(ValiationErrors.EMAIL_INVALID_ERROR);
                record.RuleFor(y => y.Fullname).NotEmpty().WithMessage(ValiationErrors.FULLNAME_EMPTY_ERROR);
                record.RuleFor(y => y.Currency).NotEmpty().WithMessage(ValiationErrors.CURRENCY_EMPTY_ERROR);
                record.RuleFor(y => y.GrossContractSalary).GreaterThanOrEqualTo(0).WithMessage(ValiationErrors.GROSS_CONTRACT_SALARY_NEGATIVE_ERROR);
                record.RuleFor(y => y.ProbationWorkingDays).GreaterThanOrEqualTo(0).WithMessage(ValiationErrors.PROBATION_WORKING_DAY_NEGATIVE_ERROR);
                record.RuleFor(y => y.ActualWorkingDays).GreaterThanOrEqualTo(0).WithMessage(ValiationErrors.ACTUAL_WORKING_DAY_NEGATIVE_ERROR);
                record.RuleFor(y => y.NonTaxableAllowance).GreaterThanOrEqualTo(0).WithMessage(ValiationErrors.NON_TAXABLE_ALLOWANCE_NEGATIVE_ERROR);
                record.RuleFor(y => y.TaxableAnnualLeave).GreaterThanOrEqualTo(0).WithMessage(ValiationErrors.TAXABLE_ANNUAL_LEAVE_NEGATIVE_ERROR);
                record.RuleFor(y => y.Taxable13MonthSalary).GreaterThanOrEqualTo(0).WithMessage(ValiationErrors.TAXABLE_13_MONTH_SALARY_NEGATIVE_ERROR);
                record.RuleFor(y => y.TaxableOthers).GreaterThanOrEqualTo(0).WithMessage(ValiationErrors.TAXABLE_OTHERS_NEGATIVE_ERROR);
                record.RuleFor(y => y.PaymentAdvance).GreaterThanOrEqualTo(0).WithMessage(ValiationErrors.PAYMENT_ADVANCE_NEGATIVE_ERROR);
                record.RuleFor(y => y.AdjustmentAddition).GreaterThanOrEqualTo(0).WithMessage(ValiationErrors.ADJUSTMENT_ADDITION_NEGATIVE_ERROR);
                record.RuleFor(y => y.AdjustmentDeduction).GreaterThanOrEqualTo(0).WithMessage(ValiationErrors.ADJUSTMENT_DEDUCTION_NEGATIVE_ERROR);
                record.RuleFor(y => y.Currency).NotEmpty().WithMessage(ValiationErrors.CURRENCY_EMPTY_ERROR);
            });
        }
        public async Task <Result <List <EmployeeProfile> > > GetAsync(string yearMonth)
        {
            try
            {
                if (YearMonth.TryParse(yearMonth, out YearMonth selectedYearMonth) is false)
                {
                    _logger.LogError(Errors.EmployeeProfile.GetProfileYearMonthInvalidError.Message);
                    return(Errors.EmployeeProfile.GetProfileYearMonthInvalidError);
                }

                //todo enhancement
                var profiles = await _dbContext.SyncEmployeeProfiles.ToListAsync();

                var filteredProfiles = profiles.Where(x => x.TerminatedDate == null ||
                                                      (x.TerminatedDate != null && new YearMonth(x.TerminatedDate.Value) >= selectedYearMonth)).ToList();

                if (filteredProfiles.Count == 0)
                {
                    _logger.LogError(Errors.EmployeeProfile.GetProfileEmptyError.Message);
                    return(Errors.EmployeeProfile.GetProfileEmptyError);
                }

                return(Result <List <EmployeeProfile> > .Ok(filteredProfiles.Select(x =>
                {
                    var dependantEndDates = string.IsNullOrEmpty(x.DependantEndDates) ? Array.Empty <DateTime>()
                                                : JsonSerializer.Deserialize <DateTime[]>(x.DependantEndDates);
                    return new EmployeeProfile(
                        email: x.Email,
                        fullname: x.FullName,
                        employeeType: x.EmployeeType,
                        position: x.Position,
                        dependantEndDates: dependantEndDates.Length == 0 ? dependantEndDates
                                                        : dependantEndDates.Where(x => x == DateTime.MinValue || new YearMonth(x) > new YearMonth(DateTime.UtcNow))
                        .ToArray(),
                        inUnion: x.InUnion,
                        terminatedDate: x.TerminatedDate,
                        isForeigner: x.IsForeigner);
                }).ToList()));
            }
            catch (SqlException ex)
            {
                _logger.LogError(ex, Errors.EmployeeProfile.GetProfileDatabaseError.Message);
                return(Errors.EmployeeProfile.GetProfileDatabaseError);
            }
        }
        public SaveSalaryConfigRequestValidation()
        {
            this.CascadeMode = CascadeMode.Stop;

            RuleFor(x => x.YearMonth).NotEmpty().WithMessage(ValiationErrors.YEAR_MONTH_NULL_ERROR)
            .Must(x => YearMonth.TryParse(x)).WithMessage(ValiationErrors.YEAR_MONTH_INVALID_FORMAT_ERROR);
            RuleFor(x => x.ProgressiveTaxRates).NotEmpty().WithMessage(ValiationErrors.PROGRESSIVE_TAX_RATE_EMPTY_ERROR)
            .Must(x => !x.GroupBy(y => y.Rate).Any(g => g.Count() > 1)).WithMessage(ValiationErrors.PROGRESSIVE_TAX_RATE_DUPLICATED_ERROR);
            RuleForEach(x => x.ProgressiveTaxRates).ChildRules(rate =>
            {
                rate.RuleFor(y => y.TaxRateLevel).IsInEnum().WithMessage(ValiationErrors.PROGRESSIVE_TAX_RATE_LEVEL_INVALID_ENUM_ERROR);
            });
            RuleForEach(x => x.ProgressiveTaxRates).ChildRules(rate =>
            {
                rate.RuleFor(y => y.LowerBound).GreaterThanOrEqualTo(0).WithMessage(ValiationErrors.LOWER_BOUND_NEGATIVE_ERROR);
            });
            RuleForEach(x => x.ProgressiveTaxRates).ChildRules(rate =>
            {
                rate.RuleFor(y => y.UpperBound).GreaterThanOrEqualTo(0).WithMessage(ValiationErrors.UPPER_BOUND_NEGATIVE_ERROR);
            });
            RuleForEach(x => x.ProgressiveTaxRates).ChildRules(rate =>
            {
                rate.RuleFor(y => y.Rate).GreaterThanOrEqualTo(0).WithMessage(ValiationErrors.PROGRESSIVE_TAX_RATE_NEGATIVE_ERROR);
            });
            RuleFor(x => x.SalarySetting).NotNull().WithMessage(ValiationErrors.SALARY_SETTING_NULL_ERROR);
            RuleFor(x => x.SalarySetting.Id).NotEmpty().WithMessage(ValiationErrors.ID_EMPTY_ERROR);
            RuleFor(x => x.SalarySetting.CommonMinimumWage).GreaterThanOrEqualTo(0).WithMessage(ValiationErrors.COMMON_MINIUM_WAGE_NEGATIVE_ERROR);
            RuleFor(x => x.SalarySetting.RegionalMinimumWage).GreaterThanOrEqualTo(0).WithMessage(ValiationErrors.REGIONAL_MINIUM_WAGE_NEGATIVE_ERROR);
            RuleFor(x => x.SalarySetting.MinimumNonWorkingDay).GreaterThanOrEqualTo(0).WithMessage(ValiationErrors.MINIUM_NON_WORKING_DAY_NEGATIVE_ERROR);
            RuleFor(x => x.SalarySetting.DefaultProbationTaxRate).GreaterThanOrEqualTo(0).WithMessage(ValiationErrors.DEFAULT_TAX_RATE_NEGATIVE_ERROR);
            RuleFor(x => x.SalarySetting.CoefficientSocialCare).GreaterThanOrEqualTo(0).WithMessage(ValiationErrors.COEFFICIENT_SOCIAL_CARE_NEGATIVE_ERROR);
            RuleFor(x => x.SalarySetting.EmployeeSocialInsuranceRate).GreaterThanOrEqualTo(0).WithMessage(ValiationErrors.EMPPLOYEE_SOCIAL_INSURANCE_RATE_NEGATIVE_ERROR);
            RuleFor(x => x.SalarySetting.EmployeeHealthCareInsuranceRate).GreaterThanOrEqualTo(0).WithMessage(ValiationErrors.EMPPLOYEE_HEALTHCARE_INSURANCE_RATE_NEGATIVE_ERROR);
            RuleFor(x => x.SalarySetting.EmployeeUnemploymentInsuranceRate).GreaterThanOrEqualTo(0).WithMessage(ValiationErrors.EMPPLOYEE_UNEMPLOYMENT_INSURANCE_RATE_NEGATIVE_ERROR);
            RuleFor(x => x.SalarySetting.EmployeeUnionFeeRate).GreaterThanOrEqualTo(0).WithMessage(ValiationErrors.EMPPLOYEE_UNION_FEE_RATE_NEGATIVE_ERROR);
            RuleFor(x => x.SalarySetting.EmployerSocialInsuranceRate).GreaterThanOrEqualTo(0).WithMessage(ValiationErrors.EMPPLOYEE_UNION_FEE_RATE_NEGATIVE_ERROR);
            RuleFor(x => x.SalarySetting.EmployerHealthCareInsuranceRate).GreaterThanOrEqualTo(0).WithMessage(ValiationErrors.EMPPLOYER_HEALTHCARE_INSURANCE_RATE_NEGATIVE_ERROR);
            RuleFor(x => x.SalarySetting.EmployerUnemploymentInsuranceRate).GreaterThanOrEqualTo(0).WithMessage(ValiationErrors.EMPPLOYER_UNEMPLOYMENT_INSURANCE_RATE_NEGATIVE_ERROR);
            RuleFor(x => x.SalarySetting.EmployerUnionFeeRate).GreaterThanOrEqualTo(0).WithMessage(ValiationErrors.EMPPLOYER_UNION_FEE_RATE_NEGATIVE_ERROR);
            RuleFor(x => x.SalarySetting.MaximumUnionFeeRate).GreaterThanOrEqualTo(0).WithMessage(ValiationErrors.EMPPLOYER_UNION_FEE_RATE_NEGATIVE_ERROR);
            RuleFor(x => x.SalarySetting.PersonalDeduction).GreaterThanOrEqualTo(0).WithMessage(ValiationErrors.EMPPLOYER_PERSONAL_DEDUCTION_NEGATIVE_ERROR);
            RuleFor(x => x.SalarySetting.DependantDeduction).GreaterThanOrEqualTo(0).WithMessage(ValiationErrors.EMPPLOYER_DEPENDANT_DEDUCTION_NEGATIVE_ERROR);
            RuleFor(x => x.SalarySetting.InsurancePaidAmount).GreaterThanOrEqualTo(0).WithMessage(ValiationErrors.EMPPLOYER_DEPENDANT_DEDUCTION_NEGATIVE_ERROR);
            RuleFor(x => x.SalarySetting.Currency).NotEmpty().WithMessage(ValiationErrors.CURRENCY_EMPTY_ERROR);
        }
Beispiel #4
0
                                    int standardWorkingDays)> > GetAsync(string yearMonth, string key)
        {
            try
            {
                if (YearMonth.TryParse(yearMonth, out YearMonth selectedYearMonth) is false)
                {
                    _logger.LogError(Errors.EmpMonthlyEnteredRecord.GetRecordYearMonthInvalidError.Message);
                    return(Errors.EmpMonthlyEnteredRecord.GetRecordYearMonthInvalidError);
                }

                if ((await _dbContext.EncryptedEmpMonthlyEnteredRecords.AnyAsync()) is false)
                {
                    return(Result <(List <EmployeeMonthlyEnteredRecord>, int standardWorkingDays)> .Ok(
                               (new List <EmployeeMonthlyEnteredRecord>(),
                                new MonthlyWorkingDay(yearMonth).CalculatedMonthlyWorkingDays())));
                }

                var existingRecord = await _dbContext.EncryptedEmpMonthlyEnteredRecords
                                     .FirstOrDefaultAsync(x => x.EncryptedYearMonth == yearMonth);

                var newYearMonth = false;
                if (existingRecord == null)
                {
                    var closestMonthRecord = await _dbContext.EncryptedEmpMonthlyEnteredRecords
                                             .OrderByDescending(x => x.CreatedDate).FirstOrDefaultAsync();

                    if (closestMonthRecord == null)
                    {
                        _logger.LogError(Errors.EmpMonthlyEnteredRecord.GetRecordNotFoundError.Message);
                        return(Errors.EmpMonthlyEnteredRecord.GetRecordNotFoundError);
                    }

                    //if user selects previous year-month, then check if the closetYearMonth is after or before selectedYearMonth
                    var closestYearMonth = new YearMonth(closestMonthRecord.EncryptedYearMonth);


                    // return empty if it is after, means that previous year-month havent's any record.
                    if (closestYearMonth > selectedYearMonth)
                    {
                        return(Result <(List <EmployeeMonthlyEnteredRecord>, int standardWorkingDays)> .Ok(
                                   (new List <EmployeeMonthlyEnteredRecord>(),
                                    new MonthlyWorkingDay(yearMonth).CalculatedMonthlyWorkingDays())));
                    }

                    // use closest monthly salary record for current monthly salary month record
                    existingRecord = closestMonthRecord;
                    newYearMonth   = true;
                }

                var decruptedJson    = _dataProtectionProvider.CreateProtector(key).Unprotect(existingRecord.EncryptedRecord);
                var decryptedRecords = JsonSerializer.Deserialize <List <DecryptedEmpMonthlyEnteredRecord> >(
                    decruptedJson
                    );

                var newMonthlyWorkingDays = new MonthlyWorkingDay(yearMonth).CalculatedMonthlyWorkingDays();
                var result = newYearMonth switch
                {
                    true => (decryptedRecords.Select(x =>
                    {
                        var currency = new Currency(x.Currency);
                        return(new EmployeeMonthlyEnteredRecord(
                                   fullname: x.Fullname,
                                   email: x.Email,
                                   grossContractSalary: new GrossContractedSalary(new Money(value: x.GrossContractSalary, currency)),
                                   probationGrossContractSalary: new GrossContractedSalary(new Money(value: x.ProbationGrossContractSalary, currency)),
                                   actualWorkingDays: newMonthlyWorkingDays,
                                   probationWorkingDays: 0,
                                   taxableAnnualLeave: new TaxableAllowance(name: x.TaxableAnnualLeave.Name,
                                                                            amount: new Money(0, currency)),
                                   taxable13MonthSalary: new TaxableAllowance(name: x.Taxable13MonthSalary.Name,
                                                                              amount: new Money(0, currency)),
                                   taxableOthers: new TaxableAllowance(name: x.TaxableOthers.Name,
                                                                       amount: new Money(0, currency)),
                                   nonTaxableAllowances: x.NonTaxableAllowances.Select(y =>
                                                                                       new NonTaxableAllowance(amount: new Money(0, currency), name: y.Name)).ToArray(),
                                   paymentAdvance: new PaymentAdvance(new Money(0, currency)),
                                   adjustmentAdditions: x.AdjustmentAdditions.Select(y => new AdjustmentAddition(new Money(0, currency))).ToArray(),
                                   adjustmentDeductions: x.AdjustmentDeductions.Select(y => new AdjustmentDeduction(new Money(0, currency))).ToArray()
                                   ));
                    }).ToList(), newMonthlyWorkingDays),
                    _ => (decryptedRecords.Select(x =>
                    {
                        var currency = new Currency(x.Currency);
                        return(new EmployeeMonthlyEnteredRecord(
                                   fullname: x.Fullname,
                                   email: x.Email,
                                   grossContractSalary: new GrossContractedSalary(new Money(value: x.GrossContractSalary, currency)),
                                   probationGrossContractSalary: new GrossContractedSalary(new Money(value: x.ProbationGrossContractSalary, currency)),
                                   actualWorkingDays: x.ActualWorkingDays,
                                   probationWorkingDays: x.ProbationWorkingDays,
                                   taxableAnnualLeave: new TaxableAllowance(name: x.TaxableAnnualLeave.Name,
                                                                            amount: new Money(x.TaxableAnnualLeave.Amount, currency)),
                                   taxable13MonthSalary: new TaxableAllowance(name: x.Taxable13MonthSalary.Name,
                                                                              amount: new Money(x.Taxable13MonthSalary.Amount, currency)),
                                   taxableOthers: new TaxableAllowance(name: x.TaxableOthers.Name,
                                                                       amount: new Money(x.TaxableOthers.Amount, currency)),
                                   nonTaxableAllowances: x.NonTaxableAllowances.Select(y =>
                                                                                       new NonTaxableAllowance(amount: new Money(y.Amount, currency), name: y.Name)).ToArray(),
                                   paymentAdvance: new PaymentAdvance(new Money(x.PaymentAdvance, currency)),
                                   adjustmentAdditions: x.AdjustmentAdditions.Select(y => new AdjustmentAddition(new Money(y, currency))).ToArray(),
                                   adjustmentDeductions: x.AdjustmentDeductions.Select(y => new AdjustmentDeduction(new Money(y, currency))).ToArray()
                                   ));
                    }).ToList(), existingRecord.StandardWorkingDays)
                };

                return(Result <(List <EmployeeMonthlyEnteredRecord>, int standardWorkingDays)> .Ok(result));
            }
            catch (CryptographicException ex)
            {
                _logger.LogError(ex, Errors.EmpMonthlyEnteredRecord.GetRecordKeyInvalidError.Message);
                return(Errors.EmpMonthlyEnteredRecord.GetRecordKeyInvalidError);
            }
            catch (SqlException ex)
            {
                _logger.LogError(ex, Errors.EmpMonthlyEnteredRecord.GetRecordDatabaseError.Message);
                return(Errors.EmpMonthlyEnteredRecord.GetRecordDatabaseError);
            }
        }